@alavida/agentpack 0.1.4 → 0.1.6
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/package.json +4 -1
- package/skills/agentpack-cli/SKILL.md +17 -14
- package/skills/agentpack-cli/references/skill-lifecycle.md +13 -11
- package/skills/authoring-skillgraphs-from-knowledge/SKILL.md +37 -16
- package/skills/authoring-skillgraphs-from-knowledge/references/authored-metadata.md +4 -3
- package/skills/getting-started-skillgraphs/SKILL.md +3 -3
- package/skills/shipping-production-plugins-and-packages/SKILL.md +16 -8
- 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 +27 -0
- package/src/domain/auth/registry-resolution.js +55 -0
- package/src/domain/skills/skill-model.js +58 -1
- 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 +199 -21
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, rmSync, symlinkSync } from 'node:fs';
|
|
1
|
+
import { existsSync, lstatSync, mkdirSync, rmSync, symlinkSync, unlinkSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, relative, resolve } from 'node:path';
|
|
3
3
|
import { writeInstallState } from '../fs/install-state-repository.js';
|
|
4
4
|
|
|
@@ -7,7 +7,17 @@ function ensureDir(pathValue) {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function removePathIfExists(pathValue) {
|
|
10
|
-
|
|
10
|
+
try {
|
|
11
|
+
const stat = lstatSync(pathValue);
|
|
12
|
+
if (stat.isSymbolicLink() || stat.isFile()) {
|
|
13
|
+
unlinkSync(pathValue);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
rmSync(pathValue, { recursive: true, force: true });
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error?.code === 'ENOENT') return;
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
11
21
|
}
|
|
12
22
|
|
|
13
23
|
export function ensureSkillLink(repoRoot, baseDir, skillName, skillDir, normalizeDisplayPath) {
|
|
@@ -25,9 +35,13 @@ export function removeSkillLinks(repoRoot, name, normalizeDisplayPath) {
|
|
|
25
35
|
join(repoRoot, '.claude', 'skills', name),
|
|
26
36
|
join(repoRoot, '.agents', 'skills', name),
|
|
27
37
|
]) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
try {
|
|
39
|
+
removePathIfExists(pathValue);
|
|
40
|
+
removed.push(normalizeDisplayPath(repoRoot, pathValue));
|
|
41
|
+
} catch (error) {
|
|
42
|
+
if (error?.code === 'ENOENT') continue;
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
31
45
|
}
|
|
32
46
|
return removed;
|
|
33
47
|
}
|
|
@@ -50,9 +64,13 @@ export function removeSkillLinksByPaths(repoRoot, paths, normalizeDisplayPath) {
|
|
|
50
64
|
const pathValue = resolve(repoRoot, relativePath);
|
|
51
65
|
const inAllowedRoot = allowedRoots.some((root) => pathValue === root || pathValue.startsWith(`${root}/`));
|
|
52
66
|
if (!inAllowedRoot) continue;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
67
|
+
try {
|
|
68
|
+
removePathIfExists(pathValue);
|
|
69
|
+
removed.push(normalizeDisplayPath(repoRoot, pathValue));
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (error?.code === 'ENOENT') continue;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
56
74
|
}
|
|
57
75
|
return [...new Set(removed)];
|
|
58
76
|
}
|
|
@@ -63,31 +81,62 @@ function ensureSymlink(targetPath, linkPath) {
|
|
|
63
81
|
symlinkSync(targetPath, linkPath, 'dir');
|
|
64
82
|
}
|
|
65
83
|
|
|
84
|
+
function inferPackageRuntimeNamespace(packageName) {
|
|
85
|
+
return packageName?.split('/').pop() || null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function buildRuntimeName(packageName, exportedSkills, entry) {
|
|
89
|
+
if (exportedSkills.length <= 1) return entry.name;
|
|
90
|
+
|
|
91
|
+
const namespace = inferPackageRuntimeNamespace(packageName);
|
|
92
|
+
if (!namespace) return entry.name;
|
|
93
|
+
if (entry.name === namespace) return namespace;
|
|
94
|
+
return `${namespace}:${entry.name}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
66
97
|
export function buildInstallRecord(repoRoot, packageDir, directTargetMap, {
|
|
67
|
-
parseSkillFrontmatterFile,
|
|
68
98
|
readPackageMetadata,
|
|
99
|
+
readInstalledSkillExports,
|
|
69
100
|
normalizeRelativePath,
|
|
70
101
|
} = {}) {
|
|
71
102
|
const packageMetadata = readPackageMetadata(packageDir);
|
|
72
103
|
if (!packageMetadata.packageName) return null;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const skillDirName = skillMetadata.name;
|
|
104
|
+
const exportedSkills = readInstalledSkillExports(packageDir);
|
|
105
|
+
if (exportedSkills.length === 0) return null;
|
|
76
106
|
const materializations = [];
|
|
107
|
+
const skills = [];
|
|
77
108
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
target: normalizeRelativePath(relative(repoRoot, claudeTargetAbs)),
|
|
82
|
-
mode: 'symlink',
|
|
83
|
-
});
|
|
109
|
+
for (const entry of exportedSkills) {
|
|
110
|
+
const runtimeName = buildRuntimeName(packageMetadata.packageName, exportedSkills, entry);
|
|
111
|
+
const skillMaterializations = [];
|
|
84
112
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
113
|
+
const claudeTargetAbs = join(repoRoot, '.claude', 'skills', runtimeName);
|
|
114
|
+
ensureSymlink(entry.skillDir, claudeTargetAbs);
|
|
115
|
+
skillMaterializations.push({
|
|
116
|
+
target: normalizeRelativePath(relative(repoRoot, claudeTargetAbs)),
|
|
117
|
+
mode: 'symlink',
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const agentsTargetAbs = join(repoRoot, '.agents', 'skills', runtimeName);
|
|
121
|
+
ensureSymlink(entry.skillDir, agentsTargetAbs);
|
|
122
|
+
skillMaterializations.push({
|
|
123
|
+
target: normalizeRelativePath(relative(repoRoot, agentsTargetAbs)),
|
|
124
|
+
mode: 'symlink',
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
materializations.push(...skillMaterializations);
|
|
128
|
+
skills.push({
|
|
129
|
+
name: entry.name,
|
|
130
|
+
runtime_name: runtimeName,
|
|
131
|
+
source_skill_path: normalizeRelativePath(relative(repoRoot, entry.skillDir)),
|
|
132
|
+
source_skill_file: normalizeRelativePath(relative(repoRoot, entry.skillFile)),
|
|
133
|
+
requires: entry.requires,
|
|
134
|
+
status: entry.status,
|
|
135
|
+
replacement: entry.replacement,
|
|
136
|
+
message: entry.message,
|
|
137
|
+
materializations: skillMaterializations,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
91
140
|
|
|
92
141
|
return {
|
|
93
142
|
packageName: packageMetadata.packageName,
|
|
@@ -95,26 +144,23 @@ export function buildInstallRecord(repoRoot, packageDir, directTargetMap, {
|
|
|
95
144
|
requestedTarget: directTargetMap.get(packageMetadata.packageName) || null,
|
|
96
145
|
packageVersion: packageMetadata.packageVersion,
|
|
97
146
|
sourcePackagePath: normalizeRelativePath(relative(repoRoot, packageDir)),
|
|
147
|
+
skills,
|
|
98
148
|
materializations,
|
|
99
149
|
};
|
|
100
150
|
}
|
|
101
151
|
|
|
102
152
|
export function rebuildInstallState(repoRoot, directTargetMap, {
|
|
103
|
-
|
|
104
|
-
parseSkillFrontmatterFile,
|
|
153
|
+
packageDirs = [],
|
|
105
154
|
readPackageMetadata,
|
|
155
|
+
readInstalledSkillExports,
|
|
106
156
|
normalizeRelativePath,
|
|
107
157
|
} = {}) {
|
|
108
|
-
const packageDirs = listInstalledPackageDirs(join(repoRoot, 'node_modules'));
|
|
109
158
|
const installs = {};
|
|
110
159
|
|
|
111
160
|
for (const packageDir of packageDirs) {
|
|
112
|
-
const skillFile = join(packageDir, 'SKILL.md');
|
|
113
|
-
if (!existsSync(skillFile)) continue;
|
|
114
|
-
|
|
115
161
|
const record = buildInstallRecord(repoRoot, packageDir, directTargetMap, {
|
|
116
|
-
parseSkillFrontmatterFile,
|
|
117
162
|
readPackageMetadata,
|
|
163
|
+
readInstalledSkillExports,
|
|
118
164
|
normalizeRelativePath,
|
|
119
165
|
});
|
|
120
166
|
if (!record) continue;
|
|
@@ -124,6 +170,7 @@ export function rebuildInstallState(repoRoot, directTargetMap, {
|
|
|
124
170
|
requested_target: record.requestedTarget,
|
|
125
171
|
package_version: record.packageVersion,
|
|
126
172
|
source_package_path: record.sourcePackagePath,
|
|
173
|
+
skills: record.skills,
|
|
127
174
|
materializations: record.materializations,
|
|
128
175
|
};
|
|
129
176
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
import { writeFileSync } from 'node:fs';
|
|
1
2
|
import { spawn } from 'node:child_process';
|
|
2
3
|
|
|
3
4
|
export function openBrowser(url) {
|
|
5
|
+
if (process.env.AGENTPACK_BROWSER_CAPTURE_PATH) {
|
|
6
|
+
writeFileSync(process.env.AGENTPACK_BROWSER_CAPTURE_PATH, `${url}\n`);
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
if (process.env.AGENTPACK_DISABLE_BROWSER === '1') return;
|
|
5
11
|
|
|
6
12
|
const command = process.platform === 'darwin'
|
package/src/lib/skills.js
CHANGED
|
@@ -19,11 +19,14 @@ 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 { inspectMaterializedSkills } from '../infrastructure/runtime/inspect-materialized-skills.js';
|
|
27
30
|
import {
|
|
28
31
|
buildStateRecordForPackageDir,
|
|
29
32
|
compareRecordedSources,
|
|
@@ -31,6 +34,10 @@ import {
|
|
|
31
34
|
readBuildState,
|
|
32
35
|
writeBuildState,
|
|
33
36
|
} from '../domain/skills/skill-provenance.js';
|
|
37
|
+
import { readUserConfig } from '../infrastructure/fs/user-config-repository.js';
|
|
38
|
+
import { readUserCredentials } from '../infrastructure/fs/user-credentials-repository.js';
|
|
39
|
+
import { getUserNpmrcPath, readUserNpmrc } from '../infrastructure/fs/user-npmrc-repository.js';
|
|
40
|
+
import { resolveRegistryConfig } from '../domain/auth/registry-resolution.js';
|
|
34
41
|
import { startSkillDevWorkbench } from '../application/skills/start-skill-dev-workbench.js';
|
|
35
42
|
import { AgentpackError, EXIT_CODES, NetworkError, NotFoundError, ValidationError } from '../utils/errors.js';
|
|
36
43
|
|
|
@@ -638,24 +645,75 @@ function readRepoNpmRegistryConfig(repoRoot, scope = null) {
|
|
|
638
645
|
: {};
|
|
639
646
|
const scopes = scope ? [scope] : MANAGED_PACKAGE_SCOPES;
|
|
640
647
|
const matchedScope = scopes.find((candidate) => config[`${candidate}:registry`]) || scope || scopes[0] || null;
|
|
648
|
+
const registry = matchedScope ? (config[`${matchedScope}:registry`] || null) : null;
|
|
649
|
+
const authKey = registry ? `//${new URL(registry).host}/:_authToken` : null;
|
|
641
650
|
|
|
642
651
|
return {
|
|
643
652
|
npmrcPath: existsSync(npmrcPath) ? npmrcPath : null,
|
|
644
653
|
scope: matchedScope,
|
|
645
|
-
registry
|
|
646
|
-
authToken: config[
|
|
654
|
+
registry,
|
|
655
|
+
authToken: authKey ? (config[authKey] || null) : null,
|
|
647
656
|
alwaysAuth: String(config['always-auth'] || '').toLowerCase() === 'true',
|
|
648
657
|
};
|
|
649
658
|
}
|
|
650
659
|
|
|
660
|
+
function readEffectiveRegistryConfig(repoRoot, scope = null, env = process.env) {
|
|
661
|
+
const userConfig = readUserConfig({ env });
|
|
662
|
+
const userNpmrc = readUserNpmrc({ env });
|
|
663
|
+
const repoConfig = readRepoNpmRegistryConfig(repoRoot, scope);
|
|
664
|
+
const userScope = MANAGED_PACKAGE_SCOPES.find((candidate) => userNpmrc[`${candidate}:registry`]) || null;
|
|
665
|
+
const resolvedScope = scope
|
|
666
|
+
|| (repoConfig.registry ? repoConfig.scope : null)
|
|
667
|
+
|| userScope
|
|
668
|
+
|| userConfig.scope
|
|
669
|
+
|| repoConfig.scope
|
|
670
|
+
|| MANAGED_PACKAGE_SCOPES[0]
|
|
671
|
+
|| null;
|
|
672
|
+
const resolved = resolveRegistryConfig({
|
|
673
|
+
scope: resolvedScope,
|
|
674
|
+
defaults: {
|
|
675
|
+
registry: userConfig.registry,
|
|
676
|
+
verificationPackage: userConfig.verificationPackage,
|
|
677
|
+
},
|
|
678
|
+
userNpmrc,
|
|
679
|
+
repoNpmrc: repoConfig.registry ? {
|
|
680
|
+
[`${repoConfig.scope}:registry`]: repoConfig.registry,
|
|
681
|
+
...(repoConfig.authToken ? { [`//${new URL(repoConfig.registry).host}/:_authToken`]: repoConfig.authToken } : {}),
|
|
682
|
+
} : {},
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
let npmrcPath = null;
|
|
686
|
+
if (resolved.source === 'repo') {
|
|
687
|
+
npmrcPath = repoConfig.npmrcPath;
|
|
688
|
+
} else if (resolved.source === 'user') {
|
|
689
|
+
npmrcPath = getUserNpmrcPath({ env });
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const alwaysAuth = resolved.source === 'repo'
|
|
693
|
+
? repoConfig.alwaysAuth
|
|
694
|
+
: String(userNpmrc['always-auth'] || '').toLowerCase() === 'true';
|
|
695
|
+
|
|
696
|
+
return {
|
|
697
|
+
scope: resolved.scope,
|
|
698
|
+
npmrcPath,
|
|
699
|
+
registry: resolved.registry,
|
|
700
|
+
authToken: resolved.authToken,
|
|
701
|
+
alwaysAuth,
|
|
702
|
+
source: resolved.source,
|
|
703
|
+
configured: resolved.source !== 'default' && Boolean(resolved.registry),
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
651
707
|
export function inspectRegistryConfig({
|
|
652
708
|
cwd = process.cwd(),
|
|
653
709
|
scope = null,
|
|
710
|
+
env = process.env,
|
|
654
711
|
} = {}) {
|
|
655
712
|
const repoRoot = findRepoRoot(cwd);
|
|
656
|
-
const { npmrcPath, scope: resolvedScope, registry, authToken, alwaysAuth } =
|
|
713
|
+
const { npmrcPath, scope: resolvedScope, registry, authToken, alwaysAuth, source, configured } = readEffectiveRegistryConfig(
|
|
657
714
|
repoRoot,
|
|
658
|
-
scope
|
|
715
|
+
scope,
|
|
716
|
+
env
|
|
659
717
|
);
|
|
660
718
|
|
|
661
719
|
let auth = {
|
|
@@ -688,10 +746,11 @@ export function inspectRegistryConfig({
|
|
|
688
746
|
scope: resolvedScope,
|
|
689
747
|
repoRoot,
|
|
690
748
|
npmrcPath: npmrcPath ? normalizeDisplayPath(repoRoot, npmrcPath) : null,
|
|
691
|
-
configured
|
|
692
|
-
registry,
|
|
749
|
+
configured,
|
|
750
|
+
registry: configured ? registry : null,
|
|
693
751
|
auth,
|
|
694
752
|
alwaysAuth,
|
|
753
|
+
source,
|
|
695
754
|
};
|
|
696
755
|
}
|
|
697
756
|
|
|
@@ -704,6 +763,22 @@ function resolveRegistryAuthToken(rawValue) {
|
|
|
704
763
|
return rawValue;
|
|
705
764
|
}
|
|
706
765
|
|
|
766
|
+
function buildNpmRegistryEnv(repoRoot, env = process.env) {
|
|
767
|
+
const effective = readEffectiveRegistryConfig(repoRoot, null, env);
|
|
768
|
+
const authToken = effective.authToken || null;
|
|
769
|
+
const envMatch = authToken?.match(/^\$\{([^}]+)\}$/) || null;
|
|
770
|
+
if (!envMatch) return env;
|
|
771
|
+
if (env[envMatch[1]]) return env;
|
|
772
|
+
|
|
773
|
+
const credentials = readUserCredentials({ env });
|
|
774
|
+
if (!credentials?.token) return env;
|
|
775
|
+
|
|
776
|
+
return {
|
|
777
|
+
...env,
|
|
778
|
+
[envMatch[1]]: credentials.token,
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
|
|
707
782
|
async function fetchRegistryLatestVersion(packageName, {
|
|
708
783
|
registry,
|
|
709
784
|
authToken,
|
|
@@ -1265,6 +1340,36 @@ function listInstalledPackageDirs(nodeModulesDir) {
|
|
|
1265
1340
|
return packageDirs;
|
|
1266
1341
|
}
|
|
1267
1342
|
|
|
1343
|
+
function findInstalledPackageDir(nodeModulesDir, packageName) {
|
|
1344
|
+
if (!packageName) return null;
|
|
1345
|
+
const packageDir = join(nodeModulesDir, ...packageName.split('/'));
|
|
1346
|
+
return existsSync(packageDir) ? packageDir : null;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
function resolveInstalledPackageClosure(repoRoot, directTargetMap) {
|
|
1350
|
+
const nodeModulesDir = join(repoRoot, 'node_modules');
|
|
1351
|
+
const queue = [...directTargetMap.keys()];
|
|
1352
|
+
const seen = new Set();
|
|
1353
|
+
const packageDirs = [];
|
|
1354
|
+
|
|
1355
|
+
while (queue.length > 0) {
|
|
1356
|
+
const packageName = queue.shift();
|
|
1357
|
+
if (seen.has(packageName)) continue;
|
|
1358
|
+
seen.add(packageName);
|
|
1359
|
+
|
|
1360
|
+
const packageDir = findInstalledPackageDir(nodeModulesDir, packageName);
|
|
1361
|
+
if (!packageDir) continue;
|
|
1362
|
+
|
|
1363
|
+
packageDirs.push(packageDir);
|
|
1364
|
+
const packageMetadata = readPackageMetadata(packageDir);
|
|
1365
|
+
for (const dependencyName of Object.keys(packageMetadata.dependencies || {})) {
|
|
1366
|
+
queue.push(dependencyName);
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
return packageDirs.sort();
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1268
1373
|
function collectLocalInstallTargets(initialTarget) {
|
|
1269
1374
|
const resolvedTarget = resolve(initialTarget);
|
|
1270
1375
|
const queue = [resolvedTarget];
|
|
@@ -1342,14 +1447,16 @@ export function installSkills(targets, { cwd = process.cwd() } = {}) {
|
|
|
1342
1447
|
|
|
1343
1448
|
execFileSync('npm', ['install', '--no-save', ...uniqueInstallTargets], {
|
|
1344
1449
|
cwd: repoRoot,
|
|
1450
|
+
env: buildNpmRegistryEnv(repoRoot, process.env),
|
|
1345
1451
|
encoding: 'utf-8',
|
|
1346
1452
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1347
1453
|
});
|
|
1348
1454
|
|
|
1455
|
+
const resolvedPackageDirs = resolveInstalledPackageClosure(repoRoot, directTargetMap);
|
|
1349
1456
|
return rebuildInstallState(repoRoot, directTargetMap, {
|
|
1350
|
-
|
|
1351
|
-
parseSkillFrontmatterFile,
|
|
1457
|
+
packageDirs: resolvedPackageDirs,
|
|
1352
1458
|
readPackageMetadata,
|
|
1459
|
+
readInstalledSkillExports,
|
|
1353
1460
|
normalizeRelativePath,
|
|
1354
1461
|
});
|
|
1355
1462
|
}
|
|
@@ -1360,11 +1467,14 @@ export function inspectSkillsEnv({ cwd = process.cwd() } = {}) {
|
|
|
1360
1467
|
const installs = Object.entries(state.installs || {})
|
|
1361
1468
|
.sort(([a], [b]) => a.localeCompare(b))
|
|
1362
1469
|
.map(([packageName, install]) => ({
|
|
1363
|
-
...
|
|
1470
|
+
...(install.skills?.length > 0
|
|
1471
|
+
? readInstalledSkillLifecycleFromRecord(install)
|
|
1472
|
+
: readInstalledSkillLifecycle(repoRoot, install.source_package_path)),
|
|
1364
1473
|
packageName,
|
|
1365
1474
|
direct: install.direct,
|
|
1366
1475
|
packageVersion: install.package_version,
|
|
1367
1476
|
sourcePackagePath: install.source_package_path,
|
|
1477
|
+
skills: install.skills || [],
|
|
1368
1478
|
materializations: install.materializations || [],
|
|
1369
1479
|
}));
|
|
1370
1480
|
|
|
@@ -1403,10 +1513,66 @@ function readInstalledSkillLifecycle(repoRoot, sourcePackagePath) {
|
|
|
1403
1513
|
};
|
|
1404
1514
|
}
|
|
1405
1515
|
|
|
1516
|
+
function readInstalledSkillLifecycleFromRecord(install) {
|
|
1517
|
+
const primarySkill = (install.skills || []).find((entry) => entry.runtime_name === entry.name)
|
|
1518
|
+
|| (install.skills || [])[0]
|
|
1519
|
+
|| null;
|
|
1520
|
+
|
|
1521
|
+
if (!primarySkill) {
|
|
1522
|
+
return {
|
|
1523
|
+
requires: [],
|
|
1524
|
+
status: null,
|
|
1525
|
+
replacement: null,
|
|
1526
|
+
message: null,
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
return {
|
|
1531
|
+
requires: primarySkill.requires || [],
|
|
1532
|
+
status: primarySkill.status || null,
|
|
1533
|
+
replacement: primarySkill.replacement || null,
|
|
1534
|
+
message: primarySkill.message || null,
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1406
1538
|
function buildInstallCommand(packageName) {
|
|
1407
1539
|
return `agentpack skills install ${packageName}`;
|
|
1408
1540
|
}
|
|
1409
1541
|
|
|
1542
|
+
function buildInstalledRequirementSet(installs) {
|
|
1543
|
+
const installed = new Set();
|
|
1544
|
+
|
|
1545
|
+
for (const install of installs) {
|
|
1546
|
+
if (install.packageName) installed.add(install.packageName);
|
|
1547
|
+
for (const skill of install.skills || []) {
|
|
1548
|
+
const requirement = buildCanonicalSkillRequirement(install.packageName, skill.name);
|
|
1549
|
+
if (requirement) installed.add(requirement);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
return installed;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
function buildInstalledRequirementRecords(installs) {
|
|
1557
|
+
return installs.map((install) => {
|
|
1558
|
+
const requires = new Set();
|
|
1559
|
+
|
|
1560
|
+
for (const skill of install.skills || []) {
|
|
1561
|
+
for (const requirement of skill.requires || []) {
|
|
1562
|
+
requires.add(requirement);
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
return {
|
|
1567
|
+
packageName: install.packageName,
|
|
1568
|
+
name: null,
|
|
1569
|
+
skillFile: install.sourcePackagePath ? `${install.sourcePackagePath}/SKILL.md` : null,
|
|
1570
|
+
direct: install.direct,
|
|
1571
|
+
requires: [...requires].sort((a, b) => a.localeCompare(b)),
|
|
1572
|
+
};
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1410
1576
|
function listLocalWorkbenchSkillRecords(repoRoot) {
|
|
1411
1577
|
const records = [];
|
|
1412
1578
|
|
|
@@ -1492,14 +1658,8 @@ export function inspectMissingSkillDependencies({
|
|
|
1492
1658
|
} = {}) {
|
|
1493
1659
|
const repoRoot = findRepoRoot(cwd);
|
|
1494
1660
|
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
|
-
}));
|
|
1661
|
+
const installed = buildInstalledRequirementSet(env.installs);
|
|
1662
|
+
const installedRecords = buildInstalledRequirementRecords(env.installs);
|
|
1503
1663
|
const localWorkbenchRecords = listLocalWorkbenchSkillRecords(repoRoot);
|
|
1504
1664
|
|
|
1505
1665
|
let records = [...installedRecords, ...localWorkbenchRecords];
|
|
@@ -1600,9 +1760,11 @@ export async function listOutdatedSkills({
|
|
|
1600
1760
|
|
|
1601
1761
|
export async function inspectSkillsStatus({ cwd = process.cwd() } = {}) {
|
|
1602
1762
|
const env = inspectSkillsEnv({ cwd });
|
|
1763
|
+
const state = readInstallState(env.repoRoot);
|
|
1603
1764
|
const registry = inspectRegistryConfig({ cwd });
|
|
1604
1765
|
const outdatedResult = await listOutdatedSkills({ cwd });
|
|
1605
1766
|
const missingResult = inspectMissingSkillDependencies({ cwd });
|
|
1767
|
+
const runtimeInspection = inspectMaterializedSkills(env.repoRoot, state);
|
|
1606
1768
|
|
|
1607
1769
|
const installedCount = env.installs.length;
|
|
1608
1770
|
const directCount = env.installs.filter((install) => install.direct).length;
|
|
@@ -1619,13 +1781,23 @@ export async function inspectSkillsStatus({ cwd = process.cwd() } = {}) {
|
|
|
1619
1781
|
const deprecatedCount = deprecated.length;
|
|
1620
1782
|
const incomplete = missingResult.skills;
|
|
1621
1783
|
const incompleteCount = missingResult.count;
|
|
1784
|
+
const runtimeDrift = runtimeInspection.runtimeDrift;
|
|
1785
|
+
const runtimeDriftCount = runtimeInspection.runtimeDriftCount;
|
|
1786
|
+
const orphanedMaterializations = runtimeInspection.orphanedMaterializations;
|
|
1787
|
+
const orphanedMaterializationCount = runtimeInspection.orphanedMaterializationCount;
|
|
1622
1788
|
|
|
1623
1789
|
let health = 'healthy';
|
|
1624
1790
|
if (!registry.configured) {
|
|
1625
1791
|
health = installedCount > 0 || outdatedCount > 0 ? 'attention-needed' : 'needs-config';
|
|
1626
|
-
} else if (
|
|
1792
|
+
} else if (
|
|
1793
|
+
outdatedCount > 0
|
|
1794
|
+
|| deprecatedCount > 0
|
|
1795
|
+
|| incompleteCount > 0
|
|
1796
|
+
|| runtimeDriftCount > 0
|
|
1797
|
+
|| orphanedMaterializationCount > 0
|
|
1798
|
+
) {
|
|
1627
1799
|
health = 'attention-needed';
|
|
1628
|
-
} else if (incompleteCount > 0) {
|
|
1800
|
+
} else if (incompleteCount > 0 || runtimeDriftCount > 0 || orphanedMaterializationCount > 0) {
|
|
1629
1801
|
health = 'attention-needed';
|
|
1630
1802
|
}
|
|
1631
1803
|
|
|
@@ -1637,10 +1809,14 @@ export async function inspectSkillsStatus({ cwd = process.cwd() } = {}) {
|
|
|
1637
1809
|
outdatedCount,
|
|
1638
1810
|
deprecatedCount,
|
|
1639
1811
|
incompleteCount,
|
|
1812
|
+
runtimeDriftCount,
|
|
1813
|
+
orphanedMaterializationCount,
|
|
1640
1814
|
registry,
|
|
1641
1815
|
outdated: outdatedResult.skills,
|
|
1642
1816
|
deprecated,
|
|
1643
1817
|
incomplete,
|
|
1818
|
+
runtimeDrift,
|
|
1819
|
+
orphanedMaterializations,
|
|
1644
1820
|
installs: env.installs,
|
|
1645
1821
|
health,
|
|
1646
1822
|
};
|
|
@@ -1662,15 +1838,17 @@ export function uninstallSkills(target, { cwd = process.cwd() } = {}) {
|
|
|
1662
1838
|
if (nextInstallTargets.length > 0) {
|
|
1663
1839
|
execFileSync('npm', ['install', '--no-save', ...nextInstallTargets], {
|
|
1664
1840
|
cwd: repoRoot,
|
|
1841
|
+
env: buildNpmRegistryEnv(repoRoot, process.env),
|
|
1665
1842
|
encoding: 'utf-8',
|
|
1666
1843
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1667
1844
|
});
|
|
1668
1845
|
}
|
|
1669
1846
|
|
|
1847
|
+
const resolvedPackageDirs = resolveInstalledPackageClosure(repoRoot, nextDirectTargetMap);
|
|
1670
1848
|
const nextState = rebuildInstallState(repoRoot, nextDirectTargetMap, {
|
|
1671
|
-
|
|
1672
|
-
parseSkillFrontmatterFile,
|
|
1849
|
+
packageDirs: resolvedPackageDirs,
|
|
1673
1850
|
readPackageMetadata,
|
|
1851
|
+
readInstalledSkillExports,
|
|
1674
1852
|
normalizeRelativePath,
|
|
1675
1853
|
});
|
|
1676
1854
|
const remainingTargets = new Set(
|