micro-models-agent 0.41.2 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/mma.mjs +41 -41
- package/dist/cli/commands.js +19 -9
- package/dist/cli/completer.js +37 -36
- package/dist/cli/index.js +2 -2
- package/dist/cli/main.js +23 -48
- package/dist/cli/repl-commands.js +12 -40
- package/dist/cli/repl.js +87 -217
- package/dist/cli/security-commands.js +7 -5
- package/dist/cli/setup.js +26 -8
- package/dist/config/config.js +5 -52
- package/dist/config/defaults.js +5 -29
- package/dist/config/experts.js +1 -1
- package/dist/config/index.js +3 -3
- package/dist/config/security.js +10 -3
- package/dist/core/agent-moe.js +10 -2
- package/dist/core/agent.js +82 -273
- package/dist/core/bootstrap.js +13 -80
- package/dist/core/index.js +2 -2
- package/dist/core/prompt-builder.js +2 -23
- package/dist/core/session-logger.js +4 -46
- package/dist/i18n/en.json +2 -75
- package/dist/i18n/ru.json +1 -74
- package/dist/index.js +1 -1
- package/dist/llm/image-utils.js +5 -4
- package/dist/llm/index.js +4 -4
- package/dist/llm/model-loader.js +6 -6
- package/dist/llm/openai-compat.js +34 -40
- package/dist/llm/orchestrator.js +29 -33
- 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 +648 -183
- package/dist/migration/backup.js +13 -13
- package/dist/migration/detect.js +11 -11
- package/dist/migration/index.js +2 -2
- package/dist/modules/browser/actions.js +4 -34
- package/dist/modules/browser/bridge-server.mjs +202 -202
- package/dist/modules/browser/cookie-store.js +6 -6
- package/dist/modules/browser/index.js +5 -7
- package/dist/modules/browser/module.js +7 -8
- package/dist/modules/browser/session.js +84 -87
- package/dist/modules/browser/snapshot.js +58 -92
- package/dist/modules/browser/types.js +1 -4
- package/dist/modules/certification/cli.js +4 -2
- package/dist/modules/certification/fact-checker.js +3 -1
- package/dist/modules/certification/loader.js +9 -3
- package/dist/modules/certification/runner.js +4 -1
- package/dist/modules/context/index.js +1 -1
- package/dist/modules/context/manager.js +86 -160
- package/dist/modules/execution/auditor.js +25 -177
- package/dist/modules/execution/module.js +544 -201
- package/dist/modules/execution/moe-executor.js +0 -25
- package/dist/modules/execution/plan-store.js +3 -1
- package/dist/modules/execution/plan-validator.js +10 -10
- package/dist/modules/execution/planner.js +1 -6
- package/dist/modules/execution/stuck-detector.js +10 -173
- package/dist/modules/execution/verifier.js +42 -86
- package/dist/modules/hallucination/confidence.js +1 -8
- package/dist/modules/hallucination/detector.js +5 -2
- package/dist/modules/hallucination/factual.js +64 -3
- package/dist/modules/hallucination/index.js +1 -1
- package/dist/modules/hallucination/js-identifiers.js +0 -190
- package/dist/modules/hallucination/llm-judge.js +3 -1
- package/dist/modules/indexer/cache.js +7 -9
- package/dist/modules/indexer/index.js +3 -3
- package/dist/modules/indexer/module.js +42 -95
- package/dist/modules/indexer/walker.js +17 -17
- package/dist/modules/lsp/client.js +31 -74
- package/dist/modules/lsp/config.js +33 -87
- package/dist/modules/lsp/index.js +3 -3
- package/dist/modules/lsp/module.js +21 -185
- package/dist/modules/mcp/module.js +6 -2
- package/dist/modules/memory/index.js +1 -1
- package/dist/modules/memory/module.js +23 -71
- package/dist/modules/memory/search.js +9 -11
- 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 +14 -17
- package/dist/modules/pipelines/template.js +1 -1
- package/dist/modules/plugins/builtin/lint-on-write.js +16 -21
- package/dist/modules/plugins/builtin/notify.js +2 -3
- package/dist/modules/plugins/index.js +1 -1
- package/dist/modules/plugins/loader.js +17 -59
- package/dist/modules/plugins/manager.js +17 -73
- package/dist/modules/processes/index.js +1 -1
- package/dist/modules/processes/registry.js +46 -135
- package/dist/modules/registry.js +2 -4
- package/dist/modules/security/audit-notifier.js +39 -39
- package/dist/modules/security/command-validator.js +8 -2
- package/dist/modules/security/data-sanitizer.js +9 -1
- package/dist/modules/security/encryption.js +56 -58
- package/dist/modules/security/network-validator.js +9 -1
- package/dist/modules/security/path-validator.js +3 -1
- package/dist/modules/security/security-policies.js +19 -3
- 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 +9 -3
- package/dist/modules/skills/module.js +2 -1
- package/dist/modules/updater/checker.js +6 -70
- package/dist/modules/updater/index.js +1 -2
- 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 +19 -178
- package/dist/tools/browser.js +29 -46
- package/dist/tools/executor.js +5 -4
- package/dist/tools/file-info.js +12 -13
- package/dist/tools/filter-tools.js +2 -9
- package/dist/tools/glob-tool.js +11 -11
- package/dist/tools/grep-tool.js +3 -1
- package/dist/tools/index.js +2 -13
- package/dist/tools/list-dir.js +17 -18
- package/dist/tools/load-skill.js +3 -1
- 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 +22 -20
- package/dist/tools/process-log.js +18 -22
- package/dist/tools/question.js +3 -1
- package/dist/tools/read-file.js +2 -10
- package/dist/tools/recall.js +37 -44
- package/dist/tools/registry.js +4 -15
- package/dist/tools/remember.js +29 -29
- package/dist/tools/scope-check.js +9 -9
- package/dist/tools/subagent.js +9 -54
- 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 +3 -1
- package/dist/ui/box.js +5 -1
- package/dist/ui/index.js +6 -6
- package/dist/ui/md-formatter.js +33 -33
- package/dist/ui/output.js +5 -5
- package/dist/ui/renderer.js +10 -15
- package/dist/ui/table.js +1 -1
- package/package.json +48 -48
- package/dist/cli/plugin-commands.js +0 -36
- package/dist/cli/run-result.js +0 -22
- package/dist/core/version.js +0 -24
- package/dist/modules/artifacts/store.js +0 -61
- package/dist/modules/browser/bridge-client.js +0 -199
- package/dist/modules/browser/bridge-path.js +0 -10
- package/dist/modules/browser/driver.js +0 -136
- package/dist/modules/context/chunk-query.js +0 -100
- package/dist/modules/context/fact-extractor.js +0 -162
- package/dist/modules/context/history.js +0 -15
- package/dist/modules/execution/audit-runners.js +0 -152
- package/dist/modules/execution/execution-plugin.js +0 -272
- package/dist/modules/execution/plan-tool.js +0 -508
- package/dist/modules/execution/windows-commands.js +0 -41
- package/dist/modules/indexer/project-profile.js +0 -183
- package/dist/modules/lsp/check-tool.js +0 -58
- package/dist/modules/lsp/command.js +0 -60
- package/dist/modules/lsp/probe.js +0 -76
- package/dist/modules/lsp/project-root.js +0 -32
- package/dist/modules/lsp/startup-check.js +0 -141
- package/dist/modules/processes/detect.js +0 -34
- package/dist/modules/skills/matcher.js +0 -27
- package/dist/modules/updater/module.js +0 -116
- package/dist/tools/chunk-query.js +0 -99
- package/dist/tools/download-file.js +0 -116
- package/dist/tools/enable-tools.js +0 -58
- package/dist/tools/hidden-tools-block.js +0 -37
- package/dist/ui/line-editor.js +0 -703
- package/dist/ui/line-math.js +0 -69
- package/dist/ui/plan-view.js +0 -103
|
@@ -7,7 +7,9 @@ function sanitizeToolName(server, tool) {
|
|
|
7
7
|
}
|
|
8
8
|
function convertInputSchema(schema) {
|
|
9
9
|
if (schema && typeof schema === "object" && "properties" in schema) {
|
|
10
|
-
const required = Array.isArray(schema.required)
|
|
10
|
+
const required = Array.isArray(schema.required)
|
|
11
|
+
? schema.required
|
|
12
|
+
: [];
|
|
11
13
|
return {
|
|
12
14
|
type: "object",
|
|
13
15
|
properties: schema.properties,
|
|
@@ -116,7 +118,9 @@ export class MCPModule {
|
|
|
116
118
|
const result = await client.callTool(d.toolName, args);
|
|
117
119
|
return {
|
|
118
120
|
success: true,
|
|
119
|
-
output: typeof result === "string"
|
|
121
|
+
output: typeof result === "string"
|
|
122
|
+
? result
|
|
123
|
+
: JSON.stringify(result, null, 2),
|
|
120
124
|
};
|
|
121
125
|
}
|
|
122
126
|
catch (e) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { MemoryStore } from
|
|
1
|
+
export { MemoryStore } from './store';
|
|
@@ -1,92 +1,44 @@
|
|
|
1
|
-
import { homedir } from
|
|
2
|
-
import { join } from
|
|
3
|
-
import { MemoryStore } from
|
|
4
|
-
import { rememberTool } from
|
|
5
|
-
import { recallTool } from
|
|
6
|
-
const MEMORY_MD_FILES = ["errors", "conventions", "decisions", "facts"];
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { MemoryStore } from './store';
|
|
4
|
+
import { rememberTool } from '../../tools/remember';
|
|
5
|
+
import { recallTool } from '../../tools/recall';
|
|
7
6
|
export class MemoryModule {
|
|
8
|
-
name =
|
|
7
|
+
name = 'memory';
|
|
9
8
|
store;
|
|
10
|
-
constructor(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
const dir = storeOrDir || join(homedir(), ".mma", "memory");
|
|
16
|
-
this.store = new MemoryStore(dir);
|
|
17
|
-
}
|
|
9
|
+
constructor(memoryDir) {
|
|
10
|
+
const dir = memoryDir || join(homedir(), '.mma', 'memory');
|
|
11
|
+
this.store = new MemoryStore(dir);
|
|
18
12
|
}
|
|
19
13
|
getSystemPromptBlock() {
|
|
20
|
-
const parts = [];
|
|
21
14
|
const prefs = this.store.getPreferences();
|
|
22
|
-
if (Object.keys(prefs).length
|
|
23
|
-
const prefStr = Object.entries(prefs)
|
|
24
|
-
.map(([k, v]) => `${k}=${v}`)
|
|
25
|
-
.join(", ");
|
|
26
|
-
parts.push(`User preferences: ${prefStr}`);
|
|
27
|
-
}
|
|
28
|
-
const memoryTail = this.getMemoryTail();
|
|
29
|
-
if (memoryTail)
|
|
30
|
-
parts.push(memoryTail);
|
|
31
|
-
if (parts.length === 0)
|
|
15
|
+
if (Object.keys(prefs).length === 0)
|
|
32
16
|
return null;
|
|
33
|
-
const
|
|
17
|
+
const prefStr = Object.entries(prefs)
|
|
18
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
19
|
+
.join(', ');
|
|
34
20
|
return {
|
|
35
|
-
content
|
|
36
|
-
priority:
|
|
21
|
+
content: `User preferences: ${prefStr}`,
|
|
22
|
+
priority: 'normal',
|
|
37
23
|
essential: false,
|
|
38
|
-
estimatedTokens: Math.ceil(
|
|
24
|
+
estimatedTokens: Math.ceil(prefStr.length / 4) + 10,
|
|
39
25
|
};
|
|
40
26
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Bounded tail of the append-only .md memory files (errors, conventions,
|
|
43
|
-
* decisions, facts). This is what makes past-session lessons visible in the
|
|
44
|
-
* system prompt — without it the loop's error rules are write-only.
|
|
45
|
-
*/
|
|
46
|
-
getMemoryTail() {
|
|
47
|
-
const sections = [];
|
|
48
|
-
const tailPerFile = 3;
|
|
49
|
-
for (const name of MEMORY_MD_FILES) {
|
|
50
|
-
const content = this.store.read(name).trim();
|
|
51
|
-
if (!content)
|
|
52
|
-
continue;
|
|
53
|
-
const entries = content
|
|
54
|
-
.split(/\r?\n/)
|
|
55
|
-
.map((l) => l.trim())
|
|
56
|
-
.filter((l) => l.startsWith("- "))
|
|
57
|
-
.slice(-tailPerFile);
|
|
58
|
-
if (entries.length === 0)
|
|
59
|
-
continue;
|
|
60
|
-
sections.push(`[Memory ${name}]:\n${entries.join("\n")}`);
|
|
61
|
-
}
|
|
62
|
-
if (sections.length === 0)
|
|
63
|
-
return null;
|
|
64
|
-
let joined = sections.join("\n\n");
|
|
65
|
-
// Hard cap so a huge errors.md can never blow the system prompt budget.
|
|
66
|
-
if (joined.length > 1200)
|
|
67
|
-
joined = joined.slice(0, 1200) + "\n…";
|
|
68
|
-
return joined;
|
|
69
|
-
}
|
|
70
27
|
getToolDefinitions() {
|
|
71
28
|
return [rememberTool, recallTool];
|
|
72
29
|
}
|
|
73
30
|
getPlugin() {
|
|
74
31
|
return {
|
|
75
|
-
name:
|
|
32
|
+
name: 'memory',
|
|
76
33
|
isBuiltin: true,
|
|
77
34
|
onBuildPrompt: () => {
|
|
78
|
-
const parts = [];
|
|
79
35
|
const prefs = this.store.getPreferences();
|
|
80
|
-
if (Object.keys(prefs).length
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
const memoryTail = this.getMemoryTail();
|
|
87
|
-
if (memoryTail)
|
|
88
|
-
parts.push(memoryTail);
|
|
89
|
-
return parts.length > 0 ? parts.join("\n\n") : null;
|
|
36
|
+
if (Object.keys(prefs).length === 0)
|
|
37
|
+
return null;
|
|
38
|
+
const prefStr = Object.entries(prefs)
|
|
39
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
40
|
+
.join(', ');
|
|
41
|
+
return `User preferences: ${prefStr}`;
|
|
90
42
|
},
|
|
91
43
|
};
|
|
92
44
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readFileSync, existsSync } from
|
|
2
|
-
import { join } from
|
|
3
|
-
const MEMORY_FILES = [
|
|
1
|
+
import { readFileSync, existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
const MEMORY_FILES = ['conventions', 'decisions', 'errors', 'facts'];
|
|
4
4
|
export class MemorySearch {
|
|
5
5
|
memoryDir;
|
|
6
6
|
constructor(memoryDir) {
|
|
@@ -13,8 +13,8 @@ export class MemorySearch {
|
|
|
13
13
|
const path = join(this.memoryDir, `${name}.md`);
|
|
14
14
|
if (!existsSync(path))
|
|
15
15
|
continue;
|
|
16
|
-
const content = readFileSync(path,
|
|
17
|
-
const lines = content.split(
|
|
16
|
+
const content = readFileSync(path, 'utf-8');
|
|
17
|
+
const lines = content.split('\n');
|
|
18
18
|
for (const line of lines) {
|
|
19
19
|
if (line.toLowerCase().includes(lowerQuery)) {
|
|
20
20
|
results.push({ file: name, match: line.trim() });
|
|
@@ -22,20 +22,18 @@ export class MemorySearch {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
// Search preferences.json
|
|
25
|
-
const prefsPath = join(this.memoryDir,
|
|
25
|
+
const prefsPath = join(this.memoryDir, 'preferences.json');
|
|
26
26
|
if (existsSync(prefsPath)) {
|
|
27
27
|
try {
|
|
28
|
-
const prefs = JSON.parse(readFileSync(prefsPath,
|
|
28
|
+
const prefs = JSON.parse(readFileSync(prefsPath, 'utf-8'));
|
|
29
29
|
for (const [key, value] of Object.entries(prefs)) {
|
|
30
30
|
const searchStr = `${key}=${value}`;
|
|
31
31
|
if (searchStr.toLowerCase().includes(lowerQuery)) {
|
|
32
|
-
results.push({ file:
|
|
32
|
+
results.push({ file: 'preferences', match: `${key} = ${value}` });
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
catch {
|
|
37
|
-
/* skip */
|
|
38
|
-
}
|
|
36
|
+
catch { /* skip */ }
|
|
39
37
|
}
|
|
40
38
|
return results;
|
|
41
39
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from
|
|
2
|
-
import { join } from
|
|
3
|
-
import { MemorySearch } from
|
|
4
|
-
const MEMORY_FILES = [
|
|
1
|
+
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { MemorySearch } from './search';
|
|
4
|
+
const MEMORY_FILES = ['conventions', 'decisions', 'errors', 'facts'];
|
|
5
5
|
export class MemoryStore {
|
|
6
6
|
memoryDir;
|
|
7
7
|
constructor(memoryDir) {
|
|
@@ -10,7 +10,7 @@ export class MemoryStore {
|
|
|
10
10
|
for (const name of MEMORY_FILES) {
|
|
11
11
|
const path = join(this.memoryDir, `${name}.md`);
|
|
12
12
|
if (!existsSync(path)) {
|
|
13
|
-
writeFileSync(path, `# ${name.charAt(0).toUpperCase() + name.slice(1)}\n\n`,
|
|
13
|
+
writeFileSync(path, `# ${name.charAt(0).toUpperCase() + name.slice(1)}\n\n`, 'utf-8');
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -22,28 +22,28 @@ export class MemoryStore {
|
|
|
22
22
|
read(name) {
|
|
23
23
|
const path = join(this.memoryDir, `${name}.md`);
|
|
24
24
|
if (!existsSync(path))
|
|
25
|
-
return
|
|
26
|
-
return readFileSync(path,
|
|
25
|
+
return '';
|
|
26
|
+
return readFileSync(path, 'utf-8');
|
|
27
27
|
}
|
|
28
28
|
append(name, entry) {
|
|
29
29
|
const path = join(this.memoryDir, `${name}.md`);
|
|
30
|
-
const timestamp = new Date().toISOString().replace(
|
|
30
|
+
const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
31
31
|
const formatted = `- **${timestamp}** — ${entry}\n`;
|
|
32
|
-
appendFileSync(path, formatted,
|
|
32
|
+
appendFileSync(path, formatted, 'utf-8');
|
|
33
33
|
}
|
|
34
34
|
search(query) {
|
|
35
35
|
const searchModule = new MemorySearch(this.memoryDir);
|
|
36
36
|
return searchModule.query(query);
|
|
37
37
|
}
|
|
38
38
|
prefsPath() {
|
|
39
|
-
return join(this.memoryDir,
|
|
39
|
+
return join(this.memoryDir, 'preferences.json');
|
|
40
40
|
}
|
|
41
41
|
getPreferences() {
|
|
42
42
|
const path = this.prefsPath();
|
|
43
43
|
if (!existsSync(path))
|
|
44
44
|
return {};
|
|
45
45
|
try {
|
|
46
|
-
return JSON.parse(readFileSync(path,
|
|
46
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
47
47
|
}
|
|
48
48
|
catch {
|
|
49
49
|
return {};
|
|
@@ -52,14 +52,14 @@ export class MemoryStore {
|
|
|
52
52
|
setPreference(key, value) {
|
|
53
53
|
const prefs = this.getPreferences();
|
|
54
54
|
prefs[key] = value;
|
|
55
|
-
writeFileSync(this.prefsPath(), JSON.stringify(prefs, null, 2),
|
|
55
|
+
writeFileSync(this.prefsPath(), JSON.stringify(prefs, null, 2), 'utf-8');
|
|
56
56
|
}
|
|
57
57
|
deletePreference(key) {
|
|
58
58
|
const prefs = this.getPreferences();
|
|
59
59
|
if (!(key in prefs))
|
|
60
60
|
return false;
|
|
61
61
|
delete prefs[key];
|
|
62
|
-
writeFileSync(this.prefsPath(), JSON.stringify(prefs, null, 2),
|
|
62
|
+
writeFileSync(this.prefsPath(), JSON.stringify(prefs, null, 2), 'utf-8');
|
|
63
63
|
return true;
|
|
64
64
|
}
|
|
65
65
|
appendRule(category, pattern, cause, solution) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t } from
|
|
1
|
+
import { t } from '../../i18n/index';
|
|
2
2
|
export class PipelineEngine {
|
|
3
3
|
stepStatus = new Map();
|
|
4
4
|
resolveDependencies(steps) {
|
|
@@ -7,7 +7,7 @@ export class PipelineEngine {
|
|
|
7
7
|
const order = [];
|
|
8
8
|
function visit(stepId, stepMap) {
|
|
9
9
|
if (inStack.has(stepId))
|
|
10
|
-
throw new Error(t(
|
|
10
|
+
throw new Error(t('pipeline.circular', { stepId }));
|
|
11
11
|
if (visited.has(stepId))
|
|
12
12
|
return;
|
|
13
13
|
inStack.add(stepId);
|
|
@@ -21,7 +21,7 @@ export class PipelineEngine {
|
|
|
21
21
|
visited.add(stepId);
|
|
22
22
|
order.push(stepId);
|
|
23
23
|
}
|
|
24
|
-
const stepMap = new Map(steps.map(
|
|
24
|
+
const stepMap = new Map(steps.map(s => [s.id, s]));
|
|
25
25
|
for (const step of steps) {
|
|
26
26
|
if (!visited.has(step.id)) {
|
|
27
27
|
visit(step.id, stepMap);
|
|
@@ -30,28 +30,28 @@ export class PipelineEngine {
|
|
|
30
30
|
return order;
|
|
31
31
|
}
|
|
32
32
|
getReadySteps(steps, completed) {
|
|
33
|
-
return steps.filter(
|
|
33
|
+
return steps.filter(s => {
|
|
34
34
|
if (completed.has(s.id))
|
|
35
35
|
return false;
|
|
36
|
-
if (this.stepStatus.get(s.id) ===
|
|
36
|
+
if (this.stepStatus.get(s.id) === 'running')
|
|
37
37
|
return false;
|
|
38
|
-
if (this.stepStatus.get(s.id) ===
|
|
38
|
+
if (this.stepStatus.get(s.id) === 'failed')
|
|
39
39
|
return false;
|
|
40
40
|
if (!s.depends_on || s.depends_on.length === 0)
|
|
41
41
|
return true;
|
|
42
|
-
return s.depends_on.every(
|
|
42
|
+
return s.depends_on.every(d => completed.has(d));
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
45
|
setStepStatus(stepId, status) {
|
|
46
46
|
this.stepStatus.set(stepId, status);
|
|
47
47
|
}
|
|
48
48
|
getStepStatus(stepId) {
|
|
49
|
-
return this.stepStatus.get(stepId) ||
|
|
49
|
+
return this.stepStatus.get(stepId) || 'pending';
|
|
50
50
|
}
|
|
51
51
|
isComplete(stepIds) {
|
|
52
|
-
return stepIds.every(
|
|
52
|
+
return stepIds.every(id => {
|
|
53
53
|
const s = this.stepStatus.get(id);
|
|
54
|
-
return s ===
|
|
54
|
+
return s === 'done' || s === 'skipped';
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
reset() {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { PipelineParser } from
|
|
2
|
-
export { PipelineEngine } from
|
|
3
|
-
export { TemplateEngine } from
|
|
1
|
+
export { PipelineParser } from './parser';
|
|
2
|
+
export { PipelineEngine } from './engine';
|
|
3
|
+
export { TemplateEngine } from './template';
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { t } from
|
|
1
|
+
import { t } from '../../i18n/index';
|
|
2
2
|
export class PipelineParser {
|
|
3
3
|
static parse(yaml) {
|
|
4
|
-
const lines = yaml.split(
|
|
4
|
+
const lines = yaml.split('\n').filter(l => l.trim());
|
|
5
5
|
const result = {};
|
|
6
6
|
let currentSection = null;
|
|
7
7
|
let currentSteps = [];
|
|
8
8
|
for (const line of lines) {
|
|
9
|
-
if (line.match(/^\w+:/) && !line.startsWith(
|
|
10
|
-
const [key, ...valParts] = line.split(
|
|
11
|
-
const val = valParts.join(
|
|
12
|
-
if (key ===
|
|
13
|
-
currentSection =
|
|
9
|
+
if (line.match(/^\w+:/) && !line.startsWith(' ') && !line.startsWith(' -')) {
|
|
10
|
+
const [key, ...valParts] = line.split(':');
|
|
11
|
+
const val = valParts.join(':').trim();
|
|
12
|
+
if (key === 'steps') {
|
|
13
|
+
currentSection = 'steps';
|
|
14
14
|
}
|
|
15
15
|
else {
|
|
16
16
|
result[key] = val;
|
|
17
17
|
currentSection = null;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
-
else if (currentSection ===
|
|
20
|
+
else if (currentSection === 'steps') {
|
|
21
21
|
const stepMatch = line.match(/^\s+- id:\s*(\S+)/);
|
|
22
22
|
if (stepMatch) {
|
|
23
23
|
currentSteps.push({ id: stepMatch[1] });
|
|
@@ -28,15 +28,12 @@ export class PipelineParser {
|
|
|
28
28
|
if (kvMatch) {
|
|
29
29
|
const key = kvMatch[1];
|
|
30
30
|
let val = kvMatch[2].trim();
|
|
31
|
-
if (val ===
|
|
31
|
+
if (val === 'true')
|
|
32
32
|
val = true;
|
|
33
|
-
else if (val ===
|
|
33
|
+
else if (val === 'false')
|
|
34
34
|
val = false;
|
|
35
|
-
else if (val.startsWith(
|
|
36
|
-
val = val
|
|
37
|
-
.slice(1, -1)
|
|
38
|
-
.split(",")
|
|
39
|
-
.map((s) => s.trim().replace(/^['"]|['"]$/g, ""));
|
|
35
|
+
else if (val.startsWith('[') && val.endsWith(']')) {
|
|
36
|
+
val = val.slice(1, -1).split(',').map((s) => s.trim().replace(/^['"]|['"]$/g, ''));
|
|
40
37
|
}
|
|
41
38
|
lastStep[key] = val;
|
|
42
39
|
}
|
|
@@ -44,11 +41,11 @@ export class PipelineParser {
|
|
|
44
41
|
}
|
|
45
42
|
}
|
|
46
43
|
if (!result.name || currentSteps.length === 0) {
|
|
47
|
-
throw new Error(t(
|
|
44
|
+
throw new Error(t('pipeline.invalid'));
|
|
48
45
|
}
|
|
49
46
|
for (const step of currentSteps) {
|
|
50
47
|
if (!step.id || !step.agent || !step.prompt) {
|
|
51
|
-
throw new Error(t(
|
|
48
|
+
throw new Error(t('pipeline.step_missing_fields', { step: JSON.stringify(step) }));
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
51
|
return { name: result.name, steps: currentSteps };
|
|
@@ -2,7 +2,7 @@ export class TemplateEngine {
|
|
|
2
2
|
static render(template, params, stepOutputs) {
|
|
3
3
|
let result = template;
|
|
4
4
|
for (const [key, val] of Object.entries(params)) {
|
|
5
|
-
result = result.replace(new RegExp(`\\{${key}\\}`,
|
|
5
|
+
result = result.replace(new RegExp(`\\{${key}\\}`, 'g'), val);
|
|
6
6
|
}
|
|
7
7
|
if (stepOutputs) {
|
|
8
8
|
result = result.replace(/\{\{steps\.(\w+)\.output\}\}/g, (_, stepId) => {
|
|
@@ -2,7 +2,6 @@ import { spawn, execSync } from "child_process";
|
|
|
2
2
|
import { existsSync, readFileSync } from "fs";
|
|
3
3
|
import { resolve, extname, join } from "path";
|
|
4
4
|
import { platform } from "os";
|
|
5
|
-
import { findProjectRoot } from "../../lsp/project-root";
|
|
6
5
|
const TYPE_CHECK_DEBOUNCE_MS = 2000;
|
|
7
6
|
// Per-file syntax checks are cached by (path, content hash): re-writing a file
|
|
8
7
|
// with identical content skips the (expensive) check. Content changes → cache
|
|
@@ -41,7 +40,6 @@ function getWinDecoder() {
|
|
|
41
40
|
export class LintOnWritePlugin {
|
|
42
41
|
name = "lint-on-write";
|
|
43
42
|
priority = 10;
|
|
44
|
-
isBuiltin = true;
|
|
45
43
|
_checkPromise = null;
|
|
46
44
|
_checkTimestamp = 0;
|
|
47
45
|
async onAfterTool(ctx, call, result) {
|
|
@@ -64,10 +62,10 @@ export class LintOnWritePlugin {
|
|
|
64
62
|
return;
|
|
65
63
|
}
|
|
66
64
|
await this.runProjectLint(ctx, result);
|
|
67
|
-
await this.runProjectTypeCheck(
|
|
65
|
+
await this.runProjectTypeCheck(ctx, result);
|
|
68
66
|
}
|
|
69
67
|
async checkSyntax(filePath, ext, baseDir) {
|
|
70
|
-
if (ext === ".ts" || ext === ".tsx"
|
|
68
|
+
if (ext === ".ts" || ext === ".tsx") {
|
|
71
69
|
// Syntax-only check via bun's parser (~200ms) instead of `npx tsc`
|
|
72
70
|
// (~3-5s per write). Full type errors are still surfaced by the
|
|
73
71
|
// debounced project typecheck below when a tsconfig exists.
|
|
@@ -95,19 +93,21 @@ export class LintOnWritePlugin {
|
|
|
95
93
|
return null;
|
|
96
94
|
}
|
|
97
95
|
const stderr = err.stderr?.toString() || err.stdout?.toString() || "";
|
|
98
|
-
const firstError = stderr.split("\n").find((line) => line.trim()) ||
|
|
96
|
+
const firstError = stderr.split("\n").find((line) => line.trim()) ||
|
|
97
|
+
"TypeScript syntax error";
|
|
99
98
|
syntaxCache.set(filePath, { hash, error: firstError.trim() });
|
|
100
99
|
return firstError.trim();
|
|
101
100
|
}
|
|
102
101
|
}
|
|
103
|
-
if (ext === ".js" || ext === ".jsx"
|
|
102
|
+
if (ext === ".js" || ext === ".jsx") {
|
|
104
103
|
try {
|
|
105
104
|
await runAsync(`node --check "${filePath}"`, baseDir, 5000);
|
|
106
105
|
return null;
|
|
107
106
|
}
|
|
108
107
|
catch (err) {
|
|
109
108
|
const stderr = err.stderr?.toString() || "";
|
|
110
|
-
const firstError = stderr.split("\n").find((line) => line.trim()) ||
|
|
109
|
+
const firstError = stderr.split("\n").find((line) => line.trim()) ||
|
|
110
|
+
"JavaScript syntax error";
|
|
111
111
|
return firstError.trim();
|
|
112
112
|
}
|
|
113
113
|
}
|
|
@@ -129,25 +129,18 @@ export class LintOnWritePlugin {
|
|
|
129
129
|
ctx.logger.debug("Lint passed");
|
|
130
130
|
}
|
|
131
131
|
catch (err) {
|
|
132
|
-
const stderr = (err.stderr?.toString?.() || "").trim();
|
|
133
|
-
const stdout = (err.stdout?.toString?.() || "").trim();
|
|
134
|
-
const detail = (stderr || stdout || err.message).split("\n").filter(Boolean).slice(-5).join("\n");
|
|
135
132
|
ctx.logger.warn(`Lint failed: ${err.message}`);
|
|
136
|
-
result.output += `\n\n[Project lint failed]: ${
|
|
133
|
+
result.output += `\n\n[Project lint failed]: ${err.message}`;
|
|
137
134
|
}
|
|
138
135
|
}
|
|
139
|
-
async runProjectTypeCheck(
|
|
140
|
-
|
|
141
|
-
// In a nested layout (`baseDir/proj/tsconfig.json`) the old baseDir-based
|
|
142
|
-
// lookup silently skipped the typecheck on every write — the whole
|
|
143
|
-
// session then ran with zero type feedback (observed in ses_mst2r0r5).
|
|
144
|
-
const projectRoot = findProjectRoot(filePath, baseDir, ["tsconfig.json", "package.json"]);
|
|
145
|
-
const tsconfigPath = join(projectRoot, "tsconfig.json");
|
|
136
|
+
async runProjectTypeCheck(ctx, result) {
|
|
137
|
+
const tsconfigPath = join(ctx.baseDir, "tsconfig.json");
|
|
146
138
|
if (!existsSync(tsconfigPath)) {
|
|
147
139
|
return;
|
|
148
140
|
}
|
|
149
141
|
const now = Date.now();
|
|
150
|
-
if (this._checkPromise &&
|
|
142
|
+
if (this._checkPromise &&
|
|
143
|
+
now - this._checkTimestamp < TYPE_CHECK_DEBOUNCE_MS) {
|
|
151
144
|
const error = await this._checkPromise;
|
|
152
145
|
if (error) {
|
|
153
146
|
result.output += `\n\n[Project typecheck failed]: ${error}`;
|
|
@@ -155,7 +148,7 @@ export class LintOnWritePlugin {
|
|
|
155
148
|
return;
|
|
156
149
|
}
|
|
157
150
|
this._checkTimestamp = now;
|
|
158
|
-
this._checkPromise = this.runTscCheck(
|
|
151
|
+
this._checkPromise = this.runTscCheck(ctx.baseDir);
|
|
159
152
|
const error = await this._checkPromise;
|
|
160
153
|
if (error) {
|
|
161
154
|
result.output += `\n\n[Project typecheck failed]: ${error}`;
|
|
@@ -174,7 +167,9 @@ export class LintOnWritePlugin {
|
|
|
174
167
|
}
|
|
175
168
|
const stderr = err.stderr?.toString() || err.stdout?.toString() || "";
|
|
176
169
|
if (stderr.includes("error TS")) {
|
|
177
|
-
const firstError = stderr
|
|
170
|
+
const firstError = stderr
|
|
171
|
+
.split("\n")
|
|
172
|
+
.find((line) => line.includes("error TS")) ||
|
|
178
173
|
"TypeScript type error";
|
|
179
174
|
return firstError.trim();
|
|
180
175
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { PluginManager } from
|
|
1
|
+
export { PluginManager } from './manager';
|
|
@@ -1,70 +1,28 @@
|
|
|
1
|
-
import { readdirSync, existsSync, statSync } from
|
|
2
|
-
import { join
|
|
3
|
-
import { t } from
|
|
4
|
-
import { semverGt } from "../updater/checker";
|
|
5
|
-
/**
|
|
6
|
-
* Entry file candidates for folder plugins. Looked up in order:
|
|
7
|
-
* `plugin.ts` > `plugin.js` > `index.ts` > `index.js`.
|
|
8
|
-
*/
|
|
9
|
-
const FOLDER_ENTRY_NAMES = ["plugin.ts", "plugin.js", "index.ts", "index.js"];
|
|
1
|
+
import { readdirSync, existsSync, statSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { t } from '../../i18n/index';
|
|
10
4
|
export class PluginLoader {
|
|
11
|
-
loadFromDir(dirPath, pluginManager, logger
|
|
5
|
+
loadFromDir(dirPath, pluginManager, logger) {
|
|
12
6
|
if (!existsSync(dirPath))
|
|
13
7
|
return;
|
|
14
|
-
const entries = readdirSync(dirPath)
|
|
8
|
+
const entries = readdirSync(dirPath);
|
|
15
9
|
for (const entry of entries) {
|
|
16
10
|
const fullPath = join(dirPath, entry);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
11
|
+
if (!statSync(fullPath).isFile())
|
|
12
|
+
continue;
|
|
13
|
+
if (!entry.endsWith('.ts') && !entry.endsWith('.js'))
|
|
14
|
+
continue;
|
|
15
|
+
try {
|
|
16
|
+
const mod = require(fullPath);
|
|
17
|
+
const plugin = mod.default || mod.plugin;
|
|
18
|
+
if (plugin && plugin.name) {
|
|
19
|
+
pluginManager.register(plugin);
|
|
20
|
+
logger?.warn(t('plugin.loaded', { name: plugin.name }));
|
|
27
21
|
}
|
|
28
22
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
findEntryFile(dir) {
|
|
32
|
-
for (const name of FOLDER_ENTRY_NAMES) {
|
|
33
|
-
const candidate = join(dir, name);
|
|
34
|
-
if (existsSync(candidate))
|
|
35
|
-
return candidate;
|
|
36
|
-
}
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
tryLoad(fullPath, label, pluginManager, logger, options) {
|
|
40
|
-
try {
|
|
41
|
-
const mod = require(fullPath);
|
|
42
|
-
const plugin = mod.default || mod.plugin;
|
|
43
|
-
if (plugin && plugin.name) {
|
|
44
|
-
if (plugin.minMmaVersion &&
|
|
45
|
-
options?.mmaVersion &&
|
|
46
|
-
semverGt(plugin.minMmaVersion, options.mmaVersion)) {
|
|
47
|
-
logger?.warn(t("plugin.incompatible", {
|
|
48
|
-
name: plugin.name,
|
|
49
|
-
min: plugin.minMmaVersion,
|
|
50
|
-
current: options.mmaVersion,
|
|
51
|
-
}));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const result = pluginManager.register(plugin, options?.source);
|
|
55
|
-
if (result.status === "registered") {
|
|
56
|
-
logger?.warn(t("plugin.loaded", { name: plugin.name }));
|
|
57
|
-
}
|
|
58
|
-
else if (result.status === "skipped_older") {
|
|
59
|
-
logger?.warn(t("plugin.dedup_older", {
|
|
60
|
-
name: plugin.name,
|
|
61
|
-
version: plugin.version || "0.0.0",
|
|
62
|
-
}));
|
|
63
|
-
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
logger?.warn(t('plugin.skipped', { entry, message: e.message }));
|
|
64
25
|
}
|
|
65
26
|
}
|
|
66
|
-
catch (e) {
|
|
67
|
-
logger?.warn(t("plugin.skipped", { entry: label, message: e.message }));
|
|
68
|
-
}
|
|
69
27
|
}
|
|
70
28
|
}
|