@alavida/agentpack 0.1.6 → 0.1.7
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/intent.js +30 -5
- package/package.json +3 -3
- package/src/commands/skills.js +35 -5
- package/src/domain/skills/skill-catalog.js +116 -0
- package/src/domain/skills/skill-model.js +12 -0
- package/src/domain/skills/skill-target-resolution.js +100 -0
- package/src/lib/skills.js +160 -171
package/bin/intent.js
CHANGED
|
@@ -1,10 +1,35 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
8
|
+
const intentPackageRoot = join(packageRoot, 'node_modules', '@tanstack', 'intent');
|
|
9
|
+
|
|
6
10
|
try {
|
|
7
|
-
|
|
11
|
+
const packageJsonPath = join(intentPackageRoot, 'package.json');
|
|
12
|
+
if (!existsSync(packageJsonPath)) {
|
|
13
|
+
const error = new Error('@tanstack/intent is not installed');
|
|
14
|
+
error.code = 'MODULE_NOT_FOUND';
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
19
|
+
const cliRelativePath = typeof packageJson.bin === 'string'
|
|
20
|
+
? packageJson.bin
|
|
21
|
+
: packageJson.bin?.intent;
|
|
22
|
+
|
|
23
|
+
if (!cliRelativePath) {
|
|
24
|
+
throw new Error('@tanstack/intent does not expose an intent cli binary');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const result = spawnSync(process.execPath, [join(intentPackageRoot, cliRelativePath), ...process.argv.slice(2)], {
|
|
28
|
+
stdio: 'inherit',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (result.error) throw result.error;
|
|
32
|
+
process.exit(result.status ?? 0);
|
|
8
33
|
} catch (e) {
|
|
9
34
|
if (e?.code === 'ERR_MODULE_NOT_FOUND' || e?.code === 'MODULE_NOT_FOUND') {
|
|
10
35
|
console.error('@tanstack/intent is not installed.')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alavida/agentpack",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Package-backed skills lifecycle CLI for agent skills and plugins",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"workspaces": [
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"smoke:monorepo": "node scripts/smoke-monorepo.mjs",
|
|
24
24
|
"docs:dev": "cd docs && mint dev",
|
|
25
25
|
"changeset": "changeset",
|
|
26
|
-
"version-packages": "
|
|
27
|
-
"release": "
|
|
26
|
+
"version-packages": "node scripts/version-packages.mjs",
|
|
27
|
+
"release": "node scripts/release.mjs",
|
|
28
28
|
"intent:validate": "npx @tanstack/intent validate",
|
|
29
29
|
"build:dashboard": "node scripts/build-dashboard.mjs"
|
|
30
30
|
},
|
package/src/commands/skills.js
CHANGED
|
@@ -304,6 +304,19 @@ export function skillsCommand() {
|
|
|
304
304
|
return;
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
+
if (result.kind === 'package') {
|
|
308
|
+
output.write(`Package: ${result.packageName}`);
|
|
309
|
+
if (result.packageVersion) output.write(`Version: ${result.packageVersion}`);
|
|
310
|
+
output.write(`Path: ${result.packagePath}`);
|
|
311
|
+
output.write('');
|
|
312
|
+
output.write('Exports:');
|
|
313
|
+
for (const entry of result.exports) {
|
|
314
|
+
output.write(`- ${entry.name}`);
|
|
315
|
+
output.write(` path: ${entry.skillFile}`);
|
|
316
|
+
}
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
307
320
|
output.write(`Skill: ${result.name}`);
|
|
308
321
|
if (result.description) output.write(`Description: ${result.description}`);
|
|
309
322
|
if (result.packageName) output.write(`Package: ${result.packageName}`);
|
|
@@ -311,6 +324,7 @@ export function skillsCommand() {
|
|
|
311
324
|
if (result.status) output.write(`Status: ${result.status}`);
|
|
312
325
|
if (result.replacement) output.write(`Replacement: ${result.replacement}`);
|
|
313
326
|
if (result.message) output.write(`Message: ${result.message}`);
|
|
327
|
+
if (result.wraps) output.write(`Wraps: ${result.wraps}`);
|
|
314
328
|
output.write(`Path: ${result.skillFile}`);
|
|
315
329
|
|
|
316
330
|
output.write('');
|
|
@@ -328,6 +342,12 @@ export function skillsCommand() {
|
|
|
328
342
|
} else {
|
|
329
343
|
for (const requirement of result.requires) output.write(`- ${requirement}`);
|
|
330
344
|
}
|
|
345
|
+
|
|
346
|
+
if (result.overrides?.length) {
|
|
347
|
+
output.write('');
|
|
348
|
+
output.write('Overrides:');
|
|
349
|
+
for (const override of result.overrides) output.write(`- ${override}`);
|
|
350
|
+
}
|
|
331
351
|
});
|
|
332
352
|
|
|
333
353
|
cmd
|
|
@@ -387,16 +407,26 @@ export function skillsCommand() {
|
|
|
387
407
|
const result = validateSkillsUseCase(target);
|
|
388
408
|
|
|
389
409
|
if (globalOpts.json) {
|
|
390
|
-
output.json(
|
|
391
|
-
target
|
|
392
|
-
? result.skills[0]
|
|
393
|
-
: result
|
|
394
|
-
);
|
|
410
|
+
output.json(target && result.count === 1 ? result.skills[0] : result);
|
|
395
411
|
if (!result.valid) process.exitCode = EXIT_CODES.VALIDATION;
|
|
396
412
|
return;
|
|
397
413
|
}
|
|
398
414
|
|
|
399
415
|
if (target) {
|
|
416
|
+
if (result.count > 1) {
|
|
417
|
+
output.write(`Validated Skills: ${result.count}`);
|
|
418
|
+
output.write(`Valid Skills: ${result.validCount}`);
|
|
419
|
+
output.write(`Invalid Skills: ${result.invalidCount}`);
|
|
420
|
+
for (const skill of result.skills) {
|
|
421
|
+
output.write('');
|
|
422
|
+
output.write(`- ${skill.name || skill.packageName || skill.packagePath}`);
|
|
423
|
+
output.write(` status: ${skill.valid ? 'valid' : 'invalid'}`);
|
|
424
|
+
output.write(` path: ${skill.skillFile}`);
|
|
425
|
+
}
|
|
426
|
+
if (!result.valid) process.exitCode = EXIT_CODES.VALIDATION;
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
|
|
400
430
|
const skill = result.skills[0];
|
|
401
431
|
output.write(`Skill: ${skill.packageName || skill.packagePath}`);
|
|
402
432
|
output.write(`Status: ${skill.valid ? 'valid' : 'invalid'}`);
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import {
|
|
4
|
+
buildCanonicalSkillRequirement,
|
|
5
|
+
normalizeDisplayPath,
|
|
6
|
+
readInstalledSkillExports,
|
|
7
|
+
readPackageMetadata,
|
|
8
|
+
} from './skill-model.js';
|
|
9
|
+
|
|
10
|
+
function isIgnoredEntry(name) {
|
|
11
|
+
return name === '.git' || name === 'node_modules' || name === '.agentpack';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function listSkillPackageDirs(repoRoot, { installed = false } = {}) {
|
|
15
|
+
const root = installed ? join(repoRoot, 'node_modules') : repoRoot;
|
|
16
|
+
if (!existsSync(root)) return [];
|
|
17
|
+
|
|
18
|
+
const stack = [root];
|
|
19
|
+
const results = [];
|
|
20
|
+
|
|
21
|
+
while (stack.length > 0) {
|
|
22
|
+
const current = stack.pop();
|
|
23
|
+
let entries = [];
|
|
24
|
+
try {
|
|
25
|
+
entries = readdirSync(current, { withFileTypes: true });
|
|
26
|
+
} catch {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let hasRootSkillFile = false;
|
|
31
|
+
let packageMetadata = null;
|
|
32
|
+
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (entry.isDirectory()) {
|
|
35
|
+
if (!installed && isIgnoredEntry(entry.name)) continue;
|
|
36
|
+
stack.push(join(current, entry.name));
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (entry.name === 'SKILL.md') hasRootSkillFile = true;
|
|
41
|
+
if (entry.name !== 'package.json') continue;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
packageMetadata = readPackageMetadata(current);
|
|
45
|
+
} catch {
|
|
46
|
+
packageMetadata = null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!packageMetadata?.packageName) continue;
|
|
51
|
+
if (packageMetadata.exportedSkills || hasRootSkillFile) {
|
|
52
|
+
results.push(current);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return results.sort();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function buildCatalogKey(packageName, exportedSkills, entry) {
|
|
60
|
+
if (!packageName) return null;
|
|
61
|
+
if (exportedSkills.length <= 1) return packageName;
|
|
62
|
+
return buildCanonicalSkillRequirement(packageName, entry.name);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function readSkillPackage(repoRoot, packageDir, { origin = 'authored' } = {}) {
|
|
66
|
+
const packageMetadata = readPackageMetadata(packageDir);
|
|
67
|
+
if (!packageMetadata.packageName) return null;
|
|
68
|
+
|
|
69
|
+
const exportedSkills = readInstalledSkillExports(packageDir);
|
|
70
|
+
if (exportedSkills.length === 0) return null;
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
origin,
|
|
74
|
+
packageDir,
|
|
75
|
+
packagePath: normalizeDisplayPath(repoRoot, packageDir),
|
|
76
|
+
packageName: packageMetadata.packageName,
|
|
77
|
+
packageVersion: packageMetadata.packageVersion,
|
|
78
|
+
packageMetadata,
|
|
79
|
+
exports: exportedSkills.map((entry) => ({
|
|
80
|
+
...entry,
|
|
81
|
+
key: buildCatalogKey(packageMetadata.packageName, exportedSkills, entry),
|
|
82
|
+
packageName: packageMetadata.packageName,
|
|
83
|
+
packageVersion: packageMetadata.packageVersion,
|
|
84
|
+
packageDir,
|
|
85
|
+
packagePath: normalizeDisplayPath(repoRoot, packageDir),
|
|
86
|
+
skillDirPath: entry.skillDir,
|
|
87
|
+
skillFilePath: entry.skillFile,
|
|
88
|
+
skillPath: normalizeDisplayPath(repoRoot, entry.skillDir),
|
|
89
|
+
skillFile: normalizeDisplayPath(repoRoot, entry.skillFile),
|
|
90
|
+
})),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function listAuthoredSkillPackages(repoRoot) {
|
|
95
|
+
return listSkillPackageDirs(repoRoot)
|
|
96
|
+
.map((packageDir) => {
|
|
97
|
+
try {
|
|
98
|
+
return readSkillPackage(repoRoot, packageDir);
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
.filter(Boolean);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function listInstalledSkillPackages(repoRoot) {
|
|
107
|
+
return listSkillPackageDirs(repoRoot, { installed: true })
|
|
108
|
+
.map((packageDir) => {
|
|
109
|
+
try {
|
|
110
|
+
return readSkillPackage(repoRoot, packageDir, { origin: 'installed' });
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
.filter(Boolean);
|
|
116
|
+
}
|
|
@@ -149,6 +149,12 @@ export function parseSkillFrontmatterFile(skillFilePath) {
|
|
|
149
149
|
status: typeof fields.metadata?.status === 'string' ? fields.metadata.status : null,
|
|
150
150
|
replacement: typeof fields.metadata?.replacement === 'string' ? fields.metadata.replacement : null,
|
|
151
151
|
message: typeof fields.metadata?.message === 'string' ? fields.metadata.message : null,
|
|
152
|
+
wraps: typeof fields.metadata?.wraps === 'string'
|
|
153
|
+
? fields.metadata.wraps
|
|
154
|
+
: (typeof fields.wraps === 'string' ? fields.wraps : null),
|
|
155
|
+
overrides: Array.isArray(fields.metadata?.overrides)
|
|
156
|
+
? fields.metadata.overrides
|
|
157
|
+
: (Array.isArray(fields.overrides) ? fields.overrides : []),
|
|
152
158
|
};
|
|
153
159
|
}
|
|
154
160
|
|
|
@@ -210,10 +216,13 @@ export function readInstalledSkillExports(packageDir) {
|
|
|
210
216
|
declaredName,
|
|
211
217
|
name: metadata.name,
|
|
212
218
|
description: metadata.description,
|
|
219
|
+
sources: metadata.sources,
|
|
213
220
|
requires: metadata.requires,
|
|
214
221
|
status: metadata.status,
|
|
215
222
|
replacement: metadata.replacement,
|
|
216
223
|
message: metadata.message,
|
|
224
|
+
wraps: metadata.wraps,
|
|
225
|
+
overrides: metadata.overrides,
|
|
217
226
|
skillDir: dirname(skillFile),
|
|
218
227
|
skillFile,
|
|
219
228
|
relativeSkillFile,
|
|
@@ -233,10 +242,13 @@ export function readInstalledSkillExports(packageDir) {
|
|
|
233
242
|
declaredName: metadata.name,
|
|
234
243
|
name: metadata.name,
|
|
235
244
|
description: metadata.description,
|
|
245
|
+
sources: metadata.sources,
|
|
236
246
|
requires: metadata.requires,
|
|
237
247
|
status: metadata.status,
|
|
238
248
|
replacement: metadata.replacement,
|
|
239
249
|
message: metadata.message,
|
|
250
|
+
wraps: metadata.wraps,
|
|
251
|
+
overrides: metadata.overrides,
|
|
240
252
|
skillDir: packageDir,
|
|
241
253
|
skillFile: rootSkillFile,
|
|
242
254
|
relativeSkillFile: 'SKILL.md',
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
3
|
+
import { listAuthoredSkillPackages, listInstalledSkillPackages } from './skill-catalog.js';
|
|
4
|
+
import { NotFoundError, ValidationError } from '../../utils/errors.js';
|
|
5
|
+
|
|
6
|
+
function dedupePackages(authoredPackages, installedPackages) {
|
|
7
|
+
const seen = new Set(authoredPackages.map((pkg) => pkg.packageName));
|
|
8
|
+
return [
|
|
9
|
+
...authoredPackages,
|
|
10
|
+
...installedPackages.filter((pkg) => !seen.has(pkg.packageName)),
|
|
11
|
+
];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function loadSkillTargetContext(repoRoot, {
|
|
15
|
+
includeAuthored = true,
|
|
16
|
+
includeInstalled = true,
|
|
17
|
+
} = {}) {
|
|
18
|
+
const authoredPackages = includeAuthored ? listAuthoredSkillPackages(repoRoot) : [];
|
|
19
|
+
const installedPackages = includeInstalled ? listInstalledSkillPackages(repoRoot) : [];
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
authoredPackages,
|
|
23
|
+
installedPackages,
|
|
24
|
+
packages: dedupePackages(authoredPackages, installedPackages),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function buildPackageResolution(pkg, source) {
|
|
29
|
+
return {
|
|
30
|
+
kind: 'package',
|
|
31
|
+
source,
|
|
32
|
+
package: pkg,
|
|
33
|
+
exports: pkg.exports,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function buildExportResolution(pkg, skillExport, source) {
|
|
38
|
+
return {
|
|
39
|
+
kind: 'export',
|
|
40
|
+
source,
|
|
41
|
+
package: pkg,
|
|
42
|
+
export: skillExport,
|
|
43
|
+
exports: [skillExport],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function resolveSkillTarget(repoRoot, target, options = {}) {
|
|
48
|
+
const context = loadSkillTargetContext(repoRoot, options);
|
|
49
|
+
const { packages } = context;
|
|
50
|
+
|
|
51
|
+
if (typeof target !== 'string' || target.length === 0) {
|
|
52
|
+
throw new NotFoundError('skill not found', {
|
|
53
|
+
code: 'skill_not_found',
|
|
54
|
+
suggestion: `Target: ${target}`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const absoluteTarget = isAbsolute(target) ? target : resolve(repoRoot, target);
|
|
59
|
+
|
|
60
|
+
if (existsSync(absoluteTarget)) {
|
|
61
|
+
for (const pkg of packages) {
|
|
62
|
+
if (pkg.packageDir === absoluteTarget) {
|
|
63
|
+
return buildPackageResolution(pkg, 'package_path');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const skillExport of pkg.exports) {
|
|
67
|
+
if (skillExport.skillDirPath === absoluteTarget) {
|
|
68
|
+
return buildExportResolution(pkg, skillExport, 'skill_path');
|
|
69
|
+
}
|
|
70
|
+
if (skillExport.skillFilePath === absoluteTarget) {
|
|
71
|
+
return buildExportResolution(pkg, skillExport, 'skill_file');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const pkg = packages.find((entry) => entry.packageName === target);
|
|
78
|
+
if (pkg) {
|
|
79
|
+
return buildPackageResolution(pkg, 'package_name');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
throw new NotFoundError('skill not found', {
|
|
83
|
+
code: 'skill_not_found',
|
|
84
|
+
suggestion: `Target: ${target}`,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function resolveSingleSkillTarget(repoRoot, target, options = {}) {
|
|
89
|
+
const resolved = resolveSkillTarget(repoRoot, target, options);
|
|
90
|
+
|
|
91
|
+
if (resolved.kind === 'export') return resolved;
|
|
92
|
+
if (resolved.exports.length === 1) {
|
|
93
|
+
return buildExportResolution(resolved.package, resolved.exports[0], resolved.source);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
throw new ValidationError('ambiguous skill target', {
|
|
97
|
+
code: 'ambiguous_skill_target',
|
|
98
|
+
suggestion: resolved.exports.map((entry) => entry.skillPath).join(', '),
|
|
99
|
+
});
|
|
100
|
+
}
|
package/src/lib/skills.js
CHANGED
|
@@ -26,6 +26,8 @@ import {
|
|
|
26
26
|
readInstalledSkillExports,
|
|
27
27
|
readPackageMetadata,
|
|
28
28
|
} from '../domain/skills/skill-model.js';
|
|
29
|
+
import { listAuthoredSkillPackages } from '../domain/skills/skill-catalog.js';
|
|
30
|
+
import { resolveSingleSkillTarget, resolveSkillTarget } from '../domain/skills/skill-target-resolution.js';
|
|
29
31
|
import { inspectMaterializedSkills } from '../infrastructure/runtime/inspect-materialized-skills.js';
|
|
30
32
|
import {
|
|
31
33
|
buildStateRecordForPackageDir,
|
|
@@ -96,24 +98,15 @@ function resolveDevLinkedSkills(repoRoot, rootSkillDir) {
|
|
|
96
98
|
}
|
|
97
99
|
|
|
98
100
|
function resolveLocalPackagedSkillDir(repoRoot, target) {
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const packageJsonPath = join(skillDir, 'package.json');
|
|
109
|
-
if (!existsSync(packageJsonPath)) {
|
|
110
|
-
throw new AgentpackError(`package.json not found: ${packageJsonPath}`, {
|
|
111
|
-
code: 'package_json_not_found',
|
|
112
|
-
exitCode: EXIT_CODES.GENERAL,
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return { skillDir, skillFile, packageJsonPath };
|
|
101
|
+
const resolved = resolveSingleSkillTarget(repoRoot, target, { includeInstalled: false });
|
|
102
|
+
return {
|
|
103
|
+
skillDir: resolved.export.skillDirPath,
|
|
104
|
+
skillFile: resolved.export.skillFilePath,
|
|
105
|
+
packageDir: resolved.package.packageDir,
|
|
106
|
+
packageJsonPath: join(resolved.package.packageDir, 'package.json'),
|
|
107
|
+
packageName: resolved.package.packageName,
|
|
108
|
+
skillName: resolved.export.name,
|
|
109
|
+
};
|
|
117
110
|
}
|
|
118
111
|
|
|
119
112
|
function resolveSkillFileTarget(repoRoot, target) {
|
|
@@ -275,11 +268,11 @@ function reconcileDevSession(repoRoot) {
|
|
|
275
268
|
}
|
|
276
269
|
|
|
277
270
|
export function syncSkillDependencies(skillDir) {
|
|
278
|
-
const
|
|
279
|
-
|
|
271
|
+
const required = [...new Set(
|
|
272
|
+
readInstalledSkillExports(skillDir).flatMap((entry) => entry.requires || [])
|
|
273
|
+
)].sort((a, b) => a.localeCompare(b));
|
|
280
274
|
const { packageJsonPath, packageJson } = readPackageJson(skillDir);
|
|
281
275
|
const nextDependencies = { ...(packageJson.dependencies || {}) };
|
|
282
|
-
const required = [...new Set(metadata.requires || [])].sort((a, b) => a.localeCompare(b));
|
|
283
276
|
const requiredSet = new Set(required);
|
|
284
277
|
const added = [];
|
|
285
278
|
const removed = [];
|
|
@@ -320,14 +313,14 @@ export function devSkill(target, {
|
|
|
320
313
|
} = {}) {
|
|
321
314
|
const repoRoot = findRepoRoot(cwd);
|
|
322
315
|
try {
|
|
323
|
-
const { skillDir } = resolveLocalPackagedSkillDir(repoRoot, target);
|
|
316
|
+
const { skillDir, packageDir } = resolveLocalPackagedSkillDir(repoRoot, target);
|
|
324
317
|
const { linkedSkills, unresolved } = resolveDevLinkedSkills(repoRoot, skillDir);
|
|
325
318
|
const rootSkill = linkedSkills.find((entry) => entry.skillDir === skillDir);
|
|
326
319
|
const synced = sync
|
|
327
|
-
? syncSkillDependencies(
|
|
320
|
+
? syncSkillDependencies(packageDir)
|
|
328
321
|
: {
|
|
329
|
-
skillDir,
|
|
330
|
-
packageJsonPath: join(
|
|
322
|
+
skillDir: packageDir,
|
|
323
|
+
packageJsonPath: join(packageDir, 'package.json'),
|
|
331
324
|
added: [],
|
|
332
325
|
removed: [],
|
|
333
326
|
unchanged: true,
|
|
@@ -372,7 +365,20 @@ export function startSkillDev(target, {
|
|
|
372
365
|
onRebuild = () => {},
|
|
373
366
|
} = {}) {
|
|
374
367
|
const outerRepoRoot = findRepoRoot(cwd);
|
|
375
|
-
|
|
368
|
+
let skillDir;
|
|
369
|
+
try {
|
|
370
|
+
({ skillDir } = resolveLocalPackagedSkillDir(outerRepoRoot, target));
|
|
371
|
+
} catch (error) {
|
|
372
|
+
if (error instanceof AgentpackError && error.exitCode === EXIT_CODES.GENERAL) {
|
|
373
|
+
throw error;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
throw new AgentpackError(error.message, {
|
|
377
|
+
code: error.code || 'skill_dev_failed',
|
|
378
|
+
exitCode: EXIT_CODES.GENERAL,
|
|
379
|
+
suggestion: error.suggestion,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
376
382
|
const repoRoot = findRepoRoot(skillDir);
|
|
377
383
|
reconcileDevSession(repoRoot);
|
|
378
384
|
let closed = false;
|
|
@@ -815,37 +821,40 @@ async function fetchRegistryLatestVersion(packageName, {
|
|
|
815
821
|
|
|
816
822
|
export function inspectSkill(target, { cwd = process.cwd() } = {}) {
|
|
817
823
|
const repoRoot = findRepoRoot(cwd);
|
|
824
|
+
const resolved = resolveSkillTarget(repoRoot, target);
|
|
818
825
|
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
}
|
|
826
|
+
if (resolved.kind === 'package' && resolved.exports.length > 1) {
|
|
827
|
+
return {
|
|
828
|
+
kind: 'package',
|
|
829
|
+
packageName: resolved.package.packageName,
|
|
830
|
+
packageVersion: resolved.package.packageVersion,
|
|
831
|
+
packagePath: resolved.package.packagePath,
|
|
832
|
+
exports: resolved.exports.map((entry) => ({
|
|
833
|
+
name: entry.name,
|
|
834
|
+
declaredName: entry.declaredName,
|
|
835
|
+
skillFile: entry.skillFile,
|
|
836
|
+
skillPath: entry.skillPath,
|
|
837
|
+
requires: entry.requires,
|
|
838
|
+
})),
|
|
839
|
+
};
|
|
833
840
|
}
|
|
834
841
|
|
|
835
|
-
const
|
|
836
|
-
const packageMetadata = readPackageMetadata(dirname(skillFile));
|
|
842
|
+
const entry = resolved.kind === 'export' ? resolved.export : resolved.exports[0];
|
|
837
843
|
|
|
838
844
|
return {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
845
|
+
kind: 'export',
|
|
846
|
+
name: entry.name,
|
|
847
|
+
description: entry.description,
|
|
848
|
+
packageName: resolved.package.packageName,
|
|
849
|
+
packageVersion: resolved.package.packageVersion,
|
|
850
|
+
skillFile: entry.skillFile,
|
|
851
|
+
sources: entry.sources,
|
|
852
|
+
requires: entry.requires,
|
|
853
|
+
status: entry.status,
|
|
854
|
+
replacement: entry.replacement,
|
|
855
|
+
message: entry.message,
|
|
856
|
+
wraps: entry.wraps,
|
|
857
|
+
overrides: entry.overrides,
|
|
849
858
|
};
|
|
850
859
|
}
|
|
851
860
|
|
|
@@ -960,55 +969,14 @@ export function inspectSkillDependencies(target, {
|
|
|
960
969
|
};
|
|
961
970
|
}
|
|
962
971
|
|
|
963
|
-
function
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if (target) {
|
|
967
|
-
skillFile = resolveSkillFileTarget(repoRoot, target);
|
|
968
|
-
|
|
969
|
-
if (!skillFile && target.startsWith('@')) {
|
|
970
|
-
const packageDir = findPackageDirByName(repoRoot, target);
|
|
971
|
-
if (packageDir) {
|
|
972
|
-
skillFile = join(packageDir, 'SKILL.md');
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
|
|
976
|
-
if (!skillFile) {
|
|
977
|
-
throw new NotFoundError('skill not found', {
|
|
978
|
-
code: 'skill_not_found',
|
|
979
|
-
suggestion: `Target: ${target}`,
|
|
980
|
-
});
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
const packageDir = dirname(skillFile);
|
|
984
|
-
const packageMetadata = readPackageMetadata(packageDir);
|
|
985
|
-
if (!packageMetadata.packageName) {
|
|
986
|
-
throw new ValidationError('validate target is not a packaged skill', {
|
|
987
|
-
code: 'invalid_validate_target',
|
|
988
|
-
suggestion: `Target: ${target}`,
|
|
989
|
-
});
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
return [packageDir];
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
return listPackagedSkillDirs(repoRoot);
|
|
972
|
+
function isPublishedSkillFile(files, relativeSkillFile) {
|
|
973
|
+
if (!files) return true;
|
|
974
|
+
return files.some((entry) => entry === relativeSkillFile || relativeSkillFile.startsWith(`${entry}/`));
|
|
996
975
|
}
|
|
997
976
|
|
|
998
|
-
function
|
|
999
|
-
const
|
|
1000
|
-
const packageMetadata = readPackageMetadata(packageDir);
|
|
977
|
+
function validatePackagedSkillExport(repoRoot, pkg, skillExport) {
|
|
978
|
+
const packageMetadata = readPackageMetadata(pkg.packageDir);
|
|
1001
979
|
const issues = [];
|
|
1002
|
-
let skillMetadata = null;
|
|
1003
|
-
|
|
1004
|
-
try {
|
|
1005
|
-
skillMetadata = parseSkillFrontmatterFile(skillFile);
|
|
1006
|
-
} catch (error) {
|
|
1007
|
-
issues.push({
|
|
1008
|
-
code: error.code || 'invalid_skill_file',
|
|
1009
|
-
message: error.message,
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
980
|
|
|
1013
981
|
if (!packageMetadata.packageName) {
|
|
1014
982
|
issues.push({
|
|
@@ -1024,10 +992,10 @@ function validatePackagedSkillDir(repoRoot, packageDir) {
|
|
|
1024
992
|
});
|
|
1025
993
|
}
|
|
1026
994
|
|
|
1027
|
-
if (packageMetadata.files
|
|
995
|
+
if (!isPublishedSkillFile(packageMetadata.files, skillExport.relativeSkillFile)) {
|
|
1028
996
|
issues.push({
|
|
1029
997
|
code: 'skill_not_published',
|
|
1030
|
-
message:
|
|
998
|
+
message: `package.json files does not include ${skillExport.relativeSkillFile}`,
|
|
1031
999
|
});
|
|
1032
1000
|
}
|
|
1033
1001
|
|
|
@@ -1047,51 +1015,50 @@ function validatePackagedSkillDir(repoRoot, packageDir) {
|
|
|
1047
1015
|
}
|
|
1048
1016
|
}
|
|
1049
1017
|
|
|
1050
|
-
if (
|
|
1051
|
-
|
|
1018
|
+
if (skillExport.status && !['deprecated', 'retired'].includes(skillExport.status)) {
|
|
1019
|
+
issues.push({
|
|
1020
|
+
code: 'invalid_skill_status',
|
|
1021
|
+
message: 'metadata.status must be "deprecated" or "retired"',
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
if (skillExport.replacement && !skillExport.replacement.startsWith('@')) {
|
|
1026
|
+
issues.push({
|
|
1027
|
+
code: 'invalid_replacement',
|
|
1028
|
+
message: 'metadata.replacement must be a package name',
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
for (const sourcePath of skillExport.sources || []) {
|
|
1033
|
+
if (!existsSync(join(repoRoot, sourcePath))) {
|
|
1052
1034
|
issues.push({
|
|
1053
|
-
code: '
|
|
1054
|
-
message: '
|
|
1035
|
+
code: 'missing_source',
|
|
1036
|
+
message: 'declared source file does not exist',
|
|
1037
|
+
path: sourcePath,
|
|
1055
1038
|
});
|
|
1056
1039
|
}
|
|
1040
|
+
}
|
|
1057
1041
|
|
|
1058
|
-
|
|
1042
|
+
for (const requirement of skillExport.requires || []) {
|
|
1043
|
+
if (!packageMetadata.dependencies[requirement]) {
|
|
1059
1044
|
issues.push({
|
|
1060
|
-
code: '
|
|
1061
|
-
message: '
|
|
1045
|
+
code: 'missing_dependency_declaration',
|
|
1046
|
+
message: 'required skill is not declared in package dependencies',
|
|
1047
|
+
dependency: requirement,
|
|
1062
1048
|
});
|
|
1063
1049
|
}
|
|
1064
|
-
|
|
1065
|
-
for (const sourcePath of skillMetadata.sources) {
|
|
1066
|
-
if (!existsSync(join(repoRoot, sourcePath))) {
|
|
1067
|
-
issues.push({
|
|
1068
|
-
code: 'missing_source',
|
|
1069
|
-
message: 'declared source file does not exist',
|
|
1070
|
-
path: sourcePath,
|
|
1071
|
-
});
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
for (const requirement of skillMetadata.requires) {
|
|
1076
|
-
if (!packageMetadata.dependencies[requirement]) {
|
|
1077
|
-
issues.push({
|
|
1078
|
-
code: 'missing_dependency_declaration',
|
|
1079
|
-
message: 'required skill is not declared in package dependencies',
|
|
1080
|
-
dependency: requirement,
|
|
1081
|
-
});
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
1050
|
}
|
|
1085
1051
|
|
|
1086
1052
|
return {
|
|
1087
1053
|
valid: issues.length === 0,
|
|
1088
|
-
|
|
1054
|
+
key: skillExport.key,
|
|
1055
|
+
name: skillExport.name || null,
|
|
1089
1056
|
packageName: packageMetadata.packageName,
|
|
1090
1057
|
packageVersion: packageMetadata.packageVersion,
|
|
1091
|
-
skillFile:
|
|
1092
|
-
packagePath:
|
|
1093
|
-
status:
|
|
1094
|
-
replacement:
|
|
1058
|
+
skillFile: skillExport.skillFile,
|
|
1059
|
+
packagePath: pkg.packagePath,
|
|
1060
|
+
status: skillExport.status || null,
|
|
1061
|
+
replacement: skillExport.replacement || null,
|
|
1095
1062
|
nextSteps: buildValidateNextSteps(packageMetadata, issues.length === 0),
|
|
1096
1063
|
issues,
|
|
1097
1064
|
};
|
|
@@ -1099,15 +1066,25 @@ function validatePackagedSkillDir(repoRoot, packageDir) {
|
|
|
1099
1066
|
|
|
1100
1067
|
export function validateSkills(target, { cwd = process.cwd() } = {}) {
|
|
1101
1068
|
const repoRoot = findRepoRoot(cwd);
|
|
1102
|
-
|
|
1069
|
+
let targets = [];
|
|
1103
1070
|
|
|
1104
|
-
|
|
1071
|
+
if (target) {
|
|
1072
|
+
const resolved = resolveSkillTarget(repoRoot, target, { includeInstalled: false });
|
|
1073
|
+
targets = resolved.kind === 'export'
|
|
1074
|
+
? [{ package: resolved.package, export: resolved.export }]
|
|
1075
|
+
: resolved.exports.map((entry) => ({ package: resolved.package, export: entry }));
|
|
1076
|
+
} else {
|
|
1077
|
+
targets = listAuthoredSkillPackages(repoRoot)
|
|
1078
|
+
.flatMap((pkg) => pkg.exports.map((entry) => ({ package: pkg, export: entry })));
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
for (const packageDir of [...new Set(targets.map((entry) => entry.package.packageDir))]) {
|
|
1105
1082
|
syncSkillDependencies(packageDir);
|
|
1106
1083
|
}
|
|
1107
1084
|
|
|
1108
|
-
const skills =
|
|
1109
|
-
.map((
|
|
1110
|
-
.sort((a, b) => (a.packageName || a.packagePath).localeCompare(b.packageName || b.packagePath));
|
|
1085
|
+
const skills = targets
|
|
1086
|
+
.map((entry) => validatePackagedSkillExport(repoRoot, entry.package, entry.export))
|
|
1087
|
+
.sort((a, b) => (a.key || a.packageName || a.packagePath).localeCompare(b.key || b.packageName || b.packagePath));
|
|
1111
1088
|
|
|
1112
1089
|
const validCount = skills.filter((skill) => skill.valid).length;
|
|
1113
1090
|
const invalidCount = skills.length - validCount;
|
|
@@ -1115,18 +1092,32 @@ export function validateSkills(target, { cwd = process.cwd() } = {}) {
|
|
|
1115
1092
|
if (validCount > 0) {
|
|
1116
1093
|
const buildState = readBuildState(repoRoot);
|
|
1117
1094
|
|
|
1118
|
-
|
|
1119
|
-
const
|
|
1120
|
-
const
|
|
1095
|
+
if (!target) {
|
|
1096
|
+
const authoredKeys = new Set(targets.map((entry) => entry.export.key));
|
|
1097
|
+
for (const key of Object.keys(buildState.skills || {})) {
|
|
1098
|
+
if (authoredKeys.has(key)) continue;
|
|
1099
|
+
delete buildState.skills[key];
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
for (const targetEntry of targets) {
|
|
1104
|
+
const result = skills.find((skill) => skill.key === targetEntry.export.key);
|
|
1121
1105
|
if (!result?.valid) continue;
|
|
1122
1106
|
|
|
1123
|
-
const
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1107
|
+
const sources = {};
|
|
1108
|
+
for (const sourcePath of targetEntry.export.sources || []) {
|
|
1109
|
+
sources[sourcePath] = {
|
|
1110
|
+
hash: hashFile(join(repoRoot, sourcePath)),
|
|
1111
|
+
};
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
buildState.skills[targetEntry.export.key] = {
|
|
1115
|
+
package_version: targetEntry.package.packageVersion,
|
|
1116
|
+
skill_path: targetEntry.export.skillPath,
|
|
1117
|
+
skill_file: targetEntry.export.skillFile,
|
|
1118
|
+
sources,
|
|
1119
|
+
requires: targetEntry.export.requires,
|
|
1120
|
+
};
|
|
1130
1121
|
}
|
|
1131
1122
|
|
|
1132
1123
|
writeBuildState(repoRoot, buildState);
|
|
@@ -1188,28 +1179,22 @@ function listPackagedSkillDirs(repoRoot) {
|
|
|
1188
1179
|
}
|
|
1189
1180
|
|
|
1190
1181
|
function listAuthoredPackagedSkills(repoRoot) {
|
|
1191
|
-
return
|
|
1192
|
-
.map((
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
skillFile: normalizeDisplayPath(repoRoot, skillFile),
|
|
1208
|
-
sources: metadata.sources,
|
|
1209
|
-
requires: metadata.requires,
|
|
1210
|
-
};
|
|
1211
|
-
})
|
|
1212
|
-
.filter(Boolean)
|
|
1182
|
+
return listAuthoredSkillPackages(repoRoot)
|
|
1183
|
+
.flatMap((pkg) => pkg.exports.map((entry) => ({
|
|
1184
|
+
key: entry.key,
|
|
1185
|
+
name: entry.name,
|
|
1186
|
+
description: entry.description,
|
|
1187
|
+
packageName: entry.packageName,
|
|
1188
|
+
packageVersion: entry.packageVersion,
|
|
1189
|
+
skillPath: entry.skillPath,
|
|
1190
|
+
skillFile: entry.skillFile,
|
|
1191
|
+
sources: entry.sources,
|
|
1192
|
+
requires: entry.requires,
|
|
1193
|
+
wraps: entry.wraps,
|
|
1194
|
+
overrides: entry.overrides,
|
|
1195
|
+
declaredName: entry.declaredName,
|
|
1196
|
+
packagePath: pkg.packagePath,
|
|
1197
|
+
})))
|
|
1213
1198
|
.sort((a, b) => a.packageName.localeCompare(b.packageName));
|
|
1214
1199
|
}
|
|
1215
1200
|
|
|
@@ -1218,7 +1203,7 @@ export function generateSkillsCatalog({ cwd = process.cwd() } = {}) {
|
|
|
1218
1203
|
const skills = {};
|
|
1219
1204
|
|
|
1220
1205
|
for (const skill of listAuthoredPackagedSkills(repoRoot)) {
|
|
1221
|
-
skills[skill.
|
|
1206
|
+
skills[skill.key] = {
|
|
1222
1207
|
name: skill.name,
|
|
1223
1208
|
description: skill.description,
|
|
1224
1209
|
path: skill.skillPath,
|
|
@@ -1227,6 +1212,8 @@ export function generateSkillsCatalog({ cwd = process.cwd() } = {}) {
|
|
|
1227
1212
|
package_version: skill.packageVersion,
|
|
1228
1213
|
sources: skill.sources,
|
|
1229
1214
|
requires: skill.requires,
|
|
1215
|
+
...(skill.wraps ? { wraps: skill.wraps } : {}),
|
|
1216
|
+
...(skill.overrides?.length ? { overrides: skill.overrides } : {}),
|
|
1230
1217
|
};
|
|
1231
1218
|
}
|
|
1232
1219
|
|
|
@@ -1248,12 +1235,14 @@ export function generateBuildState({ cwd = process.cwd() } = {}) {
|
|
|
1248
1235
|
};
|
|
1249
1236
|
}
|
|
1250
1237
|
|
|
1251
|
-
skills[skill.
|
|
1238
|
+
skills[skill.key] = {
|
|
1252
1239
|
package_version: skill.packageVersion,
|
|
1253
1240
|
skill_path: skill.skillPath,
|
|
1254
1241
|
skill_file: skill.skillFile,
|
|
1255
1242
|
sources,
|
|
1256
1243
|
requires: skill.requires,
|
|
1244
|
+
...(skill.wraps ? { wraps: skill.wraps } : {}),
|
|
1245
|
+
...(skill.overrides?.length ? { overrides: skill.overrides } : {}),
|
|
1257
1246
|
};
|
|
1258
1247
|
}
|
|
1259
1248
|
|