micro-models-agent 0.41.0 → 0.41.2
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/bin/mma.mjs +41 -41
- package/dist/cli/commands.js +9 -19
- package/dist/cli/completer.js +36 -37
- package/dist/cli/index.js +2 -2
- package/dist/cli/main.js +48 -23
- package/dist/cli/plugin-commands.js +36 -0
- package/dist/cli/repl-commands.js +40 -12
- package/dist/cli/repl.js +217 -87
- package/dist/cli/run-result.js +22 -0
- package/dist/cli/security-commands.js +5 -7
- package/dist/cli/setup.js +8 -26
- package/dist/config/config.js +52 -5
- package/dist/config/defaults.js +29 -5
- package/dist/config/experts.js +1 -1
- package/dist/config/index.js +3 -3
- package/dist/config/security.js +3 -10
- package/dist/core/agent-moe.js +2 -10
- package/dist/core/agent.js +273 -82
- package/dist/core/bootstrap.js +80 -13
- package/dist/core/index.js +2 -2
- package/dist/core/prompt-builder.js +23 -2
- package/dist/core/session-logger.js +46 -4
- package/dist/core/version.js +24 -0
- package/dist/i18n/en.json +75 -2
- package/dist/i18n/ru.json +74 -1
- package/dist/index.js +1 -1
- package/dist/llm/image-utils.js +4 -5
- package/dist/llm/index.js +4 -4
- package/dist/llm/model-loader.js +6 -6
- package/dist/llm/openai-compat.js +40 -34
- package/dist/llm/orchestrator.js +33 -29
- package/dist/llm/response.js +9 -9
- package/dist/logger/app-logger.js +1 -1
- package/dist/logger/index.js +1 -1
- package/dist/main.js +1111 -954
- package/dist/migration/backup.js +13 -13
- package/dist/migration/detect.js +11 -11
- package/dist/migration/index.js +2 -2
- package/dist/modules/artifacts/store.js +61 -0
- package/dist/modules/browser/actions.js +34 -4
- package/dist/modules/browser/bridge-client.js +199 -0
- package/dist/modules/browser/bridge-path.js +10 -0
- package/dist/modules/browser/bridge-server.mjs +202 -202
- package/dist/modules/browser/cookie-store.js +6 -6
- package/dist/modules/browser/driver.js +136 -0
- package/dist/modules/browser/index.js +7 -5
- package/dist/modules/browser/module.js +8 -7
- package/dist/modules/browser/session.js +87 -84
- package/dist/modules/browser/snapshot.js +92 -58
- package/dist/modules/browser/types.js +4 -1
- package/dist/modules/certification/cli.js +2 -4
- package/dist/modules/certification/fact-checker.js +1 -3
- package/dist/modules/certification/loader.js +3 -9
- package/dist/modules/certification/runner.js +1 -4
- package/dist/modules/context/chunk-query.js +100 -0
- package/dist/modules/context/fact-extractor.js +162 -0
- package/dist/modules/context/history.js +15 -0
- package/dist/modules/context/index.js +1 -1
- package/dist/modules/context/manager.js +160 -86
- package/dist/modules/execution/audit-runners.js +152 -0
- package/dist/modules/execution/auditor.js +177 -25
- package/dist/modules/execution/execution-plugin.js +272 -0
- package/dist/modules/execution/module.js +201 -544
- package/dist/modules/execution/moe-executor.js +25 -0
- package/dist/modules/execution/plan-store.js +1 -3
- package/dist/modules/execution/plan-tool.js +508 -0
- package/dist/modules/execution/plan-validator.js +10 -10
- package/dist/modules/execution/planner.js +6 -1
- package/dist/modules/execution/stuck-detector.js +173 -10
- package/dist/modules/execution/verifier.js +86 -42
- package/dist/modules/execution/windows-commands.js +41 -0
- package/dist/modules/hallucination/confidence.js +8 -1
- package/dist/modules/hallucination/detector.js +2 -5
- package/dist/modules/hallucination/factual.js +3 -64
- package/dist/modules/hallucination/index.js +1 -1
- package/dist/modules/hallucination/js-identifiers.js +190 -0
- package/dist/modules/hallucination/llm-judge.js +1 -3
- package/dist/modules/indexer/cache.js +9 -7
- package/dist/modules/indexer/index.js +3 -3
- package/dist/modules/indexer/module.js +95 -42
- package/dist/modules/indexer/project-profile.js +183 -0
- package/dist/modules/indexer/walker.js +17 -17
- package/dist/modules/lsp/check-tool.js +58 -0
- package/dist/modules/lsp/client.js +74 -31
- package/dist/modules/lsp/command.js +60 -0
- package/dist/modules/lsp/config.js +87 -33
- package/dist/modules/lsp/index.js +3 -3
- package/dist/modules/lsp/module.js +185 -21
- package/dist/modules/lsp/probe.js +76 -0
- package/dist/modules/lsp/project-root.js +32 -0
- package/dist/modules/lsp/startup-check.js +141 -0
- package/dist/modules/mcp/module.js +2 -6
- package/dist/modules/memory/index.js +1 -1
- package/dist/modules/memory/module.js +71 -23
- package/dist/modules/memory/search.js +11 -9
- package/dist/modules/memory/store.js +13 -13
- package/dist/modules/pipelines/engine.js +10 -10
- package/dist/modules/pipelines/index.js +3 -3
- package/dist/modules/pipelines/parser.js +17 -14
- package/dist/modules/pipelines/template.js +1 -1
- package/dist/modules/plugins/builtin/lint-on-write.js +21 -16
- package/dist/modules/plugins/builtin/notify.js +3 -2
- package/dist/modules/plugins/index.js +1 -1
- package/dist/modules/plugins/loader.js +59 -17
- package/dist/modules/plugins/manager.js +73 -17
- package/dist/modules/processes/detect.js +34 -0
- package/dist/modules/processes/index.js +1 -1
- package/dist/modules/processes/registry.js +135 -46
- package/dist/modules/registry.js +4 -2
- package/dist/modules/security/audit-notifier.js +39 -39
- package/dist/modules/security/command-validator.js +2 -8
- package/dist/modules/security/data-sanitizer.js +1 -9
- package/dist/modules/security/encryption.js +58 -56
- package/dist/modules/security/network-validator.js +1 -9
- package/dist/modules/security/path-validator.js +1 -3
- package/dist/modules/security/security-policies.js +3 -19
- package/dist/modules/security/session-encryption.js +1 -1
- package/dist/modules/security/session-isolation.js +8 -8
- package/dist/modules/session/index.js +3 -3
- package/dist/modules/session/module.js +5 -5
- package/dist/modules/session/store.js +3 -9
- package/dist/modules/skills/matcher.js +27 -0
- package/dist/modules/skills/module.js +1 -2
- package/dist/modules/updater/checker.js +70 -6
- package/dist/modules/updater/index.js +2 -1
- package/dist/modules/updater/module.js +116 -0
- package/dist/modules/user-profile/compressor.js +2 -2
- package/dist/modules/user-profile/index.js +1 -1
- package/dist/modules/user-profile/profile.js +9 -9
- package/dist/tools/attach-image.js +1 -1
- package/dist/tools/bash.js +178 -19
- package/dist/tools/browser.js +46 -29
- package/dist/tools/chunk-query.js +99 -0
- package/dist/tools/download-file.js +116 -0
- package/dist/tools/enable-tools.js +58 -0
- package/dist/tools/executor.js +4 -5
- package/dist/tools/file-info.js +13 -12
- package/dist/tools/filter-tools.js +9 -2
- package/dist/tools/glob-tool.js +11 -11
- package/dist/tools/grep-tool.js +1 -3
- package/dist/tools/hidden-tools-block.js +37 -0
- package/dist/tools/index.js +13 -2
- package/dist/tools/list-dir.js +18 -17
- package/dist/tools/load-skill.js +1 -3
- package/dist/tools/path-utils.js +4 -4
- package/dist/tools/pipeline-run.js +25 -25
- package/dist/tools/process-kill.js +11 -11
- package/dist/tools/process-list.js +20 -22
- package/dist/tools/process-log.js +22 -18
- package/dist/tools/question.js +1 -3
- package/dist/tools/read-file.js +10 -2
- package/dist/tools/recall.js +44 -37
- package/dist/tools/registry.js +15 -4
- package/dist/tools/remember.js +29 -29
- package/dist/tools/scope-check.js +9 -9
- package/dist/tools/subagent.js +54 -9
- package/dist/tools/user-input.js +1 -1
- package/dist/tools/web-browse.js +3 -3
- package/dist/tools/web-fetch.js +3 -3
- package/dist/tools/web-search.js +3 -3
- package/dist/tools/write-file.js +1 -3
- package/dist/ui/box.js +1 -5
- package/dist/ui/index.js +6 -6
- package/dist/ui/line-editor.js +703 -0
- package/dist/ui/line-math.js +69 -0
- package/dist/ui/md-formatter.js +33 -33
- package/dist/ui/output.js +5 -5
- package/dist/ui/plan-view.js +103 -0
- package/dist/ui/renderer.js +15 -10
- package/dist/ui/table.js +1 -1
- package/package.json +48 -48
package/dist/main.js
CHANGED
|
@@ -2092,15 +2092,31 @@ var init_config = __esm(() => {
|
|
|
2092
2092
|
servers: {
|
|
2093
2093
|
typescript: {
|
|
2094
2094
|
command: "npx",
|
|
2095
|
-
args: [
|
|
2096
|
-
|
|
2095
|
+
args: [
|
|
2096
|
+
"--yes",
|
|
2097
|
+
"--package",
|
|
2098
|
+
"typescript-language-server",
|
|
2099
|
+
"--package",
|
|
2100
|
+
"typescript@5",
|
|
2101
|
+
"typescript-language-server",
|
|
2102
|
+
"--stdio"
|
|
2103
|
+
],
|
|
2104
|
+
timeout: 30000,
|
|
2097
2105
|
autoInstall: true,
|
|
2098
2106
|
workspaceMarkers: ["package.json", "tsconfig.json"]
|
|
2099
2107
|
},
|
|
2100
2108
|
javascript: {
|
|
2101
2109
|
command: "npx",
|
|
2102
|
-
args: [
|
|
2103
|
-
|
|
2110
|
+
args: [
|
|
2111
|
+
"--yes",
|
|
2112
|
+
"--package",
|
|
2113
|
+
"typescript-language-server",
|
|
2114
|
+
"--package",
|
|
2115
|
+
"typescript@5",
|
|
2116
|
+
"typescript-language-server",
|
|
2117
|
+
"--stdio"
|
|
2118
|
+
],
|
|
2119
|
+
timeout: 30000,
|
|
2104
2120
|
autoInstall: true,
|
|
2105
2121
|
workspaceMarkers: ["package.json", "tsconfig.json"]
|
|
2106
2122
|
},
|
|
@@ -2142,6 +2158,8 @@ var init_config = __esm(() => {
|
|
|
2142
2158
|
"--yes",
|
|
2143
2159
|
"--package",
|
|
2144
2160
|
"vscode-langservers-extracted",
|
|
2161
|
+
"--package",
|
|
2162
|
+
"typescript@5",
|
|
2145
2163
|
"vscode-html-language-server",
|
|
2146
2164
|
"--stdio"
|
|
2147
2165
|
],
|
|
@@ -2155,6 +2173,8 @@ var init_config = __esm(() => {
|
|
|
2155
2173
|
"--yes",
|
|
2156
2174
|
"--package",
|
|
2157
2175
|
"vscode-langservers-extracted",
|
|
2176
|
+
"--package",
|
|
2177
|
+
"typescript@5",
|
|
2158
2178
|
"vscode-css-language-server",
|
|
2159
2179
|
"--stdio"
|
|
2160
2180
|
],
|
|
@@ -2494,6 +2514,7 @@ Command: {command}`,
|
|
|
2494
2514
|
"plan.title_steps": 'Plan "{title}" created with {count} steps',
|
|
2495
2515
|
"plan.step_marked": "Step {step} marked as {status}",
|
|
2496
2516
|
"plan.aborted": "Plan aborted",
|
|
2517
|
+
"plan.completed_archived": "Plan {id} is complete ({done}/{total} steps) — archived. You may create a new plan for new work (plan create), or reply with your final answer.",
|
|
2497
2518
|
"plan.unknown_action": "Unknown plan action: {action}",
|
|
2498
2519
|
"plan.updated": "Plan updated: {title} ({steps} steps)",
|
|
2499
2520
|
"plan.acknowledged": "Plan {action}: acknowledged",
|
|
@@ -2533,6 +2554,7 @@ Command: {command}`,
|
|
|
2533
2554
|
"verify.script_passed": "Script '{script}' passed",
|
|
2534
2555
|
"verify.script_failed": "Script '{script}' failed: {message}",
|
|
2535
2556
|
"verify.syntax_error": "Syntax error in: {path}",
|
|
2557
|
+
"verify.no_files": "Step {step} has no named files to verify — run a real check instead (bash: bun run build / bun test / lsp_check) and confirm the result.",
|
|
2536
2558
|
"lsp.unavailable": "static checks are unavailable (server not found). Verify via the project's own build/test instead.",
|
|
2537
2559
|
"lsp.check_disabled": "LSP checks are disabled in the config.",
|
|
2538
2560
|
"lsp.check_no_path": "Provide a path (file or directory) to check.",
|
|
@@ -3127,6 +3149,7 @@ var init_ru = __esm(() => {
|
|
|
3127
3149
|
"plan.title_steps": 'План "{title}" создан с {count} шагами',
|
|
3128
3150
|
"plan.step_marked": "Шаг {step} отмечен как {status}",
|
|
3129
3151
|
"plan.aborted": "План отменён",
|
|
3152
|
+
"plan.completed_archived": "План {id} завершён ({done}/{total} шагов) — архивирован. Для новой задачи создай новый план (plan create) или дай финальный ответ.",
|
|
3130
3153
|
"plan.unknown_action": "Неизвестное действие плана: {action}",
|
|
3131
3154
|
"plan.updated": "План обновлён: {title} ({steps} шагов)",
|
|
3132
3155
|
"plan.acknowledged": "План {action}: принято",
|
|
@@ -3166,6 +3189,7 @@ var init_ru = __esm(() => {
|
|
|
3166
3189
|
"verify.script_passed": "Скрипт '{script}' пройден",
|
|
3167
3190
|
"verify.script_failed": "Скрипт '{script}' не пройден: {message}",
|
|
3168
3191
|
"verify.syntax_error": "Синтаксическая ошибка в: {path}",
|
|
3192
|
+
"verify.no_files": "Шаг {step} не содержит именованных файлов для проверки — выполни реальную проверку (bun run build / bun test / lsp_check) и подтверди результат.",
|
|
3169
3193
|
"lsp.unavailable": "статические проверки недоступны (сервер не найден). Проверяйте через собственный build/test проекта.",
|
|
3170
3194
|
"lsp.check_disabled": "LSP-проверки отключены в конфигурации.",
|
|
3171
3195
|
"lsp.check_no_path": "Укажите путь (файл или каталог) для проверки.",
|
|
@@ -3979,6 +4003,23 @@ function regexReviver(_key, value) {
|
|
|
3979
4003
|
}
|
|
3980
4004
|
return value;
|
|
3981
4005
|
}
|
|
4006
|
+
function normalizeLspServerArgs(config) {
|
|
4007
|
+
const servers = config.lsp?.servers;
|
|
4008
|
+
if (!servers)
|
|
4009
|
+
return;
|
|
4010
|
+
for (const [lang, legacy] of Object.entries(LEGACY_LSP_SERVER_ARGS)) {
|
|
4011
|
+
const server = servers[lang];
|
|
4012
|
+
const def = DEFAULT_LSP_CONFIG.servers[lang];
|
|
4013
|
+
if (!server || server.command !== "npx" || !def)
|
|
4014
|
+
continue;
|
|
4015
|
+
const storedArgs = Array.isArray(server.args) ? server.args : null;
|
|
4016
|
+
const isLegacy = storedArgs === null || JSON.stringify(storedArgs) === JSON.stringify(legacy.args);
|
|
4017
|
+
if (isLegacy) {
|
|
4018
|
+
server.args = def.args;
|
|
4019
|
+
server.timeout = def.timeout;
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
3982
4023
|
function loadJSON(path) {
|
|
3983
4024
|
try {
|
|
3984
4025
|
if (existsSync4(path)) {
|
|
@@ -4051,6 +4092,7 @@ function loadConfig(options) {
|
|
|
4051
4092
|
if (config.security?.contentScan?.dangerousPatterns) {
|
|
4052
4093
|
config.security.contentScan.dangerousPatterns = restoreDangerousPatterns(config.security.contentScan.dangerousPatterns, DEFAULT_SECURITY_CONFIG.contentScan.dangerousPatterns);
|
|
4053
4094
|
}
|
|
4095
|
+
normalizeLspServerArgs(config);
|
|
4054
4096
|
config = applyEnvVars(config);
|
|
4055
4097
|
try {
|
|
4056
4098
|
const encryptor = new ConfigEncryptor;
|
|
@@ -4091,13 +4133,22 @@ function saveConfig(config, configPath) {
|
|
|
4091
4133
|
writeFileSync3(configPath, JSON.stringify(config, regexReplacer, 2), "utf-8");
|
|
4092
4134
|
}
|
|
4093
4135
|
}
|
|
4136
|
+
var LEGACY_LSP_SERVER_ARGS;
|
|
4094
4137
|
var init_config2 = __esm(() => {
|
|
4095
4138
|
init_defaults();
|
|
4096
4139
|
init_security();
|
|
4140
|
+
init_config();
|
|
4097
4141
|
init_i18n();
|
|
4098
4142
|
init_detect();
|
|
4099
4143
|
init_backup();
|
|
4100
4144
|
init_encryption();
|
|
4145
|
+
LEGACY_LSP_SERVER_ARGS = {
|
|
4146
|
+
typescript: { args: ["typescript-language-server", "--stdio"], timeout: 30000 },
|
|
4147
|
+
javascript: { args: ["typescript-language-server", "--stdio"], timeout: 30000 },
|
|
4148
|
+
json: { args: ["vscode-json-languageserver", "--stdio"], timeout: 30000 },
|
|
4149
|
+
html: { args: ["vscode-html-languageserver", "--stdio"], timeout: 60000 },
|
|
4150
|
+
css: { args: ["vscode-css-languageserver", "--stdio"], timeout: 60000 }
|
|
4151
|
+
};
|
|
4101
4152
|
});
|
|
4102
4153
|
|
|
4103
4154
|
// src/modules/security/data-sanitizer.ts
|
|
@@ -10192,245 +10243,750 @@ function findProjectRoot(filePath, baseDir, markers) {
|
|
|
10192
10243
|
}
|
|
10193
10244
|
var init_project_root = () => {};
|
|
10194
10245
|
|
|
10195
|
-
// src/modules/
|
|
10196
|
-
|
|
10197
|
-
|
|
10198
|
-
|
|
10199
|
-
|
|
10200
|
-
class StepVerifier {
|
|
10201
|
-
baseDir;
|
|
10202
|
-
constructor(baseDir) {
|
|
10203
|
-
this.baseDir = baseDir;
|
|
10204
|
-
}
|
|
10205
|
-
async checkFileExists(path) {
|
|
10206
|
-
const resolved = resolve11(this.baseDir, path);
|
|
10207
|
-
const exists = existsSync20(resolved);
|
|
10208
|
-
return {
|
|
10209
|
-
passed: exists,
|
|
10210
|
-
message: exists ? t("verify.file_exists", { path }) : t("verify.file_not_found", { path })
|
|
10211
|
-
};
|
|
10212
|
-
}
|
|
10213
|
-
async runScript(scriptName) {
|
|
10214
|
-
try {
|
|
10215
|
-
await this.runAsync(`bun run ${scriptName}`, this.baseDir, 60000);
|
|
10216
|
-
return { passed: true, message: t("verify.script_passed", { script: scriptName }) };
|
|
10217
|
-
} catch (e) {
|
|
10218
|
-
return {
|
|
10219
|
-
passed: false,
|
|
10220
|
-
message: t("verify.script_failed", { script: scriptName, message: e.message })
|
|
10221
|
-
};
|
|
10222
|
-
}
|
|
10223
|
-
}
|
|
10224
|
-
async runTypeCheck() {
|
|
10225
|
-
const tsconfigPath = resolve11(this.baseDir, "tsconfig.json");
|
|
10226
|
-
if (!existsSync20(tsconfigPath)) {
|
|
10227
|
-
return { passed: true, message: "No tsconfig.json found — skipping type check" };
|
|
10228
|
-
}
|
|
10229
|
-
try {
|
|
10230
|
-
await this.runAsync("npx tsc --noEmit", this.baseDir, 60000);
|
|
10231
|
-
return { passed: true, message: "TypeScript type check passed" };
|
|
10232
|
-
} catch (e) {
|
|
10233
|
-
const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
|
|
10234
|
-
return { passed: false, message: `TypeScript type check failed: ${stderr.slice(0, 500)}` };
|
|
10235
|
-
}
|
|
10236
|
-
}
|
|
10237
|
-
async runTypeCheckForFile(filePath) {
|
|
10238
|
-
const projectRoot = findProjectRoot(filePath, this.baseDir, ["tsconfig.json", "package.json"]);
|
|
10239
|
-
const tsconfigPath = join11(projectRoot, "tsconfig.json");
|
|
10240
|
-
if (!existsSync20(tsconfigPath)) {
|
|
10241
|
-
return { passed: true, message: "No tsconfig.json found — skipping type check" };
|
|
10242
|
-
}
|
|
10243
|
-
try {
|
|
10244
|
-
await this.runAsync("npx tsc --noEmit --skipLibCheck", projectRoot, 60000);
|
|
10245
|
-
return { passed: true, message: "TypeScript type check passed" };
|
|
10246
|
-
} catch (e) {
|
|
10247
|
-
const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
|
|
10248
|
-
return { passed: false, message: `TypeScript type check failed: ${stderr.slice(0, 500)}` };
|
|
10249
|
-
}
|
|
10250
|
-
}
|
|
10251
|
-
async runTests() {
|
|
10252
|
-
const pkgPath = resolve11(this.baseDir, "package.json");
|
|
10253
|
-
if (!existsSync20(pkgPath)) {
|
|
10254
|
-
return { passed: true, message: "No package.json found — skipping tests" };
|
|
10255
|
-
}
|
|
10256
|
-
try {
|
|
10257
|
-
const pkg = JSON.parse(__require("fs").readFileSync(pkgPath, "utf-8"));
|
|
10258
|
-
if (!pkg.scripts?.test) {
|
|
10259
|
-
return { passed: true, message: "No test script defined — skipping tests" };
|
|
10260
|
-
}
|
|
10261
|
-
} catch {}
|
|
10262
|
-
return this.runScript("test");
|
|
10263
|
-
}
|
|
10264
|
-
async verifyArtifactFiles(files) {
|
|
10265
|
-
return Promise.all(files.map((f) => this.checkFileExists(f)));
|
|
10266
|
-
}
|
|
10267
|
-
async verifyMoEManifest(plan, config, allToolTags) {
|
|
10268
|
-
const errors = [];
|
|
10269
|
-
const warnings = [];
|
|
10270
|
-
const details = [];
|
|
10271
|
-
const expertErrors = validateExpertConfig(config, allToolTags);
|
|
10272
|
-
for (const err of expertErrors) {
|
|
10273
|
-
errors.push(err);
|
|
10274
|
-
details.push({ passed: false, message: err });
|
|
10275
|
-
}
|
|
10276
|
-
const tscResult = await this.runTypeCheck();
|
|
10277
|
-
details.push(tscResult);
|
|
10278
|
-
if (!tscResult.passed) {
|
|
10279
|
-
errors.push(tscResult.message);
|
|
10280
|
-
}
|
|
10281
|
-
for (const sub of plan.subtasks) {
|
|
10282
|
-
const fileChecks = await this.verifyArtifactFiles(sub.allowed_files || []);
|
|
10283
|
-
for (const check of fileChecks) {
|
|
10284
|
-
details.push(check);
|
|
10285
|
-
if (!check.passed) {
|
|
10286
|
-
errors.push(check.message);
|
|
10287
|
-
}
|
|
10288
|
-
}
|
|
10289
|
-
}
|
|
10290
|
-
return {
|
|
10291
|
-
success: errors.length === 0,
|
|
10292
|
-
errors,
|
|
10293
|
-
warnings,
|
|
10294
|
-
details
|
|
10295
|
-
};
|
|
10296
|
-
}
|
|
10297
|
-
async verifyStep(stepDescription) {
|
|
10298
|
-
const fileMatches = stepDescription.match(/\b[\w./-]+\.[a-z]+/gi) || [];
|
|
10299
|
-
const results = [];
|
|
10300
|
-
let syntaxValid = true;
|
|
10301
|
-
for (const filePath of fileMatches) {
|
|
10302
|
-
const result = await this.checkFileExists(filePath);
|
|
10303
|
-
results.push(result);
|
|
10304
|
-
if (result.passed) {
|
|
10305
|
-
const fullPath = resolve11(this.baseDir, filePath);
|
|
10306
|
-
if (!await this.validateSyntax(fullPath)) {
|
|
10307
|
-
syntaxValid = false;
|
|
10308
|
-
results.push({ passed: false, message: t("verify.syntax_error", { path: filePath }) });
|
|
10309
|
-
}
|
|
10310
|
-
}
|
|
10311
|
-
}
|
|
10312
|
-
return {
|
|
10313
|
-
passed: results.every((r) => r.passed),
|
|
10314
|
-
syntaxValid,
|
|
10315
|
-
failed: results.filter((r) => !r.passed)
|
|
10316
|
-
};
|
|
10317
|
-
}
|
|
10318
|
-
async validateSyntax(filePath) {
|
|
10319
|
-
const ext = extname2(filePath);
|
|
10320
|
-
if (ext === ".ts" || ext === ".tsx" || ext === ".cts" || ext === ".mts") {
|
|
10321
|
-
try {
|
|
10322
|
-
await this.runAsync(`npx tsc --noEmit --skipLibCheck ${filePath}`, this.baseDir, 1e4);
|
|
10323
|
-
return true;
|
|
10324
|
-
} catch (err) {
|
|
10325
|
-
if (err.status === 127 || err.message.includes("not found") || err.message.includes("ENOENT")) {
|
|
10326
|
-
return true;
|
|
10327
|
-
}
|
|
10328
|
-
const stderr = err.stderr?.toString() || "";
|
|
10329
|
-
if (stderr.includes("error TS") && !stderr.includes("Cannot find module")) {
|
|
10330
|
-
return false;
|
|
10331
|
-
}
|
|
10332
|
-
return true;
|
|
10333
|
-
}
|
|
10334
|
-
}
|
|
10335
|
-
if (ext === ".js" || ext === ".jsx" || ext === ".cjs" || ext === ".mjs") {
|
|
10336
|
-
try {
|
|
10337
|
-
await this.runAsync(`node --check ${filePath}`, this.baseDir, 5000);
|
|
10338
|
-
return true;
|
|
10339
|
-
} catch {
|
|
10340
|
-
return false;
|
|
10341
|
-
}
|
|
10342
|
-
}
|
|
10343
|
-
return true;
|
|
10344
|
-
}
|
|
10345
|
-
runAsync(command, cwd, timeoutMs) {
|
|
10346
|
-
return new Promise((resolve12, reject) => {
|
|
10347
|
-
const child = spawn2(command, {
|
|
10348
|
-
cwd,
|
|
10349
|
-
shell: true,
|
|
10350
|
-
windowsHide: true,
|
|
10351
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
10352
|
-
});
|
|
10353
|
-
let stdout = "";
|
|
10354
|
-
let stderr = "";
|
|
10355
|
-
child.stdout?.on("data", (d) => {
|
|
10356
|
-
stdout += d.toString();
|
|
10357
|
-
});
|
|
10358
|
-
child.stderr?.on("data", (d) => {
|
|
10359
|
-
stderr += d.toString();
|
|
10360
|
-
});
|
|
10361
|
-
const timer = setTimeout(() => {
|
|
10362
|
-
child.kill();
|
|
10363
|
-
reject(new Error(`Command timed out after ${timeoutMs}ms`));
|
|
10364
|
-
}, timeoutMs);
|
|
10365
|
-
child.on("error", (err) => {
|
|
10366
|
-
clearTimeout(timer);
|
|
10367
|
-
reject(err);
|
|
10368
|
-
});
|
|
10369
|
-
child.on("close", (code) => {
|
|
10370
|
-
clearTimeout(timer);
|
|
10371
|
-
if (code === 0) {
|
|
10372
|
-
resolve12({ stdout, stderr });
|
|
10373
|
-
} else {
|
|
10374
|
-
const err = new Error(`Command failed with exit code ${code}`);
|
|
10375
|
-
err.stdout = stdout;
|
|
10376
|
-
err.stderr = stderr;
|
|
10377
|
-
err.status = code;
|
|
10378
|
-
reject(err);
|
|
10379
|
-
}
|
|
10380
|
-
});
|
|
10381
|
-
});
|
|
10382
|
-
}
|
|
10246
|
+
// src/modules/hallucination/js-identifiers.ts
|
|
10247
|
+
function isJsMemberAccess(candidate) {
|
|
10248
|
+
const lower = candidate.toLowerCase();
|
|
10249
|
+
const m = lower.match(/^([\w-]+)\./);
|
|
10250
|
+
return m !== null && JS_GLOBALS.has(m[1]);
|
|
10383
10251
|
}
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
provider: config.orchestrator.provider
|
|
10396
|
-
}, llmProvider);
|
|
10397
|
-
if (!orchestrator.isEnabled()) {
|
|
10398
|
-
logger.debug("MoE enabled but no orchestrator model configured — falling back to single-agent");
|
|
10399
|
-
return fallback();
|
|
10400
|
-
}
|
|
10401
|
-
onMeta?.(`\uD83E\uDD16 Planning with MoE mode...
|
|
10402
|
-
`);
|
|
10403
|
-
onPhase?.("thinking");
|
|
10404
|
-
const planResult = await orchestrator.plan(input);
|
|
10405
|
-
onPhase?.("done");
|
|
10406
|
-
if ("error" in planResult) {
|
|
10407
|
-
logger.warn(`MoE plan failed: ${planResult.error} — falling back to single-agent`);
|
|
10408
|
-
return fallback();
|
|
10409
|
-
}
|
|
10410
|
-
const { plan } = planResult;
|
|
10411
|
-
onMeta?.(`\uD83D\uDCCB Plan created: "${plan.title}" (${plan.subtasks.length} subtasks)
|
|
10412
|
-
`);
|
|
10413
|
-
const validation = validatePlan(plan, config);
|
|
10414
|
-
if (!validation.valid) {
|
|
10415
|
-
const applied = applyAutoFixes(plan, validation.autoFixes);
|
|
10416
|
-
const retry = validatePlan(applied, config);
|
|
10417
|
-
if (!retry.valid) {
|
|
10418
|
-
logger.warn(`MoE plan validation failed: ${retry.errors.join("; ")} — falling back to single-agent`);
|
|
10419
|
-
onMeta?.(`⚠️ Plan validation failed. Falling back to single-agent mode.
|
|
10420
|
-
`);
|
|
10421
|
-
return fallback();
|
|
10422
|
-
}
|
|
10423
|
-
Object.assign(plan, applied);
|
|
10424
|
-
onMeta?.(`\uD83D\uDD27 Auto-fixed ${validation.autoFixes.length} plan issues.
|
|
10425
|
-
`);
|
|
10252
|
+
function isFilePathLike(s) {
|
|
10253
|
+
const lower = s.toLowerCase();
|
|
10254
|
+
if (TECH_NAMES.has(lower))
|
|
10255
|
+
return false;
|
|
10256
|
+
if (isJsMemberAccess(s))
|
|
10257
|
+
return false;
|
|
10258
|
+
const ext = s.split(".").pop()?.toLowerCase() || "";
|
|
10259
|
+
if (!s.includes("/") && !s.includes("\\")) {
|
|
10260
|
+
if (!/^\w+\.[a-z]{1,6}$/i.test(s))
|
|
10261
|
+
return false;
|
|
10262
|
+
return FILE_EXTENSIONS.has(ext);
|
|
10426
10263
|
}
|
|
10427
|
-
const
|
|
10428
|
-
|
|
10429
|
-
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10264
|
+
const last = s.split(/[\\/]/).pop() ?? "";
|
|
10265
|
+
const lastDot = last.lastIndexOf(".");
|
|
10266
|
+
if (lastDot <= 0)
|
|
10267
|
+
return false;
|
|
10268
|
+
return true;
|
|
10269
|
+
}
|
|
10270
|
+
function extractFileLikeTokens(text) {
|
|
10271
|
+
const matches = text.match(FILE_TOKEN_RE) || [];
|
|
10272
|
+
return [...new Set(matches.filter(isFilePathLike))];
|
|
10273
|
+
}
|
|
10274
|
+
function stripUrls(text) {
|
|
10275
|
+
return text.replace(/\bhttps?:\/\/[^\s"'<>]+/gi, " ");
|
|
10276
|
+
}
|
|
10277
|
+
var JS_GLOBALS, FILE_EXTENSIONS, TECH_NAMES, FILE_TOKEN_RE;
|
|
10278
|
+
var init_js_identifiers = __esm(() => {
|
|
10279
|
+
JS_GLOBALS = new Set([
|
|
10280
|
+
"process",
|
|
10281
|
+
"console",
|
|
10282
|
+
"math",
|
|
10283
|
+
"json",
|
|
10284
|
+
"global",
|
|
10285
|
+
"globalthis",
|
|
10286
|
+
"window",
|
|
10287
|
+
"document",
|
|
10288
|
+
"buffer",
|
|
10289
|
+
"module",
|
|
10290
|
+
"require",
|
|
10291
|
+
"exports",
|
|
10292
|
+
"url",
|
|
10293
|
+
"promise",
|
|
10294
|
+
"array",
|
|
10295
|
+
"object",
|
|
10296
|
+
"string",
|
|
10297
|
+
"number",
|
|
10298
|
+
"boolean",
|
|
10299
|
+
"symbol",
|
|
10300
|
+
"date",
|
|
10301
|
+
"regexp",
|
|
10302
|
+
"error",
|
|
10303
|
+
"map",
|
|
10304
|
+
"set",
|
|
10305
|
+
"weakmap",
|
|
10306
|
+
"weakset",
|
|
10307
|
+
"proxy",
|
|
10308
|
+
"reflect",
|
|
10309
|
+
"intl",
|
|
10310
|
+
"crypto",
|
|
10311
|
+
"performance",
|
|
10312
|
+
"fetch",
|
|
10313
|
+
"navigator",
|
|
10314
|
+
"location",
|
|
10315
|
+
"settimeout",
|
|
10316
|
+
"setinterval",
|
|
10317
|
+
"cleartimeout",
|
|
10318
|
+
"clearinterval",
|
|
10319
|
+
"queuemicrotask",
|
|
10320
|
+
"structuredclone",
|
|
10321
|
+
"textencoder",
|
|
10322
|
+
"textdecoder",
|
|
10323
|
+
"atomics",
|
|
10324
|
+
"sharedarraybuffer",
|
|
10325
|
+
"dataview",
|
|
10326
|
+
"arraybuffer",
|
|
10327
|
+
"bigint",
|
|
10328
|
+
"infinity",
|
|
10329
|
+
"nan",
|
|
10330
|
+
"undefined",
|
|
10331
|
+
"bun",
|
|
10332
|
+
"deno",
|
|
10333
|
+
"node"
|
|
10334
|
+
]);
|
|
10335
|
+
FILE_EXTENSIONS = new Set([
|
|
10336
|
+
"ts",
|
|
10337
|
+
"tsx",
|
|
10338
|
+
"js",
|
|
10339
|
+
"jsx",
|
|
10340
|
+
"mjs",
|
|
10341
|
+
"cjs",
|
|
10342
|
+
"py",
|
|
10343
|
+
"pyc",
|
|
10344
|
+
"rb",
|
|
10345
|
+
"go",
|
|
10346
|
+
"rs",
|
|
10347
|
+
"java",
|
|
10348
|
+
"jar",
|
|
10349
|
+
"kt",
|
|
10350
|
+
"kts",
|
|
10351
|
+
"c",
|
|
10352
|
+
"h",
|
|
10353
|
+
"cc",
|
|
10354
|
+
"cpp",
|
|
10355
|
+
"cxx",
|
|
10356
|
+
"hpp",
|
|
10357
|
+
"hh",
|
|
10358
|
+
"cs",
|
|
10359
|
+
"php",
|
|
10360
|
+
"swift",
|
|
10361
|
+
"m",
|
|
10362
|
+
"mm",
|
|
10363
|
+
"lua",
|
|
10364
|
+
"pl",
|
|
10365
|
+
"sh",
|
|
10366
|
+
"bash",
|
|
10367
|
+
"zsh",
|
|
10368
|
+
"fish",
|
|
10369
|
+
"bat",
|
|
10370
|
+
"cmd",
|
|
10371
|
+
"ps1",
|
|
10372
|
+
"vue",
|
|
10373
|
+
"svelte",
|
|
10374
|
+
"dart",
|
|
10375
|
+
"json",
|
|
10376
|
+
"jsonc",
|
|
10377
|
+
"json5",
|
|
10378
|
+
"yaml",
|
|
10379
|
+
"yml",
|
|
10380
|
+
"toml",
|
|
10381
|
+
"ini",
|
|
10382
|
+
"cfg",
|
|
10383
|
+
"conf",
|
|
10384
|
+
"env",
|
|
10385
|
+
"xml",
|
|
10386
|
+
"html",
|
|
10387
|
+
"htm",
|
|
10388
|
+
"md",
|
|
10389
|
+
"markdown",
|
|
10390
|
+
"rst",
|
|
10391
|
+
"txt",
|
|
10392
|
+
"log",
|
|
10393
|
+
"csv",
|
|
10394
|
+
"tsv",
|
|
10395
|
+
"lock",
|
|
10396
|
+
"sql",
|
|
10397
|
+
"sqlite",
|
|
10398
|
+
"db",
|
|
10399
|
+
"wasm",
|
|
10400
|
+
"map",
|
|
10401
|
+
"css",
|
|
10402
|
+
"scss",
|
|
10403
|
+
"sass",
|
|
10404
|
+
"less",
|
|
10405
|
+
"svg",
|
|
10406
|
+
"png",
|
|
10407
|
+
"jpg",
|
|
10408
|
+
"jpeg",
|
|
10409
|
+
"gif",
|
|
10410
|
+
"webp",
|
|
10411
|
+
"bmp",
|
|
10412
|
+
"ico",
|
|
10413
|
+
"avif",
|
|
10414
|
+
"tiff",
|
|
10415
|
+
"pdf",
|
|
10416
|
+
"doc",
|
|
10417
|
+
"docx",
|
|
10418
|
+
"xls",
|
|
10419
|
+
"xlsx",
|
|
10420
|
+
"ppt",
|
|
10421
|
+
"pptx",
|
|
10422
|
+
"odt",
|
|
10423
|
+
"ods",
|
|
10424
|
+
"zip",
|
|
10425
|
+
"gz",
|
|
10426
|
+
"tar",
|
|
10427
|
+
"tgz",
|
|
10428
|
+
"bz2",
|
|
10429
|
+
"xz",
|
|
10430
|
+
"7z",
|
|
10431
|
+
"rar",
|
|
10432
|
+
"bin",
|
|
10433
|
+
"exe",
|
|
10434
|
+
"dll",
|
|
10435
|
+
"so",
|
|
10436
|
+
"o",
|
|
10437
|
+
"a",
|
|
10438
|
+
"dylib"
|
|
10439
|
+
]);
|
|
10440
|
+
TECH_NAMES = new Set([
|
|
10441
|
+
"node.js",
|
|
10442
|
+
"react.js",
|
|
10443
|
+
"vue.js",
|
|
10444
|
+
"next.js",
|
|
10445
|
+
"angular.js",
|
|
10446
|
+
"express.js",
|
|
10447
|
+
"deno.js",
|
|
10448
|
+
"qwik.js",
|
|
10449
|
+
"svelte.js",
|
|
10450
|
+
"jquery.js"
|
|
10451
|
+
]);
|
|
10452
|
+
FILE_TOKEN_RE = /\b(?:[a-zA-Z]:[\\/])?[\w./\\-]+\.[a-z]{1,6}\b/gi;
|
|
10453
|
+
});
|
|
10454
|
+
|
|
10455
|
+
// src/modules/execution/audit-runners.ts
|
|
10456
|
+
import { existsSync as existsSync20, readdirSync as readdirSync5 } from "fs";
|
|
10457
|
+
import { dirname as dirname7, join as join11, resolve as resolve11 } from "path";
|
|
10458
|
+
function findTestFile(dir, depth = 0) {
|
|
10459
|
+
if (depth > 5)
|
|
10460
|
+
return null;
|
|
10461
|
+
let entries;
|
|
10462
|
+
try {
|
|
10463
|
+
entries = readdirSync5(dir, { withFileTypes: true });
|
|
10464
|
+
} catch {
|
|
10465
|
+
return null;
|
|
10466
|
+
}
|
|
10467
|
+
for (const e of entries) {
|
|
10468
|
+
const full = join11(dir, e.name);
|
|
10469
|
+
if (e.isDirectory()) {
|
|
10470
|
+
if (SKIP_DIRS.has(e.name))
|
|
10471
|
+
continue;
|
|
10472
|
+
const found = findTestFile(full, depth + 1);
|
|
10473
|
+
if (found)
|
|
10474
|
+
return found;
|
|
10475
|
+
} else if (TEST_EXT_RE.test(e.name)) {
|
|
10476
|
+
return full;
|
|
10477
|
+
}
|
|
10478
|
+
}
|
|
10479
|
+
return null;
|
|
10480
|
+
}
|
|
10481
|
+
function hasTestStep(plan) {
|
|
10482
|
+
return plan.steps.some((s) => TEST_STEP_RE.test(s.description));
|
|
10483
|
+
}
|
|
10484
|
+
function extractFailingNames(output, limit = 5) {
|
|
10485
|
+
const names = [];
|
|
10486
|
+
for (const m of output.matchAll(/\(fail\)\s*([^\n]+)/g)) {
|
|
10487
|
+
const name = m[1].trim();
|
|
10488
|
+
if (name && !names.includes(name))
|
|
10489
|
+
names.push(name);
|
|
10490
|
+
if (names.length >= limit)
|
|
10491
|
+
break;
|
|
10492
|
+
}
|
|
10493
|
+
return names;
|
|
10494
|
+
}
|
|
10495
|
+
async function runTests(baseDir) {
|
|
10496
|
+
const entry = processRegistry.start("bun test", baseDir);
|
|
10497
|
+
const exited = await processRegistry.waitForExit(entry.id, 90000);
|
|
10498
|
+
const output = entry.log.join(`
|
|
10499
|
+
`);
|
|
10500
|
+
processRegistry.remove(entry.id);
|
|
10501
|
+
if (!exited) {
|
|
10502
|
+
return {
|
|
10503
|
+
checked: true,
|
|
10504
|
+
passed: true,
|
|
10505
|
+
failed: 0,
|
|
10506
|
+
passedCount: 0,
|
|
10507
|
+
detail: "test run timed out after 90s — result unknown",
|
|
10508
|
+
command: "bun test"
|
|
10509
|
+
};
|
|
10510
|
+
}
|
|
10511
|
+
const run = detectTestResults(output);
|
|
10512
|
+
if (!run) {
|
|
10513
|
+
return {
|
|
10514
|
+
checked: true,
|
|
10515
|
+
passed: entry.exitCode === 0,
|
|
10516
|
+
failed: entry.exitCode === 0 ? 0 : 1,
|
|
10517
|
+
passedCount: 0,
|
|
10518
|
+
detail: output.slice(0, 200).trim(),
|
|
10519
|
+
command: "bun test"
|
|
10520
|
+
};
|
|
10521
|
+
}
|
|
10522
|
+
const names = extractFailingNames(output);
|
|
10523
|
+
return {
|
|
10524
|
+
checked: true,
|
|
10525
|
+
passed: run.failed === 0,
|
|
10526
|
+
failed: run.failed,
|
|
10527
|
+
passedCount: run.passed,
|
|
10528
|
+
detail: names.length ? names.join("; ") : run.summary || `${run.failed} failed / ${run.passed} passed`,
|
|
10529
|
+
command: "bun test"
|
|
10530
|
+
};
|
|
10531
|
+
}
|
|
10532
|
+
function parseTypecheckErrors(output) {
|
|
10533
|
+
const line = output.split(`
|
|
10534
|
+
`).find((l) => /error TS\d+/.test(l));
|
|
10535
|
+
return line ? line.trim().slice(0, 300) : null;
|
|
10536
|
+
}
|
|
10537
|
+
function findTypecheckRoot(baseDir, existingFiles = []) {
|
|
10538
|
+
const candidates = [baseDir, ...existingFiles];
|
|
10539
|
+
let best = null;
|
|
10540
|
+
for (const start of candidates) {
|
|
10541
|
+
let dir = resolve11(start);
|
|
10542
|
+
for (let depth = 0;depth <= 10; depth++) {
|
|
10543
|
+
if (existsSync20(join11(dir, "tsconfig.json"))) {
|
|
10544
|
+
if (!best || depth < best.depth)
|
|
10545
|
+
best = { depth, root: dir };
|
|
10546
|
+
break;
|
|
10547
|
+
}
|
|
10548
|
+
const parent = dirname7(dir);
|
|
10549
|
+
if (parent === dir)
|
|
10550
|
+
break;
|
|
10551
|
+
dir = parent;
|
|
10552
|
+
}
|
|
10553
|
+
}
|
|
10554
|
+
return best?.root ?? null;
|
|
10555
|
+
}
|
|
10556
|
+
async function runTypecheck(baseDir) {
|
|
10557
|
+
const entry = processRegistry.start("npx --no-install tsc --noEmit --skipLibCheck", baseDir);
|
|
10558
|
+
const exited = await processRegistry.waitForExit(entry.id, 90000);
|
|
10559
|
+
const output = entry.log.join(`
|
|
10560
|
+
`);
|
|
10561
|
+
processRegistry.remove(entry.id);
|
|
10562
|
+
if (!exited)
|
|
10563
|
+
return null;
|
|
10564
|
+
return parseTypecheckErrors(output);
|
|
10565
|
+
}
|
|
10566
|
+
var SKIP_DIRS, TEST_EXT_RE, TEST_STEP_RE;
|
|
10567
|
+
var init_audit_runners = __esm(() => {
|
|
10568
|
+
init_bash();
|
|
10569
|
+
init_processes();
|
|
10570
|
+
SKIP_DIRS = new Set([
|
|
10571
|
+
"node_modules",
|
|
10572
|
+
".git",
|
|
10573
|
+
".mma",
|
|
10574
|
+
"dist",
|
|
10575
|
+
"build",
|
|
10576
|
+
"coverage",
|
|
10577
|
+
".next",
|
|
10578
|
+
".nuxt",
|
|
10579
|
+
"vendor"
|
|
10580
|
+
]);
|
|
10581
|
+
TEST_EXT_RE = /\.(test|spec)\.[jt]sx?$/i;
|
|
10582
|
+
TEST_STEP_RE = /\b(test(ing|s)?|тест(ы|ирование|ировать)?|провер\w*\s+тест|запустить\s+тест)\b|bun test|npm test|vitest|pytest|go test|jest|mocha/i;
|
|
10583
|
+
});
|
|
10584
|
+
|
|
10585
|
+
// src/modules/execution/auditor.ts
|
|
10586
|
+
import { existsSync as existsSync21, readdirSync as readdirSync6 } from "fs";
|
|
10587
|
+
import { resolve as resolve12, join as join12, basename as basename2 } from "path";
|
|
10588
|
+
function findExistingFile(baseDir, filePath) {
|
|
10589
|
+
const direct = resolve12(baseDir, filePath);
|
|
10590
|
+
if (existsSync21(direct))
|
|
10591
|
+
return direct;
|
|
10592
|
+
const name = basename2(filePath).toLowerCase();
|
|
10593
|
+
const suffix = filePath.replace(/\\/g, "/").toLowerCase();
|
|
10594
|
+
let found = null;
|
|
10595
|
+
const walk = (dir, depth) => {
|
|
10596
|
+
if (found || depth > RESOLVE_MAX_DEPTH)
|
|
10597
|
+
return;
|
|
10598
|
+
let entries;
|
|
10599
|
+
try {
|
|
10600
|
+
entries = readdirSync6(dir, { withFileTypes: true });
|
|
10601
|
+
} catch {
|
|
10602
|
+
return;
|
|
10603
|
+
}
|
|
10604
|
+
for (const e of entries) {
|
|
10605
|
+
if (found)
|
|
10606
|
+
return;
|
|
10607
|
+
const full = join12(dir, e.name);
|
|
10608
|
+
if (e.isDirectory()) {
|
|
10609
|
+
if (SKIP_DIRS.has(e.name))
|
|
10610
|
+
continue;
|
|
10611
|
+
walk(full, depth + 1);
|
|
10612
|
+
} else if (e.name.toLowerCase() === name || full.replace(/\\/g, "/").toLowerCase().endsWith("/" + suffix)) {
|
|
10613
|
+
found = full;
|
|
10614
|
+
return;
|
|
10615
|
+
}
|
|
10616
|
+
}
|
|
10617
|
+
};
|
|
10618
|
+
walk(baseDir, 0);
|
|
10619
|
+
return found;
|
|
10620
|
+
}
|
|
10621
|
+
|
|
10622
|
+
class Auditor {
|
|
10623
|
+
baseDir;
|
|
10624
|
+
constructor(baseDir) {
|
|
10625
|
+
this.baseDir = baseDir;
|
|
10626
|
+
}
|
|
10627
|
+
async audit(plan) {
|
|
10628
|
+
const doneCreate = plan.steps.filter((s) => s.status === "done" && s.kind !== "delete");
|
|
10629
|
+
const doneDelete = plan.steps.filter((s) => s.status === "done" && s.kind === "delete");
|
|
10630
|
+
const createText = doneCreate.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
|
|
10631
|
+
const deleteText = doneDelete.map((s) => s.description.replace(/\([^)]*\)/g, " ")).join(" ");
|
|
10632
|
+
const createFiles = extractFileLikeTokens(stripUrls(createText));
|
|
10633
|
+
const deleteFiles = extractFileLikeTokens(stripUrls(deleteText));
|
|
10634
|
+
const missingFiles = [];
|
|
10635
|
+
const existingFiles = [];
|
|
10636
|
+
const leftoverFiles = [];
|
|
10637
|
+
for (const filePath of createFiles) {
|
|
10638
|
+
const resolved = findExistingFile(this.baseDir, filePath);
|
|
10639
|
+
if (resolved) {
|
|
10640
|
+
existingFiles.push(resolved);
|
|
10641
|
+
} else {
|
|
10642
|
+
missingFiles.push(filePath);
|
|
10643
|
+
}
|
|
10644
|
+
}
|
|
10645
|
+
for (const filePath of deleteFiles) {
|
|
10646
|
+
if (findExistingFile(this.baseDir, filePath)) {
|
|
10647
|
+
leftoverFiles.push(filePath);
|
|
10648
|
+
}
|
|
10649
|
+
}
|
|
10650
|
+
let testRun = null;
|
|
10651
|
+
if (hasTestStep(plan) && findTestFile(this.baseDir)) {
|
|
10652
|
+
try {
|
|
10653
|
+
testRun = await runTests(this.baseDir);
|
|
10654
|
+
} catch {
|
|
10655
|
+
testRun = null;
|
|
10656
|
+
}
|
|
10657
|
+
}
|
|
10658
|
+
let typecheckError = null;
|
|
10659
|
+
if (missingFiles.length === 0 && leftoverFiles.length === 0) {
|
|
10660
|
+
const root = findTypecheckRoot(this.baseDir, existingFiles);
|
|
10661
|
+
if (root) {
|
|
10662
|
+
try {
|
|
10663
|
+
typecheckError = await runTypecheck(root);
|
|
10664
|
+
} catch {
|
|
10665
|
+
typecheckError = null;
|
|
10666
|
+
}
|
|
10667
|
+
}
|
|
10668
|
+
}
|
|
10669
|
+
const doneSteps = plan.steps.filter((s) => s.status === "done").length;
|
|
10670
|
+
const terminalSteps = plan.steps.filter((s) => s.status === "done" || s.status === "skipped").length;
|
|
10671
|
+
const totalSteps = plan.steps.length;
|
|
10672
|
+
const auditedFiles = [...new Set([...createFiles, ...deleteFiles])];
|
|
10673
|
+
let massEditWarning = null;
|
|
10674
|
+
if (auditedFiles.length > MASS_EDIT_THRESHOLD) {
|
|
10675
|
+
massEditWarning = t("exec.mass_edit_warning", {
|
|
10676
|
+
count: String(auditedFiles.length)
|
|
10677
|
+
});
|
|
10678
|
+
}
|
|
10679
|
+
const testsFailing = testRun !== null && !testRun.passed;
|
|
10680
|
+
const typecheckFailing = typecheckError !== null;
|
|
10681
|
+
const passed = missingFiles.length === 0 && leftoverFiles.length === 0 && !testsFailing && !typecheckFailing;
|
|
10682
|
+
const stepsPending = terminalSteps < totalSteps;
|
|
10683
|
+
let summary;
|
|
10684
|
+
if (missingFiles.length > 0) {
|
|
10685
|
+
summary = t("exec.audit_fail", {
|
|
10686
|
+
done: doneSteps,
|
|
10687
|
+
total: totalSteps,
|
|
10688
|
+
files: missingFiles.length
|
|
10689
|
+
});
|
|
10690
|
+
} else if (leftoverFiles.length > 0) {
|
|
10691
|
+
summary = t("exec.audit_leftovers", {
|
|
10692
|
+
done: doneSteps,
|
|
10693
|
+
total: totalSteps,
|
|
10694
|
+
files: leftoverFiles.length
|
|
10695
|
+
});
|
|
10696
|
+
} else if (testsFailing) {
|
|
10697
|
+
summary = t("exec.audit_fail_tests", {
|
|
10698
|
+
done: doneSteps,
|
|
10699
|
+
total: totalSteps,
|
|
10700
|
+
failed: String(testRun.failed),
|
|
10701
|
+
passed: String(testRun.passedCount),
|
|
10702
|
+
detail: testRun.detail
|
|
10703
|
+
});
|
|
10704
|
+
} else if (typecheckFailing) {
|
|
10705
|
+
summary = t("exec.audit_fail_typecheck", {
|
|
10706
|
+
done: doneSteps,
|
|
10707
|
+
total: totalSteps,
|
|
10708
|
+
missing: String(missingFiles.length),
|
|
10709
|
+
typeError: typecheckError
|
|
10710
|
+
});
|
|
10711
|
+
} else if (stepsPending) {
|
|
10712
|
+
summary = t("exec.audit_pending", {
|
|
10713
|
+
done: doneSteps,
|
|
10714
|
+
total: totalSteps
|
|
10715
|
+
});
|
|
10716
|
+
} else {
|
|
10717
|
+
summary = t("exec.audit_pass", {
|
|
10718
|
+
done: doneSteps,
|
|
10719
|
+
total: totalSteps,
|
|
10720
|
+
files: existingFiles.length
|
|
10721
|
+
});
|
|
10722
|
+
}
|
|
10723
|
+
return {
|
|
10724
|
+
passed,
|
|
10725
|
+
missingFiles,
|
|
10726
|
+
leftoverFiles,
|
|
10727
|
+
createdFiles: existingFiles,
|
|
10728
|
+
modifiedFiles: [],
|
|
10729
|
+
summary,
|
|
10730
|
+
massEditWarning,
|
|
10731
|
+
testRun,
|
|
10732
|
+
typecheckError
|
|
10733
|
+
};
|
|
10734
|
+
}
|
|
10735
|
+
}
|
|
10736
|
+
var MASS_EDIT_THRESHOLD = 10, RESOLVE_MAX_DEPTH = 8;
|
|
10737
|
+
var init_auditor = __esm(() => {
|
|
10738
|
+
init_i18n();
|
|
10739
|
+
init_js_identifiers();
|
|
10740
|
+
init_audit_runners();
|
|
10741
|
+
init_audit_runners();
|
|
10742
|
+
});
|
|
10743
|
+
|
|
10744
|
+
// src/modules/execution/verifier.ts
|
|
10745
|
+
import { existsSync as existsSync22 } from "fs";
|
|
10746
|
+
import { resolve as resolve13, extname as extname2, join as join13 } from "path";
|
|
10747
|
+
import { spawn as spawn2 } from "child_process";
|
|
10748
|
+
|
|
10749
|
+
class StepVerifier {
|
|
10750
|
+
baseDir;
|
|
10751
|
+
constructor(baseDir) {
|
|
10752
|
+
this.baseDir = baseDir;
|
|
10753
|
+
}
|
|
10754
|
+
async checkFileExists(path) {
|
|
10755
|
+
const resolved = resolve13(this.baseDir, path);
|
|
10756
|
+
const exists = existsSync22(resolved);
|
|
10757
|
+
return {
|
|
10758
|
+
passed: exists,
|
|
10759
|
+
message: exists ? t("verify.file_exists", { path }) : t("verify.file_not_found", { path })
|
|
10760
|
+
};
|
|
10761
|
+
}
|
|
10762
|
+
async runScript(scriptName) {
|
|
10763
|
+
try {
|
|
10764
|
+
await this.runAsync(`bun run ${scriptName}`, this.baseDir, 60000);
|
|
10765
|
+
return { passed: true, message: t("verify.script_passed", { script: scriptName }) };
|
|
10766
|
+
} catch (e) {
|
|
10767
|
+
return {
|
|
10768
|
+
passed: false,
|
|
10769
|
+
message: t("verify.script_failed", { script: scriptName, message: e.message })
|
|
10770
|
+
};
|
|
10771
|
+
}
|
|
10772
|
+
}
|
|
10773
|
+
async runTypeCheck() {
|
|
10774
|
+
const tsconfigPath = resolve13(this.baseDir, "tsconfig.json");
|
|
10775
|
+
if (!existsSync22(tsconfigPath)) {
|
|
10776
|
+
return { passed: true, message: "No tsconfig.json found — skipping type check" };
|
|
10777
|
+
}
|
|
10778
|
+
try {
|
|
10779
|
+
await this.runAsync("npx tsc --noEmit", this.baseDir, 60000);
|
|
10780
|
+
return { passed: true, message: "TypeScript type check passed" };
|
|
10781
|
+
} catch (e) {
|
|
10782
|
+
const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
|
|
10783
|
+
return { passed: false, message: `TypeScript type check failed: ${stderr.slice(0, 500)}` };
|
|
10784
|
+
}
|
|
10785
|
+
}
|
|
10786
|
+
async runTypeCheckForFile(filePath) {
|
|
10787
|
+
const projectRoot = findProjectRoot(filePath, this.baseDir, ["tsconfig.json", "package.json"]);
|
|
10788
|
+
const tsconfigPath = join13(projectRoot, "tsconfig.json");
|
|
10789
|
+
if (!existsSync22(tsconfigPath)) {
|
|
10790
|
+
return { passed: true, message: "No tsconfig.json found — skipping type check" };
|
|
10791
|
+
}
|
|
10792
|
+
try {
|
|
10793
|
+
await this.runAsync("npx tsc --noEmit --skipLibCheck", projectRoot, 60000);
|
|
10794
|
+
return { passed: true, message: "TypeScript type check passed" };
|
|
10795
|
+
} catch (e) {
|
|
10796
|
+
const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
|
|
10797
|
+
return { passed: false, message: `TypeScript type check failed: ${stderr.slice(0, 500)}` };
|
|
10798
|
+
}
|
|
10799
|
+
}
|
|
10800
|
+
async runTests() {
|
|
10801
|
+
const pkgPath = resolve13(this.baseDir, "package.json");
|
|
10802
|
+
if (!existsSync22(pkgPath)) {
|
|
10803
|
+
return { passed: true, message: "No package.json found — skipping tests" };
|
|
10804
|
+
}
|
|
10805
|
+
try {
|
|
10806
|
+
const pkg = JSON.parse(__require("fs").readFileSync(pkgPath, "utf-8"));
|
|
10807
|
+
if (!pkg.scripts?.test) {
|
|
10808
|
+
return { passed: true, message: "No test script defined — skipping tests" };
|
|
10809
|
+
}
|
|
10810
|
+
} catch {}
|
|
10811
|
+
return this.runScript("test");
|
|
10812
|
+
}
|
|
10813
|
+
async verifyArtifactFiles(files) {
|
|
10814
|
+
return Promise.all(files.map((f) => this.checkFileExists(f)));
|
|
10815
|
+
}
|
|
10816
|
+
async verifyMoEManifest(plan, config, allToolTags) {
|
|
10817
|
+
const errors = [];
|
|
10818
|
+
const warnings = [];
|
|
10819
|
+
const details = [];
|
|
10820
|
+
const expertErrors = validateExpertConfig(config, allToolTags);
|
|
10821
|
+
for (const err of expertErrors) {
|
|
10822
|
+
errors.push(err);
|
|
10823
|
+
details.push({ passed: false, message: err });
|
|
10824
|
+
}
|
|
10825
|
+
const tscResult = await this.runTypeCheck();
|
|
10826
|
+
details.push(tscResult);
|
|
10827
|
+
if (!tscResult.passed) {
|
|
10828
|
+
errors.push(tscResult.message);
|
|
10829
|
+
}
|
|
10830
|
+
for (const sub of plan.subtasks) {
|
|
10831
|
+
const fileChecks = await this.verifyArtifactFiles(sub.allowed_files || []);
|
|
10832
|
+
for (const check of fileChecks) {
|
|
10833
|
+
details.push(check);
|
|
10834
|
+
if (!check.passed) {
|
|
10835
|
+
errors.push(check.message);
|
|
10836
|
+
}
|
|
10837
|
+
}
|
|
10838
|
+
}
|
|
10839
|
+
return {
|
|
10840
|
+
success: errors.length === 0,
|
|
10841
|
+
errors,
|
|
10842
|
+
warnings,
|
|
10843
|
+
details
|
|
10844
|
+
};
|
|
10845
|
+
}
|
|
10846
|
+
async verifyStep(stepDescription) {
|
|
10847
|
+
const fileMatches = extractFileLikeTokens(stripUrls(stepDescription));
|
|
10848
|
+
if (fileMatches.length === 0) {
|
|
10849
|
+
return { passed: false, syntaxValid: true, failed: [], noFiles: true };
|
|
10850
|
+
}
|
|
10851
|
+
const results = [];
|
|
10852
|
+
let syntaxValid = true;
|
|
10853
|
+
for (const filePath of fileMatches) {
|
|
10854
|
+
const resolved = findExistingFile(this.baseDir, filePath);
|
|
10855
|
+
if (!resolved) {
|
|
10856
|
+
results.push({ passed: false, message: t("verify.file_not_found", { path: filePath }) });
|
|
10857
|
+
continue;
|
|
10858
|
+
}
|
|
10859
|
+
results.push({ passed: true, message: t("verify.file_exists", { path: filePath }) });
|
|
10860
|
+
if (!await this.validateSyntax(resolved)) {
|
|
10861
|
+
syntaxValid = false;
|
|
10862
|
+
results.push({ passed: false, message: t("verify.syntax_error", { path: filePath }) });
|
|
10863
|
+
}
|
|
10864
|
+
}
|
|
10865
|
+
return {
|
|
10866
|
+
passed: results.every((r) => r.passed),
|
|
10867
|
+
syntaxValid,
|
|
10868
|
+
failed: results.filter((r) => !r.passed),
|
|
10869
|
+
noFiles: false
|
|
10870
|
+
};
|
|
10871
|
+
}
|
|
10872
|
+
async validateSyntax(filePath) {
|
|
10873
|
+
const ext = extname2(filePath);
|
|
10874
|
+
if (ext === ".ts" || ext === ".tsx" || ext === ".cts" || ext === ".mts") {
|
|
10875
|
+
try {
|
|
10876
|
+
await this.runAsync(`npx tsc --noEmit --skipLibCheck ${filePath}`, this.baseDir, 1e4);
|
|
10877
|
+
return true;
|
|
10878
|
+
} catch (err) {
|
|
10879
|
+
if (err.status === 127 || err.message.includes("not found") || err.message.includes("ENOENT")) {
|
|
10880
|
+
return true;
|
|
10881
|
+
}
|
|
10882
|
+
const stderr = err.stderr?.toString() || "";
|
|
10883
|
+
if (stderr.includes("error TS") && !stderr.includes("Cannot find module")) {
|
|
10884
|
+
return false;
|
|
10885
|
+
}
|
|
10886
|
+
return true;
|
|
10887
|
+
}
|
|
10888
|
+
}
|
|
10889
|
+
if (ext === ".js" || ext === ".jsx" || ext === ".cjs" || ext === ".mjs") {
|
|
10890
|
+
try {
|
|
10891
|
+
await this.runAsync(`node --check ${filePath}`, this.baseDir, 5000);
|
|
10892
|
+
return true;
|
|
10893
|
+
} catch {
|
|
10894
|
+
return false;
|
|
10895
|
+
}
|
|
10896
|
+
}
|
|
10897
|
+
return true;
|
|
10898
|
+
}
|
|
10899
|
+
runAsync(command, cwd, timeoutMs) {
|
|
10900
|
+
return new Promise((resolve14, reject) => {
|
|
10901
|
+
const child = spawn2(command, {
|
|
10902
|
+
cwd,
|
|
10903
|
+
shell: true,
|
|
10904
|
+
windowsHide: true,
|
|
10905
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
10906
|
+
});
|
|
10907
|
+
let stdout = "";
|
|
10908
|
+
let stderr = "";
|
|
10909
|
+
child.stdout?.on("data", (d) => {
|
|
10910
|
+
stdout += d.toString();
|
|
10911
|
+
});
|
|
10912
|
+
child.stderr?.on("data", (d) => {
|
|
10913
|
+
stderr += d.toString();
|
|
10914
|
+
});
|
|
10915
|
+
const timer = setTimeout(() => {
|
|
10916
|
+
child.kill();
|
|
10917
|
+
reject(new Error(`Command timed out after ${timeoutMs}ms`));
|
|
10918
|
+
}, timeoutMs);
|
|
10919
|
+
child.on("error", (err) => {
|
|
10920
|
+
clearTimeout(timer);
|
|
10921
|
+
reject(err);
|
|
10922
|
+
});
|
|
10923
|
+
child.on("close", (code) => {
|
|
10924
|
+
clearTimeout(timer);
|
|
10925
|
+
if (code === 0) {
|
|
10926
|
+
resolve14({ stdout, stderr });
|
|
10927
|
+
} else {
|
|
10928
|
+
const err = new Error(`Command failed with exit code ${code}`);
|
|
10929
|
+
err.stdout = stdout;
|
|
10930
|
+
err.stderr = stderr;
|
|
10931
|
+
err.status = code;
|
|
10932
|
+
reject(err);
|
|
10933
|
+
}
|
|
10934
|
+
});
|
|
10935
|
+
});
|
|
10936
|
+
}
|
|
10937
|
+
}
|
|
10938
|
+
var init_verifier = __esm(() => {
|
|
10939
|
+
init_i18n();
|
|
10940
|
+
init_project_root();
|
|
10941
|
+
init_js_identifiers();
|
|
10942
|
+
init_auditor();
|
|
10943
|
+
});
|
|
10944
|
+
|
|
10945
|
+
// src/core/agent-moe.ts
|
|
10946
|
+
async function runWithMoE(deps, input, fallback, opts = {}) {
|
|
10947
|
+
const { config, llmProvider, logger, toolExecutor, baseDir } = deps;
|
|
10948
|
+
const { onMeta, onPhase } = opts;
|
|
10949
|
+
const orchestrator = new OrchestratorClient({
|
|
10950
|
+
model: config.orchestrator.model,
|
|
10951
|
+
provider: config.orchestrator.provider
|
|
10952
|
+
}, llmProvider);
|
|
10953
|
+
if (!orchestrator.isEnabled()) {
|
|
10954
|
+
logger.debug("MoE enabled but no orchestrator model configured — falling back to single-agent");
|
|
10955
|
+
return fallback();
|
|
10956
|
+
}
|
|
10957
|
+
onMeta?.(`\uD83E\uDD16 Planning with MoE mode...
|
|
10958
|
+
`);
|
|
10959
|
+
onPhase?.("thinking");
|
|
10960
|
+
const planResult = await orchestrator.plan(input);
|
|
10961
|
+
onPhase?.("done");
|
|
10962
|
+
if ("error" in planResult) {
|
|
10963
|
+
logger.warn(`MoE plan failed: ${planResult.error} — falling back to single-agent`);
|
|
10964
|
+
return fallback();
|
|
10965
|
+
}
|
|
10966
|
+
const { plan } = planResult;
|
|
10967
|
+
onMeta?.(`\uD83D\uDCCB Plan created: "${plan.title}" (${plan.subtasks.length} subtasks)
|
|
10968
|
+
`);
|
|
10969
|
+
const validation = validatePlan(plan, config);
|
|
10970
|
+
if (!validation.valid) {
|
|
10971
|
+
const applied = applyAutoFixes(plan, validation.autoFixes);
|
|
10972
|
+
const retry = validatePlan(applied, config);
|
|
10973
|
+
if (!retry.valid) {
|
|
10974
|
+
logger.warn(`MoE plan validation failed: ${retry.errors.join("; ")} — falling back to single-agent`);
|
|
10975
|
+
onMeta?.(`⚠️ Plan validation failed. Falling back to single-agent mode.
|
|
10976
|
+
`);
|
|
10977
|
+
return fallback();
|
|
10978
|
+
}
|
|
10979
|
+
Object.assign(plan, applied);
|
|
10980
|
+
onMeta?.(`\uD83D\uDD27 Auto-fixed ${validation.autoFixes.length} plan issues.
|
|
10981
|
+
`);
|
|
10982
|
+
}
|
|
10983
|
+
const moeDeps = {
|
|
10984
|
+
config,
|
|
10985
|
+
toolRegistry: toolExecutor.getRegistry(),
|
|
10986
|
+
toolExecutor,
|
|
10987
|
+
llmProvider,
|
|
10988
|
+
logger,
|
|
10989
|
+
baseDir
|
|
10434
10990
|
};
|
|
10435
10991
|
const executor = new MoEExecutor(moeDeps);
|
|
10436
10992
|
onMeta?.(`⚙️ Executing ${plan.subtasks.length} subtasks...
|
|
@@ -10640,6 +11196,10 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
10640
11196
|
logger.debug(`Session started: ${sessionManager.getActive()}`);
|
|
10641
11197
|
}
|
|
10642
11198
|
if (!this.systemPromptAdded && !contextManager.getActiveHistory().some((m) => m.role === "system")) {
|
|
11199
|
+
const lazy = this.deps.lazyPromptBlocks ? await this.deps.lazyPromptBlocks() : [];
|
|
11200
|
+
if (lazy.length > 0) {
|
|
11201
|
+
this.deps.promptBlocks.push(...lazy);
|
|
11202
|
+
}
|
|
10643
11203
|
const { prompt: systemPrompt, excluded } = this.buildSystemPrompt();
|
|
10644
11204
|
contextManager.addMessage({ role: "system", content: systemPrompt });
|
|
10645
11205
|
this.systemPromptAdded = true;
|
|
@@ -11840,218 +12400,9 @@ var init_confidence = __esm(() => {
|
|
|
11840
12400
|
init_i18n();
|
|
11841
12401
|
});
|
|
11842
12402
|
|
|
11843
|
-
// src/modules/hallucination/js-identifiers.ts
|
|
11844
|
-
function isJsMemberAccess(candidate) {
|
|
11845
|
-
const lower = candidate.toLowerCase();
|
|
11846
|
-
const m = lower.match(/^([\w-]+)\./);
|
|
11847
|
-
return m !== null && JS_GLOBALS.has(m[1]);
|
|
11848
|
-
}
|
|
11849
|
-
function isFilePathLike(s) {
|
|
11850
|
-
const lower = s.toLowerCase();
|
|
11851
|
-
if (TECH_NAMES.has(lower))
|
|
11852
|
-
return false;
|
|
11853
|
-
if (isJsMemberAccess(s))
|
|
11854
|
-
return false;
|
|
11855
|
-
const ext = s.split(".").pop()?.toLowerCase() || "";
|
|
11856
|
-
if (!s.includes("/") && !s.includes("\\")) {
|
|
11857
|
-
if (!/^\w+\.[a-z]{1,6}$/i.test(s))
|
|
11858
|
-
return false;
|
|
11859
|
-
return FILE_EXTENSIONS.has(ext);
|
|
11860
|
-
}
|
|
11861
|
-
const last = s.split(/[\\/]/).pop() ?? "";
|
|
11862
|
-
const lastDot = last.lastIndexOf(".");
|
|
11863
|
-
if (lastDot <= 0)
|
|
11864
|
-
return false;
|
|
11865
|
-
return true;
|
|
11866
|
-
}
|
|
11867
|
-
function extractFileLikeTokens(text) {
|
|
11868
|
-
const matches = text.match(FILE_TOKEN_RE) || [];
|
|
11869
|
-
return [...new Set(matches.filter(isFilePathLike))];
|
|
11870
|
-
}
|
|
11871
|
-
function stripUrls(text) {
|
|
11872
|
-
return text.replace(/\bhttps?:\/\/[^\s"'<>]+/gi, " ");
|
|
11873
|
-
}
|
|
11874
|
-
var JS_GLOBALS, FILE_EXTENSIONS, TECH_NAMES, FILE_TOKEN_RE;
|
|
11875
|
-
var init_js_identifiers = __esm(() => {
|
|
11876
|
-
JS_GLOBALS = new Set([
|
|
11877
|
-
"process",
|
|
11878
|
-
"console",
|
|
11879
|
-
"math",
|
|
11880
|
-
"json",
|
|
11881
|
-
"global",
|
|
11882
|
-
"globalthis",
|
|
11883
|
-
"window",
|
|
11884
|
-
"document",
|
|
11885
|
-
"buffer",
|
|
11886
|
-
"module",
|
|
11887
|
-
"require",
|
|
11888
|
-
"exports",
|
|
11889
|
-
"url",
|
|
11890
|
-
"promise",
|
|
11891
|
-
"array",
|
|
11892
|
-
"object",
|
|
11893
|
-
"string",
|
|
11894
|
-
"number",
|
|
11895
|
-
"boolean",
|
|
11896
|
-
"symbol",
|
|
11897
|
-
"date",
|
|
11898
|
-
"regexp",
|
|
11899
|
-
"error",
|
|
11900
|
-
"map",
|
|
11901
|
-
"set",
|
|
11902
|
-
"weakmap",
|
|
11903
|
-
"weakset",
|
|
11904
|
-
"proxy",
|
|
11905
|
-
"reflect",
|
|
11906
|
-
"intl",
|
|
11907
|
-
"crypto",
|
|
11908
|
-
"performance",
|
|
11909
|
-
"fetch",
|
|
11910
|
-
"navigator",
|
|
11911
|
-
"location",
|
|
11912
|
-
"settimeout",
|
|
11913
|
-
"setinterval",
|
|
11914
|
-
"cleartimeout",
|
|
11915
|
-
"clearinterval",
|
|
11916
|
-
"queuemicrotask",
|
|
11917
|
-
"structuredclone",
|
|
11918
|
-
"textencoder",
|
|
11919
|
-
"textdecoder",
|
|
11920
|
-
"atomics",
|
|
11921
|
-
"sharedarraybuffer",
|
|
11922
|
-
"dataview",
|
|
11923
|
-
"arraybuffer",
|
|
11924
|
-
"bigint",
|
|
11925
|
-
"infinity",
|
|
11926
|
-
"nan",
|
|
11927
|
-
"undefined",
|
|
11928
|
-
"bun",
|
|
11929
|
-
"deno",
|
|
11930
|
-
"node"
|
|
11931
|
-
]);
|
|
11932
|
-
FILE_EXTENSIONS = new Set([
|
|
11933
|
-
"ts",
|
|
11934
|
-
"tsx",
|
|
11935
|
-
"js",
|
|
11936
|
-
"jsx",
|
|
11937
|
-
"mjs",
|
|
11938
|
-
"cjs",
|
|
11939
|
-
"py",
|
|
11940
|
-
"pyc",
|
|
11941
|
-
"rb",
|
|
11942
|
-
"go",
|
|
11943
|
-
"rs",
|
|
11944
|
-
"java",
|
|
11945
|
-
"jar",
|
|
11946
|
-
"kt",
|
|
11947
|
-
"kts",
|
|
11948
|
-
"c",
|
|
11949
|
-
"h",
|
|
11950
|
-
"cc",
|
|
11951
|
-
"cpp",
|
|
11952
|
-
"cxx",
|
|
11953
|
-
"hpp",
|
|
11954
|
-
"hh",
|
|
11955
|
-
"cs",
|
|
11956
|
-
"php",
|
|
11957
|
-
"swift",
|
|
11958
|
-
"m",
|
|
11959
|
-
"mm",
|
|
11960
|
-
"lua",
|
|
11961
|
-
"pl",
|
|
11962
|
-
"sh",
|
|
11963
|
-
"bash",
|
|
11964
|
-
"zsh",
|
|
11965
|
-
"fish",
|
|
11966
|
-
"bat",
|
|
11967
|
-
"cmd",
|
|
11968
|
-
"ps1",
|
|
11969
|
-
"vue",
|
|
11970
|
-
"svelte",
|
|
11971
|
-
"dart",
|
|
11972
|
-
"json",
|
|
11973
|
-
"jsonc",
|
|
11974
|
-
"json5",
|
|
11975
|
-
"yaml",
|
|
11976
|
-
"yml",
|
|
11977
|
-
"toml",
|
|
11978
|
-
"ini",
|
|
11979
|
-
"cfg",
|
|
11980
|
-
"conf",
|
|
11981
|
-
"env",
|
|
11982
|
-
"xml",
|
|
11983
|
-
"html",
|
|
11984
|
-
"htm",
|
|
11985
|
-
"md",
|
|
11986
|
-
"markdown",
|
|
11987
|
-
"rst",
|
|
11988
|
-
"txt",
|
|
11989
|
-
"log",
|
|
11990
|
-
"csv",
|
|
11991
|
-
"tsv",
|
|
11992
|
-
"lock",
|
|
11993
|
-
"sql",
|
|
11994
|
-
"sqlite",
|
|
11995
|
-
"db",
|
|
11996
|
-
"wasm",
|
|
11997
|
-
"map",
|
|
11998
|
-
"css",
|
|
11999
|
-
"scss",
|
|
12000
|
-
"sass",
|
|
12001
|
-
"less",
|
|
12002
|
-
"svg",
|
|
12003
|
-
"png",
|
|
12004
|
-
"jpg",
|
|
12005
|
-
"jpeg",
|
|
12006
|
-
"gif",
|
|
12007
|
-
"webp",
|
|
12008
|
-
"bmp",
|
|
12009
|
-
"ico",
|
|
12010
|
-
"avif",
|
|
12011
|
-
"tiff",
|
|
12012
|
-
"pdf",
|
|
12013
|
-
"doc",
|
|
12014
|
-
"docx",
|
|
12015
|
-
"xls",
|
|
12016
|
-
"xlsx",
|
|
12017
|
-
"ppt",
|
|
12018
|
-
"pptx",
|
|
12019
|
-
"odt",
|
|
12020
|
-
"ods",
|
|
12021
|
-
"zip",
|
|
12022
|
-
"gz",
|
|
12023
|
-
"tar",
|
|
12024
|
-
"tgz",
|
|
12025
|
-
"bz2",
|
|
12026
|
-
"xz",
|
|
12027
|
-
"7z",
|
|
12028
|
-
"rar",
|
|
12029
|
-
"bin",
|
|
12030
|
-
"exe",
|
|
12031
|
-
"dll",
|
|
12032
|
-
"so",
|
|
12033
|
-
"o",
|
|
12034
|
-
"a",
|
|
12035
|
-
"dylib"
|
|
12036
|
-
]);
|
|
12037
|
-
TECH_NAMES = new Set([
|
|
12038
|
-
"node.js",
|
|
12039
|
-
"react.js",
|
|
12040
|
-
"vue.js",
|
|
12041
|
-
"next.js",
|
|
12042
|
-
"angular.js",
|
|
12043
|
-
"express.js",
|
|
12044
|
-
"deno.js",
|
|
12045
|
-
"qwik.js",
|
|
12046
|
-
"svelte.js",
|
|
12047
|
-
"jquery.js"
|
|
12048
|
-
]);
|
|
12049
|
-
FILE_TOKEN_RE = /\b(?:[a-zA-Z]:[\\/])?[\w./\\-]+\.[a-z]{1,6}\b/gi;
|
|
12050
|
-
});
|
|
12051
|
-
|
|
12052
12403
|
// src/modules/hallucination/factual.ts
|
|
12053
|
-
import { existsSync as
|
|
12054
|
-
import { resolve as
|
|
12404
|
+
import { existsSync as existsSync23, readdirSync as readdirSync7 } from "fs";
|
|
12405
|
+
import { resolve as resolve14, isAbsolute as isAbsolute2, join as join14 } from "path";
|
|
12055
12406
|
|
|
12056
12407
|
class FactualCheck {
|
|
12057
12408
|
baseDir;
|
|
@@ -12086,14 +12437,14 @@ class FactualCheck {
|
|
|
12086
12437
|
}
|
|
12087
12438
|
pathExists(fp) {
|
|
12088
12439
|
if (isAbsolute2(fp))
|
|
12089
|
-
return
|
|
12440
|
+
return existsSync23(fp);
|
|
12090
12441
|
if (!fp.includes("/") && !fp.includes("\\")) {
|
|
12091
12442
|
return this.bareNameExists(fp);
|
|
12092
12443
|
}
|
|
12093
|
-
return
|
|
12444
|
+
return existsSync23(resolve14(this.baseDir, fp));
|
|
12094
12445
|
}
|
|
12095
12446
|
bareNameExists(name) {
|
|
12096
|
-
if (
|
|
12447
|
+
if (existsSync23(resolve14(this.baseDir, name)))
|
|
12097
12448
|
return true;
|
|
12098
12449
|
if (this.indexHas(name))
|
|
12099
12450
|
return true;
|
|
@@ -12126,14 +12477,14 @@ class FactualCheck {
|
|
|
12126
12477
|
return count;
|
|
12127
12478
|
let entries;
|
|
12128
12479
|
try {
|
|
12129
|
-
entries =
|
|
12480
|
+
entries = readdirSync7(dir, { withFileTypes: true });
|
|
12130
12481
|
} catch {
|
|
12131
12482
|
return count;
|
|
12132
12483
|
}
|
|
12133
12484
|
for (const entry of entries) {
|
|
12134
12485
|
if (count >= FactualCheck.MAX_INDEXED_FILES)
|
|
12135
12486
|
break;
|
|
12136
|
-
const full =
|
|
12487
|
+
const full = join14(dir, entry.name);
|
|
12137
12488
|
if (entry.isDirectory()) {
|
|
12138
12489
|
if (!IGNORED_DIRS.has(entry.name)) {
|
|
12139
12490
|
count = this.scanDir(full, index, count);
|
|
@@ -12304,8 +12655,8 @@ var init_detector = __esm(() => {
|
|
|
12304
12655
|
});
|
|
12305
12656
|
|
|
12306
12657
|
// src/modules/lsp/command.ts
|
|
12307
|
-
import { delimiter, join as
|
|
12308
|
-
import { existsSync as
|
|
12658
|
+
import { delimiter, join as join15 } from "path";
|
|
12659
|
+
import { existsSync as existsSync24 } from "fs";
|
|
12309
12660
|
import { platform as platform3 } from "os";
|
|
12310
12661
|
function resolveSpawnCommand(command, platformName = platform3(), pathEnv = process.env.PATH ?? "") {
|
|
12311
12662
|
if (platformName !== "win32")
|
|
@@ -12316,8 +12667,8 @@ function resolveSpawnCommand(command, platformName = platform3(), pathEnv = proc
|
|
|
12316
12667
|
const dirs = pathEnv.split(delimiter).filter(Boolean);
|
|
12317
12668
|
for (const dir of dirs) {
|
|
12318
12669
|
for (const ext of WIN_EXTS) {
|
|
12319
|
-
const candidate =
|
|
12320
|
-
if (
|
|
12670
|
+
const candidate = join15(dir, `${command}${ext}`);
|
|
12671
|
+
if (existsSync24(candidate))
|
|
12321
12672
|
return `${command}${ext}`;
|
|
12322
12673
|
}
|
|
12323
12674
|
}
|
|
@@ -12418,8 +12769,8 @@ class Updater {
|
|
|
12418
12769
|
}
|
|
12419
12770
|
var defaultRunner = async (command, args, options) => {
|
|
12420
12771
|
const { execFile } = await import("child_process");
|
|
12421
|
-
return new Promise((
|
|
12422
|
-
execFile(command, args, options, (err) =>
|
|
12772
|
+
return new Promise((resolve15) => {
|
|
12773
|
+
execFile(command, args, options, (err) => resolve15({ error: err?.message }));
|
|
12423
12774
|
});
|
|
12424
12775
|
};
|
|
12425
12776
|
var init_checker = __esm(() => {
|
|
@@ -12789,17 +13140,17 @@ Iterations: ${result.iterationCount}`
|
|
|
12789
13140
|
});
|
|
12790
13141
|
|
|
12791
13142
|
// src/tools/scope-check.ts
|
|
12792
|
-
import { resolve as
|
|
13143
|
+
import { resolve as resolve15, normalize as normalize4 } from "path";
|
|
12793
13144
|
function isPathInScope2(baseDir, targetPath, scope) {
|
|
12794
|
-
const baseResolved =
|
|
12795
|
-
const targetResolved =
|
|
13145
|
+
const baseResolved = resolve15(baseDir);
|
|
13146
|
+
const targetResolved = resolve15(baseDir, normalize4(targetPath));
|
|
12796
13147
|
if (!targetResolved.startsWith(baseResolved)) {
|
|
12797
13148
|
return { allowed: false, reason: "Path is outside the base working directory" };
|
|
12798
13149
|
}
|
|
12799
13150
|
if (!scope)
|
|
12800
13151
|
return { allowed: true };
|
|
12801
|
-
const isRead = scope.read_only_files.some((f) => targetResolved.startsWith(
|
|
12802
|
-
const isWrite = scope.allowed_files.some((f) => targetResolved.startsWith(
|
|
13152
|
+
const isRead = scope.read_only_files.some((f) => targetResolved.startsWith(resolve15(baseDir, f)));
|
|
13153
|
+
const isWrite = scope.allowed_files.some((f) => targetResolved.startsWith(resolve15(baseDir, f)));
|
|
12803
13154
|
if (isWrite)
|
|
12804
13155
|
return { allowed: true };
|
|
12805
13156
|
if (isRead)
|
|
@@ -12913,7 +13264,7 @@ var DEFAULT_CHUNK_SYSTEM_PROMPT = 'Answer the query using ONLY the provided text
|
|
|
12913
13264
|
|
|
12914
13265
|
// src/tools/chunk-query.ts
|
|
12915
13266
|
import { readFileSync as readFileSync10 } from "node:fs";
|
|
12916
|
-
import { resolve as
|
|
13267
|
+
import { resolve as resolve16 } from "node:path";
|
|
12917
13268
|
var chunkQueryTool;
|
|
12918
13269
|
var init_chunk_query = __esm(() => {
|
|
12919
13270
|
init_i18n();
|
|
@@ -12981,7 +13332,7 @@ var init_chunk_query = __esm(() => {
|
|
|
12981
13332
|
return { success: false, output: `[SCOPE] ${check.reason || "Path not allowed"}` };
|
|
12982
13333
|
}
|
|
12983
13334
|
try {
|
|
12984
|
-
content = readFileSync10(
|
|
13335
|
+
content = readFileSync10(resolve16(ctx.baseDir, inputPath), "utf8");
|
|
12985
13336
|
} catch (e) {
|
|
12986
13337
|
return { success: false, output: `Cannot read ${inputPath}: ${e.message}` };
|
|
12987
13338
|
}
|
|
@@ -13323,7 +13674,7 @@ var init_web_browse = __esm(() => {
|
|
|
13323
13674
|
|
|
13324
13675
|
// src/tools/download-file.ts
|
|
13325
13676
|
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync12 } from "fs";
|
|
13326
|
-
import { dirname as
|
|
13677
|
+
import { dirname as dirname9 } from "path";
|
|
13327
13678
|
var MAX_DOWNLOAD_BYTES, downloadFileTool;
|
|
13328
13679
|
var init_download_file = __esm(() => {
|
|
13329
13680
|
init_i18n();
|
|
@@ -13410,7 +13761,7 @@ var init_download_file = __esm(() => {
|
|
|
13410
13761
|
output: t("tool.download_too_large", { max: String(maxBytes) })
|
|
13411
13762
|
};
|
|
13412
13763
|
}
|
|
13413
|
-
mkdirSync12(
|
|
13764
|
+
mkdirSync12(dirname9(resolved), { recursive: true });
|
|
13414
13765
|
writeFileSync8(resolved, buffer);
|
|
13415
13766
|
const contentType = response.headers.get("content-type")?.split(";")[0]?.trim() || "unknown";
|
|
13416
13767
|
logNetworkRequest(ctx.sessionId, sanitizeUrl(url), true, `Status: ${response.status}`);
|
|
@@ -13867,7 +14218,7 @@ class MCPClient {
|
|
|
13867
14218
|
}
|
|
13868
14219
|
}
|
|
13869
14220
|
connectStdio() {
|
|
13870
|
-
return new Promise((
|
|
14221
|
+
return new Promise((resolve17, reject) => {
|
|
13871
14222
|
const child = spawn3(this.config.command, this.config.args || [], {
|
|
13872
14223
|
env: { ...process.env, ...this.config.env },
|
|
13873
14224
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -13891,7 +14242,7 @@ class MCPClient {
|
|
|
13891
14242
|
this._connected = true;
|
|
13892
14243
|
this.process = child;
|
|
13893
14244
|
_resolved = true;
|
|
13894
|
-
|
|
14245
|
+
resolve17();
|
|
13895
14246
|
});
|
|
13896
14247
|
});
|
|
13897
14248
|
}
|
|
@@ -13960,7 +14311,7 @@ class MCPClient {
|
|
|
13960
14311
|
if (!this.process) {
|
|
13961
14312
|
throw new Error("Not connected to MCP server");
|
|
13962
14313
|
}
|
|
13963
|
-
return new Promise((
|
|
14314
|
+
return new Promise((resolve17, reject) => {
|
|
13964
14315
|
const request = {
|
|
13965
14316
|
jsonrpc: "2.0",
|
|
13966
14317
|
id: Date.now(),
|
|
@@ -13984,7 +14335,7 @@ class MCPClient {
|
|
|
13984
14335
|
if (response.error) {
|
|
13985
14336
|
reject(new Error(response.error.message));
|
|
13986
14337
|
} else {
|
|
13987
|
-
|
|
14338
|
+
resolve17(response.result?.tools || []);
|
|
13988
14339
|
}
|
|
13989
14340
|
return;
|
|
13990
14341
|
}
|
|
@@ -14066,8 +14417,8 @@ class MCPClient {
|
|
|
14066
14417
|
arguments: args
|
|
14067
14418
|
}
|
|
14068
14419
|
};
|
|
14069
|
-
return new Promise((
|
|
14070
|
-
this.pendingResolve =
|
|
14420
|
+
return new Promise((resolve17, reject) => {
|
|
14421
|
+
this.pendingResolve = resolve17;
|
|
14071
14422
|
this.pendingReject = reject;
|
|
14072
14423
|
const timeout = this.config.timeout || 30000;
|
|
14073
14424
|
const timer = setTimeout(() => {
|
|
@@ -14094,7 +14445,7 @@ class MCPClient {
|
|
|
14094
14445
|
if (result.error) {
|
|
14095
14446
|
reject(new Error(result.error.message));
|
|
14096
14447
|
} else {
|
|
14097
|
-
|
|
14448
|
+
resolve17(result.result || result);
|
|
14098
14449
|
}
|
|
14099
14450
|
}).catch((err) => {
|
|
14100
14451
|
clearTimeout(timer);
|
|
@@ -14108,7 +14459,7 @@ class MCPClient {
|
|
|
14108
14459
|
if (!this.process) {
|
|
14109
14460
|
throw new Error("Not connected to MCP server");
|
|
14110
14461
|
}
|
|
14111
|
-
return new Promise((
|
|
14462
|
+
return new Promise((resolve17, reject) => {
|
|
14112
14463
|
const request = {
|
|
14113
14464
|
jsonrpc: "2.0",
|
|
14114
14465
|
id: Date.now(),
|
|
@@ -14136,7 +14487,7 @@ class MCPClient {
|
|
|
14136
14487
|
if (response.error) {
|
|
14137
14488
|
reject(new Error(response.error.message));
|
|
14138
14489
|
} else {
|
|
14139
|
-
|
|
14490
|
+
resolve17(response.result);
|
|
14140
14491
|
}
|
|
14141
14492
|
return;
|
|
14142
14493
|
}
|
|
@@ -14247,7 +14598,7 @@ ${JSON.stringify(result, null, 2)}`
|
|
|
14247
14598
|
|
|
14248
14599
|
// src/tools/search-history.ts
|
|
14249
14600
|
import * as fs from "fs";
|
|
14250
|
-
import { join as
|
|
14601
|
+
import { join as join16 } from "path";
|
|
14251
14602
|
import { homedir as homedir5 } from "os";
|
|
14252
14603
|
function searchFile(filePath, query, maxResults, results) {
|
|
14253
14604
|
if (!fs.existsSync(filePath))
|
|
@@ -14291,7 +14642,7 @@ var init_search_history = __esm(() => {
|
|
|
14291
14642
|
const query = String(args.query || "").toLowerCase();
|
|
14292
14643
|
const maxResults = Number(args.maxResults) || 5;
|
|
14293
14644
|
const sessionId = args.sessionId ? String(args.sessionId) : null;
|
|
14294
|
-
const sessionDir =
|
|
14645
|
+
const sessionDir = join16(homedir5(), ".mma", "sessions");
|
|
14295
14646
|
const results = [];
|
|
14296
14647
|
try {
|
|
14297
14648
|
if (!fs.existsSync(sessionDir)) {
|
|
@@ -14306,7 +14657,7 @@ var init_search_history = __esm(() => {
|
|
|
14306
14657
|
continue;
|
|
14307
14658
|
if (sessionId && entry.name !== sessionId)
|
|
14308
14659
|
continue;
|
|
14309
|
-
const historyFile =
|
|
14660
|
+
const historyFile = join16(sessionDir, entry.name, "history.jsonl");
|
|
14310
14661
|
searchFile(historyFile, query, maxResults, results);
|
|
14311
14662
|
if (results.length >= maxResults)
|
|
14312
14663
|
break;
|
|
@@ -14333,8 +14684,8 @@ var init_search_history = __esm(() => {
|
|
|
14333
14684
|
});
|
|
14334
14685
|
|
|
14335
14686
|
// src/modules/memory/search.ts
|
|
14336
|
-
import { readFileSync as readFileSync12, existsSync as
|
|
14337
|
-
import { join as
|
|
14687
|
+
import { readFileSync as readFileSync12, existsSync as existsSync26 } from "fs";
|
|
14688
|
+
import { join as join17 } from "path";
|
|
14338
14689
|
|
|
14339
14690
|
class MemorySearch {
|
|
14340
14691
|
memoryDir;
|
|
@@ -14345,8 +14696,8 @@ class MemorySearch {
|
|
|
14345
14696
|
const results = [];
|
|
14346
14697
|
const lowerQuery = query.toLowerCase();
|
|
14347
14698
|
for (const name of MEMORY_FILES) {
|
|
14348
|
-
const path =
|
|
14349
|
-
if (!
|
|
14699
|
+
const path = join17(this.memoryDir, `${name}.md`);
|
|
14700
|
+
if (!existsSync26(path))
|
|
14350
14701
|
continue;
|
|
14351
14702
|
const content = readFileSync12(path, "utf-8");
|
|
14352
14703
|
const lines = content.split(`
|
|
@@ -14357,8 +14708,8 @@ class MemorySearch {
|
|
|
14357
14708
|
}
|
|
14358
14709
|
}
|
|
14359
14710
|
}
|
|
14360
|
-
const prefsPath =
|
|
14361
|
-
if (
|
|
14711
|
+
const prefsPath = join17(this.memoryDir, "preferences.json");
|
|
14712
|
+
if (existsSync26(prefsPath)) {
|
|
14362
14713
|
try {
|
|
14363
14714
|
const prefs = JSON.parse(readFileSync12(prefsPath, "utf-8"));
|
|
14364
14715
|
for (const [key, value] of Object.entries(prefs)) {
|
|
@@ -14378,8 +14729,8 @@ var init_search = __esm(() => {
|
|
|
14378
14729
|
});
|
|
14379
14730
|
|
|
14380
14731
|
// src/modules/memory/store.ts
|
|
14381
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, appendFileSync as appendFileSync5, existsSync as
|
|
14382
|
-
import { join as
|
|
14732
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, appendFileSync as appendFileSync5, existsSync as existsSync27, mkdirSync as mkdirSync13 } from "fs";
|
|
14733
|
+
import { join as join18 } from "path";
|
|
14383
14734
|
|
|
14384
14735
|
class MemoryStore {
|
|
14385
14736
|
memoryDir;
|
|
@@ -14387,8 +14738,8 @@ class MemoryStore {
|
|
|
14387
14738
|
this.memoryDir = memoryDir;
|
|
14388
14739
|
this.ensureDir();
|
|
14389
14740
|
for (const name of MEMORY_FILES2) {
|
|
14390
|
-
const path =
|
|
14391
|
-
if (!
|
|
14741
|
+
const path = join18(this.memoryDir, `${name}.md`);
|
|
14742
|
+
if (!existsSync27(path)) {
|
|
14392
14743
|
writeFileSync9(path, `# ${name.charAt(0).toUpperCase() + name.slice(1)}
|
|
14393
14744
|
|
|
14394
14745
|
`, "utf-8");
|
|
@@ -14396,18 +14747,18 @@ class MemoryStore {
|
|
|
14396
14747
|
}
|
|
14397
14748
|
}
|
|
14398
14749
|
ensureDir() {
|
|
14399
|
-
if (!
|
|
14750
|
+
if (!existsSync27(this.memoryDir)) {
|
|
14400
14751
|
mkdirSync13(this.memoryDir, { recursive: true });
|
|
14401
14752
|
}
|
|
14402
14753
|
}
|
|
14403
14754
|
read(name) {
|
|
14404
|
-
const path =
|
|
14405
|
-
if (!
|
|
14755
|
+
const path = join18(this.memoryDir, `${name}.md`);
|
|
14756
|
+
if (!existsSync27(path))
|
|
14406
14757
|
return "";
|
|
14407
14758
|
return readFileSync13(path, "utf-8");
|
|
14408
14759
|
}
|
|
14409
14760
|
append(name, entry) {
|
|
14410
|
-
const path =
|
|
14761
|
+
const path = join18(this.memoryDir, `${name}.md`);
|
|
14411
14762
|
const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
14412
14763
|
const formatted = `- **${timestamp}** — ${entry}
|
|
14413
14764
|
`;
|
|
@@ -14418,11 +14769,11 @@ class MemoryStore {
|
|
|
14418
14769
|
return searchModule.query(query);
|
|
14419
14770
|
}
|
|
14420
14771
|
prefsPath() {
|
|
14421
|
-
return
|
|
14772
|
+
return join18(this.memoryDir, "preferences.json");
|
|
14422
14773
|
}
|
|
14423
14774
|
getPreferences() {
|
|
14424
14775
|
const path = this.prefsPath();
|
|
14425
|
-
if (!
|
|
14776
|
+
if (!existsSync27(path))
|
|
14426
14777
|
return {};
|
|
14427
14778
|
try {
|
|
14428
14779
|
return JSON.parse(readFileSync13(path, "utf-8"));
|
|
@@ -14458,7 +14809,7 @@ var init_store2 = __esm(() => {
|
|
|
14458
14809
|
|
|
14459
14810
|
// src/tools/remember.ts
|
|
14460
14811
|
import { homedir as homedir6 } from "os";
|
|
14461
|
-
import { join as
|
|
14812
|
+
import { join as join19 } from "path";
|
|
14462
14813
|
var CATEGORIES, rememberTool;
|
|
14463
14814
|
var init_remember = __esm(() => {
|
|
14464
14815
|
init_i18n();
|
|
@@ -14496,7 +14847,7 @@ var init_remember = __esm(() => {
|
|
|
14496
14847
|
if (!CATEGORIES.includes(category)) {
|
|
14497
14848
|
return { success: false, output: t("tool.invalid_params") };
|
|
14498
14849
|
}
|
|
14499
|
-
const memoryDir =
|
|
14850
|
+
const memoryDir = join19(homedir6(), ".mma", "memory");
|
|
14500
14851
|
const store = new MemoryStore(memoryDir);
|
|
14501
14852
|
try {
|
|
14502
14853
|
if (category === "preferences") {
|
|
@@ -14529,7 +14880,7 @@ var init_remember = __esm(() => {
|
|
|
14529
14880
|
|
|
14530
14881
|
// src/tools/recall.ts
|
|
14531
14882
|
import { homedir as homedir7 } from "os";
|
|
14532
|
-
import { join as
|
|
14883
|
+
import { join as join20 } from "path";
|
|
14533
14884
|
function formatAll(store) {
|
|
14534
14885
|
const parts = [];
|
|
14535
14886
|
const prefs = store.getPreferences();
|
|
@@ -14612,7 +14963,7 @@ var init_recall = __esm(() => {
|
|
|
14612
14963
|
handler: async (_ctx, args) => {
|
|
14613
14964
|
const query = args.query ? String(args.query) : "";
|
|
14614
14965
|
const category = args.category ? String(args.category) : "";
|
|
14615
|
-
const memoryDir =
|
|
14966
|
+
const memoryDir = join20(homedir7(), ".mma", "memory");
|
|
14616
14967
|
const store = new MemoryStore(memoryDir);
|
|
14617
14968
|
try {
|
|
14618
14969
|
if (!query && !category) {
|
|
@@ -14650,9 +15001,9 @@ var init_recall = __esm(() => {
|
|
|
14650
15001
|
});
|
|
14651
15002
|
|
|
14652
15003
|
// src/modules/browser/bridge-path.ts
|
|
14653
|
-
import { existsSync as
|
|
15004
|
+
import { existsSync as existsSync28 } from "fs";
|
|
14654
15005
|
function pickExistingPath(candidates, fallback = candidates[0]) {
|
|
14655
|
-
return candidates.find((p) =>
|
|
15006
|
+
return candidates.find((p) => existsSync28(p)) ?? fallback;
|
|
14656
15007
|
}
|
|
14657
15008
|
var init_bridge_path = () => {};
|
|
14658
15009
|
|
|
@@ -14663,13 +15014,13 @@ __export(exports_bridge_client, {
|
|
|
14663
15014
|
});
|
|
14664
15015
|
import { spawn as spawn4 } from "child_process";
|
|
14665
15016
|
import { createInterface } from "readline";
|
|
14666
|
-
import { dirname as
|
|
15017
|
+
import { dirname as dirname10, join as join21 } from "path";
|
|
14667
15018
|
import { fileURLToPath } from "url";
|
|
14668
15019
|
function bridgeScriptPath() {
|
|
14669
|
-
const dir =
|
|
15020
|
+
const dir = dirname10(fileURLToPath(import.meta.url));
|
|
14670
15021
|
const candidates = [
|
|
14671
|
-
|
|
14672
|
-
|
|
15022
|
+
join21(dir, "bridge-server.mjs"),
|
|
15023
|
+
join21(dir, "modules", "browser", "bridge-server.mjs")
|
|
14673
15024
|
];
|
|
14674
15025
|
return pickExistingPath(candidates);
|
|
14675
15026
|
}
|
|
@@ -14695,14 +15046,14 @@ class BridgeDriver {
|
|
|
14695
15046
|
return this.send(cmd, params, true);
|
|
14696
15047
|
}
|
|
14697
15048
|
const id = this.nextId++;
|
|
14698
|
-
const response = await new Promise((
|
|
15049
|
+
const response = await new Promise((resolve17, reject) => {
|
|
14699
15050
|
const timer = setTimeout(() => {
|
|
14700
15051
|
this.pending.delete(id);
|
|
14701
15052
|
reject(new Error(`Bridge timeout waiting for "${cmd}"`));
|
|
14702
15053
|
}, BRIDGE_REQUEST_TIMEOUT_MS);
|
|
14703
15054
|
this.pending.set(id, (resp) => {
|
|
14704
15055
|
clearTimeout(timer);
|
|
14705
|
-
|
|
15056
|
+
resolve17(resp);
|
|
14706
15057
|
});
|
|
14707
15058
|
this.proc.stdin.write(JSON.stringify({ id, cmd, params }) + `
|
|
14708
15059
|
`);
|
|
@@ -15223,15 +15574,15 @@ function buildTextExtractionScript() {
|
|
|
15223
15574
|
|
|
15224
15575
|
// src/modules/browser/cookie-store.ts
|
|
15225
15576
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
15226
|
-
import { join as
|
|
15577
|
+
import { join as join22 } from "path";
|
|
15227
15578
|
|
|
15228
15579
|
class CookieStore {
|
|
15229
15580
|
filePath;
|
|
15230
15581
|
constructor(cookieDir) {
|
|
15231
|
-
this.filePath =
|
|
15582
|
+
this.filePath = join22(cookieDir, "cookies.json");
|
|
15232
15583
|
}
|
|
15233
15584
|
async save(cookies) {
|
|
15234
|
-
await mkdir(
|
|
15585
|
+
await mkdir(join22(this.filePath, ".."), { recursive: true });
|
|
15235
15586
|
await writeFile(this.filePath, JSON.stringify(cookies, null, 2), "utf-8");
|
|
15236
15587
|
}
|
|
15237
15588
|
async load() {
|
|
@@ -15586,10 +15937,10 @@ var init_session = __esm(() => {
|
|
|
15586
15937
|
});
|
|
15587
15938
|
|
|
15588
15939
|
// src/tools/browser.ts
|
|
15589
|
-
import { join as
|
|
15940
|
+
import { join as join23 } from "path";
|
|
15590
15941
|
function getSession(ctx) {
|
|
15591
15942
|
if (!session) {
|
|
15592
|
-
const cookieDir =
|
|
15943
|
+
const cookieDir = join23(ctx.baseDir, ".mma", "browser");
|
|
15593
15944
|
session = new BrowserSession({
|
|
15594
15945
|
...DEFAULT_BROWSER_CONFIG,
|
|
15595
15946
|
headless: ctx.config.browser?.headless ?? true,
|
|
@@ -15736,8 +16087,8 @@ async function readClipboardFallback() {
|
|
|
15736
16087
|
const { platform: platform5 } = await import("os");
|
|
15737
16088
|
const { execSync } = await import("child_process");
|
|
15738
16089
|
const { readFileSync: readFileSync15, unlinkSync: unlinkSync4 } = await import("fs");
|
|
15739
|
-
const { join:
|
|
15740
|
-
const tmpPath =
|
|
16090
|
+
const { join: join24 } = await import("path");
|
|
16091
|
+
const tmpPath = join24(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
|
|
15741
16092
|
try {
|
|
15742
16093
|
if (platform5() === "linux") {
|
|
15743
16094
|
execSync(`xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`, {
|
|
@@ -15817,8 +16168,8 @@ var init_image_utils = __esm(() => {
|
|
|
15817
16168
|
});
|
|
15818
16169
|
|
|
15819
16170
|
// src/tools/attach-image.ts
|
|
15820
|
-
import { existsSync as
|
|
15821
|
-
import { resolve as
|
|
16171
|
+
import { existsSync as existsSync29 } from "fs";
|
|
16172
|
+
import { resolve as resolve17 } from "path";
|
|
15822
16173
|
var attachImageTool;
|
|
15823
16174
|
var init_attach_image = __esm(() => {
|
|
15824
16175
|
init_i18n();
|
|
@@ -15859,8 +16210,8 @@ var init_attach_image = __esm(() => {
|
|
|
15859
16210
|
const result = await loadUrlAsDataUrl(source);
|
|
15860
16211
|
dataUrl = result.dataUrl;
|
|
15861
16212
|
} else {
|
|
15862
|
-
const absPath =
|
|
15863
|
-
if (!
|
|
16213
|
+
const absPath = resolve17(ctx.baseDir, source);
|
|
16214
|
+
if (!existsSync29(absPath)) {
|
|
15864
16215
|
return {
|
|
15865
16216
|
success: false,
|
|
15866
16217
|
output: t("image.not_found", { path: source })
|
|
@@ -16116,16 +16467,16 @@ class ModuleRegistry {
|
|
|
16116
16467
|
}
|
|
16117
16468
|
|
|
16118
16469
|
// src/modules/plugins/loader.ts
|
|
16119
|
-
import { readdirSync as
|
|
16120
|
-
import { join as
|
|
16470
|
+
import { readdirSync as readdirSync9, existsSync as existsSync30, statSync as statSync5 } from "fs";
|
|
16471
|
+
import { join as join24, basename as basename3 } from "path";
|
|
16121
16472
|
|
|
16122
16473
|
class PluginLoader {
|
|
16123
16474
|
loadFromDir(dirPath, pluginManager, logger, options) {
|
|
16124
|
-
if (!
|
|
16475
|
+
if (!existsSync30(dirPath))
|
|
16125
16476
|
return;
|
|
16126
|
-
const entries =
|
|
16477
|
+
const entries = readdirSync9(dirPath).sort();
|
|
16127
16478
|
for (const entry of entries) {
|
|
16128
|
-
const fullPath =
|
|
16479
|
+
const fullPath = join24(dirPath, entry);
|
|
16129
16480
|
const stat = statSync5(fullPath);
|
|
16130
16481
|
if (stat.isFile()) {
|
|
16131
16482
|
if (!entry.endsWith(".ts") && !entry.endsWith(".js"))
|
|
@@ -16134,15 +16485,15 @@ class PluginLoader {
|
|
|
16134
16485
|
} else if (stat.isDirectory()) {
|
|
16135
16486
|
const entryFile = this.findEntryFile(fullPath);
|
|
16136
16487
|
if (entryFile) {
|
|
16137
|
-
this.tryLoad(entryFile, `${entry}/${
|
|
16488
|
+
this.tryLoad(entryFile, `${entry}/${basename3(entryFile)}`, pluginManager, logger, options);
|
|
16138
16489
|
}
|
|
16139
16490
|
}
|
|
16140
16491
|
}
|
|
16141
16492
|
}
|
|
16142
16493
|
findEntryFile(dir) {
|
|
16143
16494
|
for (const name of FOLDER_ENTRY_NAMES) {
|
|
16144
|
-
const candidate =
|
|
16145
|
-
if (
|
|
16495
|
+
const candidate = join24(dir, name);
|
|
16496
|
+
if (existsSync30(candidate))
|
|
16146
16497
|
return candidate;
|
|
16147
16498
|
}
|
|
16148
16499
|
return null;
|
|
@@ -16184,8 +16535,8 @@ var init_loader = __esm(() => {
|
|
|
16184
16535
|
|
|
16185
16536
|
// src/modules/plugins/builtin/lint-on-write.ts
|
|
16186
16537
|
import { spawn as spawn5, execSync } from "child_process";
|
|
16187
|
-
import { existsSync as
|
|
16188
|
-
import { resolve as
|
|
16538
|
+
import { existsSync as existsSync31, readFileSync as readFileSync15 } from "fs";
|
|
16539
|
+
import { resolve as resolve18, extname as extname4, join as join25 } from "path";
|
|
16189
16540
|
import { platform as platform5 } from "os";
|
|
16190
16541
|
function contentHash(content) {
|
|
16191
16542
|
let h = 5381;
|
|
@@ -16231,8 +16582,8 @@ class LintOnWritePlugin {
|
|
|
16231
16582
|
const path = String(call.arguments.path || "");
|
|
16232
16583
|
if (!path)
|
|
16233
16584
|
return;
|
|
16234
|
-
const fullPath =
|
|
16235
|
-
if (!
|
|
16585
|
+
const fullPath = resolve18(ctx.baseDir, path);
|
|
16586
|
+
if (!existsSync31(fullPath))
|
|
16236
16587
|
return;
|
|
16237
16588
|
const ext = extname4(fullPath);
|
|
16238
16589
|
const syntaxError = await this.checkSyntax(fullPath, ext, ctx.baseDir);
|
|
@@ -16288,8 +16639,8 @@ class LintOnWritePlugin {
|
|
|
16288
16639
|
}
|
|
16289
16640
|
async runProjectLint(ctx, result) {
|
|
16290
16641
|
try {
|
|
16291
|
-
const packageJsonPath =
|
|
16292
|
-
if (!
|
|
16642
|
+
const packageJsonPath = join25(ctx.baseDir, "package.json");
|
|
16643
|
+
if (!existsSync31(packageJsonPath)) {
|
|
16293
16644
|
return;
|
|
16294
16645
|
}
|
|
16295
16646
|
const packageJson = JSON.parse(readFileSync15(packageJsonPath, "utf-8"));
|
|
@@ -16301,16 +16652,21 @@ class LintOnWritePlugin {
|
|
|
16301
16652
|
await runAsync(lintScript, ctx.baseDir, 30000);
|
|
16302
16653
|
ctx.logger.debug("Lint passed");
|
|
16303
16654
|
} catch (err) {
|
|
16655
|
+
const stderr = (err.stderr?.toString?.() || "").trim();
|
|
16656
|
+
const stdout = (err.stdout?.toString?.() || "").trim();
|
|
16657
|
+
const detail = (stderr || stdout || err.message).split(`
|
|
16658
|
+
`).filter(Boolean).slice(-5).join(`
|
|
16659
|
+
`);
|
|
16304
16660
|
ctx.logger.warn(`Lint failed: ${err.message}`);
|
|
16305
16661
|
result.output += `
|
|
16306
16662
|
|
|
16307
|
-
[Project lint failed]: ${
|
|
16663
|
+
[Project lint failed]: ${detail}`;
|
|
16308
16664
|
}
|
|
16309
16665
|
}
|
|
16310
16666
|
async runProjectTypeCheck(filePath, baseDir, result) {
|
|
16311
16667
|
const projectRoot = findProjectRoot(filePath, baseDir, ["tsconfig.json", "package.json"]);
|
|
16312
|
-
const tsconfigPath =
|
|
16313
|
-
if (!
|
|
16668
|
+
const tsconfigPath = join25(projectRoot, "tsconfig.json");
|
|
16669
|
+
if (!existsSync31(tsconfigPath)) {
|
|
16314
16670
|
return;
|
|
16315
16671
|
}
|
|
16316
16672
|
const now = Date.now();
|
|
@@ -16351,7 +16707,7 @@ class LintOnWritePlugin {
|
|
|
16351
16707
|
}
|
|
16352
16708
|
}
|
|
16353
16709
|
function runAsync(command, cwd, timeoutMs) {
|
|
16354
|
-
return new Promise((
|
|
16710
|
+
return new Promise((resolve19, reject) => {
|
|
16355
16711
|
const child = spawn5(command, {
|
|
16356
16712
|
cwd,
|
|
16357
16713
|
shell: true,
|
|
@@ -16380,7 +16736,7 @@ function runAsync(command, cwd, timeoutMs) {
|
|
|
16380
16736
|
stdout += decoder.decode();
|
|
16381
16737
|
stderr += decoder.decode();
|
|
16382
16738
|
if (code === 0) {
|
|
16383
|
-
|
|
16739
|
+
resolve19({ stdout, stderr });
|
|
16384
16740
|
} else {
|
|
16385
16741
|
const err = new Error(`Command failed with exit code ${code}`);
|
|
16386
16742
|
err.stdout = stdout;
|
|
@@ -16453,399 +16809,110 @@ class PlanCreator {
|
|
|
16453
16809
|
status: "pending",
|
|
16454
16810
|
kind: kinds?.[i] ?? "create"
|
|
16455
16811
|
})),
|
|
16456
|
-
createdAt: new Date().toISOString(),
|
|
16457
|
-
baseDir
|
|
16458
|
-
};
|
|
16459
|
-
}
|
|
16460
|
-
static replan(plan, newSteps, title) {
|
|
16461
|
-
const kept = plan.steps.filter((s) => s.status === "done" || s.status === "skipped");
|
|
16462
|
-
const baseTitle = plan.title.replace(/^\[\d+[^]]*\]\s*/, "");
|
|
16463
|
-
const newTitle = title || baseTitle;
|
|
16464
|
-
const totalSteps = kept.length + newSteps.length;
|
|
16465
|
-
const added = newSteps.map((desc, i) => ({
|
|
16466
|
-
id: kept.length + i + 1,
|
|
16467
|
-
description: desc,
|
|
16468
|
-
status: "pending",
|
|
16469
|
-
kind: "create"
|
|
16470
|
-
}));
|
|
16471
|
-
return {
|
|
16472
|
-
id: plan.id,
|
|
16473
|
-
title: `[${totalSteps}] ${newTitle}`,
|
|
16474
|
-
steps: [...kept, ...added],
|
|
16475
|
-
createdAt: plan.createdAt,
|
|
16476
|
-
baseDir: plan.baseDir,
|
|
16477
|
-
name: plan.name
|
|
16478
|
-
};
|
|
16479
|
-
}
|
|
16480
|
-
static toPromptBlock(plan, currentStepIndex) {
|
|
16481
|
-
const date = plan.createdAt.slice(0, 10);
|
|
16482
|
-
const lines = [
|
|
16483
|
-
`[${plan.id}] ${plan.title}`,
|
|
16484
|
-
`Dir: ${plan.baseDir}`,
|
|
16485
|
-
`Created: ${date} | Progress: ${plan.steps.filter((s) => s.status === "done").length}/${plan.steps.length} done, current: step ${currentStepIndex + 1}`,
|
|
16486
|
-
``
|
|
16487
|
-
];
|
|
16488
|
-
for (const step of plan.steps) {
|
|
16489
|
-
const icon = step.status === "done" ? "[x]" : step.status === "in_progress" ? "[*]" : step.status === "failed" ? "[!]" : step.status === "skipped" ? "[-]" : "[ ]";
|
|
16490
|
-
const note = step.note ? ` — ${step.note}` : "";
|
|
16491
|
-
lines.push(`${icon} ${step.id}. ${step.description}${note}`);
|
|
16492
|
-
for (const sub of step.subtasks ?? []) {
|
|
16493
|
-
lines.push(` ${sub.done ? "[x]" : "[ ]"} ${sub.text}`);
|
|
16494
|
-
}
|
|
16495
|
-
}
|
|
16496
|
-
return lines.join(`
|
|
16497
|
-
`);
|
|
16498
|
-
}
|
|
16499
|
-
}
|
|
16500
|
-
|
|
16501
|
-
// src/modules/execution/tracker.ts
|
|
16502
|
-
class PlanTracker {
|
|
16503
|
-
plan;
|
|
16504
|
-
currentStepIndex = 0;
|
|
16505
|
-
constructor(plan) {
|
|
16506
|
-
this.plan = plan;
|
|
16507
|
-
}
|
|
16508
|
-
getPlan() {
|
|
16509
|
-
return this.plan;
|
|
16510
|
-
}
|
|
16511
|
-
getCurrentStepIndex() {
|
|
16512
|
-
return this.currentStepIndex;
|
|
16513
|
-
}
|
|
16514
|
-
getCurrentStep() {
|
|
16515
|
-
return this.plan.steps[this.currentStepIndex];
|
|
16516
|
-
}
|
|
16517
|
-
getStep(id) {
|
|
16518
|
-
return this.plan.steps.find((s) => s.id === id);
|
|
16519
|
-
}
|
|
16520
|
-
updateStepStatus(id, status) {
|
|
16521
|
-
const step = this.getStep(id);
|
|
16522
|
-
if (step)
|
|
16523
|
-
step.status = status;
|
|
16524
|
-
}
|
|
16525
|
-
addNote(id, note) {
|
|
16526
|
-
const step = this.getStep(id);
|
|
16527
|
-
if (step)
|
|
16528
|
-
step.note = note;
|
|
16529
|
-
}
|
|
16530
|
-
advance() {
|
|
16531
|
-
if (this.currentStepIndex < this.plan.steps.length - 1) {
|
|
16532
|
-
this.currentStepIndex++;
|
|
16533
|
-
this.plan.steps[this.currentStepIndex].status = "in_progress";
|
|
16534
|
-
return true;
|
|
16535
|
-
}
|
|
16536
|
-
return false;
|
|
16537
|
-
}
|
|
16538
|
-
syncCurrentStep() {
|
|
16539
|
-
while (this.currentStepIndex < this.plan.steps.length - 1 && (this.plan.steps[this.currentStepIndex].status === "done" || this.plan.steps[this.currentStepIndex].status === "skipped")) {
|
|
16540
|
-
this.currentStepIndex++;
|
|
16541
|
-
}
|
|
16542
|
-
}
|
|
16543
|
-
isComplete() {
|
|
16544
|
-
return this.plan.steps.every((s) => s.status === "done" || s.status === "skipped");
|
|
16545
|
-
}
|
|
16546
|
-
getProgressString() {
|
|
16547
|
-
const done = this.plan.steps.filter((s) => s.status === "done").length;
|
|
16548
|
-
const total = this.plan.steps.length;
|
|
16549
|
-
const pct = total > 0 ? Math.round(done / total * 100) : 0;
|
|
16550
|
-
const barWidth = 10;
|
|
16551
|
-
const filled = Math.round(done / total * barWidth);
|
|
16552
|
-
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
16553
|
-
return `[${this.plan.id}] ${this.plan.title} ${done}/${total} ${bar} ${pct}%`;
|
|
16554
|
-
}
|
|
16555
|
-
toPromptBlock() {
|
|
16556
|
-
return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
|
|
16557
|
-
}
|
|
16558
|
-
}
|
|
16559
|
-
var init_tracker = () => {};
|
|
16560
|
-
|
|
16561
|
-
// src/modules/execution/audit-runners.ts
|
|
16562
|
-
import { existsSync as existsSync30, readdirSync as readdirSync8 } from "fs";
|
|
16563
|
-
import { dirname as dirname10, join as join24, resolve as resolve17 } from "path";
|
|
16564
|
-
function findTestFile(dir, depth = 0) {
|
|
16565
|
-
if (depth > 5)
|
|
16566
|
-
return null;
|
|
16567
|
-
let entries;
|
|
16568
|
-
try {
|
|
16569
|
-
entries = readdirSync8(dir, { withFileTypes: true });
|
|
16570
|
-
} catch {
|
|
16571
|
-
return null;
|
|
16572
|
-
}
|
|
16573
|
-
for (const e of entries) {
|
|
16574
|
-
const full = join24(dir, e.name);
|
|
16575
|
-
if (e.isDirectory()) {
|
|
16576
|
-
if (SKIP_DIRS.has(e.name))
|
|
16577
|
-
continue;
|
|
16578
|
-
const found = findTestFile(full, depth + 1);
|
|
16579
|
-
if (found)
|
|
16580
|
-
return found;
|
|
16581
|
-
} else if (TEST_EXT_RE.test(e.name)) {
|
|
16582
|
-
return full;
|
|
16583
|
-
}
|
|
16584
|
-
}
|
|
16585
|
-
return null;
|
|
16586
|
-
}
|
|
16587
|
-
function hasTestStep(plan) {
|
|
16588
|
-
return plan.steps.some((s) => TEST_STEP_RE.test(s.description));
|
|
16589
|
-
}
|
|
16590
|
-
function extractFailingNames(output, limit = 5) {
|
|
16591
|
-
const names = [];
|
|
16592
|
-
for (const m of output.matchAll(/\(fail\)\s*([^\n]+)/g)) {
|
|
16593
|
-
const name = m[1].trim();
|
|
16594
|
-
if (name && !names.includes(name))
|
|
16595
|
-
names.push(name);
|
|
16596
|
-
if (names.length >= limit)
|
|
16597
|
-
break;
|
|
16598
|
-
}
|
|
16599
|
-
return names;
|
|
16600
|
-
}
|
|
16601
|
-
async function runTests(baseDir) {
|
|
16602
|
-
const entry = processRegistry.start("bun test", baseDir);
|
|
16603
|
-
const exited = await processRegistry.waitForExit(entry.id, 90000);
|
|
16604
|
-
const output = entry.log.join(`
|
|
16605
|
-
`);
|
|
16606
|
-
processRegistry.remove(entry.id);
|
|
16607
|
-
if (!exited) {
|
|
16608
|
-
return {
|
|
16609
|
-
checked: true,
|
|
16610
|
-
passed: true,
|
|
16611
|
-
failed: 0,
|
|
16612
|
-
passedCount: 0,
|
|
16613
|
-
detail: "test run timed out after 90s — result unknown",
|
|
16614
|
-
command: "bun test"
|
|
16615
|
-
};
|
|
16616
|
-
}
|
|
16617
|
-
const run = detectTestResults(output);
|
|
16618
|
-
if (!run) {
|
|
16619
|
-
return {
|
|
16620
|
-
checked: true,
|
|
16621
|
-
passed: entry.exitCode === 0,
|
|
16622
|
-
failed: entry.exitCode === 0 ? 0 : 1,
|
|
16623
|
-
passedCount: 0,
|
|
16624
|
-
detail: output.slice(0, 200).trim(),
|
|
16625
|
-
command: "bun test"
|
|
16626
|
-
};
|
|
16627
|
-
}
|
|
16628
|
-
const names = extractFailingNames(output);
|
|
16629
|
-
return {
|
|
16630
|
-
checked: true,
|
|
16631
|
-
passed: run.failed === 0,
|
|
16632
|
-
failed: run.failed,
|
|
16633
|
-
passedCount: run.passed,
|
|
16634
|
-
detail: names.length ? names.join("; ") : run.summary || `${run.failed} failed / ${run.passed} passed`,
|
|
16635
|
-
command: "bun test"
|
|
16636
|
-
};
|
|
16637
|
-
}
|
|
16638
|
-
function parseTypecheckErrors(output) {
|
|
16639
|
-
const line = output.split(`
|
|
16640
|
-
`).find((l) => /error TS\d+/.test(l));
|
|
16641
|
-
return line ? line.trim().slice(0, 300) : null;
|
|
16642
|
-
}
|
|
16643
|
-
function findTypecheckRoot(baseDir, existingFiles = []) {
|
|
16644
|
-
const candidates = [baseDir, ...existingFiles];
|
|
16645
|
-
let best = null;
|
|
16646
|
-
for (const start of candidates) {
|
|
16647
|
-
let dir = resolve17(start);
|
|
16648
|
-
for (let depth = 0;depth <= 10; depth++) {
|
|
16649
|
-
if (existsSync30(join24(dir, "tsconfig.json"))) {
|
|
16650
|
-
if (!best || depth < best.depth)
|
|
16651
|
-
best = { depth, root: dir };
|
|
16652
|
-
break;
|
|
16653
|
-
}
|
|
16654
|
-
const parent = dirname10(dir);
|
|
16655
|
-
if (parent === dir)
|
|
16656
|
-
break;
|
|
16657
|
-
dir = parent;
|
|
16658
|
-
}
|
|
16659
|
-
}
|
|
16660
|
-
return best?.root ?? null;
|
|
16661
|
-
}
|
|
16662
|
-
async function runTypecheck(baseDir) {
|
|
16663
|
-
const entry = processRegistry.start("npx --no-install tsc --noEmit --skipLibCheck", baseDir);
|
|
16664
|
-
const exited = await processRegistry.waitForExit(entry.id, 90000);
|
|
16665
|
-
const output = entry.log.join(`
|
|
16666
|
-
`);
|
|
16667
|
-
processRegistry.remove(entry.id);
|
|
16668
|
-
if (!exited)
|
|
16669
|
-
return null;
|
|
16670
|
-
return parseTypecheckErrors(output);
|
|
16671
|
-
}
|
|
16672
|
-
var SKIP_DIRS, TEST_EXT_RE, TEST_STEP_RE;
|
|
16673
|
-
var init_audit_runners = __esm(() => {
|
|
16674
|
-
init_bash();
|
|
16675
|
-
init_processes();
|
|
16676
|
-
SKIP_DIRS = new Set([
|
|
16677
|
-
"node_modules",
|
|
16678
|
-
".git",
|
|
16679
|
-
".mma",
|
|
16680
|
-
"dist",
|
|
16681
|
-
"build",
|
|
16682
|
-
"coverage",
|
|
16683
|
-
".next",
|
|
16684
|
-
".nuxt",
|
|
16685
|
-
"vendor"
|
|
16686
|
-
]);
|
|
16687
|
-
TEST_EXT_RE = /\.(test|spec)\.[jt]sx?$/i;
|
|
16688
|
-
TEST_STEP_RE = /\b(test(ing|s)?|тест(ы|ирование|ировать)?|провер\w*\s+тест|запустить\s+тест)\b|bun test|npm test|vitest|pytest|go test|jest|mocha/i;
|
|
16689
|
-
});
|
|
16690
|
-
|
|
16691
|
-
// src/modules/execution/auditor.ts
|
|
16692
|
-
import { existsSync as existsSync31, readdirSync as readdirSync9 } from "fs";
|
|
16693
|
-
import { resolve as resolve18, join as join25, basename as basename3 } from "path";
|
|
16694
|
-
function findExistingFile(baseDir, filePath) {
|
|
16695
|
-
const direct = resolve18(baseDir, filePath);
|
|
16696
|
-
if (existsSync31(direct))
|
|
16697
|
-
return direct;
|
|
16698
|
-
const name = basename3(filePath).toLowerCase();
|
|
16699
|
-
const suffix = filePath.replace(/\\/g, "/").toLowerCase();
|
|
16700
|
-
let found = null;
|
|
16701
|
-
const walk = (dir, depth) => {
|
|
16702
|
-
if (found || depth > RESOLVE_MAX_DEPTH)
|
|
16703
|
-
return;
|
|
16704
|
-
let entries;
|
|
16705
|
-
try {
|
|
16706
|
-
entries = readdirSync9(dir, { withFileTypes: true });
|
|
16707
|
-
} catch {
|
|
16708
|
-
return;
|
|
16709
|
-
}
|
|
16710
|
-
for (const e of entries) {
|
|
16711
|
-
if (found)
|
|
16712
|
-
return;
|
|
16713
|
-
const full = join25(dir, e.name);
|
|
16714
|
-
if (e.isDirectory()) {
|
|
16715
|
-
if (SKIP_DIRS.has(e.name))
|
|
16716
|
-
continue;
|
|
16717
|
-
walk(full, depth + 1);
|
|
16718
|
-
} else if (e.name.toLowerCase() === name || full.replace(/\\/g, "/").toLowerCase().endsWith("/" + suffix)) {
|
|
16719
|
-
found = full;
|
|
16720
|
-
return;
|
|
16812
|
+
createdAt: new Date().toISOString(),
|
|
16813
|
+
baseDir
|
|
16814
|
+
};
|
|
16815
|
+
}
|
|
16816
|
+
static replan(plan, newSteps, title) {
|
|
16817
|
+
const kept = plan.steps.filter((s) => s.status === "done" || s.status === "skipped");
|
|
16818
|
+
const baseTitle = plan.title.replace(/^\[\d+[^]]*\]\s*/, "");
|
|
16819
|
+
const newTitle = title || baseTitle;
|
|
16820
|
+
const totalSteps = kept.length + newSteps.length;
|
|
16821
|
+
const added = newSteps.map((desc, i) => ({
|
|
16822
|
+
id: kept.length + i + 1,
|
|
16823
|
+
description: desc,
|
|
16824
|
+
status: "pending",
|
|
16825
|
+
kind: "create"
|
|
16826
|
+
}));
|
|
16827
|
+
return {
|
|
16828
|
+
id: plan.id,
|
|
16829
|
+
title: `[${totalSteps}] ${newTitle}`,
|
|
16830
|
+
steps: [...kept, ...added],
|
|
16831
|
+
createdAt: plan.createdAt,
|
|
16832
|
+
baseDir: plan.baseDir,
|
|
16833
|
+
name: plan.name
|
|
16834
|
+
};
|
|
16835
|
+
}
|
|
16836
|
+
static toPromptBlock(plan, currentStepIndex) {
|
|
16837
|
+
const date = plan.createdAt.slice(0, 10);
|
|
16838
|
+
const lines = [
|
|
16839
|
+
`[${plan.id}] ${plan.title}`,
|
|
16840
|
+
`Dir: ${plan.baseDir}`,
|
|
16841
|
+
`Created: ${date} | Progress: ${plan.steps.filter((s) => s.status === "done").length}/${plan.steps.length} done, current: step ${currentStepIndex + 1}`,
|
|
16842
|
+
``
|
|
16843
|
+
];
|
|
16844
|
+
for (const step of plan.steps) {
|
|
16845
|
+
const icon = step.status === "done" ? "[x]" : step.status === "in_progress" ? "[*]" : step.status === "failed" ? "[!]" : step.status === "skipped" ? "[-]" : "[ ]";
|
|
16846
|
+
const note = step.note ? ` — ${step.note}` : "";
|
|
16847
|
+
lines.push(`${icon} ${step.id}. ${step.description}${note}`);
|
|
16848
|
+
for (const sub of step.subtasks ?? []) {
|
|
16849
|
+
lines.push(` ${sub.done ? "[x]" : "[ ]"} ${sub.text}`);
|
|
16721
16850
|
}
|
|
16722
16851
|
}
|
|
16723
|
-
|
|
16724
|
-
|
|
16725
|
-
|
|
16852
|
+
return lines.join(`
|
|
16853
|
+
`);
|
|
16854
|
+
}
|
|
16726
16855
|
}
|
|
16727
16856
|
|
|
16728
|
-
|
|
16729
|
-
|
|
16730
|
-
|
|
16731
|
-
|
|
16857
|
+
// src/modules/execution/tracker.ts
|
|
16858
|
+
class PlanTracker {
|
|
16859
|
+
plan;
|
|
16860
|
+
currentStepIndex = 0;
|
|
16861
|
+
constructor(plan) {
|
|
16862
|
+
this.plan = plan;
|
|
16732
16863
|
}
|
|
16733
|
-
|
|
16734
|
-
|
|
16735
|
-
|
|
16736
|
-
|
|
16737
|
-
|
|
16738
|
-
|
|
16739
|
-
|
|
16740
|
-
|
|
16741
|
-
|
|
16742
|
-
|
|
16743
|
-
|
|
16744
|
-
|
|
16745
|
-
|
|
16746
|
-
|
|
16747
|
-
|
|
16748
|
-
|
|
16749
|
-
|
|
16750
|
-
|
|
16751
|
-
|
|
16752
|
-
|
|
16753
|
-
|
|
16754
|
-
|
|
16755
|
-
|
|
16756
|
-
|
|
16757
|
-
|
|
16758
|
-
|
|
16759
|
-
|
|
16760
|
-
} catch {
|
|
16761
|
-
testRun = null;
|
|
16762
|
-
}
|
|
16763
|
-
}
|
|
16764
|
-
let typecheckError = null;
|
|
16765
|
-
if (missingFiles.length === 0 && leftoverFiles.length === 0) {
|
|
16766
|
-
const root = findTypecheckRoot(this.baseDir, existingFiles);
|
|
16767
|
-
if (root) {
|
|
16768
|
-
try {
|
|
16769
|
-
typecheckError = await runTypecheck(root);
|
|
16770
|
-
} catch {
|
|
16771
|
-
typecheckError = null;
|
|
16772
|
-
}
|
|
16773
|
-
}
|
|
16774
|
-
}
|
|
16775
|
-
const doneSteps = plan.steps.filter((s) => s.status === "done").length;
|
|
16776
|
-
const terminalSteps = plan.steps.filter((s) => s.status === "done" || s.status === "skipped").length;
|
|
16777
|
-
const totalSteps = plan.steps.length;
|
|
16778
|
-
const auditedFiles = [...new Set([...createFiles, ...deleteFiles])];
|
|
16779
|
-
let massEditWarning = null;
|
|
16780
|
-
if (auditedFiles.length > MASS_EDIT_THRESHOLD) {
|
|
16781
|
-
massEditWarning = t("exec.mass_edit_warning", {
|
|
16782
|
-
count: String(auditedFiles.length)
|
|
16783
|
-
});
|
|
16864
|
+
getPlan() {
|
|
16865
|
+
return this.plan;
|
|
16866
|
+
}
|
|
16867
|
+
getCurrentStepIndex() {
|
|
16868
|
+
return this.currentStepIndex;
|
|
16869
|
+
}
|
|
16870
|
+
getCurrentStep() {
|
|
16871
|
+
return this.plan.steps[this.currentStepIndex];
|
|
16872
|
+
}
|
|
16873
|
+
getStep(id) {
|
|
16874
|
+
return this.plan.steps.find((s) => s.id === id);
|
|
16875
|
+
}
|
|
16876
|
+
updateStepStatus(id, status) {
|
|
16877
|
+
const step = this.getStep(id);
|
|
16878
|
+
if (step)
|
|
16879
|
+
step.status = status;
|
|
16880
|
+
}
|
|
16881
|
+
addNote(id, note) {
|
|
16882
|
+
const step = this.getStep(id);
|
|
16883
|
+
if (step)
|
|
16884
|
+
step.note = note;
|
|
16885
|
+
}
|
|
16886
|
+
advance() {
|
|
16887
|
+
if (this.currentStepIndex < this.plan.steps.length - 1) {
|
|
16888
|
+
this.currentStepIndex++;
|
|
16889
|
+
this.plan.steps[this.currentStepIndex].status = "in_progress";
|
|
16890
|
+
return true;
|
|
16784
16891
|
}
|
|
16785
|
-
|
|
16786
|
-
|
|
16787
|
-
|
|
16788
|
-
|
|
16789
|
-
|
|
16790
|
-
if (missingFiles.length > 0) {
|
|
16791
|
-
summary = t("exec.audit_fail", {
|
|
16792
|
-
done: doneSteps,
|
|
16793
|
-
total: totalSteps,
|
|
16794
|
-
files: missingFiles.length
|
|
16795
|
-
});
|
|
16796
|
-
} else if (leftoverFiles.length > 0) {
|
|
16797
|
-
summary = t("exec.audit_leftovers", {
|
|
16798
|
-
done: doneSteps,
|
|
16799
|
-
total: totalSteps,
|
|
16800
|
-
files: leftoverFiles.length
|
|
16801
|
-
});
|
|
16802
|
-
} else if (testsFailing) {
|
|
16803
|
-
summary = t("exec.audit_fail_tests", {
|
|
16804
|
-
done: doneSteps,
|
|
16805
|
-
total: totalSteps,
|
|
16806
|
-
failed: String(testRun.failed),
|
|
16807
|
-
passed: String(testRun.passedCount),
|
|
16808
|
-
detail: testRun.detail
|
|
16809
|
-
});
|
|
16810
|
-
} else if (typecheckFailing) {
|
|
16811
|
-
summary = t("exec.audit_fail_typecheck", {
|
|
16812
|
-
done: doneSteps,
|
|
16813
|
-
total: totalSteps,
|
|
16814
|
-
missing: String(missingFiles.length),
|
|
16815
|
-
typeError: typecheckError
|
|
16816
|
-
});
|
|
16817
|
-
} else if (stepsPending) {
|
|
16818
|
-
summary = t("exec.audit_pending", {
|
|
16819
|
-
done: doneSteps,
|
|
16820
|
-
total: totalSteps
|
|
16821
|
-
});
|
|
16822
|
-
} else {
|
|
16823
|
-
summary = t("exec.audit_pass", {
|
|
16824
|
-
done: doneSteps,
|
|
16825
|
-
total: totalSteps,
|
|
16826
|
-
files: existingFiles.length
|
|
16827
|
-
});
|
|
16892
|
+
return false;
|
|
16893
|
+
}
|
|
16894
|
+
syncCurrentStep() {
|
|
16895
|
+
while (this.currentStepIndex < this.plan.steps.length - 1 && (this.plan.steps[this.currentStepIndex].status === "done" || this.plan.steps[this.currentStepIndex].status === "skipped")) {
|
|
16896
|
+
this.currentStepIndex++;
|
|
16828
16897
|
}
|
|
16829
|
-
|
|
16830
|
-
|
|
16831
|
-
|
|
16832
|
-
|
|
16833
|
-
|
|
16834
|
-
|
|
16835
|
-
|
|
16836
|
-
|
|
16837
|
-
|
|
16838
|
-
|
|
16839
|
-
|
|
16898
|
+
}
|
|
16899
|
+
isComplete() {
|
|
16900
|
+
return this.plan.steps.every((s) => s.status === "done" || s.status === "skipped");
|
|
16901
|
+
}
|
|
16902
|
+
getProgressString() {
|
|
16903
|
+
const done = this.plan.steps.filter((s) => s.status === "done").length;
|
|
16904
|
+
const total = this.plan.steps.length;
|
|
16905
|
+
const pct = total > 0 ? Math.round(done / total * 100) : 0;
|
|
16906
|
+
const barWidth = 10;
|
|
16907
|
+
const filled = Math.round(done / total * barWidth);
|
|
16908
|
+
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
16909
|
+
return `[${this.plan.id}] ${this.plan.title} ${done}/${total} ${bar} ${pct}%`;
|
|
16910
|
+
}
|
|
16911
|
+
toPromptBlock() {
|
|
16912
|
+
return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
|
|
16840
16913
|
}
|
|
16841
16914
|
}
|
|
16842
|
-
var
|
|
16843
|
-
var init_auditor = __esm(() => {
|
|
16844
|
-
init_i18n();
|
|
16845
|
-
init_js_identifiers();
|
|
16846
|
-
init_audit_runners();
|
|
16847
|
-
init_audit_runners();
|
|
16848
|
-
});
|
|
16915
|
+
var init_tracker = () => {};
|
|
16849
16916
|
|
|
16850
16917
|
// src/modules/execution/plan-store.ts
|
|
16851
16918
|
import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, mkdirSync as mkdirSync14, existsSync as existsSync32, readdirSync as readdirSync10, rmSync } from "fs";
|
|
@@ -17279,6 +17346,20 @@ ${display}`,
|
|
|
17279
17346
|
return { success: false, output: t("plan.step_not_found") };
|
|
17280
17347
|
}
|
|
17281
17348
|
const status = String(args.status || "done");
|
|
17349
|
+
if (tracker.isComplete()) {
|
|
17350
|
+
const plan = tracker.getPlan();
|
|
17351
|
+
deps.store.archivePlan(plan);
|
|
17352
|
+
deps.trackerRef.current = null;
|
|
17353
|
+
const done = plan.steps.filter((s) => s.status === "done").length;
|
|
17354
|
+
return {
|
|
17355
|
+
success: true,
|
|
17356
|
+
output: t("plan.completed_archived", {
|
|
17357
|
+
id: plan.id,
|
|
17358
|
+
done: String(done),
|
|
17359
|
+
total: String(plan.steps.length)
|
|
17360
|
+
})
|
|
17361
|
+
};
|
|
17362
|
+
}
|
|
17282
17363
|
if (status === "done" && target.status === "done") {
|
|
17283
17364
|
const progress2 = tracker.getProgressString();
|
|
17284
17365
|
const display2 = tracker.toPromptBlock();
|
|
@@ -17334,6 +17415,21 @@ ${progress2}`,
|
|
|
17334
17415
|
tracker.addNote(Number(args.step), String(args.note));
|
|
17335
17416
|
tracker.syncCurrentStep();
|
|
17336
17417
|
deps.store.saveActive(tracker.getPlan());
|
|
17418
|
+
if (tracker.isComplete()) {
|
|
17419
|
+
const plan = tracker.getPlan();
|
|
17420
|
+
deps.store.archivePlan(plan);
|
|
17421
|
+
deps.trackerRef.current = null;
|
|
17422
|
+
const done = plan.steps.filter((s) => s.status === "done").length;
|
|
17423
|
+
return {
|
|
17424
|
+
success: true,
|
|
17425
|
+
output: `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })}
|
|
17426
|
+
${t("plan.completed_archived", {
|
|
17427
|
+
id: plan.id,
|
|
17428
|
+
done: String(done),
|
|
17429
|
+
total: String(plan.steps.length)
|
|
17430
|
+
})}`
|
|
17431
|
+
};
|
|
17432
|
+
}
|
|
17337
17433
|
const progress = tracker.getProgressString();
|
|
17338
17434
|
const display = tracker.toPromptBlock();
|
|
17339
17435
|
_ctx.sessionLog?.plan("step-update", `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })} | ${progress} | current: step ${tracker.getCurrentStepIndex() + 1}`);
|
|
@@ -17497,6 +17593,12 @@ ${lines.join(`
|
|
|
17497
17593
|
if (!step)
|
|
17498
17594
|
return { success: false, output: t("plan.step_not_found") };
|
|
17499
17595
|
const result = await deps.verifier.verifyStep(step.description);
|
|
17596
|
+
if (result.noFiles) {
|
|
17597
|
+
return {
|
|
17598
|
+
success: true,
|
|
17599
|
+
output: t("verify.no_files", { step: String(step.id) })
|
|
17600
|
+
};
|
|
17601
|
+
}
|
|
17500
17602
|
return {
|
|
17501
17603
|
success: result.passed,
|
|
17502
17604
|
output: result.passed ? t("verify.passed") : t("verify.failed", {
|
|
@@ -17814,7 +17916,13 @@ class ExecutionModule {
|
|
|
17814
17916
|
const plan = this.store.loadActive();
|
|
17815
17917
|
if (!plan)
|
|
17816
17918
|
return false;
|
|
17817
|
-
|
|
17919
|
+
const restored = new PlanTracker(plan);
|
|
17920
|
+
if (restored.isComplete()) {
|
|
17921
|
+
this.store.archivePlan(plan);
|
|
17922
|
+
this.tracker = null;
|
|
17923
|
+
return false;
|
|
17924
|
+
}
|
|
17925
|
+
this.tracker = restored;
|
|
17818
17926
|
this.tracker.syncCurrentStep();
|
|
17819
17927
|
this._auditSkipsRemaining = 1;
|
|
17820
17928
|
return true;
|
|
@@ -17850,7 +17958,7 @@ class ExecutionModule {
|
|
|
17850
17958
|
};
|
|
17851
17959
|
}
|
|
17852
17960
|
getSystemPromptBlock() {
|
|
17853
|
-
if (!this.tracker)
|
|
17961
|
+
if (!this.tracker || this.tracker.isComplete())
|
|
17854
17962
|
return null;
|
|
17855
17963
|
return {
|
|
17856
17964
|
content: this.tracker.toPromptBlock(),
|
|
@@ -17864,8 +17972,12 @@ class ExecutionModule {
|
|
|
17864
17972
|
return null;
|
|
17865
17973
|
const plan = this.tracker.getPlan();
|
|
17866
17974
|
const done = plan.steps.filter((s) => s.status === "done").length;
|
|
17975
|
+
const total = plan.steps.length;
|
|
17976
|
+
if (this.tracker.isComplete()) {
|
|
17977
|
+
return `[${plan.id}] "${plan.title}" ${done}/${total} done — plan complete. You may create a new plan for new work (plan create), or reply with your final answer.`;
|
|
17978
|
+
}
|
|
17867
17979
|
const current = this.tracker.getCurrentStepIndex() + 1;
|
|
17868
|
-
return `[${plan.id}] "${plan.title}" ${done}/${
|
|
17980
|
+
return `[${plan.id}] "${plan.title}" ${done}/${total} done, current: step ${current} — continue it (plan show / re-plan), do NOT create a new plan`;
|
|
17869
17981
|
}
|
|
17870
17982
|
getToolDefinitions() {
|
|
17871
17983
|
return createPlanToolDefinitions({
|
|
@@ -18030,6 +18142,12 @@ class ExecutionModule {
|
|
|
18030
18142
|
content: `<system-summary>${nextMsg}</system-summary>`
|
|
18031
18143
|
});
|
|
18032
18144
|
}
|
|
18145
|
+
if (this.tracker?.isComplete()) {
|
|
18146
|
+
const completed = this.tracker.getPlan();
|
|
18147
|
+
this.store.archivePlan(completed);
|
|
18148
|
+
this.tracker = null;
|
|
18149
|
+
sessionLog?.plan("auto-archive", `Plan ${completed.id} complete — archived`);
|
|
18150
|
+
}
|
|
18033
18151
|
}
|
|
18034
18152
|
missingStepDeliverables(step) {
|
|
18035
18153
|
const tokens = extractFileLikeTokens(stripUrls(step.description));
|
|
@@ -19183,7 +19301,7 @@ class LspClient {
|
|
|
19183
19301
|
} catch {} finally {
|
|
19184
19302
|
if (this.process) {
|
|
19185
19303
|
try {
|
|
19186
|
-
this.process
|
|
19304
|
+
killTree(this.process);
|
|
19187
19305
|
} catch {}
|
|
19188
19306
|
this.process = null;
|
|
19189
19307
|
}
|
|
@@ -19228,6 +19346,7 @@ class LspClient {
|
|
|
19228
19346
|
var DEFAULT_TIMEOUT = 15000;
|
|
19229
19347
|
var init_client2 = __esm(() => {
|
|
19230
19348
|
init_command();
|
|
19349
|
+
init_registry();
|
|
19231
19350
|
});
|
|
19232
19351
|
|
|
19233
19352
|
// src/modules/lsp/check-tool.ts
|
|
@@ -20648,9 +20767,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
20648
20767
|
pluginManager.register(lspPlugin);
|
|
20649
20768
|
}
|
|
20650
20769
|
let startupCheckBlock = null;
|
|
20651
|
-
|
|
20652
|
-
startupCheckBlock = await runStartupHealthCheck(config.lsp ?? DEFAULT_LSP_CONFIG, baseDir);
|
|
20653
|
-
}
|
|
20770
|
+
const startupCheckPromise = !exitOnComplete ? runStartupHealthCheck(config.lsp ?? DEFAULT_LSP_CONFIG, baseDir) : Promise.resolve(null);
|
|
20654
20771
|
const moduleTools = moduleRegistry.collectToolDefinitions();
|
|
20655
20772
|
for (const tool of moduleTools) {
|
|
20656
20773
|
toolRegistry.register(tool);
|
|
@@ -20733,8 +20850,6 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
20733
20850
|
...moduleRegistry.collectPromptBlocks(["indexer"]),
|
|
20734
20851
|
...agentsMdBlocks
|
|
20735
20852
|
];
|
|
20736
|
-
if (startupCheckBlock)
|
|
20737
|
-
promptBlocks.push(startupCheckBlock);
|
|
20738
20853
|
const agentDeps = {
|
|
20739
20854
|
config,
|
|
20740
20855
|
llmProvider,
|
|
@@ -20769,6 +20884,12 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
20769
20884
|
return blocks;
|
|
20770
20885
|
},
|
|
20771
20886
|
finalAudit: () => execModule.runFinalAudit(),
|
|
20887
|
+
lazyPromptBlocks: async () => {
|
|
20888
|
+
if (startupCheckBlock)
|
|
20889
|
+
return [startupCheckBlock];
|
|
20890
|
+
startupCheckBlock = await startupCheckPromise ?? null;
|
|
20891
|
+
return startupCheckBlock ? [startupCheckBlock] : [];
|
|
20892
|
+
},
|
|
20772
20893
|
sessionManager,
|
|
20773
20894
|
memoryStore,
|
|
20774
20895
|
exitOnComplete
|
|
@@ -32078,6 +32199,7 @@ ${pc2.dim(marker)} ${friendlyTool(tool)}${summary ? ` ${pc2.dim(summary)}` : ""}
|
|
|
32078
32199
|
|
|
32079
32200
|
// src/cli/repl.ts
|
|
32080
32201
|
init_box();
|
|
32202
|
+
init_string_width();
|
|
32081
32203
|
init_i18n();
|
|
32082
32204
|
init_repl_commands();
|
|
32083
32205
|
|
|
@@ -32086,12 +32208,15 @@ init_client2();
|
|
|
32086
32208
|
init_config();
|
|
32087
32209
|
init_check_tool();
|
|
32088
32210
|
init_project_root();
|
|
32089
|
-
var DEFAULT_PROBE_TIMEOUT_MS =
|
|
32211
|
+
var DEFAULT_PROBE_TIMEOUT_MS = 30000;
|
|
32212
|
+
var DEFAULT_PROBE_DEADLINE_MS = 60000;
|
|
32213
|
+
var DEFAULT_MAX_PARALLEL = 3;
|
|
32090
32214
|
async function probeLspServers(config, baseDir, deps = {}) {
|
|
32091
32215
|
if (!config.enabled)
|
|
32092
32216
|
return { ok: [], failed: [], noneApplicable: true };
|
|
32093
|
-
const
|
|
32094
|
-
const
|
|
32217
|
+
const timeoutCap = deps.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
32218
|
+
const deadlineMs = deps.deadlineMs ?? DEFAULT_PROBE_DEADLINE_MS;
|
|
32219
|
+
const maxParallel = deps.maxParallel ?? DEFAULT_MAX_PARALLEL;
|
|
32095
32220
|
const collect = deps.collectFiles ?? collectCheckFiles;
|
|
32096
32221
|
const files = await collect(baseDir, config);
|
|
32097
32222
|
const seen = new Set;
|
|
@@ -32108,26 +32233,37 @@ async function probeLspServers(config, baseDir, deps = {}) {
|
|
|
32108
32233
|
}
|
|
32109
32234
|
if (servers.length === 0)
|
|
32110
32235
|
return { ok: [], failed: [], noneApplicable: true };
|
|
32111
|
-
const
|
|
32236
|
+
const ok = [];
|
|
32237
|
+
const failed = [];
|
|
32238
|
+
const deadline = Date.now() + deadlineMs;
|
|
32239
|
+
const probeOne = async (language, server) => {
|
|
32112
32240
|
const projectRoot = findProjectRoot(baseDir, baseDir, server.workspaceMarkers ?? []);
|
|
32241
|
+
const serverTimeout = Math.min(server.timeout ?? timeoutCap, timeoutCap);
|
|
32113
32242
|
const started = Date.now();
|
|
32243
|
+
const probeClient = deps.client ?? new LspClient;
|
|
32114
32244
|
try {
|
|
32115
|
-
await
|
|
32116
|
-
|
|
32245
|
+
await probeClient.probe(server, projectRoot, serverTimeout);
|
|
32246
|
+
ok.push({ language, ok: true, durationMs: Date.now() - started });
|
|
32117
32247
|
} catch (e) {
|
|
32118
|
-
|
|
32248
|
+
failed.push({
|
|
32119
32249
|
language,
|
|
32120
32250
|
ok: false,
|
|
32121
32251
|
error: e instanceof Error ? e.message : String(e),
|
|
32122
32252
|
durationMs: Date.now() - started
|
|
32123
|
-
};
|
|
32253
|
+
});
|
|
32124
32254
|
}
|
|
32125
|
-
}));
|
|
32126
|
-
return {
|
|
32127
|
-
ok: results.filter((r) => r.ok),
|
|
32128
|
-
failed: results.filter((r) => !r.ok),
|
|
32129
|
-
noneApplicable: false
|
|
32130
32255
|
};
|
|
32256
|
+
for (let i = 0;i < servers.length; i += maxParallel) {
|
|
32257
|
+
if (Date.now() >= deadline) {
|
|
32258
|
+
for (const { language } of servers.slice(i)) {
|
|
32259
|
+
failed.push({ language, ok: false, error: "timeout", durationMs: 0 });
|
|
32260
|
+
}
|
|
32261
|
+
break;
|
|
32262
|
+
}
|
|
32263
|
+
const wave = servers.slice(i, i + maxParallel);
|
|
32264
|
+
await Promise.all(wave.map(({ language, config: server }) => probeOne(language, server)));
|
|
32265
|
+
}
|
|
32266
|
+
return { ok, failed, noneApplicable: false };
|
|
32131
32267
|
}
|
|
32132
32268
|
|
|
32133
32269
|
// src/cli/repl.ts
|
|
@@ -32706,10 +32842,6 @@ ${t("image.clipboard_empty")}`));
|
|
|
32706
32842
|
const names = enabledServers.map(([name]) => name).join(", ");
|
|
32707
32843
|
row(t("repl.mcp_label"), `${pc2.white(String(enabledServers.length))} ${pc2.dim(`(${names})`)}`);
|
|
32708
32844
|
}
|
|
32709
|
-
const lspSummary = await this.probeLspBanner();
|
|
32710
|
-
if (lspSummary) {
|
|
32711
|
-
row(t("repl.lsp_label"), lspSummary);
|
|
32712
|
-
}
|
|
32713
32845
|
const cwd = process.cwd();
|
|
32714
32846
|
row(t("repl.work_dir"), pc2.dim(cwd));
|
|
32715
32847
|
if (this.noAgentsMd) {
|
|
@@ -32735,14 +32867,39 @@ ${t("image.clipboard_empty")}`));
|
|
|
32735
32867
|
row(t("repl.session_label"), `${pc2.cyan(meta.name)} ${pc2.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc2.dim(sessionPath)}`);
|
|
32736
32868
|
}
|
|
32737
32869
|
const headerWidth = Math.max(50, Math.min(96, process.stdout.columns || 96));
|
|
32738
|
-
|
|
32870
|
+
const isTty2 = process.stdout.isTTY === true;
|
|
32871
|
+
const lspEnabled = isTty2 && (this.config.lsp ?? DEFAULT_LSP_CONFIG).enabled !== false;
|
|
32872
|
+
if (lspEnabled) {
|
|
32873
|
+
row(t("repl.lsp_label"), pc2.dim("…"));
|
|
32874
|
+
}
|
|
32875
|
+
const boxLines = box(info, {
|
|
32739
32876
|
title: t("repl.title", { version: version3 }),
|
|
32740
32877
|
width: headerWidth
|
|
32741
|
-
})
|
|
32878
|
+
});
|
|
32879
|
+
for (const line of boxLines) {
|
|
32742
32880
|
console.log(line);
|
|
32743
32881
|
}
|
|
32744
32882
|
console.log();
|
|
32745
32883
|
this.rl.prompt();
|
|
32884
|
+
if (lspEnabled) {
|
|
32885
|
+
this.probeLspBanner().then((lspSummary) => {
|
|
32886
|
+
if (lspSummary)
|
|
32887
|
+
this.updateLspRow(boxLines.length, headerWidth, lspSummary);
|
|
32888
|
+
}).catch(() => {});
|
|
32889
|
+
}
|
|
32890
|
+
}
|
|
32891
|
+
updateLspRow(boxLineCount, headerWidth, summary) {
|
|
32892
|
+
if (this.agentRunning || !process.stdout.isTTY)
|
|
32893
|
+
return;
|
|
32894
|
+
if ((process.stdout.columns ?? 96) < headerWidth)
|
|
32895
|
+
return;
|
|
32896
|
+
const inner = headerWidth - 4;
|
|
32897
|
+
const rowText = `${pc2.yellow(t("repl.lsp_label"))} ${summary.replace(/\s+/g, " ").trim()}`;
|
|
32898
|
+
if (stringWidth(rowText) > inner)
|
|
32899
|
+
return;
|
|
32900
|
+
const padded = rowText + " ".repeat(Math.max(0, inner - stringWidth(rowText)));
|
|
32901
|
+
const line = pc2.dim("│") + " " + padded + " " + pc2.dim("│");
|
|
32902
|
+
process.stdout.write(`\x1B[2A\r\x1B[2K${line}\x1B[2B\r`);
|
|
32746
32903
|
}
|
|
32747
32904
|
async probeLspBanner() {
|
|
32748
32905
|
const config = this.config.lsp ?? DEFAULT_LSP_CONFIG;
|