@alavida/agentpack 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/package.json +1 -1
- package/skills/agentpack-cli/SKILL.md +12 -0
- package/skills/agentpack-cli/references/skill-lifecycle.md +21 -1
- package/src/application/plugins/build-plugin.js +5 -0
- package/src/application/plugins/inspect-plugin-bundle.js +5 -0
- package/src/application/plugins/validate-plugin-bundle.js +5 -0
- package/src/application/skills/inspect-skill.js +5 -0
- package/src/application/skills/list-stale-skills.js +9 -0
- package/src/application/skills/validate-skills.js +5 -0
- package/src/commands/plugin.js +7 -4
- package/src/commands/skills.js +7 -8
- package/src/domain/skills/skill-graph.js +136 -0
- package/src/domain/skills/skill-model.js +187 -0
- package/src/domain/skills/skill-provenance.js +69 -0
- package/src/infrastructure/fs/build-state-repository.js +16 -0
- package/src/infrastructure/fs/install-state-repository.js +16 -0
- package/src/infrastructure/runtime/materialize-skills.js +117 -0
- package/src/infrastructure/runtime/watch-tree.js +44 -0
- package/src/lib/plugins.js +27 -89
- package/src/lib/skills.js +81 -447
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, rmSync, symlinkSync } from 'node:fs';
|
|
2
|
+
import { dirname, join, relative } from 'node:path';
|
|
3
|
+
import { writeInstallState } from '../fs/install-state-repository.js';
|
|
4
|
+
|
|
5
|
+
function ensureDir(pathValue) {
|
|
6
|
+
mkdirSync(pathValue, { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function removePathIfExists(pathValue) {
|
|
10
|
+
rmSync(pathValue, { recursive: true, force: true });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function ensureSkillLink(repoRoot, baseDir, skillName, skillDir, normalizeDisplayPath) {
|
|
14
|
+
const skillsDir = join(repoRoot, baseDir, 'skills');
|
|
15
|
+
ensureDir(skillsDir);
|
|
16
|
+
const linkPath = join(skillsDir, skillName);
|
|
17
|
+
removePathIfExists(linkPath);
|
|
18
|
+
symlinkSync(skillDir, linkPath, 'dir');
|
|
19
|
+
return normalizeDisplayPath(repoRoot, linkPath);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function removeSkillLinks(repoRoot, name, normalizeDisplayPath) {
|
|
23
|
+
const removed = [];
|
|
24
|
+
for (const pathValue of [
|
|
25
|
+
join(repoRoot, '.claude', 'skills', name),
|
|
26
|
+
join(repoRoot, '.agents', 'skills', name),
|
|
27
|
+
]) {
|
|
28
|
+
if (!existsSync(pathValue)) continue;
|
|
29
|
+
removePathIfExists(pathValue);
|
|
30
|
+
removed.push(normalizeDisplayPath(repoRoot, pathValue));
|
|
31
|
+
}
|
|
32
|
+
return removed;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function removeSkillLinksByNames(repoRoot, names, normalizeDisplayPath) {
|
|
36
|
+
const removed = [];
|
|
37
|
+
for (const name of names) {
|
|
38
|
+
removed.push(...removeSkillLinks(repoRoot, name, normalizeDisplayPath));
|
|
39
|
+
}
|
|
40
|
+
return [...new Set(removed)];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function ensureSymlink(targetPath, linkPath) {
|
|
44
|
+
removePathIfExists(linkPath);
|
|
45
|
+
mkdirSync(dirname(linkPath), { recursive: true });
|
|
46
|
+
symlinkSync(targetPath, linkPath, 'dir');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function buildInstallRecord(repoRoot, packageDir, directTargetMap, {
|
|
50
|
+
parseSkillFrontmatterFile,
|
|
51
|
+
readPackageMetadata,
|
|
52
|
+
normalizeRelativePath,
|
|
53
|
+
} = {}) {
|
|
54
|
+
const packageMetadata = readPackageMetadata(packageDir);
|
|
55
|
+
if (!packageMetadata.packageName) return null;
|
|
56
|
+
|
|
57
|
+
const skillMetadata = parseSkillFrontmatterFile(join(packageDir, 'SKILL.md'));
|
|
58
|
+
const skillDirName = skillMetadata.name;
|
|
59
|
+
const materializations = [];
|
|
60
|
+
|
|
61
|
+
const claudeTargetAbs = join(repoRoot, '.claude', 'skills', skillDirName);
|
|
62
|
+
ensureSymlink(packageDir, claudeTargetAbs);
|
|
63
|
+
materializations.push({
|
|
64
|
+
target: normalizeRelativePath(relative(repoRoot, claudeTargetAbs)),
|
|
65
|
+
mode: 'symlink',
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const agentsTargetAbs = join(repoRoot, '.agents', 'skills', skillDirName);
|
|
69
|
+
ensureSymlink(packageDir, agentsTargetAbs);
|
|
70
|
+
materializations.push({
|
|
71
|
+
target: normalizeRelativePath(relative(repoRoot, agentsTargetAbs)),
|
|
72
|
+
mode: 'symlink',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
packageName: packageMetadata.packageName,
|
|
77
|
+
direct: directTargetMap.has(packageMetadata.packageName),
|
|
78
|
+
requestedTarget: directTargetMap.get(packageMetadata.packageName) || null,
|
|
79
|
+
packageVersion: packageMetadata.packageVersion,
|
|
80
|
+
sourcePackagePath: normalizeRelativePath(relative(repoRoot, packageDir)),
|
|
81
|
+
materializations,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function rebuildInstallState(repoRoot, directTargetMap, {
|
|
86
|
+
listInstalledPackageDirs,
|
|
87
|
+
parseSkillFrontmatterFile,
|
|
88
|
+
readPackageMetadata,
|
|
89
|
+
normalizeRelativePath,
|
|
90
|
+
} = {}) {
|
|
91
|
+
const packageDirs = listInstalledPackageDirs(join(repoRoot, 'node_modules'));
|
|
92
|
+
const installs = {};
|
|
93
|
+
|
|
94
|
+
for (const packageDir of packageDirs) {
|
|
95
|
+
const skillFile = join(packageDir, 'SKILL.md');
|
|
96
|
+
if (!existsSync(skillFile)) continue;
|
|
97
|
+
|
|
98
|
+
const record = buildInstallRecord(repoRoot, packageDir, directTargetMap, {
|
|
99
|
+
parseSkillFrontmatterFile,
|
|
100
|
+
readPackageMetadata,
|
|
101
|
+
normalizeRelativePath,
|
|
102
|
+
});
|
|
103
|
+
if (!record) continue;
|
|
104
|
+
|
|
105
|
+
installs[record.packageName] = {
|
|
106
|
+
direct: record.direct,
|
|
107
|
+
requested_target: record.requestedTarget,
|
|
108
|
+
package_version: record.packageVersion,
|
|
109
|
+
source_package_path: record.sourcePackagePath,
|
|
110
|
+
materializations: record.materializations,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const state = { version: 1, installs };
|
|
115
|
+
writeInstallState(repoRoot, state);
|
|
116
|
+
return state;
|
|
117
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { existsSync, readdirSync, watch } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
|
|
4
|
+
export function watchDirectoryTree(rootDir, onChange) {
|
|
5
|
+
const watchers = new Map();
|
|
6
|
+
|
|
7
|
+
const watchDir = (dirPath) => {
|
|
8
|
+
if (watchers.has(dirPath) || !existsSync(dirPath)) return;
|
|
9
|
+
|
|
10
|
+
let entries = [];
|
|
11
|
+
try {
|
|
12
|
+
entries = readdirSync(dirPath, { withFileTypes: true });
|
|
13
|
+
} catch {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const watcher = watch(dirPath, (_eventType, filename) => {
|
|
18
|
+
if (filename) {
|
|
19
|
+
const changedPath = join(dirPath, String(filename));
|
|
20
|
+
if (existsSync(changedPath)) {
|
|
21
|
+
watchDir(changedPath);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onChange();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
watchers.set(dirPath, watcher);
|
|
29
|
+
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
if (!entry.isDirectory()) continue;
|
|
32
|
+
watchDir(join(dirPath, entry.name));
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
watchDir(rootDir);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
close() {
|
|
40
|
+
for (const watcher of watchers.values()) watcher.close();
|
|
41
|
+
watchers.clear();
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
package/src/lib/plugins.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync,
|
|
1
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
|
+
import { resolveDependencyClosure } from '../domain/skills/skill-graph.js';
|
|
5
|
+
import { normalizeRepoPath, parseSkillFrontmatterFile, readPackageMetadata } from '../domain/skills/skill-model.js';
|
|
6
|
+
import { watchDirectoryTree } from '../infrastructure/runtime/watch-tree.js';
|
|
4
7
|
import {
|
|
5
8
|
findPackageDirByName,
|
|
6
|
-
normalizeRepoPath,
|
|
7
|
-
parseSkillFrontmatterFile,
|
|
8
|
-
readPackageMetadata,
|
|
9
9
|
syncSkillDependencies,
|
|
10
10
|
} from './skills.js';
|
|
11
11
|
import { findRepoRoot } from './context.js';
|
|
@@ -76,50 +76,30 @@ function collectPluginLocalSkillDirs(pluginDir) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function resolveBundleClosure(repoRoot, pluginDir, directRequires) {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const metadata = parseSkillFrontmatterFile(skillFile);
|
|
102
|
-
const packageMetadata = readPackageMetadata(resolvedPackage.packageDir);
|
|
103
|
-
|
|
104
|
-
bundled.push({
|
|
105
|
-
packageName,
|
|
106
|
-
packageVersion: packageMetadata.packageVersion,
|
|
107
|
-
skillName: metadata.name,
|
|
108
|
-
skillFile,
|
|
109
|
-
packageDir: resolvedPackage.packageDir,
|
|
110
|
-
source: resolvedPackage.source,
|
|
111
|
-
requires: metadata.requires,
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
for (const requirement of metadata.requires) {
|
|
115
|
-
if (!seen.has(requirement)) queue.push(requirement);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
bundled.sort((a, b) => a.packageName.localeCompare(b.packageName));
|
|
120
|
-
unresolved.sort();
|
|
79
|
+
const { resolved, unresolved } = resolveDependencyClosure(directRequires, {
|
|
80
|
+
resolveNode(packageName) {
|
|
81
|
+
const resolvedPackage = resolvePackageDir(repoRoot, pluginDir, packageName);
|
|
82
|
+
if (!resolvedPackage) return null;
|
|
83
|
+
|
|
84
|
+
const skillFile = join(resolvedPackage.packageDir, 'SKILL.md');
|
|
85
|
+
if (!existsSync(skillFile)) return null;
|
|
86
|
+
|
|
87
|
+
const metadata = parseSkillFrontmatterFile(skillFile);
|
|
88
|
+
const packageMetadata = readPackageMetadata(resolvedPackage.packageDir);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
packageName,
|
|
92
|
+
packageVersion: packageMetadata.packageVersion,
|
|
93
|
+
skillName: metadata.name,
|
|
94
|
+
skillFile,
|
|
95
|
+
packageDir: resolvedPackage.packageDir,
|
|
96
|
+
source: resolvedPackage.source,
|
|
97
|
+
requires: metadata.requires,
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
});
|
|
121
101
|
|
|
122
|
-
return { bundled, unresolved };
|
|
102
|
+
return { bundled: resolved, unresolved };
|
|
123
103
|
}
|
|
124
104
|
|
|
125
105
|
function stagePluginRuntimeFiles(pluginDir, stageDir) {
|
|
@@ -130,48 +110,6 @@ function stagePluginRuntimeFiles(pluginDir, stageDir) {
|
|
|
130
110
|
}
|
|
131
111
|
}
|
|
132
112
|
|
|
133
|
-
function watchDirectoryTree(rootDir, onChange) {
|
|
134
|
-
const watchers = new Map();
|
|
135
|
-
|
|
136
|
-
const watchDir = (dirPath) => {
|
|
137
|
-
if (watchers.has(dirPath) || !existsSync(dirPath)) return;
|
|
138
|
-
|
|
139
|
-
let entries = [];
|
|
140
|
-
try {
|
|
141
|
-
entries = readdirSync(dirPath, { withFileTypes: true });
|
|
142
|
-
} catch {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const watcher = watch(dirPath, (_eventType, filename) => {
|
|
147
|
-
if (filename) {
|
|
148
|
-
const changedPath = join(dirPath, String(filename));
|
|
149
|
-
if (existsSync(changedPath)) {
|
|
150
|
-
watchDir(changedPath);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
onChange();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
watchers.set(dirPath, watcher);
|
|
158
|
-
|
|
159
|
-
for (const entry of entries) {
|
|
160
|
-
if (!entry.isDirectory()) continue;
|
|
161
|
-
watchDir(join(dirPath, entry.name));
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
watchDir(rootDir);
|
|
166
|
-
|
|
167
|
-
return {
|
|
168
|
-
close() {
|
|
169
|
-
for (const watcher of watchers.values()) watcher.close();
|
|
170
|
-
watchers.clear();
|
|
171
|
-
},
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
113
|
export function buildPlugin(target, {
|
|
176
114
|
cwd = process.cwd(),
|
|
177
115
|
clean = false,
|