@alavida/agentpack 0.1.5 → 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 +6 -3
- package/src/application/auth/get-auth-status.js +97 -0
- package/src/application/auth/login.js +110 -0
- package/src/application/auth/logout.js +16 -0
- package/src/application/auth/verify-auth.js +37 -0
- package/src/cli.js +2 -0
- package/src/commands/auth.js +78 -0
- package/src/commands/skills.js +62 -5
- package/src/domain/auth/registry-resolution.js +55 -0
- package/src/domain/skills/skill-catalog.js +116 -0
- package/src/domain/skills/skill-model.js +70 -1
- package/src/domain/skills/skill-target-resolution.js +100 -0
- package/src/infrastructure/fs/user-config-repository.js +40 -0
- package/src/infrastructure/fs/user-credentials-repository.js +30 -0
- package/src/infrastructure/fs/user-npmrc-repository.js +74 -0
- package/src/infrastructure/runtime/inspect-materialized-skills.js +153 -0
- package/src/infrastructure/runtime/materialize-skills.js +78 -31
- package/src/infrastructure/runtime/open-browser.js +6 -0
- package/src/lib/skills.js +359 -192
package/src/lib/skills.js
CHANGED
|
@@ -19,11 +19,16 @@ import {
|
|
|
19
19
|
removeSkillLinksByNames,
|
|
20
20
|
} from '../infrastructure/runtime/materialize-skills.js';
|
|
21
21
|
import {
|
|
22
|
+
buildCanonicalSkillRequirement,
|
|
22
23
|
normalizeDisplayPath,
|
|
23
24
|
normalizeRepoPath,
|
|
24
25
|
parseSkillFrontmatterFile,
|
|
26
|
+
readInstalledSkillExports,
|
|
25
27
|
readPackageMetadata,
|
|
26
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';
|
|
31
|
+
import { inspectMaterializedSkills } from '../infrastructure/runtime/inspect-materialized-skills.js';
|
|
27
32
|
import {
|
|
28
33
|
buildStateRecordForPackageDir,
|
|
29
34
|
compareRecordedSources,
|
|
@@ -31,6 +36,10 @@ import {
|
|
|
31
36
|
readBuildState,
|
|
32
37
|
writeBuildState,
|
|
33
38
|
} from '../domain/skills/skill-provenance.js';
|
|
39
|
+
import { readUserConfig } from '../infrastructure/fs/user-config-repository.js';
|
|
40
|
+
import { readUserCredentials } from '../infrastructure/fs/user-credentials-repository.js';
|
|
41
|
+
import { getUserNpmrcPath, readUserNpmrc } from '../infrastructure/fs/user-npmrc-repository.js';
|
|
42
|
+
import { resolveRegistryConfig } from '../domain/auth/registry-resolution.js';
|
|
34
43
|
import { startSkillDevWorkbench } from '../application/skills/start-skill-dev-workbench.js';
|
|
35
44
|
import { AgentpackError, EXIT_CODES, NetworkError, NotFoundError, ValidationError } from '../utils/errors.js';
|
|
36
45
|
|
|
@@ -89,24 +98,15 @@ function resolveDevLinkedSkills(repoRoot, rootSkillDir) {
|
|
|
89
98
|
}
|
|
90
99
|
|
|
91
100
|
function resolveLocalPackagedSkillDir(repoRoot, target) {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const packageJsonPath = join(skillDir, 'package.json');
|
|
102
|
-
if (!existsSync(packageJsonPath)) {
|
|
103
|
-
throw new AgentpackError(`package.json not found: ${packageJsonPath}`, {
|
|
104
|
-
code: 'package_json_not_found',
|
|
105
|
-
exitCode: EXIT_CODES.GENERAL,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
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
|
+
};
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
function resolveSkillFileTarget(repoRoot, target) {
|
|
@@ -268,11 +268,11 @@ function reconcileDevSession(repoRoot) {
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
export function syncSkillDependencies(skillDir) {
|
|
271
|
-
const
|
|
272
|
-
|
|
271
|
+
const required = [...new Set(
|
|
272
|
+
readInstalledSkillExports(skillDir).flatMap((entry) => entry.requires || [])
|
|
273
|
+
)].sort((a, b) => a.localeCompare(b));
|
|
273
274
|
const { packageJsonPath, packageJson } = readPackageJson(skillDir);
|
|
274
275
|
const nextDependencies = { ...(packageJson.dependencies || {}) };
|
|
275
|
-
const required = [...new Set(metadata.requires || [])].sort((a, b) => a.localeCompare(b));
|
|
276
276
|
const requiredSet = new Set(required);
|
|
277
277
|
const added = [];
|
|
278
278
|
const removed = [];
|
|
@@ -313,14 +313,14 @@ export function devSkill(target, {
|
|
|
313
313
|
} = {}) {
|
|
314
314
|
const repoRoot = findRepoRoot(cwd);
|
|
315
315
|
try {
|
|
316
|
-
const { skillDir } = resolveLocalPackagedSkillDir(repoRoot, target);
|
|
316
|
+
const { skillDir, packageDir } = resolveLocalPackagedSkillDir(repoRoot, target);
|
|
317
317
|
const { linkedSkills, unresolved } = resolveDevLinkedSkills(repoRoot, skillDir);
|
|
318
318
|
const rootSkill = linkedSkills.find((entry) => entry.skillDir === skillDir);
|
|
319
319
|
const synced = sync
|
|
320
|
-
? syncSkillDependencies(
|
|
320
|
+
? syncSkillDependencies(packageDir)
|
|
321
321
|
: {
|
|
322
|
-
skillDir,
|
|
323
|
-
packageJsonPath: join(
|
|
322
|
+
skillDir: packageDir,
|
|
323
|
+
packageJsonPath: join(packageDir, 'package.json'),
|
|
324
324
|
added: [],
|
|
325
325
|
removed: [],
|
|
326
326
|
unchanged: true,
|
|
@@ -365,7 +365,20 @@ export function startSkillDev(target, {
|
|
|
365
365
|
onRebuild = () => {},
|
|
366
366
|
} = {}) {
|
|
367
367
|
const outerRepoRoot = findRepoRoot(cwd);
|
|
368
|
-
|
|
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
|
+
}
|
|
369
382
|
const repoRoot = findRepoRoot(skillDir);
|
|
370
383
|
reconcileDevSession(repoRoot);
|
|
371
384
|
let closed = false;
|
|
@@ -638,24 +651,75 @@ function readRepoNpmRegistryConfig(repoRoot, scope = null) {
|
|
|
638
651
|
: {};
|
|
639
652
|
const scopes = scope ? [scope] : MANAGED_PACKAGE_SCOPES;
|
|
640
653
|
const matchedScope = scopes.find((candidate) => config[`${candidate}:registry`]) || scope || scopes[0] || null;
|
|
654
|
+
const registry = matchedScope ? (config[`${matchedScope}:registry`] || null) : null;
|
|
655
|
+
const authKey = registry ? `//${new URL(registry).host}/:_authToken` : null;
|
|
641
656
|
|
|
642
657
|
return {
|
|
643
658
|
npmrcPath: existsSync(npmrcPath) ? npmrcPath : null,
|
|
644
659
|
scope: matchedScope,
|
|
645
|
-
registry
|
|
646
|
-
authToken: config[
|
|
660
|
+
registry,
|
|
661
|
+
authToken: authKey ? (config[authKey] || null) : null,
|
|
647
662
|
alwaysAuth: String(config['always-auth'] || '').toLowerCase() === 'true',
|
|
648
663
|
};
|
|
649
664
|
}
|
|
650
665
|
|
|
666
|
+
function readEffectiveRegistryConfig(repoRoot, scope = null, env = process.env) {
|
|
667
|
+
const userConfig = readUserConfig({ env });
|
|
668
|
+
const userNpmrc = readUserNpmrc({ env });
|
|
669
|
+
const repoConfig = readRepoNpmRegistryConfig(repoRoot, scope);
|
|
670
|
+
const userScope = MANAGED_PACKAGE_SCOPES.find((candidate) => userNpmrc[`${candidate}:registry`]) || null;
|
|
671
|
+
const resolvedScope = scope
|
|
672
|
+
|| (repoConfig.registry ? repoConfig.scope : null)
|
|
673
|
+
|| userScope
|
|
674
|
+
|| userConfig.scope
|
|
675
|
+
|| repoConfig.scope
|
|
676
|
+
|| MANAGED_PACKAGE_SCOPES[0]
|
|
677
|
+
|| null;
|
|
678
|
+
const resolved = resolveRegistryConfig({
|
|
679
|
+
scope: resolvedScope,
|
|
680
|
+
defaults: {
|
|
681
|
+
registry: userConfig.registry,
|
|
682
|
+
verificationPackage: userConfig.verificationPackage,
|
|
683
|
+
},
|
|
684
|
+
userNpmrc,
|
|
685
|
+
repoNpmrc: repoConfig.registry ? {
|
|
686
|
+
[`${repoConfig.scope}:registry`]: repoConfig.registry,
|
|
687
|
+
...(repoConfig.authToken ? { [`//${new URL(repoConfig.registry).host}/:_authToken`]: repoConfig.authToken } : {}),
|
|
688
|
+
} : {},
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
let npmrcPath = null;
|
|
692
|
+
if (resolved.source === 'repo') {
|
|
693
|
+
npmrcPath = repoConfig.npmrcPath;
|
|
694
|
+
} else if (resolved.source === 'user') {
|
|
695
|
+
npmrcPath = getUserNpmrcPath({ env });
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const alwaysAuth = resolved.source === 'repo'
|
|
699
|
+
? repoConfig.alwaysAuth
|
|
700
|
+
: String(userNpmrc['always-auth'] || '').toLowerCase() === 'true';
|
|
701
|
+
|
|
702
|
+
return {
|
|
703
|
+
scope: resolved.scope,
|
|
704
|
+
npmrcPath,
|
|
705
|
+
registry: resolved.registry,
|
|
706
|
+
authToken: resolved.authToken,
|
|
707
|
+
alwaysAuth,
|
|
708
|
+
source: resolved.source,
|
|
709
|
+
configured: resolved.source !== 'default' && Boolean(resolved.registry),
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
|
|
651
713
|
export function inspectRegistryConfig({
|
|
652
714
|
cwd = process.cwd(),
|
|
653
715
|
scope = null,
|
|
716
|
+
env = process.env,
|
|
654
717
|
} = {}) {
|
|
655
718
|
const repoRoot = findRepoRoot(cwd);
|
|
656
|
-
const { npmrcPath, scope: resolvedScope, registry, authToken, alwaysAuth } =
|
|
719
|
+
const { npmrcPath, scope: resolvedScope, registry, authToken, alwaysAuth, source, configured } = readEffectiveRegistryConfig(
|
|
657
720
|
repoRoot,
|
|
658
|
-
scope
|
|
721
|
+
scope,
|
|
722
|
+
env
|
|
659
723
|
);
|
|
660
724
|
|
|
661
725
|
let auth = {
|
|
@@ -688,10 +752,11 @@ export function inspectRegistryConfig({
|
|
|
688
752
|
scope: resolvedScope,
|
|
689
753
|
repoRoot,
|
|
690
754
|
npmrcPath: npmrcPath ? normalizeDisplayPath(repoRoot, npmrcPath) : null,
|
|
691
|
-
configured
|
|
692
|
-
registry,
|
|
755
|
+
configured,
|
|
756
|
+
registry: configured ? registry : null,
|
|
693
757
|
auth,
|
|
694
758
|
alwaysAuth,
|
|
759
|
+
source,
|
|
695
760
|
};
|
|
696
761
|
}
|
|
697
762
|
|
|
@@ -704,6 +769,22 @@ function resolveRegistryAuthToken(rawValue) {
|
|
|
704
769
|
return rawValue;
|
|
705
770
|
}
|
|
706
771
|
|
|
772
|
+
function buildNpmRegistryEnv(repoRoot, env = process.env) {
|
|
773
|
+
const effective = readEffectiveRegistryConfig(repoRoot, null, env);
|
|
774
|
+
const authToken = effective.authToken || null;
|
|
775
|
+
const envMatch = authToken?.match(/^\$\{([^}]+)\}$/) || null;
|
|
776
|
+
if (!envMatch) return env;
|
|
777
|
+
if (env[envMatch[1]]) return env;
|
|
778
|
+
|
|
779
|
+
const credentials = readUserCredentials({ env });
|
|
780
|
+
if (!credentials?.token) return env;
|
|
781
|
+
|
|
782
|
+
return {
|
|
783
|
+
...env,
|
|
784
|
+
[envMatch[1]]: credentials.token,
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
707
788
|
async function fetchRegistryLatestVersion(packageName, {
|
|
708
789
|
registry,
|
|
709
790
|
authToken,
|
|
@@ -740,37 +821,40 @@ async function fetchRegistryLatestVersion(packageName, {
|
|
|
740
821
|
|
|
741
822
|
export function inspectSkill(target, { cwd = process.cwd() } = {}) {
|
|
742
823
|
const repoRoot = findRepoRoot(cwd);
|
|
824
|
+
const resolved = resolveSkillTarget(repoRoot, target);
|
|
743
825
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
}
|
|
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
|
+
};
|
|
758
840
|
}
|
|
759
841
|
|
|
760
|
-
const
|
|
761
|
-
const packageMetadata = readPackageMetadata(dirname(skillFile));
|
|
842
|
+
const entry = resolved.kind === 'export' ? resolved.export : resolved.exports[0];
|
|
762
843
|
|
|
763
844
|
return {
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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,
|
|
774
858
|
};
|
|
775
859
|
}
|
|
776
860
|
|
|
@@ -885,55 +969,14 @@ export function inspectSkillDependencies(target, {
|
|
|
885
969
|
};
|
|
886
970
|
}
|
|
887
971
|
|
|
888
|
-
function
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
if (target) {
|
|
892
|
-
skillFile = resolveSkillFileTarget(repoRoot, target);
|
|
893
|
-
|
|
894
|
-
if (!skillFile && target.startsWith('@')) {
|
|
895
|
-
const packageDir = findPackageDirByName(repoRoot, target);
|
|
896
|
-
if (packageDir) {
|
|
897
|
-
skillFile = join(packageDir, 'SKILL.md');
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
if (!skillFile) {
|
|
902
|
-
throw new NotFoundError('skill not found', {
|
|
903
|
-
code: 'skill_not_found',
|
|
904
|
-
suggestion: `Target: ${target}`,
|
|
905
|
-
});
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
const packageDir = dirname(skillFile);
|
|
909
|
-
const packageMetadata = readPackageMetadata(packageDir);
|
|
910
|
-
if (!packageMetadata.packageName) {
|
|
911
|
-
throw new ValidationError('validate target is not a packaged skill', {
|
|
912
|
-
code: 'invalid_validate_target',
|
|
913
|
-
suggestion: `Target: ${target}`,
|
|
914
|
-
});
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
return [packageDir];
|
|
918
|
-
}
|
|
919
|
-
|
|
920
|
-
return listPackagedSkillDirs(repoRoot);
|
|
972
|
+
function isPublishedSkillFile(files, relativeSkillFile) {
|
|
973
|
+
if (!files) return true;
|
|
974
|
+
return files.some((entry) => entry === relativeSkillFile || relativeSkillFile.startsWith(`${entry}/`));
|
|
921
975
|
}
|
|
922
976
|
|
|
923
|
-
function
|
|
924
|
-
const
|
|
925
|
-
const packageMetadata = readPackageMetadata(packageDir);
|
|
977
|
+
function validatePackagedSkillExport(repoRoot, pkg, skillExport) {
|
|
978
|
+
const packageMetadata = readPackageMetadata(pkg.packageDir);
|
|
926
979
|
const issues = [];
|
|
927
|
-
let skillMetadata = null;
|
|
928
|
-
|
|
929
|
-
try {
|
|
930
|
-
skillMetadata = parseSkillFrontmatterFile(skillFile);
|
|
931
|
-
} catch (error) {
|
|
932
|
-
issues.push({
|
|
933
|
-
code: error.code || 'invalid_skill_file',
|
|
934
|
-
message: error.message,
|
|
935
|
-
});
|
|
936
|
-
}
|
|
937
980
|
|
|
938
981
|
if (!packageMetadata.packageName) {
|
|
939
982
|
issues.push({
|
|
@@ -949,10 +992,10 @@ function validatePackagedSkillDir(repoRoot, packageDir) {
|
|
|
949
992
|
});
|
|
950
993
|
}
|
|
951
994
|
|
|
952
|
-
if (packageMetadata.files
|
|
995
|
+
if (!isPublishedSkillFile(packageMetadata.files, skillExport.relativeSkillFile)) {
|
|
953
996
|
issues.push({
|
|
954
997
|
code: 'skill_not_published',
|
|
955
|
-
message:
|
|
998
|
+
message: `package.json files does not include ${skillExport.relativeSkillFile}`,
|
|
956
999
|
});
|
|
957
1000
|
}
|
|
958
1001
|
|
|
@@ -972,51 +1015,50 @@ function validatePackagedSkillDir(repoRoot, packageDir) {
|
|
|
972
1015
|
}
|
|
973
1016
|
}
|
|
974
1017
|
|
|
975
|
-
if (
|
|
976
|
-
|
|
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))) {
|
|
977
1034
|
issues.push({
|
|
978
|
-
code: '
|
|
979
|
-
message: '
|
|
1035
|
+
code: 'missing_source',
|
|
1036
|
+
message: 'declared source file does not exist',
|
|
1037
|
+
path: sourcePath,
|
|
980
1038
|
});
|
|
981
1039
|
}
|
|
1040
|
+
}
|
|
982
1041
|
|
|
983
|
-
|
|
1042
|
+
for (const requirement of skillExport.requires || []) {
|
|
1043
|
+
if (!packageMetadata.dependencies[requirement]) {
|
|
984
1044
|
issues.push({
|
|
985
|
-
code: '
|
|
986
|
-
message: '
|
|
1045
|
+
code: 'missing_dependency_declaration',
|
|
1046
|
+
message: 'required skill is not declared in package dependencies',
|
|
1047
|
+
dependency: requirement,
|
|
987
1048
|
});
|
|
988
1049
|
}
|
|
989
|
-
|
|
990
|
-
for (const sourcePath of skillMetadata.sources) {
|
|
991
|
-
if (!existsSync(join(repoRoot, sourcePath))) {
|
|
992
|
-
issues.push({
|
|
993
|
-
code: 'missing_source',
|
|
994
|
-
message: 'declared source file does not exist',
|
|
995
|
-
path: sourcePath,
|
|
996
|
-
});
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
for (const requirement of skillMetadata.requires) {
|
|
1001
|
-
if (!packageMetadata.dependencies[requirement]) {
|
|
1002
|
-
issues.push({
|
|
1003
|
-
code: 'missing_dependency_declaration',
|
|
1004
|
-
message: 'required skill is not declared in package dependencies',
|
|
1005
|
-
dependency: requirement,
|
|
1006
|
-
});
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
1050
|
}
|
|
1010
1051
|
|
|
1011
1052
|
return {
|
|
1012
1053
|
valid: issues.length === 0,
|
|
1013
|
-
|
|
1054
|
+
key: skillExport.key,
|
|
1055
|
+
name: skillExport.name || null,
|
|
1014
1056
|
packageName: packageMetadata.packageName,
|
|
1015
1057
|
packageVersion: packageMetadata.packageVersion,
|
|
1016
|
-
skillFile:
|
|
1017
|
-
packagePath:
|
|
1018
|
-
status:
|
|
1019
|
-
replacement:
|
|
1058
|
+
skillFile: skillExport.skillFile,
|
|
1059
|
+
packagePath: pkg.packagePath,
|
|
1060
|
+
status: skillExport.status || null,
|
|
1061
|
+
replacement: skillExport.replacement || null,
|
|
1020
1062
|
nextSteps: buildValidateNextSteps(packageMetadata, issues.length === 0),
|
|
1021
1063
|
issues,
|
|
1022
1064
|
};
|
|
@@ -1024,15 +1066,25 @@ function validatePackagedSkillDir(repoRoot, packageDir) {
|
|
|
1024
1066
|
|
|
1025
1067
|
export function validateSkills(target, { cwd = process.cwd() } = {}) {
|
|
1026
1068
|
const repoRoot = findRepoRoot(cwd);
|
|
1027
|
-
|
|
1069
|
+
let targets = [];
|
|
1028
1070
|
|
|
1029
|
-
|
|
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))]) {
|
|
1030
1082
|
syncSkillDependencies(packageDir);
|
|
1031
1083
|
}
|
|
1032
1084
|
|
|
1033
|
-
const skills =
|
|
1034
|
-
.map((
|
|
1035
|
-
.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));
|
|
1036
1088
|
|
|
1037
1089
|
const validCount = skills.filter((skill) => skill.valid).length;
|
|
1038
1090
|
const invalidCount = skills.length - validCount;
|
|
@@ -1040,18 +1092,32 @@ export function validateSkills(target, { cwd = process.cwd() } = {}) {
|
|
|
1040
1092
|
if (validCount > 0) {
|
|
1041
1093
|
const buildState = readBuildState(repoRoot);
|
|
1042
1094
|
|
|
1043
|
-
|
|
1044
|
-
const
|
|
1045
|
-
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);
|
|
1046
1105
|
if (!result?.valid) continue;
|
|
1047
1106
|
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
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
|
+
};
|
|
1055
1121
|
}
|
|
1056
1122
|
|
|
1057
1123
|
writeBuildState(repoRoot, buildState);
|
|
@@ -1113,28 +1179,22 @@ function listPackagedSkillDirs(repoRoot) {
|
|
|
1113
1179
|
}
|
|
1114
1180
|
|
|
1115
1181
|
function listAuthoredPackagedSkills(repoRoot) {
|
|
1116
|
-
return
|
|
1117
|
-
.map((
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
skillFile: normalizeDisplayPath(repoRoot, skillFile),
|
|
1133
|
-
sources: metadata.sources,
|
|
1134
|
-
requires: metadata.requires,
|
|
1135
|
-
};
|
|
1136
|
-
})
|
|
1137
|
-
.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
|
+
})))
|
|
1138
1198
|
.sort((a, b) => a.packageName.localeCompare(b.packageName));
|
|
1139
1199
|
}
|
|
1140
1200
|
|
|
@@ -1143,7 +1203,7 @@ export function generateSkillsCatalog({ cwd = process.cwd() } = {}) {
|
|
|
1143
1203
|
const skills = {};
|
|
1144
1204
|
|
|
1145
1205
|
for (const skill of listAuthoredPackagedSkills(repoRoot)) {
|
|
1146
|
-
skills[skill.
|
|
1206
|
+
skills[skill.key] = {
|
|
1147
1207
|
name: skill.name,
|
|
1148
1208
|
description: skill.description,
|
|
1149
1209
|
path: skill.skillPath,
|
|
@@ -1152,6 +1212,8 @@ export function generateSkillsCatalog({ cwd = process.cwd() } = {}) {
|
|
|
1152
1212
|
package_version: skill.packageVersion,
|
|
1153
1213
|
sources: skill.sources,
|
|
1154
1214
|
requires: skill.requires,
|
|
1215
|
+
...(skill.wraps ? { wraps: skill.wraps } : {}),
|
|
1216
|
+
...(skill.overrides?.length ? { overrides: skill.overrides } : {}),
|
|
1155
1217
|
};
|
|
1156
1218
|
}
|
|
1157
1219
|
|
|
@@ -1173,12 +1235,14 @@ export function generateBuildState({ cwd = process.cwd() } = {}) {
|
|
|
1173
1235
|
};
|
|
1174
1236
|
}
|
|
1175
1237
|
|
|
1176
|
-
skills[skill.
|
|
1238
|
+
skills[skill.key] = {
|
|
1177
1239
|
package_version: skill.packageVersion,
|
|
1178
1240
|
skill_path: skill.skillPath,
|
|
1179
1241
|
skill_file: skill.skillFile,
|
|
1180
1242
|
sources,
|
|
1181
1243
|
requires: skill.requires,
|
|
1244
|
+
...(skill.wraps ? { wraps: skill.wraps } : {}),
|
|
1245
|
+
...(skill.overrides?.length ? { overrides: skill.overrides } : {}),
|
|
1182
1246
|
};
|
|
1183
1247
|
}
|
|
1184
1248
|
|
|
@@ -1265,6 +1329,36 @@ function listInstalledPackageDirs(nodeModulesDir) {
|
|
|
1265
1329
|
return packageDirs;
|
|
1266
1330
|
}
|
|
1267
1331
|
|
|
1332
|
+
function findInstalledPackageDir(nodeModulesDir, packageName) {
|
|
1333
|
+
if (!packageName) return null;
|
|
1334
|
+
const packageDir = join(nodeModulesDir, ...packageName.split('/'));
|
|
1335
|
+
return existsSync(packageDir) ? packageDir : null;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
function resolveInstalledPackageClosure(repoRoot, directTargetMap) {
|
|
1339
|
+
const nodeModulesDir = join(repoRoot, 'node_modules');
|
|
1340
|
+
const queue = [...directTargetMap.keys()];
|
|
1341
|
+
const seen = new Set();
|
|
1342
|
+
const packageDirs = [];
|
|
1343
|
+
|
|
1344
|
+
while (queue.length > 0) {
|
|
1345
|
+
const packageName = queue.shift();
|
|
1346
|
+
if (seen.has(packageName)) continue;
|
|
1347
|
+
seen.add(packageName);
|
|
1348
|
+
|
|
1349
|
+
const packageDir = findInstalledPackageDir(nodeModulesDir, packageName);
|
|
1350
|
+
if (!packageDir) continue;
|
|
1351
|
+
|
|
1352
|
+
packageDirs.push(packageDir);
|
|
1353
|
+
const packageMetadata = readPackageMetadata(packageDir);
|
|
1354
|
+
for (const dependencyName of Object.keys(packageMetadata.dependencies || {})) {
|
|
1355
|
+
queue.push(dependencyName);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
return packageDirs.sort();
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1268
1362
|
function collectLocalInstallTargets(initialTarget) {
|
|
1269
1363
|
const resolvedTarget = resolve(initialTarget);
|
|
1270
1364
|
const queue = [resolvedTarget];
|
|
@@ -1342,14 +1436,16 @@ export function installSkills(targets, { cwd = process.cwd() } = {}) {
|
|
|
1342
1436
|
|
|
1343
1437
|
execFileSync('npm', ['install', '--no-save', ...uniqueInstallTargets], {
|
|
1344
1438
|
cwd: repoRoot,
|
|
1439
|
+
env: buildNpmRegistryEnv(repoRoot, process.env),
|
|
1345
1440
|
encoding: 'utf-8',
|
|
1346
1441
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1347
1442
|
});
|
|
1348
1443
|
|
|
1444
|
+
const resolvedPackageDirs = resolveInstalledPackageClosure(repoRoot, directTargetMap);
|
|
1349
1445
|
return rebuildInstallState(repoRoot, directTargetMap, {
|
|
1350
|
-
|
|
1351
|
-
parseSkillFrontmatterFile,
|
|
1446
|
+
packageDirs: resolvedPackageDirs,
|
|
1352
1447
|
readPackageMetadata,
|
|
1448
|
+
readInstalledSkillExports,
|
|
1353
1449
|
normalizeRelativePath,
|
|
1354
1450
|
});
|
|
1355
1451
|
}
|
|
@@ -1360,11 +1456,14 @@ export function inspectSkillsEnv({ cwd = process.cwd() } = {}) {
|
|
|
1360
1456
|
const installs = Object.entries(state.installs || {})
|
|
1361
1457
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
1362
1458
|
.map(([packageName, install]) => ({
|
|
1363
|
-
...
|
|
1459
|
+
...(install.skills?.length > 0
|
|
1460
|
+
? readInstalledSkillLifecycleFromRecord(install)
|
|
1461
|
+
: readInstalledSkillLifecycle(repoRoot, install.source_package_path)),
|
|
1364
1462
|
packageName,
|
|
1365
1463
|
direct: install.direct,
|
|
1366
1464
|
packageVersion: install.package_version,
|
|
1367
1465
|
sourcePackagePath: install.source_package_path,
|
|
1466
|
+
skills: install.skills || [],
|
|
1368
1467
|
materializations: install.materializations || [],
|
|
1369
1468
|
}));
|
|
1370
1469
|
|
|
@@ -1403,10 +1502,66 @@ function readInstalledSkillLifecycle(repoRoot, sourcePackagePath) {
|
|
|
1403
1502
|
};
|
|
1404
1503
|
}
|
|
1405
1504
|
|
|
1505
|
+
function readInstalledSkillLifecycleFromRecord(install) {
|
|
1506
|
+
const primarySkill = (install.skills || []).find((entry) => entry.runtime_name === entry.name)
|
|
1507
|
+
|| (install.skills || [])[0]
|
|
1508
|
+
|| null;
|
|
1509
|
+
|
|
1510
|
+
if (!primarySkill) {
|
|
1511
|
+
return {
|
|
1512
|
+
requires: [],
|
|
1513
|
+
status: null,
|
|
1514
|
+
replacement: null,
|
|
1515
|
+
message: null,
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
return {
|
|
1520
|
+
requires: primarySkill.requires || [],
|
|
1521
|
+
status: primarySkill.status || null,
|
|
1522
|
+
replacement: primarySkill.replacement || null,
|
|
1523
|
+
message: primarySkill.message || null,
|
|
1524
|
+
};
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1406
1527
|
function buildInstallCommand(packageName) {
|
|
1407
1528
|
return `agentpack skills install ${packageName}`;
|
|
1408
1529
|
}
|
|
1409
1530
|
|
|
1531
|
+
function buildInstalledRequirementSet(installs) {
|
|
1532
|
+
const installed = new Set();
|
|
1533
|
+
|
|
1534
|
+
for (const install of installs) {
|
|
1535
|
+
if (install.packageName) installed.add(install.packageName);
|
|
1536
|
+
for (const skill of install.skills || []) {
|
|
1537
|
+
const requirement = buildCanonicalSkillRequirement(install.packageName, skill.name);
|
|
1538
|
+
if (requirement) installed.add(requirement);
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
return installed;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
function buildInstalledRequirementRecords(installs) {
|
|
1546
|
+
return installs.map((install) => {
|
|
1547
|
+
const requires = new Set();
|
|
1548
|
+
|
|
1549
|
+
for (const skill of install.skills || []) {
|
|
1550
|
+
for (const requirement of skill.requires || []) {
|
|
1551
|
+
requires.add(requirement);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
return {
|
|
1556
|
+
packageName: install.packageName,
|
|
1557
|
+
name: null,
|
|
1558
|
+
skillFile: install.sourcePackagePath ? `${install.sourcePackagePath}/SKILL.md` : null,
|
|
1559
|
+
direct: install.direct,
|
|
1560
|
+
requires: [...requires].sort((a, b) => a.localeCompare(b)),
|
|
1561
|
+
};
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1410
1565
|
function listLocalWorkbenchSkillRecords(repoRoot) {
|
|
1411
1566
|
const records = [];
|
|
1412
1567
|
|
|
@@ -1492,14 +1647,8 @@ export function inspectMissingSkillDependencies({
|
|
|
1492
1647
|
} = {}) {
|
|
1493
1648
|
const repoRoot = findRepoRoot(cwd);
|
|
1494
1649
|
const env = inspectSkillsEnv({ cwd });
|
|
1495
|
-
const installed =
|
|
1496
|
-
const installedRecords = env.installs
|
|
1497
|
-
packageName: install.packageName,
|
|
1498
|
-
name: null,
|
|
1499
|
-
skillFile: install.sourcePackagePath ? `${install.sourcePackagePath}/SKILL.md` : null,
|
|
1500
|
-
direct: install.direct,
|
|
1501
|
-
requires: install.requires,
|
|
1502
|
-
}));
|
|
1650
|
+
const installed = buildInstalledRequirementSet(env.installs);
|
|
1651
|
+
const installedRecords = buildInstalledRequirementRecords(env.installs);
|
|
1503
1652
|
const localWorkbenchRecords = listLocalWorkbenchSkillRecords(repoRoot);
|
|
1504
1653
|
|
|
1505
1654
|
let records = [...installedRecords, ...localWorkbenchRecords];
|
|
@@ -1600,9 +1749,11 @@ export async function listOutdatedSkills({
|
|
|
1600
1749
|
|
|
1601
1750
|
export async function inspectSkillsStatus({ cwd = process.cwd() } = {}) {
|
|
1602
1751
|
const env = inspectSkillsEnv({ cwd });
|
|
1752
|
+
const state = readInstallState(env.repoRoot);
|
|
1603
1753
|
const registry = inspectRegistryConfig({ cwd });
|
|
1604
1754
|
const outdatedResult = await listOutdatedSkills({ cwd });
|
|
1605
1755
|
const missingResult = inspectMissingSkillDependencies({ cwd });
|
|
1756
|
+
const runtimeInspection = inspectMaterializedSkills(env.repoRoot, state);
|
|
1606
1757
|
|
|
1607
1758
|
const installedCount = env.installs.length;
|
|
1608
1759
|
const directCount = env.installs.filter((install) => install.direct).length;
|
|
@@ -1619,13 +1770,23 @@ export async function inspectSkillsStatus({ cwd = process.cwd() } = {}) {
|
|
|
1619
1770
|
const deprecatedCount = deprecated.length;
|
|
1620
1771
|
const incomplete = missingResult.skills;
|
|
1621
1772
|
const incompleteCount = missingResult.count;
|
|
1773
|
+
const runtimeDrift = runtimeInspection.runtimeDrift;
|
|
1774
|
+
const runtimeDriftCount = runtimeInspection.runtimeDriftCount;
|
|
1775
|
+
const orphanedMaterializations = runtimeInspection.orphanedMaterializations;
|
|
1776
|
+
const orphanedMaterializationCount = runtimeInspection.orphanedMaterializationCount;
|
|
1622
1777
|
|
|
1623
1778
|
let health = 'healthy';
|
|
1624
1779
|
if (!registry.configured) {
|
|
1625
1780
|
health = installedCount > 0 || outdatedCount > 0 ? 'attention-needed' : 'needs-config';
|
|
1626
|
-
} else if (
|
|
1781
|
+
} else if (
|
|
1782
|
+
outdatedCount > 0
|
|
1783
|
+
|| deprecatedCount > 0
|
|
1784
|
+
|| incompleteCount > 0
|
|
1785
|
+
|| runtimeDriftCount > 0
|
|
1786
|
+
|| orphanedMaterializationCount > 0
|
|
1787
|
+
) {
|
|
1627
1788
|
health = 'attention-needed';
|
|
1628
|
-
} else if (incompleteCount > 0) {
|
|
1789
|
+
} else if (incompleteCount > 0 || runtimeDriftCount > 0 || orphanedMaterializationCount > 0) {
|
|
1629
1790
|
health = 'attention-needed';
|
|
1630
1791
|
}
|
|
1631
1792
|
|
|
@@ -1637,10 +1798,14 @@ export async function inspectSkillsStatus({ cwd = process.cwd() } = {}) {
|
|
|
1637
1798
|
outdatedCount,
|
|
1638
1799
|
deprecatedCount,
|
|
1639
1800
|
incompleteCount,
|
|
1801
|
+
runtimeDriftCount,
|
|
1802
|
+
orphanedMaterializationCount,
|
|
1640
1803
|
registry,
|
|
1641
1804
|
outdated: outdatedResult.skills,
|
|
1642
1805
|
deprecated,
|
|
1643
1806
|
incomplete,
|
|
1807
|
+
runtimeDrift,
|
|
1808
|
+
orphanedMaterializations,
|
|
1644
1809
|
installs: env.installs,
|
|
1645
1810
|
health,
|
|
1646
1811
|
};
|
|
@@ -1662,15 +1827,17 @@ export function uninstallSkills(target, { cwd = process.cwd() } = {}) {
|
|
|
1662
1827
|
if (nextInstallTargets.length > 0) {
|
|
1663
1828
|
execFileSync('npm', ['install', '--no-save', ...nextInstallTargets], {
|
|
1664
1829
|
cwd: repoRoot,
|
|
1830
|
+
env: buildNpmRegistryEnv(repoRoot, process.env),
|
|
1665
1831
|
encoding: 'utf-8',
|
|
1666
1832
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1667
1833
|
});
|
|
1668
1834
|
}
|
|
1669
1835
|
|
|
1836
|
+
const resolvedPackageDirs = resolveInstalledPackageClosure(repoRoot, nextDirectTargetMap);
|
|
1670
1837
|
const nextState = rebuildInstallState(repoRoot, nextDirectTargetMap, {
|
|
1671
|
-
|
|
1672
|
-
parseSkillFrontmatterFile,
|
|
1838
|
+
packageDirs: resolvedPackageDirs,
|
|
1673
1839
|
readPackageMetadata,
|
|
1840
|
+
readInstalledSkillExports,
|
|
1674
1841
|
normalizeRelativePath,
|
|
1675
1842
|
});
|
|
1676
1843
|
const remainingTargets = new Set(
|