skillshop 2.0.0 → 2.1.0
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 +217 -104
- package/agents-content/000_communication-writing.block.md +11 -0
- package/agents-content/001_external-actions.block.md +9 -0
- package/agents-content/AGENTS.md +11 -0
- package/package.json +6 -3
- package/skills-content/architecture-scanner/SKILL.md +274 -0
- package/skills-content/architecture-scanner/files.json +10 -0
- package/skills-content/architecture-scanner/scan.sh +823 -0
- package/skills-content/change-review/SKILL.md +76 -0
- package/skills-content/change-review/agents/openai.yaml +4 -0
- package/skills-content/changelog/SKILL.md +99 -0
- package/skills-content/changeset/changeset.md +80 -0
- package/skills-content/code-buddy/SKILL.md +136 -0
- package/skills-content/design-system-catalog/SKILL.md +106 -0
- package/skills-content/design-system-catalog/extract-components.mjs +441 -0
- package/skills-content/design-system-catalog/generate-catalog.sh +255 -0
- package/skills-content/flare/SKILL.md +96 -0
- package/skills-content/grilling/SKILL.md +31 -0
- package/skills-content/install-sigmap/SKILL.md +97 -0
- package/skills-content/one-shot/SKILL.md +51 -0
- package/skills-content/release/SKILL.md +128 -0
- package/skills-content/shadow-clone/SKILL.md +228 -0
- package/skills-content/shadow-clone/extract-signatures.mjs +202 -0
- package/skills-content/shadow-clone/scan-repo.mjs +343 -0
- package/skills-content/ship/SKILL.md +95 -0
- package/skills-content/shots/SKILL.md +89 -0
- package/skills-content/shots/agents/openai.yaml +4 -0
- package/skills-content/skillshop/SKILL.md +104 -0
- package/skills-content/skillz/SKILL.md +15 -0
- package/skills-content/transcript-clean/SKILL.md +55 -0
- package/skills-content/transcript-edit/SKILL.md +80 -0
- package/skills-content/vitest/GENERATION.md +5 -0
- package/skills-content/vitest/SKILL.md +52 -0
- package/skills-content/vitest/references/advanced-environments.md +264 -0
- package/skills-content/vitest/references/advanced-projects.md +300 -0
- package/skills-content/vitest/references/advanced-type-testing.md +237 -0
- package/skills-content/vitest/references/advanced-vi.md +249 -0
- package/skills-content/vitest/references/core-cli.md +166 -0
- package/skills-content/vitest/references/core-config.md +174 -0
- package/skills-content/vitest/references/core-describe.md +193 -0
- package/skills-content/vitest/references/core-expect.md +219 -0
- package/skills-content/vitest/references/core-hooks.md +244 -0
- package/skills-content/vitest/references/core-test-api.md +233 -0
- package/skills-content/vitest/references/features-concurrency.md +250 -0
- package/skills-content/vitest/references/features-context.md +238 -0
- package/skills-content/vitest/references/features-coverage.md +207 -0
- package/skills-content/vitest/references/features-filtering.md +211 -0
- package/skills-content/vitest/references/features-mocking.md +265 -0
- package/skills-content/vitest/references/features-snapshots.md +207 -0
- package/skills-content/workleaf/SKILL.md +77 -0
- package/skills-content/worklog/SKILL.md +145 -0
- package/skills-content/worklog/references/summary-format.md +114 -0
- package/skills-content/worktree/SKILL.md +71 -0
- package/src/agents-instructions.js +219 -0
- package/src/cli.js +87 -23
- package/src/import.js +12 -7
- package/src/index.js +1 -0
- package/src/legacy-agents-content.js +36 -0
- package/src/legacy-cli.js +1234 -0
- package/src/library.js +9 -0
- package/src/specifier.js +11 -0
- package/src/user-install.js +48 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { legacyAgentsContent } from './legacy-agents-content.js';
|
|
4
|
+
|
|
5
|
+
export class AgentsInstructionsError extends Error {}
|
|
6
|
+
|
|
7
|
+
const blockSuffix = '.block.md';
|
|
8
|
+
const referencePattern = /\{\{\s*([^{}]+?)\s*\}\}/g;
|
|
9
|
+
const namedReferencePattern = /^fragments\s*\[\s*(["'])([^"'\[\]]+)\1\s*\]$/;
|
|
10
|
+
const aggregateExpression = 'blocks';
|
|
11
|
+
const regionBeginPrefix = '<!-- skillshop agents: begin v';
|
|
12
|
+
const regionBeginPattern = /^<!-- (?:skillshop|skillz) agents: begin v\S+ -->$/;
|
|
13
|
+
const regionEndLine = '<!-- skillshop agents: end -->';
|
|
14
|
+
const oldRegionEndLine = '<!-- skillz agents: end -->';
|
|
15
|
+
const legacyMarkerPattern = /^<!-- skillz agents: AGENTS\.md@\S+ -->$/;
|
|
16
|
+
const fragmentsBegin = '<!-- skillshop fragments: begin -->';
|
|
17
|
+
const fragmentsEnd = '<!-- skillshop fragments: end -->';
|
|
18
|
+
|
|
19
|
+
export function discoverBlocks(contentDirectory) {
|
|
20
|
+
if (!fs.existsSync(contentDirectory)) return [];
|
|
21
|
+
return fs
|
|
22
|
+
.readdirSync(contentDirectory, { withFileTypes: true })
|
|
23
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(blockSuffix))
|
|
24
|
+
.map((entry) => entry.name)
|
|
25
|
+
.sort()
|
|
26
|
+
.map((name) => ({
|
|
27
|
+
name,
|
|
28
|
+
content: fs.readFileSync(path.join(contentDirectory, name), 'utf8').trim()
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function invalidReference(expression, line) {
|
|
33
|
+
return new AgentsInstructionsError(
|
|
34
|
+
`invalid fragment reference {{ ${expression} }} at line ${line} of the agents base template; ` +
|
|
35
|
+
'use {{ fragments["name.block.md"] }} or {{ blocks }}'
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function composeAgentsInstructions(contentDirectory) {
|
|
40
|
+
const basePath = path.join(contentDirectory, 'AGENTS.md');
|
|
41
|
+
if (!fs.existsSync(basePath)) {
|
|
42
|
+
throw new AgentsInstructionsError(`missing agents base template: ${basePath}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const base = fs.readFileSync(basePath, 'utf8');
|
|
46
|
+
const blocks = discoverBlocks(contentDirectory);
|
|
47
|
+
const blocksByName = new Map(blocks.map((block) => [block.name, block.content]));
|
|
48
|
+
|
|
49
|
+
const references = [];
|
|
50
|
+
for (const match of base.matchAll(referencePattern)) {
|
|
51
|
+
const expression = match[1];
|
|
52
|
+
const line = base.slice(0, match.index).split('\n').length;
|
|
53
|
+
if (expression === aggregateExpression) {
|
|
54
|
+
references.push({ kind: 'aggregate', start: match.index, end: match.index + match[0].length });
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const named = expression.match(namedReferencePattern);
|
|
58
|
+
if (!named || named[2].includes('/') || named[2].includes('\\')) {
|
|
59
|
+
throw invalidReference(expression, line);
|
|
60
|
+
}
|
|
61
|
+
references.push({ kind: 'named', name: named[2], start: match.index, end: match.index + match[0].length });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const reference of references) {
|
|
65
|
+
if (reference.kind !== 'named' || blocksByName.has(reference.name)) continue;
|
|
66
|
+
if (fs.existsSync(path.join(contentDirectory, reference.name))) {
|
|
67
|
+
throw new AgentsInstructionsError(
|
|
68
|
+
`agents base template references ${reference.name}, which is not a *.block.md fragment`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
throw new AgentsInstructionsError(`agents base template references missing fragment: ${reference.name}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const referencedNames = new Set(
|
|
75
|
+
references.filter((reference) => reference.kind === 'named').map((reference) => reference.name)
|
|
76
|
+
);
|
|
77
|
+
const aggregate = blocks
|
|
78
|
+
.filter((block) => !referencedNames.has(block.name))
|
|
79
|
+
.map((block) => block.content)
|
|
80
|
+
.join('\n\n');
|
|
81
|
+
|
|
82
|
+
let content = '';
|
|
83
|
+
let cursor = 0;
|
|
84
|
+
for (const reference of references) {
|
|
85
|
+
content += base.slice(cursor, reference.start);
|
|
86
|
+
content += reference.kind === 'aggregate' ? aggregate : blocksByName.get(reference.name);
|
|
87
|
+
cursor = reference.end;
|
|
88
|
+
}
|
|
89
|
+
content += base.slice(cursor);
|
|
90
|
+
|
|
91
|
+
return { content };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function regionText(version, composed) {
|
|
95
|
+
return `${regionBeginPrefix}${version} -->\n${composed}\n${regionEndLine}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function ambiguous(message) {
|
|
99
|
+
return new AgentsInstructionsError(
|
|
100
|
+
`refusing to update agents instructions: ${message}; fix or remove the affected block manually, then rerun skillshop agents`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function appendRegion(existing, region) {
|
|
105
|
+
if (existing === '') return `${region}\n`;
|
|
106
|
+
const separator = existing.endsWith('\n\n') ? '' : existing.endsWith('\n') ? '\n' : '\n\n';
|
|
107
|
+
return `${existing}${separator}${region}\n`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function applyManagedRegion(lines, starts, ends, region, composed, version) {
|
|
111
|
+
const [start] = starts;
|
|
112
|
+
const [end] = ends;
|
|
113
|
+
const inner = lines.slice(start + 1, end).join('\n');
|
|
114
|
+
const installedVersion = lines[start].match(/<!-- (?:skillshop|skillz) agents: begin v(\S+) -->/)[1];
|
|
115
|
+
if (installedVersion === version && inner === composed && lines[start].startsWith(regionBeginPrefix) && lines[end] === regionEndLine) {
|
|
116
|
+
return { action: 'unchanged', text: lines.join('\n') };
|
|
117
|
+
}
|
|
118
|
+
const next = [...lines.slice(0, start), ...region.split('\n'), ...lines.slice(end + 1)];
|
|
119
|
+
return { action: 'refreshed', text: next.join('\n') };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function legacyChunks(lines, markerIndices) {
|
|
123
|
+
const legacyLines = legacyAgentsContent.split('\n');
|
|
124
|
+
const spans = [];
|
|
125
|
+
for (const markerIndex of markerIndices) {
|
|
126
|
+
const start = markerIndex - 1 - legacyLines.length;
|
|
127
|
+
const owned =
|
|
128
|
+
start >= 0 &&
|
|
129
|
+
lines.slice(start, start + legacyLines.length).join('\n') === legacyAgentsContent &&
|
|
130
|
+
lines[start + legacyLines.length] === '' &&
|
|
131
|
+
(start === 0 || lines[start - 1] === '');
|
|
132
|
+
if (!owned) {
|
|
133
|
+
throw new AgentsInstructionsError(
|
|
134
|
+
`refusing to update agents instructions: unrecognized legacy skillz agents marker at line ${markerIndex + 1} ` +
|
|
135
|
+
'(the installed guidance was edited or does not match the historical skillz content); ' +
|
|
136
|
+
'remove the installed legacy guidance manually, then rerun skillshop agents'
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
spans.push([start, markerIndex]);
|
|
140
|
+
}
|
|
141
|
+
spans.sort((a, b) => a[0] - b[0]);
|
|
142
|
+
return spans;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function migrateLegacy(lines, markerIndices, region, composed, version) {
|
|
146
|
+
const spans = legacyChunks(lines, markerIndices);
|
|
147
|
+
const insertPoint = '\u0000skillz-region-insert-point\u0000';
|
|
148
|
+
const remainder = [];
|
|
149
|
+
let cursor = 0;
|
|
150
|
+
for (const [start, end] of spans) {
|
|
151
|
+
remainder.push(...lines.slice(cursor, start));
|
|
152
|
+
if (end === spans[spans.length - 1][1]) remainder.push(insertPoint);
|
|
153
|
+
cursor = end + 1;
|
|
154
|
+
}
|
|
155
|
+
remainder.push(...lines.slice(cursor));
|
|
156
|
+
|
|
157
|
+
const collapsed = [];
|
|
158
|
+
for (const line of remainder) {
|
|
159
|
+
if (line === '' && line !== insertPoint && collapsed[collapsed.length - 1] === '') continue;
|
|
160
|
+
collapsed.push(line);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const insertAt = collapsed.indexOf(insertPoint);
|
|
164
|
+
const before = collapsed.slice(0, insertAt);
|
|
165
|
+
const after = collapsed.slice(insertAt + 1);
|
|
166
|
+
let text = before.join('\n');
|
|
167
|
+
if (text !== '' && !text.endsWith('\n\n')) text += text.endsWith('\n') ? '\n' : '\n\n';
|
|
168
|
+
text += `${region}\n`;
|
|
169
|
+
const afterText = after.join('\n');
|
|
170
|
+
if (afterText !== '') text += afterText.startsWith('\n') ? afterText : `\n\n${afterText}`;
|
|
171
|
+
return { action: 'installed', text: text.replace(/\n+$/, '\n') };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function applyInstructions(existing, composed, version) {
|
|
175
|
+
const composedText = composed.trimEnd();
|
|
176
|
+
const region = regionText(version, composedText);
|
|
177
|
+
const lines = existing.split('\n');
|
|
178
|
+
const begins = [];
|
|
179
|
+
const ends = [];
|
|
180
|
+
const legacyMarkers = [];
|
|
181
|
+
lines.forEach((line, index) => {
|
|
182
|
+
if (regionBeginPattern.test(line)) begins.push(index);
|
|
183
|
+
else if (line === regionEndLine || line === oldRegionEndLine) ends.push(index);
|
|
184
|
+
else if (legacyMarkerPattern.test(line)) legacyMarkers.push(index);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
if (begins.length !== ends.length || begins.length > 1) {
|
|
188
|
+
throw ambiguous(
|
|
189
|
+
'the managed agents block markers are malformed (each marker must appear exactly once on its own line)'
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
if (legacyMarkers.length && begins.length) {
|
|
193
|
+
throw ambiguous('the file contains both legacy skillz agents markers and a managed agents block');
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (begins.length) return applyManagedRegion(lines, begins, ends, region, composedText, version);
|
|
197
|
+
if (legacyMarkers.length) return migrateLegacy(lines, legacyMarkers, region, composedText, version);
|
|
198
|
+
return { action: 'installed', text: appendRegion(existing, region) };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function applyLocalFragments(existing, contentDirectory) {
|
|
202
|
+
const blocks = discoverBlocks(contentDirectory);
|
|
203
|
+
const lines = existing.split('\n');
|
|
204
|
+
const begins = lines.flatMap((line, index) => line === fragmentsBegin ? [index] : []);
|
|
205
|
+
const ends = lines.flatMap((line, index) => line === fragmentsEnd ? [index] : []);
|
|
206
|
+
if (begins.length !== ends.length || begins.length > 1 || (begins.length && begins[0] >= ends[0])) {
|
|
207
|
+
throw new AgentsInstructionsError('local fragment markers are malformed');
|
|
208
|
+
}
|
|
209
|
+
if (!blocks.length && !begins.length) return { action: 'unchanged', text: existing };
|
|
210
|
+
const region = blocks.length
|
|
211
|
+
? `${fragmentsBegin}\n${blocks.map((block) => block.content).join('\n\n')}\n${fragmentsEnd}`
|
|
212
|
+
: '';
|
|
213
|
+
if (!begins.length) return { action: 'installed', text: appendRegion(existing, region) };
|
|
214
|
+
const current = lines.slice(begins[0], ends[0] + 1).join('\n');
|
|
215
|
+
if (current === region) return { action: 'unchanged', text: existing };
|
|
216
|
+
const before = lines.slice(0, begins[0]);
|
|
217
|
+
const after = lines.slice(ends[0] + 1);
|
|
218
|
+
return { action: 'refreshed', text: [...before, ...(region ? region.split('\n') : []), ...after].join('\n') };
|
|
219
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import fs from 'node:fs';
|
|
4
3
|
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
5
|
import { buildSkillLibrary, initSkillLibrary, readSkillLibrary, validateSkillLibrary } from './library.js';
|
|
6
6
|
import { exportSkill, reexportSkill, unexportSkill } from './export.js';
|
|
7
7
|
import { importSkill, resolveSkill } from './import.js';
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
import { installDeclaredUserSkills } from './user-install.js';
|
|
9
|
+
import { reconcileLegacyState, runLegacyCli } from './legacy-cli.js';
|
|
10
|
+
import { parseLibrarySkillReference, skillName } from './specifier.js';
|
|
10
11
|
|
|
11
12
|
function help() {
|
|
12
|
-
console.log(`
|
|
13
|
+
console.log(`
|
|
14
|
+
Library commands:
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
skillshop
|
|
16
|
+
skillshop library init
|
|
17
|
+
skillshop library list
|
|
16
18
|
skillshop export skill:<name> <directory>
|
|
17
19
|
skillshop export skill:<name> --from <package> [--skill skill:<source-name>]
|
|
18
20
|
skillshop unexport skill:<name>
|
|
19
|
-
skillshop list
|
|
20
21
|
skillshop validate
|
|
21
22
|
skillshop build
|
|
22
|
-
skillshop resolve <library
|
|
23
|
-
skillshop import <library
|
|
23
|
+
skillshop resolve <library>:<skill>
|
|
24
|
+
skillshop import <library>:<skill> [--global] [--to <directory>] [--force]
|
|
25
|
+
skillshop install-user [package-dir] [--if-global] [--to <skills-dir>]
|
|
24
26
|
|
|
25
|
-
A library publishes skills by portable specifier, for example skill:
|
|
26
|
-
|
|
27
|
+
A library publishes skills by portable specifier, for example skill:review.
|
|
28
|
+
Import uses the short <library>:<skill> form and copies to .agents/skills/<skill> by default.
|
|
29
|
+
--global copies to ~/.agents/skills/<skill> and replaces an existing directory.`);
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
function flag(args, name) {
|
|
@@ -31,10 +34,29 @@ function flag(args, name) {
|
|
|
31
34
|
return index === -1 ? null : args[index + 1];
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
function librarySelection(args, usage) {
|
|
38
|
+
if (args.length === 1) return { reference: args[0] };
|
|
39
|
+
if (args.length === 2 && args[1].startsWith('skill:')) {
|
|
40
|
+
return { library: args[0], specifier: args[1] };
|
|
41
|
+
}
|
|
42
|
+
throw new Error(usage);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const libraryCommands = new Set(['export', 'unexport', 'validate', 'build', 'resolve', 'import', 'install-user']);
|
|
46
|
+
|
|
47
|
+
async function main(argv, { libraryNamespace = false } = {}) {
|
|
35
48
|
const [command, ...args] = argv;
|
|
36
|
-
if (!
|
|
37
|
-
|
|
49
|
+
if (!libraryNamespace && command === 'library') {
|
|
50
|
+
if (args[0] === 'init' || args[0] === 'list') return main(args, { libraryNamespace: true });
|
|
51
|
+
if (!args.length || args[0] === '--help' || args[0] === '-h') return help();
|
|
52
|
+
throw new Error('Usage: skillshop library init | list');
|
|
53
|
+
}
|
|
54
|
+
if (!libraryNamespace && (!command || command === '--help' || command === '-h' || command === 'help')) {
|
|
55
|
+
await runLegacyCli(['--help']);
|
|
56
|
+
return help();
|
|
57
|
+
}
|
|
58
|
+
if (!libraryNamespace && (command === '--version' || command === '-v')) return runLegacyCli(argv);
|
|
59
|
+
if (!libraryNamespace && !libraryCommands.has(command)) return runLegacyCli(argv);
|
|
38
60
|
|
|
39
61
|
if (command === 'init') {
|
|
40
62
|
initSkillLibrary();
|
|
@@ -75,22 +97,64 @@ async function main(argv) {
|
|
|
75
97
|
return;
|
|
76
98
|
}
|
|
77
99
|
if (command === 'resolve') {
|
|
78
|
-
|
|
79
|
-
console.log(JSON.stringify(await resolveSkill(
|
|
100
|
+
const selection = librarySelection(args, 'Usage: skillshop resolve <library>:<skill>');
|
|
101
|
+
console.log(JSON.stringify(await resolveSkill(selection), null, 2));
|
|
80
102
|
return;
|
|
81
103
|
}
|
|
82
104
|
if (command === 'import') {
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
105
|
+
const usage = 'Usage: skillshop import <library>:<skill> [--global] [--to <directory>] [--force]';
|
|
106
|
+
const positional = [];
|
|
107
|
+
let targetDir;
|
|
108
|
+
let globalInstall = false;
|
|
109
|
+
let force = false;
|
|
110
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
111
|
+
const argument = args[index];
|
|
112
|
+
if (argument === '--to') {
|
|
113
|
+
targetDir = args[++index];
|
|
114
|
+
if (!targetDir || targetDir.startsWith('--')) throw new Error(usage);
|
|
115
|
+
} else if (argument === '--global' || argument === '-g') globalInstall = true;
|
|
116
|
+
else if (argument === '--force') force = true;
|
|
117
|
+
else if (argument.startsWith('-')) throw new Error(usage);
|
|
118
|
+
else positional.push(argument);
|
|
119
|
+
}
|
|
120
|
+
const selection = librarySelection(positional, usage);
|
|
121
|
+
if (globalInstall && targetDir) throw new Error('--global and --to cannot be combined');
|
|
122
|
+
const name = selection.reference
|
|
123
|
+
? parseLibrarySkillReference(selection.reference).name
|
|
124
|
+
: skillName(selection.specifier);
|
|
125
|
+
const result = await importSkill({
|
|
126
|
+
...selection,
|
|
127
|
+
targetDir: globalInstall ? path.join(os.homedir(), '.agents', 'skills', name) : targetDir,
|
|
128
|
+
overwrite: force || globalInstall,
|
|
129
|
+
});
|
|
87
130
|
console.log(`${result.specifier} -> ${result.targetDir}`);
|
|
88
131
|
return;
|
|
89
132
|
}
|
|
90
|
-
|
|
133
|
+
if (command === 'install-user') {
|
|
134
|
+
if (args.includes('--if-global') && process.env.npm_config_global !== 'true') {
|
|
135
|
+
console.log('Skipped user skills: this is not a global npm install');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const library = args[0] && !args[0].startsWith('--') ? args[0] : '.';
|
|
139
|
+
const userSkillsDir = flag(args, '--to') || undefined;
|
|
140
|
+
const results = await installDeclaredUserSkills({ library, userSkillsDir });
|
|
141
|
+
for (const result of results) console.log(`${result.specifier} -> ${result.targetDir}${result.updated ? ' (updated)' : ''}`);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
throw new Error(`Unknown library command: ${command}`);
|
|
91
145
|
}
|
|
92
146
|
|
|
93
|
-
|
|
147
|
+
const argv = process.argv.slice(2);
|
|
148
|
+
const usingLibraryCommands = argv[0] === 'library' || libraryCommands.has(argv[0]);
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
if (usingLibraryCommands) reconcileLegacyState();
|
|
152
|
+
try {
|
|
153
|
+
await main(argv);
|
|
154
|
+
} finally {
|
|
155
|
+
if (usingLibraryCommands) reconcileLegacyState();
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
94
158
|
console.error(`skillshop: ${error.message}`);
|
|
95
159
|
process.exitCode = 1;
|
|
96
|
-
}
|
|
160
|
+
}
|
package/src/import.js
CHANGED
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { createHash } from 'node:crypto';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
5
|
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
6
|
-
import { normalizeSkillSpecifier, parseSkillSpecifier } from './specifier.js';
|
|
6
|
+
import { normalizeSkillSpecifier, parseSkillSpecifier, parseLibrarySkillReference } from './specifier.js';
|
|
7
7
|
import { SKILLSHOP_MANIFEST_FILE, SKILLSHOP_SCHEMA_VERSION } from './library.js';
|
|
8
8
|
|
|
9
9
|
async function importManifestFile(file) {
|
|
@@ -42,9 +42,13 @@ export async function loadSkillLibrary(library, { cwd = process.cwd() } = {}) {
|
|
|
42
42
|
return validatePublishedManifest(await importManifestFile(resolved), value);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export async function resolveSkill({ library, specifier, cwd = process.cwd() }) {
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
export async function resolveSkill({ reference, library, specifier, cwd = process.cwd() }) {
|
|
46
|
+
if (reference && (library !== undefined || specifier !== undefined)) {
|
|
47
|
+
throw new Error('Use reference or library and specifier, not both');
|
|
48
|
+
}
|
|
49
|
+
const selected = reference ? parseLibrarySkillReference(reference) : { library, specifier: normalizeSkillSpecifier(specifier) };
|
|
50
|
+
const normalized = selected.specifier;
|
|
51
|
+
const manifest = await loadSkillLibrary(selected.library, { cwd });
|
|
48
52
|
const href = manifest.skills[normalized];
|
|
49
53
|
if (typeof href !== 'string' || !href) throw new Error(`${normalized} is not exported by ${manifest.packageName || 'the library'}`);
|
|
50
54
|
const url = new URL(href);
|
|
@@ -84,6 +88,7 @@ function assertSafeMergeTarget(target, relativeFile) {
|
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
export async function importSkill({
|
|
91
|
+
reference,
|
|
87
92
|
library,
|
|
88
93
|
specifier,
|
|
89
94
|
targetDir,
|
|
@@ -91,10 +96,10 @@ export async function importSkill({
|
|
|
91
96
|
overwrite = false,
|
|
92
97
|
merge = false,
|
|
93
98
|
}) {
|
|
94
|
-
if (typeof targetDir !== 'string' || !targetDir.trim()) throw new Error('targetDir
|
|
99
|
+
if (targetDir !== undefined && (typeof targetDir !== 'string' || !targetDir.trim())) throw new Error('targetDir must be a nonempty path');
|
|
95
100
|
if (merge && overwrite) throw new Error('merge and overwrite cannot both be enabled');
|
|
96
|
-
const skill = await resolveSkill({ library, specifier, cwd });
|
|
97
|
-
const target = path.resolve(cwd, targetDir);
|
|
101
|
+
const skill = await resolveSkill({ reference, library, specifier, cwd });
|
|
102
|
+
const target = path.resolve(cwd, targetDir ?? path.join('.agents', 'skills', skill.name));
|
|
98
103
|
const exists = fs.existsSync(target);
|
|
99
104
|
|
|
100
105
|
if (exists && merge) {
|
package/src/index.js
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// Snapshot of agents-content/AGENTS.md as shipped through skillz 0.3.x.
|
|
2
|
+
// Installations made before the managed-block format appended this content verbatim
|
|
3
|
+
// followed by a "<!-- skillz agents: AGENTS.md@<version> -->" marker line.
|
|
4
|
+
export const legacyAgentsContent = [
|
|
5
|
+
"### Baseline tone",
|
|
6
|
+
"",
|
|
7
|
+
"This applies to every text that you write.",
|
|
8
|
+
"",
|
|
9
|
+
"Adopt a skeptical, questioning approach. Don't default to praise, criticize my ideas, provide counterpoints, dig deeper. I need you to find the blind spots in my thinking and account for them, in a straight shooting and constructive way.",
|
|
10
|
+
"",
|
|
11
|
+
"Don't overuse adjectives, don't compliment or criticize my arguments in a personalized way, just attend to the facts and topics. Tell it like it is; don't sugar-coat responses. Also don't explicitly mention you're NOT praising me or actively say you're “getting to the point”. Don't comment on your tone at all.",
|
|
12
|
+
"",
|
|
13
|
+
"When explaining things, provide depth of concepts, be extensive, with context, clarity and explanation of concepts along the way.",
|
|
14
|
+
"",
|
|
15
|
+
"## Communication and writing",
|
|
16
|
+
"",
|
|
17
|
+
"- Write directly and concisely. Prefer short, distinct sentences over long compound ones.",
|
|
18
|
+
"- Do not overuse em dashes. There are periods, commas, semicolons, and exclamation marks. Use them.",
|
|
19
|
+
"- Remove hedges and filler that add no information. Keep technical terms when they add precision.",
|
|
20
|
+
"- Avoid exact counts unless the number matters. \"All the callers\" is usually better than \"all 4 callers.\"",
|
|
21
|
+
"- Short, natural reactions are fine. Use \"Good catch!\" rather than an inflated response.",
|
|
22
|
+
"- Avoid throat clearing, restating the question, routine process narration, and closing offers. Brief accountability or correction is fine when it helps the reader.",
|
|
23
|
+
"- Do not explain in prose what a table or diff already makes clear.",
|
|
24
|
+
"- State genuine uncertainty briefly. Do not add defensive caveats or pre-empt objections nobody raised.",
|
|
25
|
+
"- Brevity must not hide important behavior, risk, uncertainty, or evidence.",
|
|
26
|
+
"",
|
|
27
|
+
"### Approval for external actions",
|
|
28
|
+
"",
|
|
29
|
+
"All external actions require explicit approval before proceeding. Even on autopilot mode, do not post without explicit written authorizaton from the user.",
|
|
30
|
+
"",
|
|
31
|
+
"- Before posting or editing a PR comment, review reply, issue comment, Slack message, or other external message, show me the exact draft and get an explicit OK. This includes edits to existing messages.",
|
|
32
|
+
"- Before resolving review threads, requesting reviewers, or changing other visible review state, describe the exact action and get an explicit OK.",
|
|
33
|
+
"- Create an issue, epic, or PR only when I explicitly request it. Before opening one, show me its proposed title and body and get an explicit OK.",
|
|
34
|
+
"- A direct request to update an existing PR or issue body authorizes that update without another approval. Fetch the latest body from the host immediately before editing it.",
|
|
35
|
+
"- Pushing requested commits does not require another approval."
|
|
36
|
+
].join("\n");
|