aweskill 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/README.md +119 -51
- package/README.zh-CN.md +121 -49
- package/dist/index.js +504 -275
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/resources/skill_archives/.gitkeep +1 -0
- package/resources/skill_archives/backup.md +7 -0
- /package/{template/bundles → resources/bundle_templates}/K-Dense-AI-scientific-skills.yaml +0 -0
- /package/{template/bundles → resources/bundle_templates}/Superpowers.yaml +0 -0
- /package/{template/bundles → resources/bundle_templates}/global.yaml +0 -0
package/dist/index.js
CHANGED
|
@@ -4,18 +4,41 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { homedir as homedir2 } from "os";
|
|
6
6
|
|
|
7
|
+
// src/commands/backup.ts
|
|
8
|
+
import path3 from "path";
|
|
9
|
+
|
|
7
10
|
// src/lib/backup.ts
|
|
8
|
-
import {
|
|
11
|
+
import { mkdtemp, mkdir, stat } from "fs/promises";
|
|
9
12
|
import { tmpdir } from "os";
|
|
10
13
|
import path2 from "path";
|
|
11
14
|
import { spawn } from "child_process";
|
|
12
15
|
|
|
16
|
+
// src/lib/fs.ts
|
|
17
|
+
import { access } from "fs/promises";
|
|
18
|
+
async function pathExists(targetPath) {
|
|
19
|
+
try {
|
|
20
|
+
await access(targetPath);
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
13
27
|
// src/lib/path.ts
|
|
14
28
|
import { homedir } from "os";
|
|
15
29
|
import path from "path";
|
|
16
30
|
function sanitizeName(input) {
|
|
17
31
|
return input.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+/, "").replace(/-+$/, "").slice(0, 80);
|
|
18
32
|
}
|
|
33
|
+
function expandHomePath(targetPath, homeDir = homedir()) {
|
|
34
|
+
if (targetPath === "~") {
|
|
35
|
+
return homeDir;
|
|
36
|
+
}
|
|
37
|
+
if (targetPath.startsWith("~/")) {
|
|
38
|
+
return path.join(homeDir, targetPath.slice(2));
|
|
39
|
+
}
|
|
40
|
+
return targetPath;
|
|
41
|
+
}
|
|
19
42
|
function getAweskillPaths(homeDir) {
|
|
20
43
|
const rootDir = path.join(homeDir, ".aweskill");
|
|
21
44
|
return {
|
|
@@ -51,11 +74,19 @@ async function runTar(args) {
|
|
|
51
74
|
});
|
|
52
75
|
});
|
|
53
76
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
77
|
+
function formatBackupLabel(includeBundles) {
|
|
78
|
+
return includeBundles ? "skills and bundles" : "skills";
|
|
79
|
+
}
|
|
80
|
+
function archiveEntries(homeDir, includeBundles) {
|
|
81
|
+
const { rootDir, skillsDir, bundlesDir } = getAweskillPaths(homeDir);
|
|
82
|
+
return includeBundles ? [path2.relative(rootDir, skillsDir), path2.relative(rootDir, bundlesDir)] : [path2.relative(rootDir, skillsDir)];
|
|
83
|
+
}
|
|
84
|
+
async function createSkillsBackupArchive(homeDir, options = {}) {
|
|
85
|
+
const { rootDir, backupDir } = getAweskillPaths(homeDir);
|
|
86
|
+
const includeBundles = options.includeBundles ?? false;
|
|
87
|
+
const archivePath = await resolveBackupArchivePath(backupDir, options.archivePath);
|
|
88
|
+
await mkdir(path2.dirname(archivePath), { recursive: true });
|
|
89
|
+
await runTar(["-czf", archivePath, "-C", rootDir, ...archiveEntries(homeDir, includeBundles)]);
|
|
59
90
|
return archivePath;
|
|
60
91
|
}
|
|
61
92
|
async function extractSkillsArchive(archivePath) {
|
|
@@ -63,9 +94,23 @@ async function extractSkillsArchive(archivePath) {
|
|
|
63
94
|
await runTar(["-xzf", archivePath, "-C", tempDir]);
|
|
64
95
|
return {
|
|
65
96
|
tempDir,
|
|
66
|
-
extractedSkillsDir: path2.join(tempDir, "skills")
|
|
97
|
+
extractedSkillsDir: path2.join(tempDir, "skills"),
|
|
98
|
+
extractedBundlesDir: path2.join(tempDir, "bundles")
|
|
67
99
|
};
|
|
68
100
|
}
|
|
101
|
+
async function resolveBackupArchivePath(backupDir, requestedPath) {
|
|
102
|
+
if (!requestedPath) {
|
|
103
|
+
return nextBackupArchivePath(backupDir);
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const requestedStats = await stat(requestedPath);
|
|
107
|
+
if (requestedStats.isDirectory()) {
|
|
108
|
+
return nextBackupArchivePath(requestedPath);
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
return requestedPath;
|
|
113
|
+
}
|
|
69
114
|
async function nextBackupArchivePath(backupDir) {
|
|
70
115
|
const base = `skills-${formatTimestamp(/* @__PURE__ */ new Date())}`;
|
|
71
116
|
const primary = path2.join(backupDir, `${base}.tar.gz`);
|
|
@@ -81,38 +126,25 @@ async function nextBackupArchivePath(backupDir) {
|
|
|
81
126
|
index += 1;
|
|
82
127
|
}
|
|
83
128
|
}
|
|
84
|
-
async function pathExists(targetPath) {
|
|
85
|
-
try {
|
|
86
|
-
await access(targetPath);
|
|
87
|
-
return true;
|
|
88
|
-
} catch {
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
129
|
|
|
93
130
|
// src/commands/backup.ts
|
|
94
|
-
async function runBackup(context) {
|
|
95
|
-
const archivePath = await createSkillsBackupArchive(context.homeDir
|
|
96
|
-
|
|
131
|
+
async function runBackup(context, options = {}) {
|
|
132
|
+
const archivePath = await createSkillsBackupArchive(context.homeDir, {
|
|
133
|
+
archivePath: options.archivePath ? path3.resolve(context.cwd, expandHomePath(options.archivePath, context.homeDir)) : void 0,
|
|
134
|
+
includeBundles: options.includeBundles
|
|
135
|
+
});
|
|
136
|
+
context.write(`Backed up ${formatBackupLabel(options.includeBundles ?? false)} to ${archivePath}`);
|
|
97
137
|
return { archivePath };
|
|
98
138
|
}
|
|
99
139
|
|
|
100
140
|
// src/lib/bundles.ts
|
|
101
|
-
import {
|
|
102
|
-
import
|
|
141
|
+
import { mkdir as mkdir3, readFile, readdir as readdir2, rm, writeFile } from "fs/promises";
|
|
142
|
+
import path5 from "path";
|
|
103
143
|
import { parse, stringify } from "yaml";
|
|
104
144
|
|
|
105
145
|
// src/lib/skills.ts
|
|
106
|
-
import {
|
|
107
|
-
import
|
|
108
|
-
async function pathExists2(targetPath) {
|
|
109
|
-
try {
|
|
110
|
-
await access2(targetPath);
|
|
111
|
-
return true;
|
|
112
|
-
} catch {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
146
|
+
import { mkdir as mkdir2, readdir } from "fs/promises";
|
|
147
|
+
import path4 from "path";
|
|
116
148
|
async function ensureHomeLayout(homeDir) {
|
|
117
149
|
const paths = getAweskillPaths(homeDir);
|
|
118
150
|
await mkdir2(paths.rootDir, { recursive: true });
|
|
@@ -122,53 +154,45 @@ async function ensureHomeLayout(homeDir) {
|
|
|
122
154
|
await mkdir2(paths.bundlesDir, { recursive: true });
|
|
123
155
|
}
|
|
124
156
|
function getSkillPath(homeDir, skillName) {
|
|
125
|
-
return
|
|
157
|
+
return path4.join(getAweskillPaths(homeDir).skillsDir, sanitizeName(skillName));
|
|
126
158
|
}
|
|
127
159
|
async function listSkills(homeDir) {
|
|
128
160
|
const skillsDir = getAweskillPaths(homeDir).skillsDir;
|
|
129
161
|
return listSkillEntriesInDirectory(skillsDir);
|
|
130
162
|
}
|
|
131
163
|
async function listSkillEntriesInDirectory(skillsDir) {
|
|
132
|
-
if (!await
|
|
164
|
+
if (!await pathExists(skillsDir)) {
|
|
133
165
|
return [];
|
|
134
166
|
}
|
|
135
167
|
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
136
168
|
const skills = await Promise.all(
|
|
137
169
|
entries.filter((entry) => entry.isDirectory() || entry.isSymbolicLink()).map(async (entry) => {
|
|
138
|
-
const skillPath =
|
|
170
|
+
const skillPath = path4.join(skillsDir, entry.name);
|
|
139
171
|
return {
|
|
140
172
|
name: entry.name,
|
|
141
173
|
path: skillPath,
|
|
142
|
-
hasSKILLMd: await
|
|
174
|
+
hasSKILLMd: await pathExists(path4.join(skillPath, "SKILL.md"))
|
|
143
175
|
};
|
|
144
176
|
})
|
|
145
177
|
);
|
|
146
178
|
return skills.sort((left, right) => left.name.localeCompare(right.name));
|
|
147
179
|
}
|
|
148
180
|
async function assertSkillSource(sourcePath) {
|
|
149
|
-
const skillReadme =
|
|
150
|
-
if (!await
|
|
181
|
+
const skillReadme = path4.join(sourcePath, "SKILL.md");
|
|
182
|
+
if (!await pathExists(skillReadme)) {
|
|
151
183
|
throw new Error(`Skill source must contain SKILL.md: ${sourcePath}`);
|
|
152
184
|
}
|
|
153
185
|
}
|
|
154
186
|
async function skillExists(homeDir, skillName) {
|
|
155
|
-
return
|
|
187
|
+
return pathExists(getSkillPath(homeDir, skillName));
|
|
156
188
|
}
|
|
157
189
|
|
|
158
190
|
// src/lib/bundles.ts
|
|
159
|
-
async function pathExists3(targetPath) {
|
|
160
|
-
try {
|
|
161
|
-
await access3(targetPath);
|
|
162
|
-
return true;
|
|
163
|
-
} catch {
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
191
|
function bundleFilePath(homeDir, bundleName) {
|
|
168
|
-
return
|
|
192
|
+
return path5.join(getAweskillPaths(homeDir).bundlesDir, `${sanitizeName(bundleName)}.yaml`);
|
|
169
193
|
}
|
|
170
194
|
function bundleFilePathInDirectory(bundlesDir, bundleName) {
|
|
171
|
-
return
|
|
195
|
+
return path5.join(bundlesDir, `${sanitizeName(bundleName)}.yaml`);
|
|
172
196
|
}
|
|
173
197
|
function normalizeBundle(raw, fallbackName) {
|
|
174
198
|
const data = raw ?? {};
|
|
@@ -202,7 +226,7 @@ async function readBundleFromDirectory(bundlesDir, bundleName) {
|
|
|
202
226
|
async function writeBundle(homeDir, bundle) {
|
|
203
227
|
const normalized = normalizeBundle(bundle, bundle.name);
|
|
204
228
|
const filePath = bundleFilePath(homeDir, normalized.name);
|
|
205
|
-
await mkdir3(
|
|
229
|
+
await mkdir3(path5.dirname(filePath), { recursive: true });
|
|
206
230
|
await writeFile(filePath, stringify(normalized), "utf8");
|
|
207
231
|
return normalized;
|
|
208
232
|
}
|
|
@@ -227,7 +251,7 @@ async function removeSkillFromBundle(homeDir, bundleName, skillName) {
|
|
|
227
251
|
}
|
|
228
252
|
async function deleteBundle(homeDir, bundleName) {
|
|
229
253
|
const filePath = bundleFilePath(homeDir, bundleName);
|
|
230
|
-
if (!await
|
|
254
|
+
if (!await pathExists(filePath)) {
|
|
231
255
|
return false;
|
|
232
256
|
}
|
|
233
257
|
await rm(filePath, { force: true });
|
|
@@ -235,25 +259,16 @@ async function deleteBundle(homeDir, bundleName) {
|
|
|
235
259
|
}
|
|
236
260
|
|
|
237
261
|
// src/lib/templates.ts
|
|
238
|
-
import
|
|
239
|
-
import path5 from "path";
|
|
262
|
+
import path6 from "path";
|
|
240
263
|
import { fileURLToPath } from "url";
|
|
241
|
-
async function pathExists4(targetPath) {
|
|
242
|
-
try {
|
|
243
|
-
await access4(targetPath);
|
|
244
|
-
return true;
|
|
245
|
-
} catch {
|
|
246
|
-
return false;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
264
|
async function getTemplateBundlesDir() {
|
|
250
|
-
const moduleDir =
|
|
265
|
+
const moduleDir = path6.dirname(fileURLToPath(import.meta.url));
|
|
251
266
|
const candidates = [
|
|
252
|
-
|
|
253
|
-
|
|
267
|
+
path6.resolve(moduleDir, "..", "..", "resources", "bundle_templates"),
|
|
268
|
+
path6.resolve(moduleDir, "..", "resources", "bundle_templates")
|
|
254
269
|
];
|
|
255
270
|
for (const candidate of candidates) {
|
|
256
|
-
if (await
|
|
271
|
+
if (await pathExists(candidate)) {
|
|
257
272
|
return candidate;
|
|
258
273
|
}
|
|
259
274
|
}
|
|
@@ -337,8 +352,8 @@ async function runBundleAddTemplate(context, bundleName) {
|
|
|
337
352
|
}
|
|
338
353
|
|
|
339
354
|
// src/lib/import.ts
|
|
340
|
-
import {
|
|
341
|
-
import
|
|
355
|
+
import { cp, lstat, mkdir as mkdir4, readlink, readdir as readdir3, rename, rm as rm2, stat as stat2 } from "fs/promises";
|
|
356
|
+
import path7 from "path";
|
|
342
357
|
var MissingSymlinkSourceError = class extends Error {
|
|
343
358
|
sourcePath;
|
|
344
359
|
resolvedSourcePath;
|
|
@@ -349,14 +364,6 @@ var MissingSymlinkSourceError = class extends Error {
|
|
|
349
364
|
this.resolvedSourcePath = resolvedSourcePath;
|
|
350
365
|
}
|
|
351
366
|
};
|
|
352
|
-
async function pathExists5(targetPath) {
|
|
353
|
-
try {
|
|
354
|
-
await access5(targetPath);
|
|
355
|
-
return true;
|
|
356
|
-
} catch {
|
|
357
|
-
return false;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
367
|
async function resolveImportSource(sourcePath) {
|
|
361
368
|
const statResult = await lstat(sourcePath);
|
|
362
369
|
if (!statResult.isSymbolicLink()) {
|
|
@@ -366,8 +373,8 @@ async function resolveImportSource(sourcePath) {
|
|
|
366
373
|
};
|
|
367
374
|
}
|
|
368
375
|
const linkTarget = await readlink(sourcePath);
|
|
369
|
-
const resolvedSourcePath =
|
|
370
|
-
if (!await
|
|
376
|
+
const resolvedSourcePath = path7.resolve(path7.dirname(sourcePath), linkTarget);
|
|
377
|
+
if (!await pathExists(resolvedSourcePath)) {
|
|
371
378
|
throw new MissingSymlinkSourceError(sourcePath, resolvedSourcePath);
|
|
372
379
|
}
|
|
373
380
|
return {
|
|
@@ -376,30 +383,30 @@ async function resolveImportSource(sourcePath) {
|
|
|
376
383
|
};
|
|
377
384
|
}
|
|
378
385
|
async function mergeMissingEntries(sourcePath, destinationPath) {
|
|
379
|
-
const sourceStat = await
|
|
386
|
+
const sourceStat = await stat2(sourcePath);
|
|
380
387
|
if (sourceStat.isDirectory()) {
|
|
381
388
|
await mkdir4(destinationPath, { recursive: true });
|
|
382
389
|
const entries = await readdir3(sourcePath, { withFileTypes: true });
|
|
383
390
|
for (const entry of entries) {
|
|
384
|
-
await mergeMissingEntries(
|
|
391
|
+
await mergeMissingEntries(path7.join(sourcePath, entry.name), path7.join(destinationPath, entry.name));
|
|
385
392
|
}
|
|
386
393
|
return;
|
|
387
394
|
}
|
|
388
|
-
if (await
|
|
395
|
+
if (await pathExists(destinationPath)) {
|
|
389
396
|
return;
|
|
390
397
|
}
|
|
391
|
-
await mkdir4(
|
|
398
|
+
await mkdir4(path7.dirname(destinationPath), { recursive: true });
|
|
392
399
|
await cp(sourcePath, destinationPath, { recursive: false, errorOnExist: true, force: false });
|
|
393
400
|
}
|
|
394
401
|
async function copyIntoDestination(sourcePath, destination, override) {
|
|
395
|
-
if (!override && await
|
|
402
|
+
if (!override && await pathExists(destination)) {
|
|
396
403
|
await mergeMissingEntries(sourcePath, destination);
|
|
397
404
|
return;
|
|
398
405
|
}
|
|
399
406
|
await cp(sourcePath, destination, { recursive: true, errorOnExist: false, force: override });
|
|
400
407
|
}
|
|
401
408
|
async function moveIntoDestination(sourcePath, destination, override) {
|
|
402
|
-
if (!await
|
|
409
|
+
if (!await pathExists(destination)) {
|
|
403
410
|
await rename(sourcePath, destination);
|
|
404
411
|
return;
|
|
405
412
|
}
|
|
@@ -464,8 +471,8 @@ async function listImportableChildren(sourceRoot) {
|
|
|
464
471
|
if (!(entry.isDirectory() || entry.isSymbolicLink())) {
|
|
465
472
|
continue;
|
|
466
473
|
}
|
|
467
|
-
const childPath =
|
|
468
|
-
if (await
|
|
474
|
+
const childPath = path7.join(sourceRoot, entry.name);
|
|
475
|
+
if (await pathExists(path7.join(childPath, "SKILL.md"))) {
|
|
469
476
|
sources.push({
|
|
470
477
|
name: sanitizeName(entry.name),
|
|
471
478
|
path: childPath
|
|
@@ -484,12 +491,12 @@ async function listImportableChildren(sourceRoot) {
|
|
|
484
491
|
async function importSkill(options) {
|
|
485
492
|
const { effectiveSourcePath, isSymlinkSource } = await resolveImportSource(options.sourcePath);
|
|
486
493
|
await assertSkillSource(effectiveSourcePath);
|
|
487
|
-
const skillName = sanitizeName(
|
|
494
|
+
const skillName = sanitizeName(path7.basename(options.sourcePath));
|
|
488
495
|
if (!skillName) {
|
|
489
496
|
throw new Error(`Unable to infer skill name from path: ${options.sourcePath}`);
|
|
490
497
|
}
|
|
491
498
|
const destination = getSkillPath(options.homeDir, skillName);
|
|
492
|
-
await mkdir4(
|
|
499
|
+
await mkdir4(path7.dirname(destination), { recursive: true });
|
|
493
500
|
const warnings = [];
|
|
494
501
|
if (isSymlinkSource) {
|
|
495
502
|
warnings.push(`Source ${options.sourcePath} is a symlink; copied from ${effectiveSourcePath} to ${destination}`);
|
|
@@ -513,8 +520,8 @@ async function importScannedSkills(options) {
|
|
|
513
520
|
});
|
|
514
521
|
}
|
|
515
522
|
async function importPath(options) {
|
|
516
|
-
if (await
|
|
517
|
-
const skillName = sanitizeName(
|
|
523
|
+
if (await pathExists(path7.join(options.sourcePath, "SKILL.md"))) {
|
|
524
|
+
const skillName = sanitizeName(path7.basename(options.sourcePath));
|
|
518
525
|
const alreadyExisted = skillName ? await skillExists(options.homeDir, skillName) : false;
|
|
519
526
|
if (alreadyExisted && !options.override) {
|
|
520
527
|
return {
|
|
@@ -542,107 +549,268 @@ async function importPath(options) {
|
|
|
542
549
|
}
|
|
543
550
|
|
|
544
551
|
// src/lib/scanner.ts
|
|
545
|
-
import { access as
|
|
546
|
-
import
|
|
552
|
+
import { access as access2, lstat as lstat2, readdir as readdir4, readlink as readlink2 } from "fs/promises";
|
|
553
|
+
import path9 from "path";
|
|
547
554
|
|
|
548
555
|
// src/lib/agents.ts
|
|
549
|
-
import
|
|
550
|
-
|
|
556
|
+
import path8 from "path";
|
|
557
|
+
function defineAgent(id, displayName, options) {
|
|
558
|
+
return {
|
|
559
|
+
id,
|
|
560
|
+
displayName,
|
|
561
|
+
defaultProjectionMode: options.defaultProjectionMode ?? "symlink",
|
|
562
|
+
supportsGlobal: Boolean(options.globalSkillsDir),
|
|
563
|
+
supportsProject: Boolean(options.projectSkillsDir),
|
|
564
|
+
rootDir: options.rootDir,
|
|
565
|
+
globalSkillsDir: options.globalSkillsDir,
|
|
566
|
+
projectSkillsDir: options.projectSkillsDir
|
|
567
|
+
};
|
|
568
|
+
}
|
|
551
569
|
var AGENTS = {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
rootDir: (homeDir) =>
|
|
589
|
-
globalSkillsDir: (homeDir) =>
|
|
590
|
-
projectSkillsDir: (projectDir) =>
|
|
591
|
-
},
|
|
592
|
-
"
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
rootDir: (homeDir) =>
|
|
629
|
-
globalSkillsDir: (homeDir) =>
|
|
630
|
-
projectSkillsDir: (projectDir) =>
|
|
631
|
-
}
|
|
570
|
+
adal: defineAgent("adal", "AdaL", {
|
|
571
|
+
rootDir: (homeDir) => path8.join(homeDir, ".adal"),
|
|
572
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".adal", "skills"),
|
|
573
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".adal", "skills")
|
|
574
|
+
}),
|
|
575
|
+
amp: defineAgent("amp", "Amp", {
|
|
576
|
+
rootDir: (homeDir) => path8.join(homeDir, ".agents"),
|
|
577
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".agents", "skills"),
|
|
578
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".agents", "skills")
|
|
579
|
+
}),
|
|
580
|
+
antigravity: defineAgent("antigravity", "Antigravity", {
|
|
581
|
+
rootDir: (homeDir) => path8.join(homeDir, ".gemini", "antigravity"),
|
|
582
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".gemini", "antigravity", "skills"),
|
|
583
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".gemini", "antigravity", "skills")
|
|
584
|
+
}),
|
|
585
|
+
augment: defineAgent("augment", "Augment", {
|
|
586
|
+
rootDir: (homeDir) => path8.join(homeDir, ".augment"),
|
|
587
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".augment", "skills"),
|
|
588
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".augment", "skills")
|
|
589
|
+
}),
|
|
590
|
+
bob: defineAgent("bob", "IBM Bob", {
|
|
591
|
+
rootDir: (homeDir) => path8.join(homeDir, ".bob"),
|
|
592
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".bob", "skills"),
|
|
593
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".bob", "skills")
|
|
594
|
+
}),
|
|
595
|
+
"claude-code": defineAgent("claude-code", "Claude Code", {
|
|
596
|
+
rootDir: (homeDir) => path8.join(homeDir, ".claude"),
|
|
597
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".claude", "skills"),
|
|
598
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".claude", "skills")
|
|
599
|
+
}),
|
|
600
|
+
cline: defineAgent("cline", "Cline", {
|
|
601
|
+
rootDir: (homeDir) => path8.join(homeDir, ".cline"),
|
|
602
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".cline", "skills"),
|
|
603
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".cline", "skills")
|
|
604
|
+
}),
|
|
605
|
+
codebuddy: defineAgent("codebuddy", "CodeBuddy", {
|
|
606
|
+
rootDir: (homeDir) => path8.join(homeDir, ".codebuddy"),
|
|
607
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".codebuddy", "skills"),
|
|
608
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".codebuddy", "skills")
|
|
609
|
+
}),
|
|
610
|
+
"command-code": defineAgent("command-code", "Command Code", {
|
|
611
|
+
rootDir: (homeDir) => path8.join(homeDir, ".commandcode"),
|
|
612
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".commandcode", "skills"),
|
|
613
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".commandcode", "skills")
|
|
614
|
+
}),
|
|
615
|
+
continue: defineAgent("continue", "Continue", {
|
|
616
|
+
rootDir: (homeDir) => path8.join(homeDir, ".continue"),
|
|
617
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".continue", "skills"),
|
|
618
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".continue", "skills")
|
|
619
|
+
}),
|
|
620
|
+
codex: defineAgent("codex", "Codex", {
|
|
621
|
+
rootDir: (homeDir) => path8.join(homeDir, ".codex"),
|
|
622
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".codex", "skills"),
|
|
623
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".codex", "skills")
|
|
624
|
+
}),
|
|
625
|
+
copilot: defineAgent("copilot", "GitHub Copilot", {
|
|
626
|
+
rootDir: (homeDir) => path8.join(homeDir, ".copilot"),
|
|
627
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".copilot", "skills"),
|
|
628
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".copilot", "skills")
|
|
629
|
+
}),
|
|
630
|
+
cortex: defineAgent("cortex", "Cortex Code", {
|
|
631
|
+
rootDir: (homeDir) => path8.join(homeDir, ".snowflake", "cortex"),
|
|
632
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".snowflake", "cortex", "skills"),
|
|
633
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".cortex", "skills")
|
|
634
|
+
}),
|
|
635
|
+
crush: defineAgent("crush", "Crush", {
|
|
636
|
+
rootDir: (homeDir) => path8.join(homeDir, ".config", "crush"),
|
|
637
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".config", "crush", "skills"),
|
|
638
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".config", "crush", "skills")
|
|
639
|
+
}),
|
|
640
|
+
cursor: defineAgent("cursor", "Cursor", {
|
|
641
|
+
rootDir: (homeDir) => path8.join(homeDir, ".cursor"),
|
|
642
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".cursor", "skills"),
|
|
643
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".cursor", "skills")
|
|
644
|
+
}),
|
|
645
|
+
deepagents: defineAgent("deepagents", "Deep Agents", {
|
|
646
|
+
rootDir: (homeDir) => path8.join(homeDir, ".deepagents"),
|
|
647
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".deepagents", "agent", "skills"),
|
|
648
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".deepagents", "agent", "skills")
|
|
649
|
+
}),
|
|
650
|
+
droid: defineAgent("droid", "Droid", {
|
|
651
|
+
rootDir: (homeDir) => path8.join(homeDir, ".factory"),
|
|
652
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".factory", "skills"),
|
|
653
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".factory", "skills")
|
|
654
|
+
}),
|
|
655
|
+
firebender: defineAgent("firebender", "Firebender", {
|
|
656
|
+
rootDir: (homeDir) => path8.join(homeDir, ".firebender"),
|
|
657
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".firebender", "skills"),
|
|
658
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".firebender", "skills")
|
|
659
|
+
}),
|
|
660
|
+
"gemini-cli": defineAgent("gemini-cli", "Gemini CLI", {
|
|
661
|
+
rootDir: (homeDir) => path8.join(homeDir, ".gemini"),
|
|
662
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".gemini", "skills"),
|
|
663
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".gemini", "skills")
|
|
664
|
+
}),
|
|
665
|
+
"github-copilot": defineAgent("github-copilot", "GitHub Copilot", {
|
|
666
|
+
rootDir: (homeDir) => path8.join(homeDir, ".copilot"),
|
|
667
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".copilot", "skills"),
|
|
668
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".copilot", "skills")
|
|
669
|
+
}),
|
|
670
|
+
goose: defineAgent("goose", "Goose", {
|
|
671
|
+
rootDir: (homeDir) => path8.join(homeDir, ".goose"),
|
|
672
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".goose", "skills"),
|
|
673
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".goose", "skills")
|
|
674
|
+
}),
|
|
675
|
+
"iflow-cli": defineAgent("iflow-cli", "iFlow CLI", {
|
|
676
|
+
rootDir: (homeDir) => path8.join(homeDir, ".iflow"),
|
|
677
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".iflow", "skills"),
|
|
678
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".iflow", "skills")
|
|
679
|
+
}),
|
|
680
|
+
junie: defineAgent("junie", "Junie", {
|
|
681
|
+
rootDir: (homeDir) => path8.join(homeDir, ".junie"),
|
|
682
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".junie", "skills"),
|
|
683
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".junie", "skills")
|
|
684
|
+
}),
|
|
685
|
+
kilo: defineAgent("kilo", "Kilo Code", {
|
|
686
|
+
rootDir: (homeDir) => path8.join(homeDir, ".kilocode"),
|
|
687
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".kilocode", "skills"),
|
|
688
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".kilocode", "skills")
|
|
689
|
+
}),
|
|
690
|
+
"kiro-cli": defineAgent("kiro-cli", "Kiro CLI", {
|
|
691
|
+
rootDir: (homeDir) => path8.join(homeDir, ".kiro"),
|
|
692
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".kiro", "skills"),
|
|
693
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".kiro", "skills")
|
|
694
|
+
}),
|
|
695
|
+
"kilo-code": defineAgent("kilo-code", "Kilo Code", {
|
|
696
|
+
rootDir: (homeDir) => path8.join(homeDir, ".kilocode"),
|
|
697
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".kilocode", "skills"),
|
|
698
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".kilocode", "skills")
|
|
699
|
+
}),
|
|
700
|
+
"kimi-cli": defineAgent("kimi-cli", "Kimi Code CLI", {
|
|
701
|
+
rootDir: (homeDir) => path8.join(homeDir, ".kimi"),
|
|
702
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".kimi", "skills"),
|
|
703
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".kimi", "skills")
|
|
704
|
+
}),
|
|
705
|
+
kode: defineAgent("kode", "Kode", {
|
|
706
|
+
rootDir: (homeDir) => path8.join(homeDir, ".kode"),
|
|
707
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".kode", "skills"),
|
|
708
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".kode", "skills")
|
|
709
|
+
}),
|
|
710
|
+
mcpjam: defineAgent("mcpjam", "MCPJam", {
|
|
711
|
+
rootDir: (homeDir) => path8.join(homeDir, ".mcpjam"),
|
|
712
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".mcpjam", "skills"),
|
|
713
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".mcpjam", "skills")
|
|
714
|
+
}),
|
|
715
|
+
"mistral-vibe": defineAgent("mistral-vibe", "Mistral Vibe", {
|
|
716
|
+
rootDir: (homeDir) => path8.join(homeDir, ".vibe"),
|
|
717
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".vibe", "skills"),
|
|
718
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".vibe", "skills")
|
|
719
|
+
}),
|
|
720
|
+
mux: defineAgent("mux", "Mux", {
|
|
721
|
+
rootDir: (homeDir) => path8.join(homeDir, ".mux"),
|
|
722
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".mux", "skills"),
|
|
723
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".mux", "skills")
|
|
724
|
+
}),
|
|
725
|
+
neovate: defineAgent("neovate", "Neovate", {
|
|
726
|
+
rootDir: (homeDir) => path8.join(homeDir, ".neovate"),
|
|
727
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".neovate", "skills"),
|
|
728
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".neovate", "skills")
|
|
729
|
+
}),
|
|
730
|
+
openclaw: defineAgent("openclaw", "OpenClaw", {
|
|
731
|
+
rootDir: (homeDir) => path8.join(homeDir, ".openclaw"),
|
|
732
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".openclaw", "skills"),
|
|
733
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".openclaw", "skills")
|
|
734
|
+
}),
|
|
735
|
+
"openclaude-ide": defineAgent("openclaude-ide", "OpenClaude IDE", {
|
|
736
|
+
rootDir: (homeDir) => path8.join(homeDir, ".openclaude"),
|
|
737
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".openclaude", "skills"),
|
|
738
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".openclaude", "skills")
|
|
739
|
+
}),
|
|
740
|
+
openhands: defineAgent("openhands", "OpenHands", {
|
|
741
|
+
rootDir: (homeDir) => path8.join(homeDir, ".openhands"),
|
|
742
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".openhands", "skills"),
|
|
743
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".openhands", "skills")
|
|
744
|
+
}),
|
|
745
|
+
opencode: defineAgent("opencode", "OpenCode", {
|
|
746
|
+
rootDir: (homeDir) => path8.join(homeDir, ".opencode"),
|
|
747
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".opencode", "skills"),
|
|
748
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".opencode", "skills")
|
|
749
|
+
}),
|
|
750
|
+
pi: defineAgent("pi", "Pi", {
|
|
751
|
+
rootDir: (homeDir) => path8.join(homeDir, ".pi", "agent"),
|
|
752
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".pi", "agent", "skills"),
|
|
753
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".pi", "agent", "skills")
|
|
754
|
+
}),
|
|
755
|
+
pochi: defineAgent("pochi", "Pochi", {
|
|
756
|
+
rootDir: (homeDir) => path8.join(homeDir, ".pochi"),
|
|
757
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".pochi", "skills"),
|
|
758
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".pochi", "skills")
|
|
759
|
+
}),
|
|
760
|
+
qoder: defineAgent("qoder", "Qoder", {
|
|
761
|
+
rootDir: (homeDir) => path8.join(homeDir, ".qoder"),
|
|
762
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".qoder", "skills"),
|
|
763
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".qoder", "skills")
|
|
764
|
+
}),
|
|
765
|
+
"qwen-code": defineAgent("qwen-code", "Qwen Code", {
|
|
766
|
+
rootDir: (homeDir) => path8.join(homeDir, ".qwen"),
|
|
767
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".qwen", "skills"),
|
|
768
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".qwen", "skills")
|
|
769
|
+
}),
|
|
770
|
+
replit: defineAgent("replit", "Replit", {
|
|
771
|
+
rootDir: (homeDir) => path8.join(homeDir, ".config", "replit"),
|
|
772
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".agent", "skills")
|
|
773
|
+
}),
|
|
774
|
+
roo: defineAgent("roo", "Roo Code", {
|
|
775
|
+
rootDir: (homeDir) => path8.join(homeDir, ".roo"),
|
|
776
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".roo", "skills"),
|
|
777
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".roo", "skills")
|
|
778
|
+
}),
|
|
779
|
+
trae: defineAgent("trae", "Trae", {
|
|
780
|
+
rootDir: (homeDir) => path8.join(homeDir, ".trae"),
|
|
781
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".trae", "skills"),
|
|
782
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".trae", "skills")
|
|
783
|
+
}),
|
|
784
|
+
"trae-cn": defineAgent("trae-cn", "Trae CN", {
|
|
785
|
+
rootDir: (homeDir) => path8.join(homeDir, ".trae-cn"),
|
|
786
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".trae-cn", "skills"),
|
|
787
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".trae-cn", "skills")
|
|
788
|
+
}),
|
|
789
|
+
warp: defineAgent("warp", "Warp", {
|
|
790
|
+
rootDir: (homeDir) => path8.join(homeDir, ".warp"),
|
|
791
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".warp", "skills"),
|
|
792
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".warp", "skills")
|
|
793
|
+
}),
|
|
794
|
+
windsurf: defineAgent("windsurf", "Windsurf", {
|
|
795
|
+
rootDir: (homeDir) => path8.join(homeDir, ".codeium", "windsurf"),
|
|
796
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".codeium", "windsurf", "skills"),
|
|
797
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".codeium", "windsurf", "skills")
|
|
798
|
+
}),
|
|
799
|
+
zencoder: defineAgent("zencoder", "Zencoder", {
|
|
800
|
+
rootDir: (homeDir) => path8.join(homeDir, ".zencoder"),
|
|
801
|
+
globalSkillsDir: (homeDir) => path8.join(homeDir, ".zencoder", "skills"),
|
|
802
|
+
projectSkillsDir: (projectDir) => path8.join(projectDir, ".zencoder", "skills")
|
|
803
|
+
})
|
|
632
804
|
};
|
|
633
|
-
async function pathExists6(targetPath) {
|
|
634
|
-
try {
|
|
635
|
-
await access6(targetPath);
|
|
636
|
-
return true;
|
|
637
|
-
} catch {
|
|
638
|
-
return false;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
805
|
function listSupportedAgents() {
|
|
642
|
-
return Object.values(AGENTS);
|
|
806
|
+
return Object.values(AGENTS).sort((left, right) => left.id.localeCompare(right.id));
|
|
643
807
|
}
|
|
644
808
|
function listSupportedAgentIds() {
|
|
645
|
-
return listSupportedAgents().map((agent) => agent.id)
|
|
809
|
+
return listSupportedAgents().map((agent) => agent.id);
|
|
810
|
+
}
|
|
811
|
+
function supportsScope(agentId, scope) {
|
|
812
|
+
const definition = getAgentDefinition(agentId);
|
|
813
|
+
return scope === "global" ? definition.supportsGlobal : definition.supportsProject;
|
|
646
814
|
}
|
|
647
815
|
function isAgentId(value) {
|
|
648
816
|
return value in AGENTS;
|
|
@@ -652,7 +820,11 @@ function getAgentDefinition(agentId) {
|
|
|
652
820
|
}
|
|
653
821
|
function resolveAgentSkillsDir(agentId, scope, baseDir) {
|
|
654
822
|
const definition = getAgentDefinition(agentId);
|
|
655
|
-
|
|
823
|
+
const resolver = scope === "global" ? definition.globalSkillsDir : definition.projectSkillsDir;
|
|
824
|
+
if (!resolver) {
|
|
825
|
+
throw new Error(`Agent ${agentId} does not support ${scope} scope.`);
|
|
826
|
+
}
|
|
827
|
+
return resolver(baseDir);
|
|
656
828
|
}
|
|
657
829
|
function getProjectionMode(agentId) {
|
|
658
830
|
return getAgentDefinition(agentId).defaultProjectionMode;
|
|
@@ -660,23 +832,23 @@ function getProjectionMode(agentId) {
|
|
|
660
832
|
async function detectInstalledAgents(options) {
|
|
661
833
|
const installed = [];
|
|
662
834
|
for (const agent of listSupportedAgents()) {
|
|
663
|
-
const globalRootPath = agent.rootDir(options.homeDir);
|
|
664
|
-
const projectPath = options.projectDir ? agent.
|
|
665
|
-
if (await
|
|
835
|
+
const globalRootPath = agent.supportsGlobal ? agent.rootDir(options.homeDir) : null;
|
|
836
|
+
const projectPath = agent.supportsProject && options.projectDir ? resolveAgentSkillsDir(agent.id, "project", options.projectDir) : null;
|
|
837
|
+
if (globalRootPath && await pathExists(globalRootPath)) {
|
|
666
838
|
installed.push(agent.id);
|
|
667
839
|
continue;
|
|
668
840
|
}
|
|
669
|
-
if (projectPath && await
|
|
841
|
+
if (projectPath && await pathExists(projectPath)) {
|
|
670
842
|
installed.push(agent.id);
|
|
671
843
|
}
|
|
672
844
|
}
|
|
673
|
-
return installed
|
|
845
|
+
return installed;
|
|
674
846
|
}
|
|
675
847
|
|
|
676
848
|
// src/lib/scanner.ts
|
|
677
849
|
async function hasSkillReadme(skillDir) {
|
|
678
850
|
try {
|
|
679
|
-
await
|
|
851
|
+
await access2(path9.join(skillDir, "SKILL.md"));
|
|
680
852
|
return true;
|
|
681
853
|
} catch {
|
|
682
854
|
return false;
|
|
@@ -689,21 +861,13 @@ async function isSymlinkPath(targetPath) {
|
|
|
689
861
|
return false;
|
|
690
862
|
}
|
|
691
863
|
}
|
|
692
|
-
async function pathExists7(targetPath) {
|
|
693
|
-
try {
|
|
694
|
-
await access7(targetPath);
|
|
695
|
-
return true;
|
|
696
|
-
} catch {
|
|
697
|
-
return false;
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
864
|
async function resolveSymlinkSource(targetPath) {
|
|
701
865
|
try {
|
|
702
866
|
const linkTarget = await readlink2(targetPath);
|
|
703
|
-
const sourcePath =
|
|
867
|
+
const sourcePath = path9.resolve(path9.dirname(targetPath), linkTarget);
|
|
704
868
|
return {
|
|
705
869
|
sourcePath,
|
|
706
|
-
isBroken: !await
|
|
870
|
+
isBroken: !await pathExists(sourcePath)
|
|
707
871
|
};
|
|
708
872
|
} catch {
|
|
709
873
|
return {
|
|
@@ -719,7 +883,7 @@ async function scanDirectory(baseDir, agentId, scope, projectDir) {
|
|
|
719
883
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
720
884
|
continue;
|
|
721
885
|
}
|
|
722
|
-
const fullPath =
|
|
886
|
+
const fullPath = path9.join(baseDir, entry.name);
|
|
723
887
|
const isSymlink = await isSymlinkPath(fullPath);
|
|
724
888
|
const symlinkInfo = isSymlink ? await resolveSymlinkSource(fullPath) : { isBroken: false };
|
|
725
889
|
if (!await hasSkillReadme(fullPath) && !symlinkInfo.isBroken) {
|
|
@@ -744,9 +908,13 @@ async function scanDirectory(baseDir, agentId, scope, projectDir) {
|
|
|
744
908
|
async function scanSkills(options) {
|
|
745
909
|
const results = [];
|
|
746
910
|
for (const agent of listSupportedAgents()) {
|
|
747
|
-
|
|
911
|
+
if (supportsScope(agent.id, "global")) {
|
|
912
|
+
results.push(...await scanDirectory(agent.globalSkillsDir(options.homeDir), agent.id, "global"));
|
|
913
|
+
}
|
|
748
914
|
for (const projectDir of options.projectDirs ?? []) {
|
|
749
|
-
|
|
915
|
+
if (supportsScope(agent.id, "project")) {
|
|
916
|
+
results.push(...await scanDirectory(agent.projectSkillsDir(projectDir), agent.id, "project", projectDir));
|
|
917
|
+
}
|
|
750
918
|
}
|
|
751
919
|
}
|
|
752
920
|
return results.sort((left, right) => left.path.localeCompare(right.path));
|
|
@@ -826,7 +994,7 @@ async function runImport(context, options) {
|
|
|
826
994
|
|
|
827
995
|
// src/lib/symlink.ts
|
|
828
996
|
import { cp as cp2, lstat as lstat3, mkdir as mkdir5, readFile as readFile2, readdir as readdir5, readlink as readlink3, rm as rm3, symlink, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
829
|
-
import
|
|
997
|
+
import path10 from "path";
|
|
830
998
|
var COPY_MARKER = ".aweskill-projection.json";
|
|
831
999
|
async function tryLstat(targetPath) {
|
|
832
1000
|
try {
|
|
@@ -837,7 +1005,7 @@ async function tryLstat(targetPath) {
|
|
|
837
1005
|
}
|
|
838
1006
|
async function readCopyMarker(targetPath) {
|
|
839
1007
|
try {
|
|
840
|
-
const content = await readFile2(
|
|
1008
|
+
const content = await readFile2(path10.join(targetPath, COPY_MARKER), "utf8");
|
|
841
1009
|
const parsed = JSON.parse(content);
|
|
842
1010
|
return parsed.managedBy === "aweskill" ? parsed : null;
|
|
843
1011
|
} catch {
|
|
@@ -857,8 +1025,8 @@ async function assertProjectionTargetSafe(mode, sourcePath, targetPath, options
|
|
|
857
1025
|
throw new Error(`Refusing to overwrite non-symlink target: ${targetPath}`);
|
|
858
1026
|
}
|
|
859
1027
|
const currentTarget = await readlink3(targetPath);
|
|
860
|
-
const resolvedCurrent =
|
|
861
|
-
if (resolvedCurrent ===
|
|
1028
|
+
const resolvedCurrent = path10.resolve(path10.dirname(targetPath), currentTarget);
|
|
1029
|
+
if (resolvedCurrent === path10.resolve(sourcePath)) {
|
|
862
1030
|
return;
|
|
863
1031
|
}
|
|
864
1032
|
return;
|
|
@@ -875,12 +1043,12 @@ async function assertProjectionTargetSafe(mode, sourcePath, targetPath, options
|
|
|
875
1043
|
}
|
|
876
1044
|
}
|
|
877
1045
|
async function createSkillSymlink(sourcePath, targetPath, options = {}) {
|
|
878
|
-
await mkdir5(
|
|
1046
|
+
await mkdir5(path10.dirname(targetPath), { recursive: true });
|
|
879
1047
|
const existing = await tryLstat(targetPath);
|
|
880
1048
|
if (existing?.isSymbolicLink()) {
|
|
881
1049
|
const currentTarget = await readlink3(targetPath);
|
|
882
|
-
const resolvedCurrent =
|
|
883
|
-
if (resolvedCurrent ===
|
|
1050
|
+
const resolvedCurrent = path10.resolve(path10.dirname(targetPath), currentTarget);
|
|
1051
|
+
if (resolvedCurrent === path10.resolve(sourcePath)) {
|
|
884
1052
|
return "skipped";
|
|
885
1053
|
}
|
|
886
1054
|
await unlink(targetPath);
|
|
@@ -891,12 +1059,12 @@ async function createSkillSymlink(sourcePath, targetPath, options = {}) {
|
|
|
891
1059
|
throw new Error(`Refusing to overwrite non-symlink target: ${targetPath}`);
|
|
892
1060
|
}
|
|
893
1061
|
}
|
|
894
|
-
const linkTarget =
|
|
1062
|
+
const linkTarget = path10.relative(path10.dirname(targetPath), sourcePath) || ".";
|
|
895
1063
|
await symlink(linkTarget, targetPath, "dir");
|
|
896
1064
|
return "created";
|
|
897
1065
|
}
|
|
898
1066
|
async function createSkillCopy(sourcePath, targetPath, options = {}) {
|
|
899
|
-
await mkdir5(
|
|
1067
|
+
await mkdir5(path10.dirname(targetPath), { recursive: true });
|
|
900
1068
|
const existing = await tryLstat(targetPath);
|
|
901
1069
|
if (existing?.isSymbolicLink()) {
|
|
902
1070
|
await unlink(targetPath);
|
|
@@ -910,8 +1078,8 @@ async function createSkillCopy(sourcePath, targetPath, options = {}) {
|
|
|
910
1078
|
await rm3(targetPath, { force: true, recursive: true });
|
|
911
1079
|
}
|
|
912
1080
|
await cp2(sourcePath, targetPath, { recursive: true });
|
|
913
|
-
const marker = { managedBy: "aweskill", sourcePath:
|
|
914
|
-
await writeFile2(
|
|
1081
|
+
const marker = { managedBy: "aweskill", sourcePath: path10.resolve(sourcePath) };
|
|
1082
|
+
await writeFile2(path10.join(targetPath, COPY_MARKER), JSON.stringify(marker, null, 2), "utf8");
|
|
915
1083
|
return "created";
|
|
916
1084
|
}
|
|
917
1085
|
async function removeManagedProjection(targetPath) {
|
|
@@ -937,12 +1105,12 @@ async function listManagedSkillNames(skillsDir, centralSkillsDir) {
|
|
|
937
1105
|
try {
|
|
938
1106
|
const entries = await readdir5(skillsDir, { withFileTypes: true });
|
|
939
1107
|
for (const entry of entries) {
|
|
940
|
-
const targetPath =
|
|
1108
|
+
const targetPath = path10.join(skillsDir, entry.name);
|
|
941
1109
|
if (entry.isSymbolicLink()) {
|
|
942
1110
|
try {
|
|
943
1111
|
const currentTarget = await readlink3(targetPath);
|
|
944
|
-
const resolvedCurrent =
|
|
945
|
-
if (resolvedCurrent.startsWith(
|
|
1112
|
+
const resolvedCurrent = path10.resolve(path10.dirname(targetPath), currentTarget);
|
|
1113
|
+
if (resolvedCurrent.startsWith(path10.resolve(centralSkillsDir))) {
|
|
946
1114
|
result.set(entry.name, "symlink");
|
|
947
1115
|
}
|
|
948
1116
|
} catch {
|
|
@@ -974,13 +1142,17 @@ async function resolveAgentsForScope(context, requestedAgents, scope, projectDir
|
|
|
974
1142
|
homeDir: context.homeDir,
|
|
975
1143
|
projectDir: scope === "project" ? projectDir : void 0
|
|
976
1144
|
});
|
|
977
|
-
|
|
1145
|
+
const candidates = detected.length > 0 ? detected : listSupportedAgentIds();
|
|
1146
|
+
return candidates.filter((agentId) => supportsScope(agentId, scope));
|
|
978
1147
|
}
|
|
979
1148
|
return uniqueSorted(
|
|
980
1149
|
requestedAgents.map((agent) => {
|
|
981
1150
|
if (!isAgentId(agent)) {
|
|
982
1151
|
throw new Error(`Unsupported agent: ${agent}`);
|
|
983
1152
|
}
|
|
1153
|
+
if (!supportsScope(agent, scope)) {
|
|
1154
|
+
throw new Error(`Agent ${agent} does not support ${scope} scope.`);
|
|
1155
|
+
}
|
|
984
1156
|
return agent;
|
|
985
1157
|
})
|
|
986
1158
|
);
|
|
@@ -1089,13 +1261,17 @@ async function resolveAgentsForScope2(context, requestedAgents, scope, projectDi
|
|
|
1089
1261
|
homeDir: context.homeDir,
|
|
1090
1262
|
projectDir: scope === "project" ? projectDir : void 0
|
|
1091
1263
|
});
|
|
1092
|
-
|
|
1264
|
+
const candidates = detected.length > 0 ? detected : listSupportedAgentIds();
|
|
1265
|
+
return candidates.filter((agentId) => supportsScope(agentId, scope));
|
|
1093
1266
|
}
|
|
1094
1267
|
return uniqueSorted(
|
|
1095
1268
|
requestedAgents.map((agent) => {
|
|
1096
1269
|
if (!isAgentId(agent)) {
|
|
1097
1270
|
throw new Error(`Unsupported agent: ${agent}`);
|
|
1098
1271
|
}
|
|
1272
|
+
if (!supportsScope(agent, scope)) {
|
|
1273
|
+
throw new Error(`Agent ${agent} does not support ${scope} scope.`);
|
|
1274
|
+
}
|
|
1099
1275
|
return agent;
|
|
1100
1276
|
})
|
|
1101
1277
|
);
|
|
@@ -1112,7 +1288,7 @@ async function resolveSkillNames(context, type, names) {
|
|
|
1112
1288
|
homeDir: context.homeDir
|
|
1113
1289
|
});
|
|
1114
1290
|
const globalManaged = await Promise.all(
|
|
1115
|
-
detected.map(async (agentId) => {
|
|
1291
|
+
detected.filter((agentId) => supportsScope(agentId, "global")).map(async (agentId) => {
|
|
1116
1292
|
const agentSkillsDir = resolveAgentSkillsDir(agentId, "global", context.homeDir);
|
|
1117
1293
|
return listManagedSkillNames(agentSkillsDir, centralSkillsDir);
|
|
1118
1294
|
})
|
|
@@ -1220,13 +1396,17 @@ async function resolveAgentsForScope3(context, requestedAgents, scope, projectDi
|
|
|
1220
1396
|
homeDir: context.homeDir,
|
|
1221
1397
|
projectDir: scope === "project" ? projectDir : void 0
|
|
1222
1398
|
});
|
|
1223
|
-
|
|
1399
|
+
const candidates = detected.length > 0 ? detected : listSupportedAgentIds();
|
|
1400
|
+
return candidates.filter((agentId) => supportsScope(agentId, scope));
|
|
1224
1401
|
}
|
|
1225
1402
|
return uniqueSorted(
|
|
1226
1403
|
requestedAgents.map((agent) => {
|
|
1227
1404
|
if (!isAgentId(agent)) {
|
|
1228
1405
|
throw new Error(`Unsupported agent: ${agent}`);
|
|
1229
1406
|
}
|
|
1407
|
+
if (!supportsScope(agent, scope)) {
|
|
1408
|
+
throw new Error(`Agent ${agent} does not support ${scope} scope.`);
|
|
1409
|
+
}
|
|
1230
1410
|
return agent;
|
|
1231
1411
|
})
|
|
1232
1412
|
);
|
|
@@ -1421,7 +1601,7 @@ async function runListTemplateBundles(context, options = {}) {
|
|
|
1421
1601
|
|
|
1422
1602
|
// src/commands/recover.ts
|
|
1423
1603
|
import { cp as cp3, mkdir as mkdir7, readlink as readlink4, unlink as unlink2 } from "fs/promises";
|
|
1424
|
-
import
|
|
1604
|
+
import path11 from "path";
|
|
1425
1605
|
function getProjectDir4(context, explicitProjectDir) {
|
|
1426
1606
|
return explicitProjectDir ?? context.cwd;
|
|
1427
1607
|
}
|
|
@@ -1431,13 +1611,17 @@ async function resolveAgentsForScope4(context, requestedAgents, scope, projectDi
|
|
|
1431
1611
|
homeDir: context.homeDir,
|
|
1432
1612
|
projectDir: scope === "project" ? projectDir : void 0
|
|
1433
1613
|
});
|
|
1434
|
-
|
|
1614
|
+
const candidates = detected.length > 0 ? detected : listSupportedAgentIds();
|
|
1615
|
+
return candidates.filter((agentId) => supportsScope(agentId, scope));
|
|
1435
1616
|
}
|
|
1436
1617
|
return uniqueSorted(
|
|
1437
1618
|
requestedAgents.map((agent) => {
|
|
1438
1619
|
if (!isAgentId(agent)) {
|
|
1439
1620
|
throw new Error(`Unsupported agent: ${agent}`);
|
|
1440
1621
|
}
|
|
1622
|
+
if (!supportsScope(agent, scope)) {
|
|
1623
|
+
throw new Error(`Agent ${agent} does not support ${scope} scope.`);
|
|
1624
|
+
}
|
|
1441
1625
|
return agent;
|
|
1442
1626
|
})
|
|
1443
1627
|
);
|
|
@@ -1455,15 +1639,15 @@ async function runRecover(context, options) {
|
|
|
1455
1639
|
if (mode !== "symlink") {
|
|
1456
1640
|
continue;
|
|
1457
1641
|
}
|
|
1458
|
-
const targetPath =
|
|
1459
|
-
const sourcePath =
|
|
1642
|
+
const targetPath = path11.join(agentSkillsDir, skillName);
|
|
1643
|
+
const sourcePath = path11.join(centralSkillsDir, skillName);
|
|
1460
1644
|
const currentTarget = await readlink4(targetPath);
|
|
1461
|
-
const resolvedCurrent =
|
|
1462
|
-
if (resolvedCurrent !==
|
|
1645
|
+
const resolvedCurrent = path11.resolve(path11.dirname(targetPath), currentTarget);
|
|
1646
|
+
if (resolvedCurrent !== path11.resolve(sourcePath)) {
|
|
1463
1647
|
continue;
|
|
1464
1648
|
}
|
|
1465
1649
|
await unlink2(targetPath);
|
|
1466
|
-
await mkdir7(
|
|
1650
|
+
await mkdir7(path11.dirname(targetPath), { recursive: true });
|
|
1467
1651
|
await cp3(sourcePath, targetPath, { recursive: true });
|
|
1468
1652
|
recovered.push(`${agentId}:${skillName}`);
|
|
1469
1653
|
}
|
|
@@ -1475,7 +1659,7 @@ async function runRecover(context, options) {
|
|
|
1475
1659
|
|
|
1476
1660
|
// src/lib/references.ts
|
|
1477
1661
|
import { rm as rm4 } from "fs/promises";
|
|
1478
|
-
import
|
|
1662
|
+
import path12 from "path";
|
|
1479
1663
|
async function findSkillReferences(options) {
|
|
1480
1664
|
const normalizedSkill = sanitizeName(options.skillName);
|
|
1481
1665
|
const { skillsDir } = getAweskillPaths(options.homeDir);
|
|
@@ -1489,6 +1673,9 @@ async function findSkillReferences(options) {
|
|
|
1489
1673
|
}
|
|
1490
1674
|
for (const { scope, dir } of baseDirs) {
|
|
1491
1675
|
for (const agent of listSupportedAgents()) {
|
|
1676
|
+
if (!supportsScope(agent.id, scope)) {
|
|
1677
|
+
continue;
|
|
1678
|
+
}
|
|
1492
1679
|
const agentSkillsDir = resolveAgentSkillsDir(agent.id, scope, dir);
|
|
1493
1680
|
const managed = await listManagedSkillNames(agentSkillsDir, skillsDir);
|
|
1494
1681
|
if (managed.has(normalizedSkill)) {
|
|
@@ -1517,10 +1704,13 @@ async function removeSkillWithReferences(options) {
|
|
|
1517
1704
|
}
|
|
1518
1705
|
for (const { scope, dir } of baseDirs) {
|
|
1519
1706
|
for (const agent of listSupportedAgents()) {
|
|
1707
|
+
if (!supportsScope(agent.id, scope)) {
|
|
1708
|
+
continue;
|
|
1709
|
+
}
|
|
1520
1710
|
const agentSkillsDir = resolveAgentSkillsDir(agent.id, scope, dir);
|
|
1521
1711
|
const managed = await listManagedSkillNames(agentSkillsDir, skillsDir);
|
|
1522
1712
|
if (managed.has(normalizedSkill)) {
|
|
1523
|
-
await removeManagedProjection(
|
|
1713
|
+
await removeManagedProjection(path12.join(agentSkillsDir, normalizedSkill));
|
|
1524
1714
|
}
|
|
1525
1715
|
}
|
|
1526
1716
|
}
|
|
@@ -1555,34 +1745,58 @@ async function runRemove(context, options) {
|
|
|
1555
1745
|
|
|
1556
1746
|
// src/commands/restore.ts
|
|
1557
1747
|
import { cp as cp4, mkdir as mkdir8, rm as rm5 } from "fs/promises";
|
|
1558
|
-
import
|
|
1748
|
+
import path13 from "path";
|
|
1559
1749
|
async function runRestore(context, options) {
|
|
1560
|
-
const
|
|
1750
|
+
const includeBundles = options.includeBundles ?? false;
|
|
1751
|
+
const { tempDir, extractedSkillsDir, extractedBundlesDir } = await extractSkillsArchive(options.archivePath);
|
|
1561
1752
|
try {
|
|
1562
1753
|
const extracted = await listSkillEntriesInDirectory(extractedSkillsDir);
|
|
1563
1754
|
if (extracted.length === 0) {
|
|
1564
1755
|
throw new Error(`Archive does not contain a skills/ directory: ${options.archivePath}`);
|
|
1565
1756
|
}
|
|
1566
|
-
const { skillsDir } = getAweskillPaths(context.homeDir);
|
|
1757
|
+
const { skillsDir, bundlesDir } = getAweskillPaths(context.homeDir);
|
|
1567
1758
|
const current = await listSkillEntriesInDirectory(skillsDir);
|
|
1568
1759
|
const currentNames = new Set(current.map((entry) => entry.name));
|
|
1569
1760
|
const conflicts = extracted.map((entry) => entry.name).filter((name) => currentNames.has(name));
|
|
1570
|
-
|
|
1571
|
-
|
|
1761
|
+
const extractedBundles = includeBundles ? await listBundlesInDirectory(extractedBundlesDir) : [];
|
|
1762
|
+
const currentBundles = includeBundles ? await listBundlesInDirectory(bundlesDir) : [];
|
|
1763
|
+
const currentBundleNames = new Set(currentBundles.map((bundle) => bundle.name));
|
|
1764
|
+
const bundleConflicts = extractedBundles.map((bundle) => bundle.name).filter((name) => currentBundleNames.has(name));
|
|
1765
|
+
if ((conflicts.length > 0 || bundleConflicts.length > 0) && !options.override) {
|
|
1766
|
+
const conflictMessages = [];
|
|
1767
|
+
if (conflicts.length > 0) {
|
|
1768
|
+
conflictMessages.push(`skills: ${conflicts.join(", ")}`);
|
|
1769
|
+
}
|
|
1770
|
+
if (bundleConflicts.length > 0) {
|
|
1771
|
+
conflictMessages.push(`bundles: ${bundleConflicts.join(", ")}`);
|
|
1772
|
+
}
|
|
1773
|
+
throw new Error(`Restore would overwrite existing ${conflictMessages.join("; ")}. Use --override to replace them.`);
|
|
1572
1774
|
}
|
|
1573
|
-
const backupArchivePath = await createSkillsBackupArchive(context.homeDir);
|
|
1775
|
+
const backupArchivePath = await createSkillsBackupArchive(context.homeDir, { includeBundles });
|
|
1574
1776
|
if (options.override) {
|
|
1575
1777
|
await rm5(skillsDir, { recursive: true, force: true });
|
|
1576
|
-
await mkdir8(
|
|
1778
|
+
await mkdir8(path13.dirname(skillsDir), { recursive: true });
|
|
1577
1779
|
await cp4(extractedSkillsDir, skillsDir, { recursive: true });
|
|
1780
|
+
if (includeBundles) {
|
|
1781
|
+
await rm5(bundlesDir, { recursive: true, force: true });
|
|
1782
|
+
await mkdir8(path13.dirname(bundlesDir), { recursive: true });
|
|
1783
|
+
await cp4(extractedBundlesDir, bundlesDir, { recursive: true });
|
|
1784
|
+
}
|
|
1578
1785
|
} else {
|
|
1579
1786
|
await mkdir8(skillsDir, { recursive: true });
|
|
1580
1787
|
for (const entry of extracted) {
|
|
1581
|
-
await cp4(entry.path,
|
|
1788
|
+
await cp4(entry.path, path13.join(skillsDir, entry.name), { recursive: true });
|
|
1789
|
+
}
|
|
1790
|
+
if (includeBundles) {
|
|
1791
|
+
await mkdir8(bundlesDir, { recursive: true });
|
|
1792
|
+
for (const bundle of extractedBundles) {
|
|
1793
|
+
await cp4(path13.join(extractedBundlesDir, `${bundle.name}.yaml`), path13.join(bundlesDir, `${bundle.name}.yaml`), { recursive: true });
|
|
1794
|
+
}
|
|
1582
1795
|
}
|
|
1583
1796
|
}
|
|
1584
|
-
|
|
1585
|
-
context.write(
|
|
1797
|
+
const restoredLabel = includeBundles ? `Restored ${extracted.length} skills and ${extractedBundles.length} bundles from ${options.archivePath}` : `Restored ${extracted.length} skills from ${options.archivePath}`;
|
|
1798
|
+
context.write(restoredLabel);
|
|
1799
|
+
context.write(`Backed up current ${formatBackupLabel(includeBundles)} to ${backupArchivePath}`);
|
|
1586
1800
|
return { restored: extracted.map((entry) => entry.name), backupArchivePath };
|
|
1587
1801
|
} finally {
|
|
1588
1802
|
await rm5(tempDir, { recursive: true, force: true });
|
|
@@ -1591,7 +1805,7 @@ async function runRestore(context, options) {
|
|
|
1591
1805
|
|
|
1592
1806
|
// src/lib/rmdup.ts
|
|
1593
1807
|
import { mkdir as mkdir9, readdir as readdir7, rename as rename2, rm as rm6 } from "fs/promises";
|
|
1594
|
-
import
|
|
1808
|
+
import path14 from "path";
|
|
1595
1809
|
var NUMERIC_SUFFIX_PATTERN = /^(.*?)-(\d+(?:\.\d+)*)$/;
|
|
1596
1810
|
function parseSkillName(name) {
|
|
1597
1811
|
const match = name.match(NUMERIC_SUFFIX_PATTERN);
|
|
@@ -1681,13 +1895,13 @@ async function removeDuplicateSkills(homeDir, duplicates, options = {}) {
|
|
|
1681
1895
|
async function nextAvailableDupPath(dupSkillsDir, skillName) {
|
|
1682
1896
|
const entries = new Set(await readdir7(dupSkillsDir).catch(() => []));
|
|
1683
1897
|
if (!entries.has(skillName)) {
|
|
1684
|
-
return
|
|
1898
|
+
return path14.join(dupSkillsDir, skillName);
|
|
1685
1899
|
}
|
|
1686
1900
|
let index = 1;
|
|
1687
1901
|
while (entries.has(`${skillName}-${index}`)) {
|
|
1688
1902
|
index += 1;
|
|
1689
1903
|
}
|
|
1690
|
-
return
|
|
1904
|
+
return path14.join(dupSkillsDir, `${skillName}-${index}`);
|
|
1691
1905
|
}
|
|
1692
1906
|
|
|
1693
1907
|
// src/commands/rmdup.ts
|
|
@@ -1729,16 +1943,7 @@ async function runRmdup(context, options) {
|
|
|
1729
1943
|
}
|
|
1730
1944
|
|
|
1731
1945
|
// src/commands/sync.ts
|
|
1732
|
-
import
|
|
1733
|
-
import path14 from "path";
|
|
1734
|
-
async function pathExists8(targetPath) {
|
|
1735
|
-
try {
|
|
1736
|
-
await access8(targetPath);
|
|
1737
|
-
return true;
|
|
1738
|
-
} catch {
|
|
1739
|
-
return false;
|
|
1740
|
-
}
|
|
1741
|
-
}
|
|
1946
|
+
import path15 from "path";
|
|
1742
1947
|
async function runSync(context, options) {
|
|
1743
1948
|
const { skillsDir } = getAweskillPaths(context.homeDir);
|
|
1744
1949
|
const baseDirs = /* @__PURE__ */ new Set();
|
|
@@ -1746,19 +1951,22 @@ async function runSync(context, options) {
|
|
|
1746
1951
|
if (options.projectDir) {
|
|
1747
1952
|
baseDirs.add({ scope: "project", dir: options.projectDir });
|
|
1748
1953
|
}
|
|
1749
|
-
if (await
|
|
1954
|
+
if (await pathExists(path15.join(context.cwd, ".aweskill.yaml"))) {
|
|
1750
1955
|
baseDirs.add({ scope: "project", dir: context.cwd });
|
|
1751
1956
|
}
|
|
1752
1957
|
let removed = 0;
|
|
1753
1958
|
const warnings = [];
|
|
1754
1959
|
for (const { scope, dir } of baseDirs) {
|
|
1755
1960
|
for (const agent of listSupportedAgents()) {
|
|
1961
|
+
if (!supportsScope(agent.id, scope)) {
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1756
1964
|
const skillsDir2 = resolveAgentSkillsDir(agent.id, scope, dir);
|
|
1757
1965
|
const managed = await listManagedSkillNames(skillsDir2, skillsDir);
|
|
1758
1966
|
for (const [skillName] of managed) {
|
|
1759
|
-
const sourcePath =
|
|
1760
|
-
if (!await
|
|
1761
|
-
const wasRemoved = await removeManagedProjection(
|
|
1967
|
+
const sourcePath = path15.join(skillsDir, skillName);
|
|
1968
|
+
if (!await pathExists(sourcePath)) {
|
|
1969
|
+
const wasRemoved = await removeManagedProjection(path15.join(skillsDir2, skillName));
|
|
1762
1970
|
if (wasRemoved) {
|
|
1763
1971
|
removed += 1;
|
|
1764
1972
|
warnings.push(`Removed stale projection: ${agent.id}:${skillName} (source missing)`);
|
|
@@ -1774,16 +1982,39 @@ async function runSync(context, options) {
|
|
|
1774
1982
|
return { removed, warnings };
|
|
1775
1983
|
}
|
|
1776
1984
|
|
|
1985
|
+
// src/lib/version.ts
|
|
1986
|
+
import { existsSync, readFileSync } from "fs";
|
|
1987
|
+
import path16 from "path";
|
|
1988
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1989
|
+
function readVersionFromPackageJson(packageJsonPath) {
|
|
1990
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
1991
|
+
if (!packageJson.version) {
|
|
1992
|
+
throw new Error(`Missing version in ${packageJsonPath}`);
|
|
1993
|
+
}
|
|
1994
|
+
return packageJson.version;
|
|
1995
|
+
}
|
|
1996
|
+
function resolveVersionForModuleUrl(moduleUrl) {
|
|
1997
|
+
const moduleDir = path16.dirname(fileURLToPath2(moduleUrl));
|
|
1998
|
+
for (const relativePath of ["../package.json", "../../package.json"]) {
|
|
1999
|
+
const packageJsonPath = path16.resolve(moduleDir, relativePath);
|
|
2000
|
+
if (existsSync(packageJsonPath)) {
|
|
2001
|
+
return readVersionFromPackageJson(packageJsonPath);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
throw new Error(`Cannot find package.json for module ${moduleUrl}`);
|
|
2005
|
+
}
|
|
2006
|
+
var AWESKILL_VERSION = resolveVersionForModuleUrl(import.meta.url);
|
|
2007
|
+
|
|
1777
2008
|
// src/lib/runtime.ts
|
|
1778
2009
|
import { realpathSync } from "fs";
|
|
1779
|
-
import { fileURLToPath as
|
|
2010
|
+
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "url";
|
|
1780
2011
|
function isDirectCliEntry(importMetaUrl, argv1) {
|
|
1781
2012
|
if (!argv1) {
|
|
1782
2013
|
return false;
|
|
1783
2014
|
}
|
|
1784
2015
|
try {
|
|
1785
2016
|
const executedPath = realpathSync(argv1);
|
|
1786
|
-
const modulePath = realpathSync(
|
|
2017
|
+
const modulePath = realpathSync(fileURLToPath3(importMetaUrl));
|
|
1787
2018
|
return pathToFileURL(modulePath).href === pathToFileURL(executedPath).href;
|
|
1788
2019
|
} catch {
|
|
1789
2020
|
return importMetaUrl === pathToFileURL(argv1).href;
|
|
@@ -1933,10 +2164,10 @@ function formatCliErrorMessage(message) {
|
|
|
1933
2164
|
return 'Option --agent <agent> argument missing. Use one or more supported agent ids, for example "codex" or "codex,cursor". Run "aweskill agent supported" to see the supported agent list.';
|
|
1934
2165
|
}
|
|
1935
2166
|
const normalizedMessage = message.replace(/^error:\s*/i, "");
|
|
1936
|
-
const bundleFileMatch = normalizedMessage.match(/ENOENT: no such file or directory, open '([^']+\/bundles\/([^/'"]+)\.ya?ml)'/i);
|
|
2167
|
+
const bundleFileMatch = normalizedMessage.match(/ENOENT: no such file or directory, open '([^']+\/(bundles|resources\/bundle_templates)\/([^/'"]+)\.ya?ml)'/i);
|
|
1937
2168
|
if (bundleFileMatch) {
|
|
1938
|
-
const bundleName = bundleFileMatch[
|
|
1939
|
-
if (bundleFileMatch[
|
|
2169
|
+
const bundleName = bundleFileMatch[3];
|
|
2170
|
+
if (bundleFileMatch[2] === "resources/bundle_templates") {
|
|
1940
2171
|
return `Bundle template not found: ${bundleName}. Run "aweskill bundle template list" to see available bundle templates.`;
|
|
1941
2172
|
}
|
|
1942
2173
|
return `Bundle not found: ${bundleName}. Run "aweskill bundle list" to see available bundles.`;
|
|
@@ -1965,16 +2196,7 @@ function formatCliErrorMessage(message) {
|
|
|
1965
2196
|
function writeSupportedAgents(context) {
|
|
1966
2197
|
const lines = [
|
|
1967
2198
|
"Supported agents:",
|
|
1968
|
-
|
|
1969
|
-
"claude-code (Claude Code)",
|
|
1970
|
-
"cline (Cline)",
|
|
1971
|
-
"codex (Codex)",
|
|
1972
|
-
"cursor (Cursor)",
|
|
1973
|
-
"gemini-cli (Gemini CLI)",
|
|
1974
|
-
"goose (Goose)",
|
|
1975
|
-
"opencode (OpenCode)",
|
|
1976
|
-
"roo (Roo Code)",
|
|
1977
|
-
"windsurf (Windsurf)"
|
|
2199
|
+
...listSupportedAgents().map((agent) => `${agent.id} (${agent.displayName})`)
|
|
1978
2200
|
];
|
|
1979
2201
|
for (const line of lines) {
|
|
1980
2202
|
context.write(line);
|
|
@@ -1992,7 +2214,7 @@ function configureCommandTree(command) {
|
|
|
1992
2214
|
function createProgram(overrides = {}) {
|
|
1993
2215
|
const context = createRuntimeContext(overrides);
|
|
1994
2216
|
const program = new Command();
|
|
1995
|
-
program.name("aweskill").description("Local skill orchestration CLI for AI agents").version(
|
|
2217
|
+
program.name("aweskill").description("Local skill orchestration CLI for AI agents").version(AWESKILL_VERSION).helpOption("-h, --help", "Display help");
|
|
1996
2218
|
const skill = program.command("skill").description("Manage skills in the central store");
|
|
1997
2219
|
skill.command("list").description("List skills in the central store").option("--verbose", "show all skills instead of a short preview", false).action(async (options) => {
|
|
1998
2220
|
await runListSkills(context, { verbose: options.verbose });
|
|
@@ -2126,15 +2348,22 @@ function createProgram(overrides = {}) {
|
|
|
2126
2348
|
store.command("init").description("Create ~/.aweskill layout").option("--scan", "scan existing agent directories after init", false).option("--verbose", "show scanned skill details instead of per-agent totals", false).action(async (options) => {
|
|
2127
2349
|
await runFramedCommand(" aweskill store init ", async () => runInit(context, options));
|
|
2128
2350
|
});
|
|
2129
|
-
store.command("backup").description("Create a timestamped archive of the central skills repository").action(async () => {
|
|
2130
|
-
await runFramedCommand(
|
|
2351
|
+
store.command("backup").argument("[archive]").description("Create a timestamped archive of the central skills repository").option("--both", "include bundle definitions in the backup archive", false).action(async (archivePath, options) => {
|
|
2352
|
+
await runFramedCommand(
|
|
2353
|
+
" aweskill store backup ",
|
|
2354
|
+
async () => runBackup(context, {
|
|
2355
|
+
archivePath,
|
|
2356
|
+
includeBundles: options.both
|
|
2357
|
+
})
|
|
2358
|
+
);
|
|
2131
2359
|
});
|
|
2132
|
-
store.command("restore").argument("<archive>").description("Restore skills from a backup archive and auto-back up the current skills first").option("--override", "replace existing skills with the archive contents", false).action(async (archivePath, options) => {
|
|
2360
|
+
store.command("restore").argument("<archive>").description("Restore skills from a backup archive and auto-back up the current skills first").option("--override", "replace existing skills with the archive contents", false).option("--both", "restore bundle definitions and include them in the pre-restore backup", false).action(async (archivePath, options) => {
|
|
2133
2361
|
await runFramedCommand(
|
|
2134
2362
|
" aweskill store restore ",
|
|
2135
2363
|
async () => runRestore(context, {
|
|
2136
2364
|
archivePath,
|
|
2137
|
-
override: options.override
|
|
2365
|
+
override: options.override,
|
|
2366
|
+
includeBundles: options.both
|
|
2138
2367
|
})
|
|
2139
2368
|
);
|
|
2140
2369
|
});
|