mocode-ai 0.7.0 → 0.7.1
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/README.md +4 -3
- package/README.zh-CN.md +3 -3
- package/dist/agent/core.js +162 -108
- package/dist/agent/index.js +20 -0
- package/dist/agent/spawn.js +9 -1
- package/dist/config/index.js +12 -0
- package/dist/i18n/index.js +34 -0
- package/dist/llm/index.js +6 -2
- package/dist/mcp/index.js +15 -2
- package/dist/permissions/index.js +110 -93
- package/dist/repl/index.js +39 -5
- package/dist/rollback/index.js +31 -0
- package/dist/session/index.js +1 -0
- package/dist/session/trace.js +26 -0
- package/dist/tools/builtins/index.js +35 -4
- package/dist/tools/builtins/run-command.js +115 -66
- package/dist/tools/builtins/task.js +5 -2
- package/dist/tools/constants.js +5 -1
- package/dist/tools/registry.js +108 -25
- package/dist/verification/affected.js +149 -0
- package/dist/verification/discovery.js +40 -0
- package/dist/verification/index.js +163 -0
- package/dist/verification/profile.js +237 -0
- package/dist/verification/types.js +1 -0
- package/package.json +5 -2
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { getSandboxRoot } from '../sandbox/index.js';
|
|
3
|
+
import { checkPermission } from '../permissions/index.js';
|
|
4
|
+
import { beginWorkspaceMutation, endWorkspaceMutation, getCurrentTurnMutationState, } from '../rollback/index.js';
|
|
5
|
+
import { formatCommandResult, runCommandRaw, runCommandTool, } from '../tools/builtins/run-command.js';
|
|
6
|
+
import { resolveAffectedPackages } from './affected.js';
|
|
7
|
+
import { discoverPackageValidationCommand } from './discovery.js';
|
|
8
|
+
import { discoverProjectProfile } from './profile.js';
|
|
9
|
+
export { resolveAffectedPackages } from './affected.js';
|
|
10
|
+
export { discoverPackageValidationCommand, discoverValidationCommand } from './discovery.js';
|
|
11
|
+
export { clearProjectProfileCache, discoverProjectProfile } from './profile.js';
|
|
12
|
+
const NON_CODE_EXTENSIONS = new Set([
|
|
13
|
+
'.md', '.mdx', '.txt', '.rst', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp',
|
|
14
|
+
]);
|
|
15
|
+
function isNonCodePath(file) {
|
|
16
|
+
return NON_CODE_EXTENSIONS.has(path.extname(file).toLowerCase());
|
|
17
|
+
}
|
|
18
|
+
function affectedSummary(affected) {
|
|
19
|
+
return affected.packages.map((item) => ({
|
|
20
|
+
name: item.package.name,
|
|
21
|
+
root: item.package.root,
|
|
22
|
+
reasons: item.reasons,
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
function selectionOutput(affected) {
|
|
26
|
+
if (affected.packages.length === 0)
|
|
27
|
+
return '';
|
|
28
|
+
return [
|
|
29
|
+
'Affected packages:',
|
|
30
|
+
...affected.packages.map((item) => {
|
|
31
|
+
const reasons = item.reasons.map((reason) => `${reason.kind}(${reason.changedPath})`).join(', ');
|
|
32
|
+
return `- ${item.package.name}: ${reasons}`;
|
|
33
|
+
}),
|
|
34
|
+
].join('\n');
|
|
35
|
+
}
|
|
36
|
+
function skipped(reason, patch = {}) {
|
|
37
|
+
const state = getCurrentTurnMutationState();
|
|
38
|
+
return {
|
|
39
|
+
status: 'skipped',
|
|
40
|
+
output: '',
|
|
41
|
+
durationMs: 0,
|
|
42
|
+
skipReason: reason,
|
|
43
|
+
changedFiles: state.changedFiles.map((item) => item.path),
|
|
44
|
+
mutationVersion: state.version,
|
|
45
|
+
...patch,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function commandSummary(selected) {
|
|
49
|
+
return selected.map((item) => `${item.affected.package.name}: ${item.command.command}`).join(' | ');
|
|
50
|
+
}
|
|
51
|
+
/** Run the lowest-cost validation command for each package affected by this turn's code changes. */
|
|
52
|
+
export async function runAutomaticValidation(signal, callbacks = {}) {
|
|
53
|
+
const before = getCurrentTurnMutationState();
|
|
54
|
+
const changedPaths = before.changedFiles.map((item) => item.path);
|
|
55
|
+
if (changedPaths.length === 0)
|
|
56
|
+
return skipped('no_changes');
|
|
57
|
+
const validationPaths = changedPaths.filter((item) => !isNonCodePath(item));
|
|
58
|
+
if (validationPaths.length === 0)
|
|
59
|
+
return skipped('non_code_changes');
|
|
60
|
+
const root = path.resolve(getSandboxRoot() ?? process.cwd());
|
|
61
|
+
let affected;
|
|
62
|
+
let selected;
|
|
63
|
+
try {
|
|
64
|
+
const profile = discoverProjectProfile(root);
|
|
65
|
+
affected = resolveAffectedPackages(profile, validationPaths, { changedFilesBase: process.cwd() });
|
|
66
|
+
selected = affected.packages.flatMap((item) => {
|
|
67
|
+
const command = discoverPackageValidationCommand(profile, item.package);
|
|
68
|
+
return command ? [{ affected: item, command }] : [];
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return skipped('no_validation_script');
|
|
73
|
+
}
|
|
74
|
+
const summary = affectedSummary(affected);
|
|
75
|
+
const selectedOutput = selectionOutput(affected);
|
|
76
|
+
if (affected.rejected.length > 0) {
|
|
77
|
+
return skipped('invalid_changed_path', {
|
|
78
|
+
output: [
|
|
79
|
+
selectedOutput,
|
|
80
|
+
...affected.rejected.map((item) => `Rejected changed path: ${item.input} (${item.reason})`),
|
|
81
|
+
].filter(Boolean).join('\n'),
|
|
82
|
+
affectedPackages: summary,
|
|
83
|
+
rejectedChangedFiles: affected.rejected,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
if (affected.packages.length === 0) {
|
|
87
|
+
return skipped('no_affected_package', { output: selectedOutput, affectedPackages: summary });
|
|
88
|
+
}
|
|
89
|
+
if (selected.length === 0) {
|
|
90
|
+
return skipped('no_validation_script', { output: selectedOutput, affectedPackages: summary });
|
|
91
|
+
}
|
|
92
|
+
const commands = commandSummary(selected);
|
|
93
|
+
for (const item of selected) {
|
|
94
|
+
const relativeCwd = path.relative(root, item.command.cwd) || '.';
|
|
95
|
+
const permission = await checkPermission(runCommandTool, { command: item.command.command, path: relativeCwd }, signal, { projectRoot: root });
|
|
96
|
+
if (signal?.aborted) {
|
|
97
|
+
const state = getCurrentTurnMutationState();
|
|
98
|
+
return {
|
|
99
|
+
status: 'aborted',
|
|
100
|
+
command: commands,
|
|
101
|
+
output: selectedOutput,
|
|
102
|
+
durationMs: 0,
|
|
103
|
+
affectedPackages: summary,
|
|
104
|
+
changedFiles: state.changedFiles.map((change) => change.path),
|
|
105
|
+
mutationVersion: state.version,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (permission === 'deny') {
|
|
109
|
+
return skipped('permission_denied', {
|
|
110
|
+
command: commands,
|
|
111
|
+
output: selectedOutput,
|
|
112
|
+
affectedPackages: summary,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const packageResults = [];
|
|
117
|
+
const capture = beginWorkspaceMutation();
|
|
118
|
+
try {
|
|
119
|
+
for (const item of selected) {
|
|
120
|
+
callbacks.onCommandStart?.(`[${item.affected.package.name}] ${item.command.command}`);
|
|
121
|
+
const raw = await runCommandRaw(item.command.command, 120000, signal, item.command.cwd);
|
|
122
|
+
const status = raw.status === 'passed'
|
|
123
|
+
? 'passed'
|
|
124
|
+
: raw.status === 'aborted'
|
|
125
|
+
? 'aborted'
|
|
126
|
+
: 'failed';
|
|
127
|
+
packageResults.push({
|
|
128
|
+
packageName: item.affected.package.name,
|
|
129
|
+
packageRoot: item.affected.package.root,
|
|
130
|
+
status,
|
|
131
|
+
script: item.command.script,
|
|
132
|
+
command: item.command.command,
|
|
133
|
+
exitCode: raw.exitCode,
|
|
134
|
+
output: formatCommandResult(raw),
|
|
135
|
+
durationMs: raw.durationMs,
|
|
136
|
+
});
|
|
137
|
+
if (status !== 'passed')
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
finally {
|
|
142
|
+
endWorkspaceMutation(capture, 'automatic_validation');
|
|
143
|
+
}
|
|
144
|
+
const after = getCurrentTurnMutationState();
|
|
145
|
+
const terminal = packageResults.find((item) => item.status !== 'passed')
|
|
146
|
+
?? packageResults[packageResults.length - 1];
|
|
147
|
+
const output = [
|
|
148
|
+
selectedOutput,
|
|
149
|
+
...packageResults.map((item) => `[${item.packageName}] ${item.command} (cwd: ${path.relative(root, item.packageRoot) || '.'})\n${item.output}`),
|
|
150
|
+
].filter(Boolean).join('\n\n');
|
|
151
|
+
return {
|
|
152
|
+
status: terminal.status,
|
|
153
|
+
script: selected.length === 1 ? selected[0]?.command.script : undefined,
|
|
154
|
+
command: commands,
|
|
155
|
+
exitCode: terminal.exitCode,
|
|
156
|
+
output,
|
|
157
|
+
durationMs: packageResults.reduce((total, item) => total + item.durationMs, 0),
|
|
158
|
+
affectedPackages: summary,
|
|
159
|
+
packageResults,
|
|
160
|
+
changedFiles: after.changedFiles.map((item) => item.path),
|
|
161
|
+
mutationVersion: after.version,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fg from 'fast-glob';
|
|
4
|
+
const cache = new Map();
|
|
5
|
+
const ROOT_INPUTS = [
|
|
6
|
+
'package.json', 'pnpm-workspace.yaml', 'pnpm-workspace.yml',
|
|
7
|
+
'package-lock.json', 'pnpm-lock.yaml', 'yarn.lock', 'bun.lock', 'bun.lockb',
|
|
8
|
+
];
|
|
9
|
+
const SOURCE_DIRS = ['src', 'lib', 'app'];
|
|
10
|
+
const TEST_DIRS = ['test', 'tests', '__tests__', 'spec', 'src/test', 'src/tests', 'src/__tests__'];
|
|
11
|
+
const GENERATED_DIRS = ['generated', 'gen', 'dist', 'build', 'coverage', 'src/generated', 'src/gen'];
|
|
12
|
+
const VENDOR_DIRS = ['vendor', 'vendors', 'third_party', 'third-party', 'node_modules'];
|
|
13
|
+
const FIXTURE_DIRS = ['fixture', 'fixtures', '__fixtures__', 'test/fixtures', 'tests/fixtures'];
|
|
14
|
+
const ALL_MARKED_DIRS = [...SOURCE_DIRS, ...TEST_DIRS, ...GENERATED_DIRS, ...VENDOR_DIRS, ...FIXTURE_DIRS];
|
|
15
|
+
const TSCONFIG_GLOBS = ['tsconfig*.json', '{config,configs}/tsconfig*.json'];
|
|
16
|
+
const TEST_CONFIG_GLOBS = [
|
|
17
|
+
'{vitest,jest,playwright,cypress}.config.{js,cjs,mjs,ts,cts,mts,json}',
|
|
18
|
+
'.mocharc.{js,cjs,mjs,json,yaml,yml}',
|
|
19
|
+
];
|
|
20
|
+
const LINT_CONFIG_GLOBS = [
|
|
21
|
+
'eslint.config.{js,cjs,mjs,ts,cts,mts}',
|
|
22
|
+
'.eslintrc',
|
|
23
|
+
'.eslintrc.{js,cjs,json,yaml,yml}',
|
|
24
|
+
'biome.json',
|
|
25
|
+
'biome.jsonc',
|
|
26
|
+
];
|
|
27
|
+
const GLOB_OPTIONS = {
|
|
28
|
+
absolute: true,
|
|
29
|
+
onlyFiles: true,
|
|
30
|
+
unique: true,
|
|
31
|
+
followSymbolicLinks: false,
|
|
32
|
+
suppressErrors: true,
|
|
33
|
+
ignore: ['**/node_modules/**', '**/.git/**'],
|
|
34
|
+
};
|
|
35
|
+
function readManifest(file) {
|
|
36
|
+
const parsed = JSON.parse(readFileSync(file, 'utf8'));
|
|
37
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
38
|
+
throw new Error(`Invalid package manifest: ${file}`);
|
|
39
|
+
}
|
|
40
|
+
return parsed;
|
|
41
|
+
}
|
|
42
|
+
function packageManagerFromField(value) {
|
|
43
|
+
if (typeof value !== 'string')
|
|
44
|
+
return null;
|
|
45
|
+
const match = /^(npm|pnpm|yarn|bun)(?:@|$)/.exec(value.trim());
|
|
46
|
+
return match ? match[1] : null;
|
|
47
|
+
}
|
|
48
|
+
function detectPackageManager(root, field) {
|
|
49
|
+
const declared = packageManagerFromField(field);
|
|
50
|
+
if (declared)
|
|
51
|
+
return declared;
|
|
52
|
+
if (existsSync(path.join(root, 'pnpm-lock.yaml')))
|
|
53
|
+
return 'pnpm';
|
|
54
|
+
if (existsSync(path.join(root, 'yarn.lock')))
|
|
55
|
+
return 'yarn';
|
|
56
|
+
if (existsSync(path.join(root, 'bun.lock')) || existsSync(path.join(root, 'bun.lockb')))
|
|
57
|
+
return 'bun';
|
|
58
|
+
return 'npm';
|
|
59
|
+
}
|
|
60
|
+
function manifestWorkspacePatterns(value) {
|
|
61
|
+
const patterns = Array.isArray(value)
|
|
62
|
+
? value
|
|
63
|
+
: value && typeof value === 'object'
|
|
64
|
+
? value.packages
|
|
65
|
+
: [];
|
|
66
|
+
return Array.isArray(patterns)
|
|
67
|
+
? patterns.filter((item) => typeof item === 'string' && item.trim().length > 0)
|
|
68
|
+
: [];
|
|
69
|
+
}
|
|
70
|
+
function cleanYamlValue(value) {
|
|
71
|
+
const withoutComment = value.replace(/\s+#.*$/, '').trim();
|
|
72
|
+
const unquoted = withoutComment.replace(/^(['"])(.*)\1$/, '$2').trim();
|
|
73
|
+
return unquoted || null;
|
|
74
|
+
}
|
|
75
|
+
function pnpmWorkspacePatterns(file) {
|
|
76
|
+
if (!existsSync(file))
|
|
77
|
+
return [];
|
|
78
|
+
const lines = readFileSync(file, 'utf8').split(/\r?\n/);
|
|
79
|
+
const result = [];
|
|
80
|
+
let packageIndent = null;
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
const declaration = /^(\s*)packages\s*:\s*(.*)$/.exec(line);
|
|
83
|
+
if (declaration) {
|
|
84
|
+
packageIndent = declaration[1]?.length ?? 0;
|
|
85
|
+
const inline = declaration[2]?.trim();
|
|
86
|
+
if (inline?.startsWith('[') && inline.endsWith(']')) {
|
|
87
|
+
for (const item of inline.slice(1, -1).split(',')) {
|
|
88
|
+
const value = cleanYamlValue(item);
|
|
89
|
+
if (value)
|
|
90
|
+
result.push(value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (packageIndent === null || /^\s*(?:#.*)?$/.test(line))
|
|
96
|
+
continue;
|
|
97
|
+
const indent = /^\s*/.exec(line)?.[0].length ?? 0;
|
|
98
|
+
if (indent <= packageIndent)
|
|
99
|
+
break;
|
|
100
|
+
const item = /^\s*-\s*(.+?)\s*$/.exec(line);
|
|
101
|
+
if (item?.[1]) {
|
|
102
|
+
const value = cleanYamlValue(item[1]);
|
|
103
|
+
if (value)
|
|
104
|
+
result.push(value);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
function uniqueSorted(values) {
|
|
110
|
+
return [...new Set(values)].sort((left, right) => left.localeCompare(right));
|
|
111
|
+
}
|
|
112
|
+
function workspaceManifestGlobs(patterns) {
|
|
113
|
+
return patterns.map((raw) => {
|
|
114
|
+
const negative = raw.startsWith('!');
|
|
115
|
+
const value = (negative ? raw.slice(1) : raw).replace(/[\\/]+$/, '');
|
|
116
|
+
const manifest = value.endsWith('package.json') ? value : `${value}/package.json`;
|
|
117
|
+
return negative ? `!${manifest}` : manifest;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
function isInside(root, candidate) {
|
|
121
|
+
const relative = path.relative(root, candidate);
|
|
122
|
+
return relative === '' || (!relative.startsWith(`..${path.sep}`) && relative !== '..' && !path.isAbsolute(relative));
|
|
123
|
+
}
|
|
124
|
+
function discoverManifestPaths(root, patterns) {
|
|
125
|
+
const rootManifest = path.join(root, 'package.json');
|
|
126
|
+
const matches = fg.sync(workspaceManifestGlobs(patterns), { cwd: root, ...GLOB_OPTIONS });
|
|
127
|
+
const paths = [
|
|
128
|
+
...(existsSync(rootManifest) ? [rootManifest] : []),
|
|
129
|
+
...matches.map((item) => path.resolve(item)),
|
|
130
|
+
].filter((item) => isInside(root, item));
|
|
131
|
+
return uniqueSorted(paths.map((item) => path.resolve(item)));
|
|
132
|
+
}
|
|
133
|
+
function discoverConfigPaths(root, patterns) {
|
|
134
|
+
return uniqueSorted(fg.sync(patterns, { cwd: root, ...GLOB_OPTIONS }).map((item) => path.resolve(item)));
|
|
135
|
+
}
|
|
136
|
+
function existingDirectories(root, relativePaths) {
|
|
137
|
+
return relativePaths.map((item) => path.resolve(root, item)).filter((item) => {
|
|
138
|
+
try {
|
|
139
|
+
return statSync(item).isDirectory();
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function scriptsFromManifest(value) {
|
|
147
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
148
|
+
return {};
|
|
149
|
+
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === 'string'));
|
|
150
|
+
}
|
|
151
|
+
function createPackageProfile(manifestPath) {
|
|
152
|
+
const root = path.dirname(manifestPath);
|
|
153
|
+
const manifest = readManifest(manifestPath);
|
|
154
|
+
return {
|
|
155
|
+
name: typeof manifest.name === 'string' && manifest.name ? manifest.name : path.basename(root),
|
|
156
|
+
root,
|
|
157
|
+
scripts: scriptsFromManifest(manifest.scripts),
|
|
158
|
+
sourceRoots: existingDirectories(root, SOURCE_DIRS),
|
|
159
|
+
testRoots: existingDirectories(root, TEST_DIRS),
|
|
160
|
+
tsconfigPaths: discoverConfigPaths(root, TSCONFIG_GLOBS),
|
|
161
|
+
testConfigPaths: discoverConfigPaths(root, TEST_CONFIG_GLOBS),
|
|
162
|
+
lintConfigPaths: discoverConfigPaths(root, LINT_CONFIG_GLOBS),
|
|
163
|
+
generatedRoots: existingDirectories(root, GENERATED_DIRS),
|
|
164
|
+
vendorRoots: existingDirectories(root, VENDOR_DIRS),
|
|
165
|
+
fixtureRoots: existingDirectories(root, FIXTURE_DIRS),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function inputSignatures(root, workspacePatterns) {
|
|
169
|
+
const manifestPaths = discoverManifestPaths(root, workspacePatterns);
|
|
170
|
+
const packageRoots = uniqueSorted([root, ...manifestPaths.map((item) => path.dirname(item))]);
|
|
171
|
+
const configPaths = packageRoots.flatMap((packageRoot) => [
|
|
172
|
+
...discoverConfigPaths(packageRoot, TSCONFIG_GLOBS),
|
|
173
|
+
...discoverConfigPaths(packageRoot, TEST_CONFIG_GLOBS),
|
|
174
|
+
...discoverConfigPaths(packageRoot, LINT_CONFIG_GLOBS),
|
|
175
|
+
]);
|
|
176
|
+
const directoryPaths = packageRoots.flatMap((packageRoot) => ALL_MARKED_DIRS.map((item) => path.resolve(packageRoot, item)));
|
|
177
|
+
const candidates = uniqueSorted([
|
|
178
|
+
...ROOT_INPUTS.map((item) => path.join(root, item)),
|
|
179
|
+
...manifestPaths,
|
|
180
|
+
...configPaths,
|
|
181
|
+
...directoryPaths,
|
|
182
|
+
]);
|
|
183
|
+
return candidates.flatMap((item) => {
|
|
184
|
+
try {
|
|
185
|
+
const stat = statSync(item);
|
|
186
|
+
return [`${item}\0${stat.isDirectory() ? 'd' : 'f'}\0${stat.size}\0${stat.mtimeMs}`];
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return [];
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function sameSignatures(left, right) {
|
|
194
|
+
return left.length === right.length && left.every((item, index) => item === right[index]);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Build a safe, cached description of a package project. Manifests are parsed strictly as JSON;
|
|
198
|
+
* package scripts and configuration files are never imported or executed.
|
|
199
|
+
*/
|
|
200
|
+
export function discoverProjectProfile(projectRoot) {
|
|
201
|
+
const root = path.resolve(projectRoot);
|
|
202
|
+
const cached = cache.get(root);
|
|
203
|
+
if (cached) {
|
|
204
|
+
const current = inputSignatures(root, cached.workspacePatterns);
|
|
205
|
+
if (sameSignatures(current, cached.signatures))
|
|
206
|
+
return cached.profile;
|
|
207
|
+
}
|
|
208
|
+
const rootManifestPath = path.join(root, 'package.json');
|
|
209
|
+
const rootManifest = existsSync(rootManifestPath) ? readManifest(rootManifestPath) : {};
|
|
210
|
+
const pnpmFiles = ['pnpm-workspace.yaml', 'pnpm-workspace.yml']
|
|
211
|
+
.map((item) => path.join(root, item))
|
|
212
|
+
.filter((item) => existsSync(item));
|
|
213
|
+
const explicitPatterns = [
|
|
214
|
+
...manifestWorkspacePatterns(rootManifest.workspaces),
|
|
215
|
+
...pnpmFiles.flatMap((item) => pnpmWorkspacePatterns(item)),
|
|
216
|
+
];
|
|
217
|
+
const workspacePatterns = uniqueSorted(explicitPatterns.length > 0 ? explicitPatterns : ['packages/*']);
|
|
218
|
+
const profile = {
|
|
219
|
+
root,
|
|
220
|
+
packageManager: detectPackageManager(root, rootManifest.packageManager),
|
|
221
|
+
workspaceConfigPaths: uniqueSorted([
|
|
222
|
+
...(manifestWorkspacePatterns(rootManifest.workspaces).length > 0 ? [rootManifestPath] : []),
|
|
223
|
+
...pnpmFiles,
|
|
224
|
+
]),
|
|
225
|
+
workspacePatterns,
|
|
226
|
+
packages: discoverManifestPaths(root, workspacePatterns).map(createPackageProfile),
|
|
227
|
+
};
|
|
228
|
+
cache.set(root, { profile, workspacePatterns, signatures: inputSignatures(root, workspacePatterns) });
|
|
229
|
+
return profile;
|
|
230
|
+
}
|
|
231
|
+
/** Clear one cached project profile, or all profiles when no root is provided. */
|
|
232
|
+
export function clearProjectProfileCache(projectRoot) {
|
|
233
|
+
if (projectRoot === undefined)
|
|
234
|
+
cache.clear();
|
|
235
|
+
else
|
|
236
|
+
cache.delete(path.resolve(projectRoot));
|
|
237
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mocode-ai",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "终端编码 agent:LLM + tool-call 循环 + 流式输出(含思考)+ 16 个工具,接任意 OpenAI 兼容后端。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,10 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"start": "tsx src/index.ts",
|
|
22
22
|
"build": "tsc -p tsconfig.build.json",
|
|
23
|
-
"typecheck": "tsc --noEmit",
|
|
23
|
+
"typecheck": "tsc --noEmit && tsc -p evals/tsconfig.json",
|
|
24
|
+
"eval:smoke": "tsx evals/smoke.ts && tsx evals/coding/smoke.ts",
|
|
25
|
+
"eval:coding": "tsx evals/coding/runner.ts",
|
|
26
|
+
"eval:coding:list": "tsx evals/coding/runner.ts --list",
|
|
24
27
|
"prepare": "npm run build"
|
|
25
28
|
},
|
|
26
29
|
"dependencies": {
|