@mearl/setup 3.0.0 → 3.0.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/README.en.md +46 -43
- package/README.md +46 -43
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +50 -19
- package/dist/preset.d.ts +7 -2
- package/dist/preset.js +296 -49
- package/dist/skillSourceReceipts.d.ts +1 -1
- package/dist/skillSourceReceipts.js +54 -10
- package/dist/skillSources.d.ts +1 -1
- package/dist/skillSources.js +85 -33
- package/dist/skills.d.ts +1 -1
- package/dist/skills.js +60 -14
- package/package.json +6 -6
package/dist/skillSources.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type CommandRunner } from '@mearl/daemon-core';
|
|
2
2
|
import { readTrackedSkillSources, type SourceOptions, type SkillSourceScope, type TrackedSkillSource } from './skillSourceReceipts.js';
|
|
3
|
-
export { isPublicSkillSource, matchesTrackedSkillSource, readTrackedSkillSources } from './skillSourceReceipts.js';
|
|
3
|
+
export { isPublicSkillSource, matchesTrackedSkillSource, readTrackedSkillSources, } from './skillSourceReceipts.js';
|
|
4
4
|
export type { SkillSourceScope, TrackedSkillSource } from './skillSourceReceipts.js';
|
|
5
5
|
/** Matches the public Skills folder hash without writing to the installed Skill. */
|
|
6
6
|
export declare function hashSkillDirectory(directory: string, ignoreRuntimeFiles?: boolean): Promise<string>;
|
package/dist/skillSources.js
CHANGED
|
@@ -6,12 +6,15 @@ import { homedir, tmpdir } from 'node:os';
|
|
|
6
6
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
7
7
|
import { runCommand } from '@mearl/daemon-core';
|
|
8
8
|
import { satisfies, valid } from 'semver';
|
|
9
|
-
import { DISABLED_SKILL_MANIFEST, getSkillEnabled, findPluginDirectoryEntry, findPluginRegistration, listPluginNames, OFFICIAL_PLUGIN_MANAGER, pluginRegistrationNeedsRefresh, readInstalledSkillMetadata } from '@mearl/client/plugin';
|
|
9
|
+
import { DISABLED_SKILL_MANIFEST, getSkillEnabled, findPluginDirectoryEntry, findPluginRegistration, listPluginNames, OFFICIAL_PLUGIN_MANAGER, pluginRegistrationNeedsRefresh, readInstalledSkillMetadata, } from '@mearl/client/plugin';
|
|
10
10
|
import { existsSync } from 'node:fs';
|
|
11
|
-
import { gitSource, isPublicSkillSource, matchesTrackedSkillSource, readTrackedSkillSources } from './skillSourceReceipts.js';
|
|
12
|
-
export { isPublicSkillSource, matchesTrackedSkillSource, readTrackedSkillSources } from './skillSourceReceipts.js';
|
|
11
|
+
import { gitSource, isPublicSkillSource, matchesTrackedSkillSource, readTrackedSkillSources, } from './skillSourceReceipts.js';
|
|
12
|
+
export { isPublicSkillSource, matchesTrackedSkillSource, readTrackedSkillSources, } from './skillSourceReceipts.js';
|
|
13
13
|
function skillName(text) {
|
|
14
|
-
return text
|
|
14
|
+
return text
|
|
15
|
+
.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1]
|
|
16
|
+
.match(/^name:\s*["']?([^"'\r\n]+?)["']?\s*$/m)?.[1]
|
|
17
|
+
?.trim();
|
|
15
18
|
}
|
|
16
19
|
async function findSkillDirectory(root, name, skillPath) {
|
|
17
20
|
if (skillPath) {
|
|
@@ -47,7 +50,8 @@ export async function hashSkillDirectory(directory, ignoreRuntimeFiles = false)
|
|
|
47
50
|
let total = 0;
|
|
48
51
|
const visit = async (current) => {
|
|
49
52
|
for (const entry of await readdir(current, { withFileTypes: true })) {
|
|
50
|
-
if (['.git', 'node_modules'].includes(entry.name) ||
|
|
53
|
+
if (['.git', 'node_modules'].includes(entry.name) ||
|
|
54
|
+
(ignoreRuntimeFiles && ['__pycache__', '.DS_Store'].includes(entry.name)))
|
|
51
55
|
continue;
|
|
52
56
|
const file = join(current, entry.name);
|
|
53
57
|
if (entry.isDirectory())
|
|
@@ -57,7 +61,13 @@ export async function hashSkillDirectory(directory, ignoreRuntimeFiles = false)
|
|
|
57
61
|
total += info.size;
|
|
58
62
|
if (total > 64 * 1024 * 1024 || files.length >= 10_000)
|
|
59
63
|
throw new Error('Skill source exceeds the comparison limit');
|
|
60
|
-
files.push({
|
|
64
|
+
files.push({
|
|
65
|
+
path: relative(directory, file)
|
|
66
|
+
.split(sep)
|
|
67
|
+
.join('/')
|
|
68
|
+
.replace(ignoreRuntimeFiles ? /^SKILL\.md\.mearl-disabled$/ : /$^/, 'SKILL.md'),
|
|
69
|
+
bytes: await readFile(file),
|
|
70
|
+
});
|
|
61
71
|
}
|
|
62
72
|
}
|
|
63
73
|
};
|
|
@@ -71,7 +81,10 @@ export async function hashSkillDirectory(directory, ignoreRuntimeFiles = false)
|
|
|
71
81
|
}
|
|
72
82
|
/** Checks source content in a temporary checkout; never invokes the mutating Skills `check` alias. */
|
|
73
83
|
export async function checkTrackedSkillUpdates(options = {}) {
|
|
74
|
-
const sources = [
|
|
84
|
+
const sources = [
|
|
85
|
+
...readTrackedSkillSources('global', undefined, options),
|
|
86
|
+
...(options.cwd ? readTrackedSkillSources('project', options.cwd, options) : []),
|
|
87
|
+
];
|
|
75
88
|
if (sources.some(source => !isPublicSkillSource(source)))
|
|
76
89
|
throw new Error('Installed Skills require an external manager. Enable its preset.');
|
|
77
90
|
if (sources.length > 500)
|
|
@@ -88,12 +101,27 @@ export async function checkTrackedSkillUpdates(options = {}) {
|
|
|
88
101
|
let checkout = checkouts.get(key);
|
|
89
102
|
if (!checkout) {
|
|
90
103
|
const target = join(temporary, String(checkouts.size));
|
|
91
|
-
checkout = local
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
104
|
+
checkout = local
|
|
105
|
+
? Promise.resolve(local)
|
|
106
|
+
: (async () => {
|
|
107
|
+
const result = await runner('git', [
|
|
108
|
+
'clone',
|
|
109
|
+
'--depth',
|
|
110
|
+
'1',
|
|
111
|
+
'--filter=blob:none',
|
|
112
|
+
...(location.ref ? ['--branch', location.ref] : []),
|
|
113
|
+
'--',
|
|
114
|
+
location.repository,
|
|
115
|
+
target,
|
|
116
|
+
], {
|
|
117
|
+
capture: true,
|
|
118
|
+
env: { ...(options.env ?? process.env), GIT_TERMINAL_PROMPT: '0' },
|
|
119
|
+
timeoutMs: 120_000,
|
|
120
|
+
});
|
|
121
|
+
if (result.code !== 0)
|
|
122
|
+
throw new Error(`Unable to inspect Skill source: ${source.name}`);
|
|
123
|
+
return target;
|
|
124
|
+
})();
|
|
97
125
|
checkouts.set(key, checkout);
|
|
98
126
|
}
|
|
99
127
|
const root = await realpath(await checkout);
|
|
@@ -107,10 +135,12 @@ export async function checkTrackedSkillUpdates(options = {}) {
|
|
|
107
135
|
changed = result.stdout.trim() !== source.expectedHash;
|
|
108
136
|
}
|
|
109
137
|
else if (source.expectedHash && source.hashKind === 'folder')
|
|
110
|
-
changed = await hashSkillDirectory(directory) !== source.expectedHash;
|
|
138
|
+
changed = (await hashSkillDirectory(directory)) !== source.expectedHash;
|
|
111
139
|
else {
|
|
112
|
-
const installed = join(source.scope === 'global' ? options.homeDirectory ?? homedir() : options.cwd, '.agents', 'skills', source.name);
|
|
113
|
-
changed =
|
|
140
|
+
const installed = join(source.scope === 'global' ? (options.homeDirectory ?? homedir()) : options.cwd, '.agents', 'skills', source.name);
|
|
141
|
+
changed =
|
|
142
|
+
(await hashSkillDirectory(directory, true)) !==
|
|
143
|
+
(await hashSkillDirectory(installed, true));
|
|
114
144
|
}
|
|
115
145
|
if (changed)
|
|
116
146
|
updates.push({ name: source.name, source: source.source, scope: source.scope });
|
|
@@ -128,7 +158,10 @@ export function readLinkedSkillSource(directory, name) {
|
|
|
128
158
|
return undefined;
|
|
129
159
|
const actual = realpathSync(directory);
|
|
130
160
|
const git = (...args) => execFileSync('git', ['--no-optional-locks', '-c', 'core.fsmonitor=false', '-C', actual, ...args], {
|
|
131
|
-
encoding: 'utf8',
|
|
161
|
+
encoding: 'utf8',
|
|
162
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
163
|
+
timeout: 2_000,
|
|
164
|
+
maxBuffer: 16_384,
|
|
132
165
|
}).trim();
|
|
133
166
|
const root = git('rev-parse', '--show-toplevel');
|
|
134
167
|
const folder = relative(root, actual);
|
|
@@ -169,29 +202,44 @@ function canonicalPath(path) {
|
|
|
169
202
|
return parent === absolute ? absolute : join(canonicalPath(parent), basename(absolute));
|
|
170
203
|
}
|
|
171
204
|
}
|
|
172
|
-
function hasOnlySetupRegistrations(directory) {
|
|
205
|
+
function hasOnlySetupRegistrations(directory, allowUnregistered = false) {
|
|
173
206
|
const contains = (root, entry) => {
|
|
174
207
|
const path = relative(root, entry);
|
|
175
208
|
return path !== '' && path !== '..' && !path.startsWith('..' + sep) && !isAbsolute(path);
|
|
176
209
|
};
|
|
177
|
-
const registrations = listPluginNames()
|
|
178
|
-
|
|
210
|
+
const registrations = listPluginNames()
|
|
211
|
+
.flatMap(name => findPluginRegistration(name) ?? [])
|
|
212
|
+
.filter(registration => contains(resolve(directory), resolve(registration.entry)) ||
|
|
213
|
+
contains(canonicalPath(directory), canonicalPath(registration.entry)));
|
|
214
|
+
return ((allowUnregistered || registrations.length > 0) &&
|
|
215
|
+
registrations.every(registration => registration.managedBy === OFFICIAL_PLUGIN_MANAGER));
|
|
179
216
|
}
|
|
180
217
|
/** CLI and Agent readiness use the same source, manifest and registration evidence. */
|
|
181
218
|
export function inspectGlobalSkill(source, name, version, options = {}) {
|
|
182
219
|
if (!source.trim() || !name || name === '.' || name === '..' || /[\\/\0\r\n]/.test(name))
|
|
183
220
|
throw new Error('Invalid Skill identity');
|
|
184
221
|
const directory = join(options.homeDirectory ?? homedir(), '.agents', 'skills', name);
|
|
185
|
-
if (existsSync(join(directory, 'SKILL.md')) &&
|
|
222
|
+
if (existsSync(join(directory, 'SKILL.md')) &&
|
|
223
|
+
existsSync(join(directory, DISABLED_SKILL_MANIFEST)))
|
|
186
224
|
return { state: 'conflict', reason: 'SKILL_MANIFEST_CONFLICT' };
|
|
187
|
-
const receipt = (options.readSources ?? readTrackedSkillSources)('global', undefined, {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
225
|
+
const receipt = (options.readSources ?? readTrackedSkillSources)('global', undefined, {
|
|
226
|
+
homeDirectory: options.homeDirectory,
|
|
227
|
+
}).find(entry => entry.name === name);
|
|
228
|
+
const tracked = receipt ??
|
|
229
|
+
(!/(?:#|\/(?:tree|blob)\/)/.test(source) ? readLinkedSkillSource(directory, name) : undefined);
|
|
230
|
+
if (tracked &&
|
|
231
|
+
((!options.externalManager && !isPublicSkillSource(tracked)) ||
|
|
232
|
+
!matchesTrackedSkillSource(source, tracked, options.normalizeSource))) {
|
|
191
233
|
const metadata = readInstalledSkillMetadata(directory);
|
|
192
|
-
if (receipt &&
|
|
193
|
-
options.
|
|
194
|
-
|
|
234
|
+
if (receipt &&
|
|
235
|
+
(options.externalManager || isPublicSkillSource(receipt)) &&
|
|
236
|
+
metadata?.name === name) {
|
|
237
|
+
// A missing ref requires installing the requested ref before the Skill can be ready.
|
|
238
|
+
const missingRef = matchesTrackedSkillSource(source, receipt, options.normalizeSource, 'missing');
|
|
239
|
+
const declaredMigration = options.migrateFrom?.some(previous => matchesTrackedSkillSource(previous, receipt, options.normalizeSource));
|
|
240
|
+
if ((missingRef || declaredMigration) && hasOnlySetupRegistrations(directory, missingRef)) {
|
|
241
|
+
return { state: 'migration', reason: 'SKILL_SOURCE_MIGRATION_REQUIRED' };
|
|
242
|
+
}
|
|
195
243
|
}
|
|
196
244
|
return { state: 'conflict', reason: 'SKILL_SOURCE_CONFLICT' };
|
|
197
245
|
}
|
|
@@ -202,7 +250,8 @@ export function inspectGlobalSkill(source, name, version, options = {}) {
|
|
|
202
250
|
return { state: 'conflict', reason: 'SKILL_SOURCE_UNKNOWN' };
|
|
203
251
|
if (metadata.name !== name)
|
|
204
252
|
return { state: 'conflict', reason: 'SKILL_MANIFEST_IDENTITY' };
|
|
205
|
-
if (version &&
|
|
253
|
+
if (version &&
|
|
254
|
+
(!metadata.version || !valid(metadata.version) || !satisfies(metadata.version, version)))
|
|
206
255
|
return { state: 'version', reason: 'SKILL_VERSION_REQUIRED' };
|
|
207
256
|
const entry = findPluginDirectoryEntry(directory);
|
|
208
257
|
const names = listPluginNames().filter(name => {
|
|
@@ -212,10 +261,13 @@ export function inspectGlobalSkill(source, name, version, options = {}) {
|
|
|
212
261
|
const managedNames = names.filter(name => findPluginRegistration(name)?.managedBy === OFFICIAL_PLUGIN_MANAGER);
|
|
213
262
|
// User aliases may share a managed Skill without participating in its readiness or cleanup.
|
|
214
263
|
const requiredNames = managedNames.length ? managedNames : names;
|
|
215
|
-
if (entry &&
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
264
|
+
if (entry &&
|
|
265
|
+
(requiredNames.length !== 1 ||
|
|
266
|
+
!requiredNames.some(name => {
|
|
267
|
+
const registration = findPluginRegistration(name);
|
|
268
|
+
return (resolve(registration.entry) === resolve(entry) &&
|
|
269
|
+
!pluginRegistrationNeedsRefresh(registration, true));
|
|
270
|
+
})))
|
|
219
271
|
return { state: 'registration', reason: 'SKILL_PLUGIN_REGISTRATION_REQUIRED' };
|
|
220
272
|
if (!entry && names.length)
|
|
221
273
|
return { state: 'registration', reason: 'SKILL_PLUGIN_REGISTRATION_REQUIRED' };
|
package/dist/skills.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { inspectGlobalSkill } from './skillSources.js';
|
|
|
2
2
|
import type { PluginCatalogSelection } from '@mearl/client/plugin';
|
|
3
3
|
import type { ResolvedComponent } from '@mearl/preset';
|
|
4
4
|
import { type CommandRunner, type CommandResult, type RunCommandOptions } from '@mearl/daemon-core';
|
|
5
|
-
export declare const SKILLS_CLI_PACKAGE = "skills
|
|
5
|
+
export declare const SKILLS_CLI_PACKAGE = "skills";
|
|
6
6
|
export declare const MEARL_SKILL_SOURCE: string;
|
|
7
7
|
export interface ResolveSkillsOptions {
|
|
8
8
|
env?: NodeJS.ProcessEnv;
|
package/dist/skills.js
CHANGED
|
@@ -5,7 +5,7 @@ import { NodeCapabilityRegistry } from '@mearl/daemon-core/capabilities';
|
|
|
5
5
|
import { inspectGlobalSkill, readTrackedSkillSources } from './skillSources.js';
|
|
6
6
|
import { PresetInstallationStore } from './presetStore.js';
|
|
7
7
|
import { findExecutable, isExecutable, npmExecEnvironment, prependCommandPath, prependNodePath, runCommand, } from '@mearl/daemon-core';
|
|
8
|
-
export const SKILLS_CLI_PACKAGE = 'skills
|
|
8
|
+
export const SKILLS_CLI_PACKAGE = 'skills';
|
|
9
9
|
export const MEARL_SKILL_SOURCE = SETUP_VERSION.includes('-beta.')
|
|
10
10
|
? `https://github.com/mearlhq/mearl/tree/v${SETUP_VERSION}`
|
|
11
11
|
: 'https://github.com/mearlhq/mearl';
|
|
@@ -40,7 +40,7 @@ export function resolveSkillsInvocation(options = {}) {
|
|
|
40
40
|
return null;
|
|
41
41
|
return {
|
|
42
42
|
command: npx,
|
|
43
|
-
argsPrefix: ['-y', SKILLS_CLI_PACKAGE],
|
|
43
|
+
argsPrefix: ['-y', `--package=${SKILLS_CLI_PACKAGE}`, '--', 'skills'],
|
|
44
44
|
env: npmExecEnvironment(prependNodePath(prependCommandPath(npx, env), processExecPath)),
|
|
45
45
|
};
|
|
46
46
|
}
|
|
@@ -49,37 +49,81 @@ export async function captureSkillsCli(options = {}) {
|
|
|
49
49
|
const env = options.env ?? process.env;
|
|
50
50
|
const runner = options.runCommand ?? runCommand;
|
|
51
51
|
const directory = env.MEARL_CAPABILITY_DIR || path.join(homedir(), '.mearl', 'capabilities');
|
|
52
|
-
const current = await new PresetInstallationStore({ directory }).current({
|
|
52
|
+
const current = await new PresetInstallationStore({ directory }).current({
|
|
53
|
+
verifyResources: false,
|
|
54
|
+
});
|
|
53
55
|
const registry = new NodeCapabilityRegistry({ env: Object.freeze({ ...env }), runCommand: runner }, directory, { hooks: ['skills.manager'] });
|
|
54
56
|
await registry.refresh();
|
|
55
|
-
if (current.activation?.revision !== registry.revision ||
|
|
57
|
+
if (current.activation?.revision !== registry.revision ||
|
|
58
|
+
current.activation?.installationRevision !== registry.installationRevision)
|
|
56
59
|
throw new Error('SKILL_MANAGER_CHANGED');
|
|
57
60
|
const readSources = (scope, cwd) => readTrackedSkillSources(scope, cwd, { env });
|
|
58
|
-
const selection = current.rawHash
|
|
61
|
+
const selection = current.rawHash
|
|
62
|
+
? { path: path.resolve(directory, 'active-node.json'), sha256: current.rawHash }
|
|
63
|
+
: undefined;
|
|
59
64
|
const managedSkills = Object.fromEntries(Object.entries(current.preset?.skills ?? {}).filter(([, entry]) => entry.enabled !== false));
|
|
60
65
|
const backend = registry.get('skills.manager')?.();
|
|
61
66
|
if (backend !== undefined) {
|
|
62
67
|
if (backend?.readSources !== undefined && typeof backend.readSources !== 'function')
|
|
63
68
|
throw new Error('Invalid Skill source reader');
|
|
64
|
-
if (!backend ||
|
|
69
|
+
if (!backend ||
|
|
70
|
+
typeof backend.id !== 'string' ||
|
|
71
|
+
!/^[a-z][a-z0-9-]{0,63}$/.test(backend.id) ||
|
|
72
|
+
typeof backend.cli !== 'function')
|
|
65
73
|
throw new Error('The selected Skill manager requires a preset update for CLI operations');
|
|
66
|
-
return {
|
|
74
|
+
return {
|
|
75
|
+
id: backend.id,
|
|
76
|
+
managedSkills,
|
|
77
|
+
selection,
|
|
78
|
+
inspectGlobal: (source, name, version, inspection) => inspectGlobalSkill(source, name, version, {
|
|
79
|
+
migrateFrom: inspection?.migrateFrom,
|
|
80
|
+
externalManager: true,
|
|
81
|
+
normalizeSource: backend.normalizeSource,
|
|
82
|
+
readSources: backend.readSources ?? readSources,
|
|
83
|
+
}),
|
|
84
|
+
run: async (args, settings) => {
|
|
67
85
|
const result = await backend.cli([...args], settings);
|
|
68
|
-
if (!result ||
|
|
86
|
+
if (!result ||
|
|
87
|
+
!Number.isInteger(result.code) ||
|
|
88
|
+
typeof result.stdout !== 'string' ||
|
|
89
|
+
typeof result.stderr !== 'string')
|
|
69
90
|
throw new Error('Invalid Skill manager CLI result');
|
|
70
91
|
return result;
|
|
71
|
-
}
|
|
92
|
+
},
|
|
93
|
+
};
|
|
72
94
|
}
|
|
73
95
|
const invocation = resolveSkillsInvocation(options);
|
|
74
96
|
if (!invocation)
|
|
75
97
|
return undefined;
|
|
76
|
-
return {
|
|
98
|
+
return {
|
|
99
|
+
id: 'skills',
|
|
100
|
+
managedSkills,
|
|
101
|
+
selection,
|
|
102
|
+
inspectGlobal: (source, name, version, inspection) => inspectGlobalSkill(source, name, version, {
|
|
103
|
+
migrateFrom: inspection?.migrateFrom,
|
|
104
|
+
readSources,
|
|
105
|
+
}),
|
|
106
|
+
run: (args, settings) => runner(invocation.command, [...invocation.argsPrefix, ...args], {
|
|
107
|
+
...settings,
|
|
108
|
+
env: invocation.env,
|
|
109
|
+
}),
|
|
110
|
+
};
|
|
77
111
|
}
|
|
78
112
|
export function parseInstalledSkills(output) {
|
|
79
113
|
const value = JSON.parse(stripAnsi(output).trim());
|
|
80
|
-
if (!Array.isArray(value)
|
|
114
|
+
if (!Array.isArray(value))
|
|
81
115
|
throw new Error('Skills returned an invalid skill list');
|
|
82
|
-
|
|
116
|
+
const installed = [];
|
|
117
|
+
for (const item of value) {
|
|
118
|
+
if (!item || typeof item.name !== 'string' || !['global', 'project'].includes(item.scope))
|
|
119
|
+
throw new Error('Skills returned an invalid skill list');
|
|
120
|
+
if (item.status === 'missing' && item.path === null)
|
|
121
|
+
continue;
|
|
122
|
+
if (typeof item.path !== 'string')
|
|
123
|
+
throw new Error('Skills returned an invalid skill list');
|
|
124
|
+
installed.push(item);
|
|
125
|
+
}
|
|
126
|
+
return installed;
|
|
83
127
|
}
|
|
84
128
|
export function parseGlobalMearlSkillPaths(output) {
|
|
85
129
|
const parsed = parseInstalledSkills(output);
|
|
@@ -201,12 +245,14 @@ export async function runGlobalMearlSkillOperation(operation, options = {}) {
|
|
|
201
245
|
${result.stderr}`).trim();
|
|
202
246
|
if (detail)
|
|
203
247
|
writeLine(detail);
|
|
204
|
-
if (/Failed to (?:update|remove)\s+[1-9]\d*\s+skills?/i.test(detail) ||
|
|
248
|
+
if (/Failed to (?:update|remove)\s+[1-9]\d*\s+skills?/i.test(detail) ||
|
|
249
|
+
(operation === 'update' && !/\bInstalled [1-9]\d* skills?\b/.test(detail))) {
|
|
205
250
|
writeLine(`Skill: ${operation} failed.`);
|
|
206
251
|
return false;
|
|
207
252
|
}
|
|
208
253
|
const remainingPaths = await listMearlPaths();
|
|
209
|
-
if (!remainingPaths ||
|
|
254
|
+
if (!remainingPaths ||
|
|
255
|
+
(operation === 'uninstall' ? remainingPaths.length !== 0 : remainingPaths.length === 0)) {
|
|
210
256
|
writeLine(`Skill: ${operation} did not reach the expected installation state.`);
|
|
211
257
|
return false;
|
|
212
258
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mearl/setup",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "One-shot environment setup and configuration for Mearl",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -79,11 +79,11 @@
|
|
|
79
79
|
"ws": "^8.18.3",
|
|
80
80
|
"semver": "^7.8.5",
|
|
81
81
|
"adm-zip": "^0.6.1",
|
|
82
|
-
"@mearl/
|
|
83
|
-
"@mearl/
|
|
84
|
-
"@mearl/
|
|
85
|
-
"@mearl/
|
|
86
|
-
"@mearl/
|
|
82
|
+
"@mearl/provider": "3.0.2",
|
|
83
|
+
"@mearl/preset": "3.0.2",
|
|
84
|
+
"@mearl/browser-core": "3.0.2",
|
|
85
|
+
"@mearl/daemon-core": "3.0.2",
|
|
86
|
+
"@mearl/client": "3.0.2"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
89
|
"@types/node": "^24.9.1",
|