@alavida/agentpack 0.1.1 → 0.1.3
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 +16 -2
- package/bin/intent.js +20 -0
- package/package.json +11 -5
- package/skills/agentpack-cli/SKILL.md +16 -1
- package/skills/agentpack-cli/references/skill-lifecycle.md +21 -1
- package/skills/authoring-skillgraphs-from-knowledge/SKILL.md +148 -0
- package/skills/authoring-skillgraphs-from-knowledge/references/authored-metadata.md +6 -0
- package/skills/developing-and-testing-skills/SKILL.md +109 -0
- package/skills/developing-and-testing-skills/references/local-workbench.md +7 -0
- package/skills/getting-started-skillgraphs/SKILL.md +115 -0
- package/skills/getting-started-skillgraphs/references/command-routing.md +7 -0
- package/skills/identifying-skill-opportunities/SKILL.md +119 -0
- package/skills/identifying-skill-opportunities/references/capability-boundaries.md +6 -0
- package/skills/maintaining-skillgraph-freshness/SKILL.md +110 -0
- package/skills/repairing-broken-skill-or-plugin-state/SKILL.md +112 -0
- package/skills/repairing-broken-skill-or-plugin-state/references/diagnostic-flows.md +6 -0
- package/skills/shipping-production-plugins-and-packages/SKILL.md +123 -0
- package/skills/shipping-production-plugins-and-packages/references/plugin-delivery.md +6 -0
- 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/build-skill-workbench-model.js +194 -0
- package/src/application/skills/inspect-skill.js +5 -0
- package/src/application/skills/list-stale-skills.js +9 -0
- package/src/application/skills/run-skill-workbench-action.js +23 -0
- package/src/application/skills/start-skill-dev-workbench.js +192 -0
- package/src/application/skills/validate-skills.js +5 -0
- package/src/cli.js +1 -1
- package/src/commands/plugin.js +7 -4
- package/src/commands/skills.js +14 -9
- package/src/dashboard/App.jsx +343 -0
- package/src/dashboard/components/Breadcrumbs.jsx +45 -0
- package/src/dashboard/components/ControlStrip.jsx +153 -0
- package/src/dashboard/components/InspectorPanel.jsx +203 -0
- package/src/dashboard/components/SkillGraph.jsx +567 -0
- package/src/dashboard/components/Tooltip.jsx +111 -0
- package/src/dashboard/dist/dashboard.js +26692 -0
- package/src/dashboard/index.html +81 -0
- package/src/dashboard/lib/api.js +19 -0
- package/src/dashboard/lib/router.js +15 -0
- package/src/dashboard/main.jsx +4 -0
- package/src/domain/plugins/load-plugin-definition.js +163 -0
- package/src/domain/plugins/plugin-diagnostic-error.js +18 -0
- package/src/domain/plugins/plugin-requirements.js +15 -0
- package/src/domain/skills/skill-graph.js +137 -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/open-browser.js +20 -0
- package/src/infrastructure/runtime/skill-dev-workbench-server.js +96 -0
- package/src/infrastructure/runtime/watch-skill-workbench.js +68 -0
- package/src/infrastructure/runtime/watch-tree.js +44 -0
- package/src/lib/plugins.js +46 -117
- package/src/lib/skills.js +141 -459
- package/src/utils/errors.js +33 -1
package/src/lib/skills.js
CHANGED
|
@@ -1,8 +1,35 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, watch, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
3
|
-
import { createHash } from 'node:crypto';
|
|
4
3
|
import { execFileSync } from 'node:child_process';
|
|
5
4
|
import { findAllWorkbenches, findRepoRoot, findWorkbenchContext, resolveWorkbenchFlag } from './context.js';
|
|
5
|
+
import {
|
|
6
|
+
buildReverseDependencies,
|
|
7
|
+
buildSkillGraph,
|
|
8
|
+
buildSkillStatusMap,
|
|
9
|
+
readNodeStatus,
|
|
10
|
+
} from '../domain/skills/skill-graph.js';
|
|
11
|
+
import { readInstallState } from '../infrastructure/fs/install-state-repository.js';
|
|
12
|
+
import {
|
|
13
|
+
ensureSkillLink,
|
|
14
|
+
rebuildInstallState,
|
|
15
|
+
removePathIfExists,
|
|
16
|
+
removeSkillLinks,
|
|
17
|
+
removeSkillLinksByNames,
|
|
18
|
+
} from '../infrastructure/runtime/materialize-skills.js';
|
|
19
|
+
import {
|
|
20
|
+
normalizeDisplayPath,
|
|
21
|
+
normalizeRepoPath,
|
|
22
|
+
parseSkillFrontmatterFile,
|
|
23
|
+
readPackageMetadata,
|
|
24
|
+
} from '../domain/skills/skill-model.js';
|
|
25
|
+
import {
|
|
26
|
+
buildStateRecordForPackageDir,
|
|
27
|
+
compareRecordedSources,
|
|
28
|
+
hashFile,
|
|
29
|
+
readBuildState,
|
|
30
|
+
writeBuildState,
|
|
31
|
+
} from '../domain/skills/skill-provenance.js';
|
|
32
|
+
import { startSkillDevWorkbench } from '../application/skills/start-skill-dev-workbench.js';
|
|
6
33
|
import { AgentpackError, EXIT_CODES, NetworkError, NotFoundError, ValidationError } from '../utils/errors.js';
|
|
7
34
|
|
|
8
35
|
const GITHUB_PACKAGES_REGISTRY = 'https://npm.pkg.github.com';
|
|
@@ -17,173 +44,6 @@ function inferManagedScope(packageName) {
|
|
|
17
44
|
return MANAGED_PACKAGE_SCOPES.find((scope) => packageName?.startsWith(`${scope}/`)) || null;
|
|
18
45
|
}
|
|
19
46
|
|
|
20
|
-
function parseScalar(value) {
|
|
21
|
-
const trimmed = value.trim();
|
|
22
|
-
if (
|
|
23
|
-
(trimmed.startsWith('"') && trimmed.endsWith('"')) ||
|
|
24
|
-
(trimmed.startsWith("'") && trimmed.endsWith("'"))
|
|
25
|
-
) {
|
|
26
|
-
return trimmed.slice(1, -1);
|
|
27
|
-
}
|
|
28
|
-
return trimmed;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function foldBlockScalar(lines, startIndex, baseIndent) {
|
|
32
|
-
const values = [];
|
|
33
|
-
let index = startIndex + 1;
|
|
34
|
-
|
|
35
|
-
while (index < lines.length) {
|
|
36
|
-
const rawLine = lines[index];
|
|
37
|
-
if (!rawLine.trim()) {
|
|
38
|
-
values.push('');
|
|
39
|
-
index += 1;
|
|
40
|
-
continue;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const indentMatch = rawLine.match(/^(\s*)/);
|
|
44
|
-
const indent = indentMatch ? indentMatch[1].length : 0;
|
|
45
|
-
if (indent <= baseIndent) break;
|
|
46
|
-
|
|
47
|
-
values.push(rawLine.slice(baseIndent + 2).trimEnd());
|
|
48
|
-
index += 1;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const folded = values
|
|
52
|
-
.join('\n')
|
|
53
|
-
.split('\n\n')
|
|
54
|
-
.map((chunk) => chunk.split('\n').join(' ').trim())
|
|
55
|
-
.filter((chunk, idx, arr) => chunk.length > 0 || idx < arr.length - 1)
|
|
56
|
-
.join('\n\n')
|
|
57
|
-
.trim();
|
|
58
|
-
|
|
59
|
-
return { value: folded, nextIndex: index };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function ensureContainer(target, key) {
|
|
63
|
-
if (!target[key] || typeof target[key] !== 'object' || Array.isArray(target[key])) {
|
|
64
|
-
target[key] = {};
|
|
65
|
-
}
|
|
66
|
-
return target[key];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function parseSkillFrontmatterFile(skillFilePath) {
|
|
70
|
-
if (!existsSync(skillFilePath)) {
|
|
71
|
-
throw new NotFoundError(`skill file not found: ${skillFilePath}`, { code: 'skill_not_found' });
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const content = readFileSync(skillFilePath, 'utf-8');
|
|
75
|
-
if (!content.startsWith('---\n')) {
|
|
76
|
-
throw new ValidationError('SKILL.md missing frontmatter', { code: 'missing_frontmatter' });
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const fmEnd = content.indexOf('\n---', 4);
|
|
80
|
-
if (fmEnd === -1) {
|
|
81
|
-
throw new ValidationError('SKILL.md has unclosed frontmatter', { code: 'unclosed_frontmatter' });
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const lines = content.slice(4, fmEnd).split('\n');
|
|
85
|
-
const fields = {};
|
|
86
|
-
let activeArrayKey = null;
|
|
87
|
-
let activeArrayTarget = null;
|
|
88
|
-
let activeParentKey = null;
|
|
89
|
-
|
|
90
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
91
|
-
const rawLine = lines[index];
|
|
92
|
-
const line = rawLine.trimEnd();
|
|
93
|
-
if (!line.trim()) continue;
|
|
94
|
-
|
|
95
|
-
const listMatch = rawLine.match(/^(\s*)-\s+(.+)$/);
|
|
96
|
-
if (listMatch && activeArrayKey && activeArrayTarget) {
|
|
97
|
-
activeArrayTarget[activeArrayKey].push(parseScalar(listMatch[2]));
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const nestedKeyMatch = rawLine.match(/^\s{2}([A-Za-z][\w-]*):\s*(.*)$/);
|
|
102
|
-
if (nestedKeyMatch && activeParentKey) {
|
|
103
|
-
const [, key, value] = nestedKeyMatch;
|
|
104
|
-
const parent = ensureContainer(fields, activeParentKey);
|
|
105
|
-
if (value === '') {
|
|
106
|
-
parent[key] = [];
|
|
107
|
-
activeArrayKey = key;
|
|
108
|
-
activeArrayTarget = parent;
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
parent[key] = parseScalar(value);
|
|
113
|
-
activeArrayKey = null;
|
|
114
|
-
activeArrayTarget = null;
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const keyMatch = rawLine.match(/^([A-Za-z][\w-]*):\s*(.*)$/);
|
|
119
|
-
if (!keyMatch) continue;
|
|
120
|
-
|
|
121
|
-
const [, key, value] = keyMatch;
|
|
122
|
-
if (value === '>' || value === '|') {
|
|
123
|
-
const { value: blockValue, nextIndex } = foldBlockScalar(lines, index, 0);
|
|
124
|
-
fields[key] = blockValue;
|
|
125
|
-
activeParentKey = null;
|
|
126
|
-
activeArrayKey = null;
|
|
127
|
-
activeArrayTarget = null;
|
|
128
|
-
index = nextIndex - 1;
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (value === '') {
|
|
133
|
-
fields[key] = fields[key] && typeof fields[key] === 'object' && !Array.isArray(fields[key])
|
|
134
|
-
? fields[key]
|
|
135
|
-
: [];
|
|
136
|
-
activeParentKey = key;
|
|
137
|
-
activeArrayKey = Array.isArray(fields[key]) ? key : null;
|
|
138
|
-
activeArrayTarget = Array.isArray(fields[key]) ? fields : null;
|
|
139
|
-
continue;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
fields[key] = parseScalar(value);
|
|
143
|
-
activeParentKey = null;
|
|
144
|
-
activeArrayKey = null;
|
|
145
|
-
activeArrayTarget = null;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (!fields.name) {
|
|
149
|
-
throw new ValidationError('SKILL.md frontmatter missing "name" field', { code: 'missing_name' });
|
|
150
|
-
}
|
|
151
|
-
if (!fields.description) {
|
|
152
|
-
throw new ValidationError('SKILL.md frontmatter missing "description" field', { code: 'missing_description' });
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
name: fields.name,
|
|
157
|
-
description: fields.description,
|
|
158
|
-
sources: Array.isArray(fields.metadata?.sources)
|
|
159
|
-
? fields.metadata.sources
|
|
160
|
-
: (Array.isArray(fields.sources) ? fields.sources : []),
|
|
161
|
-
requires: Array.isArray(fields.metadata?.requires)
|
|
162
|
-
? fields.metadata.requires
|
|
163
|
-
: (Array.isArray(fields.requires) ? fields.requires : []),
|
|
164
|
-
status: typeof fields.metadata?.status === 'string' ? fields.metadata.status : null,
|
|
165
|
-
replacement: typeof fields.metadata?.replacement === 'string' ? fields.metadata.replacement : null,
|
|
166
|
-
message: typeof fields.metadata?.message === 'string' ? fields.metadata.message : null,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function normalizeDisplayPath(repoRoot, absolutePath) {
|
|
171
|
-
return relative(repoRoot, absolutePath).split('\\').join('/');
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function ensureDir(pathValue) {
|
|
175
|
-
mkdirSync(pathValue, { recursive: true });
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function ensureSkillLink(repoRoot, baseDir, skillName, skillDir) {
|
|
179
|
-
const skillsDir = join(repoRoot, baseDir, 'skills');
|
|
180
|
-
ensureDir(skillsDir);
|
|
181
|
-
const linkPath = join(skillsDir, skillName);
|
|
182
|
-
removePathIfExists(linkPath);
|
|
183
|
-
symlinkSync(skillDir, linkPath, 'dir');
|
|
184
|
-
return normalizeDisplayPath(repoRoot, linkPath);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
47
|
function resolveDevLinkedSkills(repoRoot, rootSkillDir) {
|
|
188
48
|
const queue = [rootSkillDir];
|
|
189
49
|
const seenDirs = new Set();
|
|
@@ -296,32 +156,6 @@ export function findPackageDirByName(repoRoot, packageName) {
|
|
|
296
156
|
return null;
|
|
297
157
|
}
|
|
298
158
|
|
|
299
|
-
export function readPackageMetadata(packageDir) {
|
|
300
|
-
const packageJsonPath = join(packageDir, 'package.json');
|
|
301
|
-
if (!existsSync(packageJsonPath)) {
|
|
302
|
-
return {
|
|
303
|
-
packageName: null,
|
|
304
|
-
packageVersion: null,
|
|
305
|
-
dependencies: {},
|
|
306
|
-
devDependencies: {},
|
|
307
|
-
files: null,
|
|
308
|
-
repository: null,
|
|
309
|
-
publishConfigRegistry: null,
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
314
|
-
return {
|
|
315
|
-
packageName: pkg.name || null,
|
|
316
|
-
packageVersion: pkg.version || null,
|
|
317
|
-
dependencies: pkg.dependencies || {},
|
|
318
|
-
devDependencies: pkg.devDependencies || {},
|
|
319
|
-
files: Array.isArray(pkg.files) ? pkg.files : null,
|
|
320
|
-
repository: pkg.repository || null,
|
|
321
|
-
publishConfigRegistry: pkg.publishConfig?.registry || null,
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
|
|
325
159
|
function readPackageJson(packageDir) {
|
|
326
160
|
const packageJsonPath = join(packageDir, 'package.json');
|
|
327
161
|
if (!existsSync(packageJsonPath)) {
|
|
@@ -398,8 +232,8 @@ export function devSkill(target, {
|
|
|
398
232
|
const links = [];
|
|
399
233
|
|
|
400
234
|
for (const linkedSkill of linkedSkills) {
|
|
401
|
-
links.push(ensureSkillLink(repoRoot, '.claude', linkedSkill.name, linkedSkill.skillDir));
|
|
402
|
-
links.push(ensureSkillLink(repoRoot, '.agents', linkedSkill.name, linkedSkill.skillDir));
|
|
235
|
+
links.push(ensureSkillLink(repoRoot, '.claude', linkedSkill.name, linkedSkill.skillDir, normalizeDisplayPath));
|
|
236
|
+
links.push(ensureSkillLink(repoRoot, '.agents', linkedSkill.name, linkedSkill.skillDir, normalizeDisplayPath));
|
|
403
237
|
}
|
|
404
238
|
|
|
405
239
|
return {
|
|
@@ -427,46 +261,30 @@ export function devSkill(target, {
|
|
|
427
261
|
}
|
|
428
262
|
}
|
|
429
263
|
|
|
430
|
-
function removeSkillLinks(repoRoot, name) {
|
|
431
|
-
const removed = [];
|
|
432
|
-
for (const pathValue of [
|
|
433
|
-
join(repoRoot, '.claude', 'skills', name),
|
|
434
|
-
join(repoRoot, '.agents', 'skills', name),
|
|
435
|
-
]) {
|
|
436
|
-
if (!existsSync(pathValue)) continue;
|
|
437
|
-
removePathIfExists(pathValue);
|
|
438
|
-
removed.push(normalizeDisplayPath(repoRoot, pathValue));
|
|
439
|
-
}
|
|
440
|
-
return removed;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function removeSkillLinksByNames(repoRoot, names) {
|
|
444
|
-
const removed = [];
|
|
445
|
-
for (const name of names) {
|
|
446
|
-
removed.push(...removeSkillLinks(repoRoot, name));
|
|
447
|
-
}
|
|
448
|
-
return [...new Set(removed)];
|
|
449
|
-
}
|
|
450
|
-
|
|
451
264
|
export function startSkillDev(target, {
|
|
452
265
|
cwd = process.cwd(),
|
|
453
266
|
sync = true,
|
|
267
|
+
dashboard = true,
|
|
454
268
|
onStart = () => {},
|
|
455
269
|
onRebuild = () => {},
|
|
456
270
|
} = {}) {
|
|
457
|
-
const
|
|
458
|
-
const { skillDir } = resolveLocalPackagedSkillDir(
|
|
271
|
+
const outerRepoRoot = findRepoRoot(cwd);
|
|
272
|
+
const { skillDir } = resolveLocalPackagedSkillDir(outerRepoRoot, target);
|
|
273
|
+
const repoRoot = findRepoRoot(skillDir);
|
|
459
274
|
let closed = false;
|
|
460
275
|
let timer = null;
|
|
461
276
|
let currentNames = [];
|
|
462
277
|
let watcher = null;
|
|
278
|
+
let workbench = null;
|
|
279
|
+
let initialResult = null;
|
|
463
280
|
|
|
464
281
|
const cleanup = () => {
|
|
465
282
|
if (closed) return { name: currentNames[0] || null, unlinked: false, removed: [] };
|
|
466
283
|
closed = true;
|
|
467
284
|
clearTimeout(timer);
|
|
468
285
|
if (watcher) watcher.close();
|
|
469
|
-
|
|
286
|
+
if (workbench) workbench.close();
|
|
287
|
+
const removed = removeSkillLinksByNames(repoRoot, currentNames, normalizeDisplayPath);
|
|
470
288
|
detachProcessCleanup();
|
|
471
289
|
return {
|
|
472
290
|
name: currentNames[0] || null,
|
|
@@ -493,19 +311,56 @@ export function startSkillDev(target, {
|
|
|
493
311
|
processCleanupHandlers.clear();
|
|
494
312
|
};
|
|
495
313
|
|
|
496
|
-
const
|
|
497
|
-
|
|
314
|
+
const enrichResult = (result) => ({
|
|
315
|
+
...result,
|
|
316
|
+
workbench: workbench
|
|
317
|
+
? {
|
|
318
|
+
enabled: true,
|
|
319
|
+
url: workbench.url,
|
|
320
|
+
port: workbench.port,
|
|
321
|
+
}
|
|
322
|
+
: {
|
|
323
|
+
enabled: false,
|
|
324
|
+
url: null,
|
|
325
|
+
port: null,
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
const applyDevResult = (result) => {
|
|
498
330
|
const nextNames = result.linkedSkills.map((entry) => entry.name);
|
|
499
331
|
const staleNames = currentNames.filter((name) => !nextNames.includes(name));
|
|
500
332
|
if (staleNames.length > 0) {
|
|
501
|
-
removeSkillLinksByNames(repoRoot, staleNames);
|
|
333
|
+
removeSkillLinksByNames(repoRoot, staleNames, normalizeDisplayPath);
|
|
502
334
|
}
|
|
503
335
|
currentNames = nextNames;
|
|
504
336
|
return result;
|
|
505
337
|
};
|
|
506
338
|
|
|
507
|
-
const
|
|
508
|
-
|
|
339
|
+
const startOrRefreshWorkbench = async () => {
|
|
340
|
+
if (dashboard && !workbench) {
|
|
341
|
+
workbench = await startSkillDevWorkbench({
|
|
342
|
+
repoRoot,
|
|
343
|
+
skillDir,
|
|
344
|
+
open: true,
|
|
345
|
+
disableBrowser: process.env.AGENTPACK_DISABLE_BROWSER === '1',
|
|
346
|
+
});
|
|
347
|
+
} else if (workbench) {
|
|
348
|
+
workbench.refresh();
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
initialResult = enrichResult(applyDevResult(devSkill(target, { cwd, sync })));
|
|
353
|
+
const ready = Promise.resolve(startOrRefreshWorkbench())
|
|
354
|
+
.then(() => {
|
|
355
|
+
const result = enrichResult(initialResult);
|
|
356
|
+
initialResult = result;
|
|
357
|
+
onStart(result);
|
|
358
|
+
return result;
|
|
359
|
+
})
|
|
360
|
+
.catch((error) => {
|
|
361
|
+
cleanup();
|
|
362
|
+
throw error;
|
|
363
|
+
});
|
|
509
364
|
|
|
510
365
|
attachProcessCleanup();
|
|
511
366
|
|
|
@@ -513,17 +368,22 @@ export function startSkillDev(target, {
|
|
|
513
368
|
if (closed) return;
|
|
514
369
|
clearTimeout(timer);
|
|
515
370
|
timer = setTimeout(() => {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
371
|
+
Promise.resolve()
|
|
372
|
+
.then(() => applyDevResult(devSkill(target, { cwd, sync })))
|
|
373
|
+
.then(async (result) => {
|
|
374
|
+
await startOrRefreshWorkbench();
|
|
375
|
+
return enrichResult(result);
|
|
376
|
+
})
|
|
377
|
+
.then(onRebuild)
|
|
378
|
+
.catch((error) => {
|
|
379
|
+
onRebuild({ error });
|
|
380
|
+
});
|
|
522
381
|
}, 100);
|
|
523
382
|
});
|
|
524
383
|
|
|
525
384
|
return {
|
|
526
385
|
initialResult,
|
|
386
|
+
ready,
|
|
527
387
|
close() {
|
|
528
388
|
return cleanup();
|
|
529
389
|
},
|
|
@@ -544,7 +404,7 @@ export function unlinkSkill(name, { cwd = process.cwd() } = {}) {
|
|
|
544
404
|
});
|
|
545
405
|
}
|
|
546
406
|
|
|
547
|
-
const removed = removeSkillLinks(repoRoot, name);
|
|
407
|
+
const removed = removeSkillLinks(repoRoot, name, normalizeDisplayPath);
|
|
548
408
|
|
|
549
409
|
return {
|
|
550
410
|
name,
|
|
@@ -553,10 +413,6 @@ export function unlinkSkill(name, { cwd = process.cwd() } = {}) {
|
|
|
553
413
|
};
|
|
554
414
|
}
|
|
555
415
|
|
|
556
|
-
export function normalizeRepoPath(repoRoot, absolutePath) {
|
|
557
|
-
return normalizeDisplayPath(repoRoot, absolutePath);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
416
|
function buildValidateNextSteps(packageMetadata, valid) {
|
|
561
417
|
if (!valid || !packageMetadata.packageName) return [];
|
|
562
418
|
|
|
@@ -737,48 +593,16 @@ export function inspectSkill(target, { cwd = process.cwd() } = {}) {
|
|
|
737
593
|
};
|
|
738
594
|
}
|
|
739
595
|
|
|
740
|
-
function readSkillGraphNode(repoRoot, packageDir, directInstallNames = new Set()) {
|
|
741
|
-
const skillFile = join(packageDir, 'SKILL.md');
|
|
742
|
-
if (!existsSync(skillFile)) return null;
|
|
743
|
-
|
|
744
|
-
const skillMetadata = parseSkillFrontmatterFile(skillFile);
|
|
745
|
-
const packageMetadata = readPackageMetadata(packageDir);
|
|
746
|
-
if (!packageMetadata.packageName) return null;
|
|
747
|
-
|
|
748
|
-
const dependencyNames = Object.keys(packageMetadata.dependencies || {})
|
|
749
|
-
.filter((dependencyName) => {
|
|
750
|
-
const localPackageDir = findPackageDirByName(repoRoot, dependencyName);
|
|
751
|
-
if (localPackageDir && existsSync(join(localPackageDir, 'SKILL.md'))) return true;
|
|
752
|
-
const installedPackageDir = join(repoRoot, 'node_modules', ...dependencyName.split('/'));
|
|
753
|
-
return existsSync(join(installedPackageDir, 'SKILL.md'));
|
|
754
|
-
})
|
|
755
|
-
.sort((a, b) => a.localeCompare(b));
|
|
756
|
-
|
|
757
|
-
return {
|
|
758
|
-
name: skillMetadata.name,
|
|
759
|
-
packageName: packageMetadata.packageName,
|
|
760
|
-
packageVersion: packageMetadata.packageVersion,
|
|
761
|
-
skillPath: normalizeDisplayPath(repoRoot, packageDir),
|
|
762
|
-
skillFile: normalizeDisplayPath(repoRoot, skillFile),
|
|
763
|
-
direct: directInstallNames.has(packageMetadata.packageName),
|
|
764
|
-
dependencies: dependencyNames,
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
|
|
768
596
|
function buildAuthoredSkillGraph(repoRoot) {
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
return nodes;
|
|
597
|
+
return buildSkillGraph(repoRoot, listPackagedSkillDirs(repoRoot), {
|
|
598
|
+
parseSkillFrontmatterFile,
|
|
599
|
+
readPackageMetadata,
|
|
600
|
+
findPackageDirByName,
|
|
601
|
+
normalizeDisplayPath,
|
|
602
|
+
});
|
|
778
603
|
}
|
|
779
604
|
|
|
780
605
|
function buildInstalledSkillGraph(repoRoot) {
|
|
781
|
-
const nodes = new Map();
|
|
782
606
|
const installState = readInstallState(repoRoot);
|
|
783
607
|
const directInstallNames = new Set(
|
|
784
608
|
Object.entries(installState.installs || {})
|
|
@@ -786,73 +610,19 @@ function buildInstalledSkillGraph(repoRoot) {
|
|
|
786
610
|
.map(([packageName]) => packageName)
|
|
787
611
|
);
|
|
788
612
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
function buildReverseDependencies(nodes) {
|
|
799
|
-
const reverse = new Map();
|
|
800
|
-
for (const packageName of nodes.keys()) reverse.set(packageName, []);
|
|
801
|
-
|
|
802
|
-
for (const node of nodes.values()) {
|
|
803
|
-
for (const dependencyName of node.dependencies) {
|
|
804
|
-
if (!reverse.has(dependencyName)) continue;
|
|
805
|
-
reverse.get(dependencyName).push(node.packageName);
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
for (const values of reverse.values()) values.sort((a, b) => a.localeCompare(b));
|
|
810
|
-
return reverse;
|
|
613
|
+
return buildSkillGraph(repoRoot, listInstalledPackageDirs(join(repoRoot, 'node_modules')), {
|
|
614
|
+
directInstallNames,
|
|
615
|
+
parseSkillFrontmatterFile,
|
|
616
|
+
readPackageMetadata,
|
|
617
|
+
findPackageDirByName,
|
|
618
|
+
normalizeDisplayPath,
|
|
619
|
+
});
|
|
811
620
|
}
|
|
812
621
|
|
|
813
|
-
function
|
|
622
|
+
function buildSkillStatusMapForRepo(repoRoot) {
|
|
814
623
|
const nodes = buildAuthoredSkillGraph(repoRoot);
|
|
815
624
|
const staleSkills = new Set(listStaleSkills({ cwd: repoRoot }).map((skill) => skill.packageName));
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
function resolveStatus(packageName, seen = new Set()) {
|
|
819
|
-
if (cache.has(packageName)) return cache.get(packageName);
|
|
820
|
-
if (staleSkills.has(packageName)) {
|
|
821
|
-
cache.set(packageName, 'stale');
|
|
822
|
-
return 'stale';
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
if (seen.has(packageName)) return 'current';
|
|
826
|
-
seen.add(packageName);
|
|
827
|
-
|
|
828
|
-
const node = nodes.get(packageName);
|
|
829
|
-
if (!node) {
|
|
830
|
-
cache.set(packageName, null);
|
|
831
|
-
return null;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
const dependencyStatuses = node.dependencies
|
|
835
|
-
.map((dependencyName) => resolveStatus(dependencyName, new Set(seen)))
|
|
836
|
-
.filter(Boolean);
|
|
837
|
-
|
|
838
|
-
const status = dependencyStatuses.some((value) => value === 'stale' || value === 'affected')
|
|
839
|
-
? 'affected'
|
|
840
|
-
: 'current';
|
|
841
|
-
|
|
842
|
-
cache.set(packageName, status);
|
|
843
|
-
return status;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
for (const packageName of nodes.keys()) {
|
|
847
|
-
resolveStatus(packageName);
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
return cache;
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
function readNodeStatus(statusMap, packageName) {
|
|
854
|
-
if (!statusMap) return null;
|
|
855
|
-
return statusMap.get(packageName) || null;
|
|
625
|
+
return buildSkillStatusMap(nodes, staleSkills);
|
|
856
626
|
}
|
|
857
627
|
|
|
858
628
|
export function inspectSkillDependencies(target, {
|
|
@@ -863,7 +633,7 @@ export function inspectSkillDependencies(target, {
|
|
|
863
633
|
const authoredNodes = buildAuthoredSkillGraph(repoRoot);
|
|
864
634
|
const installedNodes = buildInstalledSkillGraph(repoRoot);
|
|
865
635
|
const statusRoot = discoveryRoot ? resolve(discoveryRoot) : repoRoot;
|
|
866
|
-
const statusMap =
|
|
636
|
+
const statusMap = buildSkillStatusMapForRepo(statusRoot);
|
|
867
637
|
|
|
868
638
|
const authoredTarget = authoredNodes.get(target) || null;
|
|
869
639
|
const installedTarget = installedNodes.get(target) || null;
|
|
@@ -1086,6 +856,26 @@ export function validateSkills(target, { cwd = process.cwd() } = {}) {
|
|
|
1086
856
|
const validCount = skills.filter((skill) => skill.valid).length;
|
|
1087
857
|
const invalidCount = skills.length - validCount;
|
|
1088
858
|
|
|
859
|
+
if (validCount > 0) {
|
|
860
|
+
const buildState = readBuildState(repoRoot);
|
|
861
|
+
|
|
862
|
+
for (const packageDir of packageDirs) {
|
|
863
|
+
const packageMetadata = readPackageMetadata(packageDir);
|
|
864
|
+
const result = skills.find((skill) => skill.packageName === packageMetadata.packageName);
|
|
865
|
+
if (!result?.valid) continue;
|
|
866
|
+
|
|
867
|
+
const { packageName, record } = buildStateRecordForPackageDir(repoRoot, packageDir, {
|
|
868
|
+
parseSkillFrontmatterFile,
|
|
869
|
+
readPackageMetadata,
|
|
870
|
+
normalizeDisplayPath,
|
|
871
|
+
});
|
|
872
|
+
if (!packageName) continue;
|
|
873
|
+
buildState.skills[packageName] = record;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
writeBuildState(repoRoot, buildState);
|
|
877
|
+
}
|
|
878
|
+
|
|
1089
879
|
return {
|
|
1090
880
|
valid: invalidCount === 0,
|
|
1091
881
|
count: skills.length,
|
|
@@ -1094,67 +884,16 @@ export function validateSkills(target, { cwd = process.cwd() } = {}) {
|
|
|
1094
884
|
skills,
|
|
1095
885
|
};
|
|
1096
886
|
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
function hashFile(filePath) {
|
|
1100
|
-
const digest = createHash('sha256').update(readFileSync(filePath)).digest('hex');
|
|
1101
|
-
return `sha256:${digest}`;
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
887
|
function normalizeRelativePath(pathValue) {
|
|
1105
888
|
return pathValue.split('\\').join('/');
|
|
1106
889
|
}
|
|
1107
890
|
|
|
1108
|
-
function readBuildState(repoRoot) {
|
|
1109
|
-
const buildStatePath = join(repoRoot, '.agentpack', 'build-state.json');
|
|
1110
|
-
if (!existsSync(buildStatePath)) {
|
|
1111
|
-
return { version: 1, skills: {} };
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
return JSON.parse(readFileSync(buildStatePath, 'utf-8'));
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
function readInstallState(repoRoot) {
|
|
1118
|
-
const installStatePath = join(repoRoot, '.agentpack', 'install.json');
|
|
1119
|
-
if (!existsSync(installStatePath)) {
|
|
1120
|
-
return { version: 1, installs: {} };
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
return JSON.parse(readFileSync(installStatePath, 'utf-8'));
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
function writeInstallState(repoRoot, state) {
|
|
1127
|
-
mkdirSync(join(repoRoot, '.agentpack'), { recursive: true });
|
|
1128
|
-
writeFileSync(join(repoRoot, '.agentpack', 'install.json'), JSON.stringify(state, null, 2) + '\n');
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
891
|
function normalizeRequestedTarget(target, cwd = process.cwd()) {
|
|
1132
892
|
if (typeof target !== 'string') return target;
|
|
1133
893
|
if (target.startsWith('@')) return target;
|
|
1134
894
|
return normalizeRelativePath(resolve(cwd, target));
|
|
1135
895
|
}
|
|
1136
896
|
|
|
1137
|
-
function compareRecordedSources(repoRoot, record) {
|
|
1138
|
-
const changes = [];
|
|
1139
|
-
const recordedSources = record.sources || {};
|
|
1140
|
-
|
|
1141
|
-
for (const [sourcePath, sourceRecord] of Object.entries(recordedSources)) {
|
|
1142
|
-
const absoluteSourcePath = join(repoRoot, sourcePath);
|
|
1143
|
-
const currentHash = hashFile(absoluteSourcePath);
|
|
1144
|
-
const recordedHash = sourceRecord.hash;
|
|
1145
|
-
|
|
1146
|
-
if (currentHash !== recordedHash) {
|
|
1147
|
-
changes.push({
|
|
1148
|
-
path: sourcePath,
|
|
1149
|
-
recorded: recordedHash,
|
|
1150
|
-
current: currentHash,
|
|
1151
|
-
});
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
return changes;
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
897
|
function listPackagedSkillDirs(repoRoot) {
|
|
1159
898
|
const stack = [repoRoot];
|
|
1160
899
|
const results = [];
|
|
@@ -1387,73 +1126,6 @@ function resolveNpmInstallTargets(directTargetMap) {
|
|
|
1387
1126
|
return [...new Set(npmInstallTargets)];
|
|
1388
1127
|
}
|
|
1389
1128
|
|
|
1390
|
-
function ensureSymlink(targetPath, linkPath) {
|
|
1391
|
-
rmSync(linkPath, { recursive: true, force: true });
|
|
1392
|
-
mkdirSync(dirname(linkPath), { recursive: true });
|
|
1393
|
-
symlinkSync(targetPath, linkPath, 'dir');
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
function removePathIfExists(pathValue) {
|
|
1397
|
-
rmSync(pathValue, { recursive: true, force: true });
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
function buildInstallRecord(repoRoot, packageDir, directTargetMap) {
|
|
1401
|
-
const packageMetadata = readPackageMetadata(packageDir);
|
|
1402
|
-
if (!packageMetadata.packageName) return null;
|
|
1403
|
-
|
|
1404
|
-
const skillMetadata = parseSkillFrontmatterFile(join(packageDir, 'SKILL.md'));
|
|
1405
|
-
const skillDirName = skillMetadata.name;
|
|
1406
|
-
const materializations = [];
|
|
1407
|
-
|
|
1408
|
-
const claudeTargetAbs = join(repoRoot, '.claude', 'skills', skillDirName);
|
|
1409
|
-
ensureSymlink(packageDir, claudeTargetAbs);
|
|
1410
|
-
materializations.push({
|
|
1411
|
-
target: normalizeRelativePath(relative(repoRoot, claudeTargetAbs)),
|
|
1412
|
-
mode: 'symlink',
|
|
1413
|
-
});
|
|
1414
|
-
|
|
1415
|
-
const agentsTargetAbs = join(repoRoot, '.agents', 'skills', skillDirName);
|
|
1416
|
-
ensureSymlink(packageDir, agentsTargetAbs);
|
|
1417
|
-
materializations.push({
|
|
1418
|
-
target: normalizeRelativePath(relative(repoRoot, agentsTargetAbs)),
|
|
1419
|
-
mode: 'symlink',
|
|
1420
|
-
});
|
|
1421
|
-
|
|
1422
|
-
return {
|
|
1423
|
-
packageName: packageMetadata.packageName,
|
|
1424
|
-
direct: directTargetMap.has(packageMetadata.packageName),
|
|
1425
|
-
requestedTarget: directTargetMap.get(packageMetadata.packageName) || null,
|
|
1426
|
-
packageVersion: packageMetadata.packageVersion,
|
|
1427
|
-
sourcePackagePath: normalizeRelativePath(relative(repoRoot, packageDir)),
|
|
1428
|
-
materializations,
|
|
1429
|
-
};
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
function rebuildInstallState(repoRoot, directTargetMap) {
|
|
1433
|
-
const packageDirs = listInstalledPackageDirs(join(repoRoot, 'node_modules'));
|
|
1434
|
-
const installs = {};
|
|
1435
|
-
|
|
1436
|
-
for (const packageDir of packageDirs) {
|
|
1437
|
-
const skillFile = join(packageDir, 'SKILL.md');
|
|
1438
|
-
if (!existsSync(skillFile)) continue;
|
|
1439
|
-
|
|
1440
|
-
const record = buildInstallRecord(repoRoot, packageDir, directTargetMap);
|
|
1441
|
-
if (!record) continue;
|
|
1442
|
-
|
|
1443
|
-
installs[record.packageName] = {
|
|
1444
|
-
direct: record.direct,
|
|
1445
|
-
requested_target: record.requestedTarget,
|
|
1446
|
-
package_version: record.packageVersion,
|
|
1447
|
-
source_package_path: record.sourcePackagePath,
|
|
1448
|
-
materializations: record.materializations,
|
|
1449
|
-
};
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
const state = { version: 1, installs };
|
|
1453
|
-
writeInstallState(repoRoot, state);
|
|
1454
|
-
return state;
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
1129
|
export function installSkills(targets, { cwd = process.cwd() } = {}) {
|
|
1458
1130
|
const repoRoot = findRepoRoot(cwd);
|
|
1459
1131
|
const previousState = readInstallState(repoRoot);
|
|
@@ -1493,7 +1165,12 @@ export function installSkills(targets, { cwd = process.cwd() } = {}) {
|
|
|
1493
1165
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
1494
1166
|
});
|
|
1495
1167
|
|
|
1496
|
-
return rebuildInstallState(repoRoot, directTargetMap
|
|
1168
|
+
return rebuildInstallState(repoRoot, directTargetMap, {
|
|
1169
|
+
listInstalledPackageDirs,
|
|
1170
|
+
parseSkillFrontmatterFile,
|
|
1171
|
+
readPackageMetadata,
|
|
1172
|
+
normalizeRelativePath,
|
|
1173
|
+
});
|
|
1497
1174
|
}
|
|
1498
1175
|
|
|
1499
1176
|
export function inspectSkillsEnv({ cwd = process.cwd() } = {}) {
|
|
@@ -1809,7 +1486,12 @@ export function uninstallSkills(target, { cwd = process.cwd() } = {}) {
|
|
|
1809
1486
|
});
|
|
1810
1487
|
}
|
|
1811
1488
|
|
|
1812
|
-
const nextState = rebuildInstallState(repoRoot, nextDirectTargetMap
|
|
1489
|
+
const nextState = rebuildInstallState(repoRoot, nextDirectTargetMap, {
|
|
1490
|
+
listInstalledPackageDirs,
|
|
1491
|
+
parseSkillFrontmatterFile,
|
|
1492
|
+
readPackageMetadata,
|
|
1493
|
+
normalizeRelativePath,
|
|
1494
|
+
});
|
|
1813
1495
|
const remainingTargets = new Set(
|
|
1814
1496
|
Object.values(nextState.installs)
|
|
1815
1497
|
.flatMap((install) => (install.materializations || []).map((entry) => entry.target))
|