@anytio/pspm 0.0.7 → 0.1.1
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/LICENSE +149 -0
- package/README.md +62 -15
- package/dist/index.js +1871 -447
- package/dist/index.js.map +1 -1
- package/package.json +67 -67
package/dist/index.js
CHANGED
|
@@ -1,31 +1,39 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync } from 'fs';
|
|
3
|
-
import { dirname, join, basename, relative } from 'path';
|
|
4
|
-
import { fileURLToPath, URL as URL$1 } from 'url';
|
|
5
|
-
import { Command } from 'commander';
|
|
6
2
|
import { createHash, randomBytes } from 'crypto';
|
|
7
3
|
import * as semver from 'semver';
|
|
8
|
-
import { stat, writeFile, readdir, mkdir, rm, rename, access as access$1, readFile, unlink } from 'fs/promises';
|
|
4
|
+
import { stat, writeFile, readdir, mkdir, rm, rename, access as access$1, readFile, lstat, unlink, cp, readlink, symlink } from 'fs/promises';
|
|
9
5
|
import { homedir } from 'os';
|
|
6
|
+
import { dirname, join, basename, relative } from 'path';
|
|
10
7
|
import * as ini from 'ini';
|
|
8
|
+
import { checkbox } from '@inquirer/prompts';
|
|
9
|
+
import { readFileSync } from 'fs';
|
|
10
|
+
import { fileURLToPath, URL as URL$1 } from 'url';
|
|
11
|
+
import { Command } from 'commander';
|
|
12
|
+
import { createInterface } from 'readline';
|
|
11
13
|
import http from 'http';
|
|
12
14
|
import open from 'open';
|
|
13
15
|
import { exec as exec$1 } from 'child_process';
|
|
14
16
|
import { promisify } from 'util';
|
|
15
17
|
|
|
18
|
+
var __defProp = Object.defineProperty;
|
|
19
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
20
|
+
var __esm = (fn, res) => function __init() {
|
|
21
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
22
|
+
};
|
|
23
|
+
var __export = (target, all) => {
|
|
24
|
+
for (var name in all)
|
|
25
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
26
|
+
};
|
|
16
27
|
function calculateIntegrity(data) {
|
|
17
28
|
const hash = createHash("sha256").update(data).digest("base64");
|
|
18
29
|
return `sha256-${hash}`;
|
|
19
30
|
}
|
|
31
|
+
var init_integrity = __esm({
|
|
32
|
+
"../../packages/shared/pspm-types/src/integrity.ts"() {
|
|
33
|
+
}
|
|
34
|
+
});
|
|
20
35
|
|
|
21
36
|
// ../../packages/shared/pspm-types/src/manifest.ts
|
|
22
|
-
var DEFAULT_SKILL_FILES = [
|
|
23
|
-
"SKILL.md",
|
|
24
|
-
"runtime",
|
|
25
|
-
"scripts",
|
|
26
|
-
"data"
|
|
27
|
-
];
|
|
28
|
-
var PSPM_SCHEMA_URL = "https://pspm.dev/schema/pspm.json";
|
|
29
37
|
function validateManifest(manifest) {
|
|
30
38
|
if (!manifest.name) {
|
|
31
39
|
return { valid: false, error: "Manifest must have a 'name' field" };
|
|
@@ -47,9 +55,20 @@ function validateManifest(manifest) {
|
|
|
47
55
|
}
|
|
48
56
|
return { valid: true };
|
|
49
57
|
}
|
|
58
|
+
var DEFAULT_SKILL_FILES, PSPM_SCHEMA_URL;
|
|
59
|
+
var init_manifest = __esm({
|
|
60
|
+
"../../packages/shared/pspm-types/src/manifest.ts"() {
|
|
61
|
+
DEFAULT_SKILL_FILES = [
|
|
62
|
+
"SKILL.md",
|
|
63
|
+
"runtime",
|
|
64
|
+
"scripts",
|
|
65
|
+
"data"
|
|
66
|
+
];
|
|
67
|
+
PSPM_SCHEMA_URL = "https://pspm.dev/schema/v1/pspm.json";
|
|
68
|
+
}
|
|
69
|
+
});
|
|
50
70
|
|
|
51
71
|
// ../../packages/shared/pspm-types/src/specifier.ts
|
|
52
|
-
var SPECIFIER_PATTERN = /^@user\/([a-zA-Z0-9_-]+)\/([a-z][a-z0-9_-]*)(?:@(.+))?$/;
|
|
53
72
|
function parseSkillSpecifier(specifier) {
|
|
54
73
|
const match = specifier.match(SPECIFIER_PATTERN);
|
|
55
74
|
if (!match) {
|
|
@@ -61,6 +80,47 @@ function parseSkillSpecifier(specifier) {
|
|
|
61
80
|
versionRange: match[3]
|
|
62
81
|
};
|
|
63
82
|
}
|
|
83
|
+
function parseGitHubSpecifier(specifier) {
|
|
84
|
+
const match = specifier.match(GITHUB_SPECIFIER_PATTERN);
|
|
85
|
+
if (!match) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const [, owner, repo, pathWithSlash, ref] = match;
|
|
89
|
+
return {
|
|
90
|
+
owner,
|
|
91
|
+
repo,
|
|
92
|
+
// Remove leading slash from path
|
|
93
|
+
path: pathWithSlash ? pathWithSlash.slice(1) : void 0,
|
|
94
|
+
ref: ref || void 0
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function formatGitHubSpecifier(spec) {
|
|
98
|
+
let result = `github:${spec.owner}/${spec.repo}`;
|
|
99
|
+
if (spec.path) {
|
|
100
|
+
result += `/${spec.path}`;
|
|
101
|
+
}
|
|
102
|
+
if (spec.ref) {
|
|
103
|
+
result += `@${spec.ref}`;
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
function getGitHubSkillName(spec) {
|
|
108
|
+
if (spec.path) {
|
|
109
|
+
const segments = spec.path.split("/").filter(Boolean);
|
|
110
|
+
return segments[segments.length - 1];
|
|
111
|
+
}
|
|
112
|
+
return spec.repo;
|
|
113
|
+
}
|
|
114
|
+
function isGitHubSpecifier(specifier) {
|
|
115
|
+
return specifier.startsWith("github:");
|
|
116
|
+
}
|
|
117
|
+
var SPECIFIER_PATTERN, GITHUB_SPECIFIER_PATTERN;
|
|
118
|
+
var init_specifier = __esm({
|
|
119
|
+
"../../packages/shared/pspm-types/src/specifier.ts"() {
|
|
120
|
+
SPECIFIER_PATTERN = /^@user\/([a-zA-Z0-9_-]+)\/([a-z][a-z0-9_-]*)(?:@(.+))?$/;
|
|
121
|
+
GITHUB_SPECIFIER_PATTERN = /^github:([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+)(\/[^@]+)?(?:@(.+))?$/;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
64
124
|
function resolveVersion(range, availableVersions) {
|
|
65
125
|
const sorted = availableVersions.filter((v) => semver.valid(v)).sort((a, b) => semver.rcompare(a, b));
|
|
66
126
|
if (!range || range === "latest" || range === "*") {
|
|
@@ -68,9 +128,22 @@ function resolveVersion(range, availableVersions) {
|
|
|
68
128
|
}
|
|
69
129
|
return semver.maxSatisfying(sorted, range);
|
|
70
130
|
}
|
|
131
|
+
var init_version = __esm({
|
|
132
|
+
"../../packages/shared/pspm-types/src/version.ts"() {
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ../../packages/shared/pspm-types/src/index.ts
|
|
137
|
+
var init_src = __esm({
|
|
138
|
+
"../../packages/shared/pspm-types/src/index.ts"() {
|
|
139
|
+
init_integrity();
|
|
140
|
+
init_manifest();
|
|
141
|
+
init_specifier();
|
|
142
|
+
init_version();
|
|
143
|
+
}
|
|
144
|
+
});
|
|
71
145
|
|
|
72
146
|
// ../../packages/sdk/src/fetchers/cli-fetcher.ts
|
|
73
|
-
var config = null;
|
|
74
147
|
function configure(options) {
|
|
75
148
|
config = options;
|
|
76
149
|
}
|
|
@@ -109,84 +182,104 @@ async function customFetch(url, options) {
|
|
|
109
182
|
headers: response.headers
|
|
110
183
|
};
|
|
111
184
|
}
|
|
185
|
+
var config;
|
|
186
|
+
var init_cli_fetcher = __esm({
|
|
187
|
+
"../../packages/sdk/src/fetchers/cli-fetcher.ts"() {
|
|
188
|
+
config = null;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
112
191
|
|
|
113
192
|
// ../../packages/sdk/src/generated/fetch/index.ts
|
|
114
|
-
var getMeUrl
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
193
|
+
var getMeUrl, me, getListSkillVersionsUrl, listSkillVersions, getGetSkillVersionUrl, getSkillVersion, getPublishSkillUrl, publishSkill, getDeleteSkillUrl, deleteSkill, getDeleteSkillVersionUrl, deleteSkillVersion;
|
|
194
|
+
var init_fetch = __esm({
|
|
195
|
+
"../../packages/sdk/src/generated/fetch/index.ts"() {
|
|
196
|
+
init_cli_fetcher();
|
|
197
|
+
getMeUrl = () => {
|
|
198
|
+
return `/api/skills/me`;
|
|
199
|
+
};
|
|
200
|
+
me = async (options) => {
|
|
201
|
+
return customFetch(
|
|
202
|
+
getMeUrl(),
|
|
203
|
+
{
|
|
204
|
+
...options,
|
|
205
|
+
method: "GET"
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
};
|
|
209
|
+
getListSkillVersionsUrl = (username, name) => {
|
|
210
|
+
return `/api/skills/@user/${username}/${name}/versions`;
|
|
211
|
+
};
|
|
212
|
+
listSkillVersions = async (username, name, options) => {
|
|
213
|
+
return customFetch(
|
|
214
|
+
getListSkillVersionsUrl(username, name),
|
|
215
|
+
{
|
|
216
|
+
...options,
|
|
217
|
+
method: "GET"
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
};
|
|
221
|
+
getGetSkillVersionUrl = (username, name, version2) => {
|
|
222
|
+
return `/api/skills/@user/${username}/${name}/${version2}`;
|
|
223
|
+
};
|
|
224
|
+
getSkillVersion = async (username, name, version2, options) => {
|
|
225
|
+
return customFetch(
|
|
226
|
+
getGetSkillVersionUrl(username, name, version2),
|
|
227
|
+
{
|
|
228
|
+
...options,
|
|
229
|
+
method: "GET"
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
getPublishSkillUrl = () => {
|
|
234
|
+
return `/api/skills/publish`;
|
|
235
|
+
};
|
|
236
|
+
publishSkill = async (publishSkillInput, options) => {
|
|
237
|
+
return customFetch(
|
|
238
|
+
getPublishSkillUrl(),
|
|
239
|
+
{
|
|
240
|
+
...options,
|
|
241
|
+
method: "POST",
|
|
242
|
+
headers: { "Content-Type": "application/json", ...options?.headers },
|
|
243
|
+
body: JSON.stringify(
|
|
244
|
+
publishSkillInput
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
};
|
|
249
|
+
getDeleteSkillUrl = (name) => {
|
|
250
|
+
return `/api/skills/${name}`;
|
|
251
|
+
};
|
|
252
|
+
deleteSkill = async (name, options) => {
|
|
253
|
+
return customFetch(
|
|
254
|
+
getDeleteSkillUrl(name),
|
|
255
|
+
{
|
|
256
|
+
...options,
|
|
257
|
+
method: "DELETE"
|
|
258
|
+
}
|
|
259
|
+
);
|
|
260
|
+
};
|
|
261
|
+
getDeleteSkillVersionUrl = (name, version2) => {
|
|
262
|
+
return `/api/skills/${name}/${version2}`;
|
|
263
|
+
};
|
|
264
|
+
deleteSkillVersion = async (name, version2, options) => {
|
|
265
|
+
return customFetch(
|
|
266
|
+
getDeleteSkillVersionUrl(name, version2),
|
|
267
|
+
{
|
|
268
|
+
...options,
|
|
269
|
+
method: "DELETE"
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// ../../packages/sdk/src/index.ts
|
|
277
|
+
var init_src2 = __esm({
|
|
278
|
+
"../../packages/sdk/src/index.ts"() {
|
|
279
|
+
init_cli_fetcher();
|
|
280
|
+
init_fetch();
|
|
281
|
+
}
|
|
282
|
+
});
|
|
190
283
|
|
|
191
284
|
// src/api-client.ts
|
|
192
285
|
function registryUrlToBaseUrl(registryUrl) {
|
|
@@ -308,22 +401,13 @@ async function changeSkillAccess(skillName, input) {
|
|
|
308
401
|
};
|
|
309
402
|
}
|
|
310
403
|
}
|
|
404
|
+
var init_api_client = __esm({
|
|
405
|
+
"src/api-client.ts"() {
|
|
406
|
+
init_src2();
|
|
407
|
+
}
|
|
408
|
+
});
|
|
311
409
|
|
|
312
410
|
// src/errors.ts
|
|
313
|
-
var ConfigError = class extends Error {
|
|
314
|
-
constructor(message) {
|
|
315
|
-
super(message);
|
|
316
|
-
this.name = "ConfigError";
|
|
317
|
-
}
|
|
318
|
-
};
|
|
319
|
-
var NotLoggedInError = class extends ConfigError {
|
|
320
|
-
constructor() {
|
|
321
|
-
super(
|
|
322
|
-
"Not logged in. Run 'pspm login --api-key <key>' first, or set PSPM_API_KEY env var."
|
|
323
|
-
);
|
|
324
|
-
this.name = "NotLoggedInError";
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
411
|
function extractApiErrorMessage(response, fallbackMessage) {
|
|
328
412
|
const errorData = response.data;
|
|
329
413
|
if (process.env.PSPM_DEBUG) {
|
|
@@ -358,9 +442,25 @@ ${issueMessages}`;
|
|
|
358
442
|
}
|
|
359
443
|
return errorMessage;
|
|
360
444
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
445
|
+
var ConfigError, NotLoggedInError;
|
|
446
|
+
var init_errors = __esm({
|
|
447
|
+
"src/errors.ts"() {
|
|
448
|
+
ConfigError = class extends Error {
|
|
449
|
+
constructor(message) {
|
|
450
|
+
super(message);
|
|
451
|
+
this.name = "ConfigError";
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
NotLoggedInError = class extends ConfigError {
|
|
455
|
+
constructor() {
|
|
456
|
+
super(
|
|
457
|
+
"Not logged in. Run 'pspm login --api-key <key>' first, or set PSPM_API_KEY env var."
|
|
458
|
+
);
|
|
459
|
+
this.name = "NotLoggedInError";
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
});
|
|
364
464
|
function getConfigPath() {
|
|
365
465
|
return join(homedir(), ".pspmrc");
|
|
366
466
|
}
|
|
@@ -630,88 +730,261 @@ async function getRegistryUrl() {
|
|
|
630
730
|
const resolved = await resolveConfig();
|
|
631
731
|
return resolved.registryUrl;
|
|
632
732
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
733
|
+
var DEFAULT_REGISTRY_URL;
|
|
734
|
+
var init_config = __esm({
|
|
735
|
+
"src/config.ts"() {
|
|
736
|
+
init_errors();
|
|
737
|
+
DEFAULT_REGISTRY_URL = "https://pspm.dev";
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
function resolveAgentConfig(name, overrides) {
|
|
741
|
+
if (overrides?.[name]) {
|
|
742
|
+
return overrides[name];
|
|
743
|
+
}
|
|
744
|
+
if (name in DEFAULT_AGENT_CONFIGS) {
|
|
745
|
+
return DEFAULT_AGENT_CONFIGS[name];
|
|
746
|
+
}
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
function parseAgentArg(agentArg) {
|
|
750
|
+
if (!agentArg) {
|
|
751
|
+
return [...DEFAULT_AGENTS];
|
|
752
|
+
}
|
|
753
|
+
if (agentArg === "none") {
|
|
754
|
+
return ["none"];
|
|
755
|
+
}
|
|
756
|
+
return agentArg.split(",").map((a) => a.trim()).filter(Boolean);
|
|
757
|
+
}
|
|
758
|
+
function getAvailableAgents(overrides) {
|
|
759
|
+
const builtIn = Object.keys(DEFAULT_AGENT_CONFIGS);
|
|
760
|
+
const custom = overrides ? Object.keys(overrides) : [];
|
|
761
|
+
return [.../* @__PURE__ */ new Set([...builtIn, ...custom])];
|
|
762
|
+
}
|
|
763
|
+
async function promptForAgents() {
|
|
764
|
+
const choices = ALL_AGENTS.map((agent) => ({
|
|
765
|
+
name: `${AGENT_INFO[agent].displayName} (${AGENT_INFO[agent].skillsDir})`,
|
|
766
|
+
value: agent,
|
|
767
|
+
checked: true
|
|
768
|
+
// All selected by default
|
|
769
|
+
}));
|
|
770
|
+
const selected = await checkbox({
|
|
771
|
+
message: "Select agents to install skills to",
|
|
772
|
+
choices
|
|
773
|
+
});
|
|
774
|
+
if (selected.length === 0) {
|
|
775
|
+
return ["none"];
|
|
776
|
+
}
|
|
777
|
+
return selected;
|
|
778
|
+
}
|
|
779
|
+
var AGENT_INFO, DEFAULT_AGENT_CONFIGS, ALL_AGENTS, DEFAULT_AGENTS;
|
|
780
|
+
var init_agents = __esm({
|
|
781
|
+
"src/agents.ts"() {
|
|
782
|
+
AGENT_INFO = {
|
|
783
|
+
"claude-code": {
|
|
784
|
+
displayName: "Claude Code",
|
|
785
|
+
skillsDir: ".claude/skills"
|
|
786
|
+
},
|
|
787
|
+
codex: {
|
|
788
|
+
displayName: "Codex",
|
|
789
|
+
skillsDir: ".codex/skills"
|
|
790
|
+
},
|
|
791
|
+
cursor: {
|
|
792
|
+
displayName: "Cursor",
|
|
793
|
+
skillsDir: ".cursor/skills"
|
|
794
|
+
},
|
|
795
|
+
gemini: {
|
|
796
|
+
displayName: "Gemini CLI",
|
|
797
|
+
skillsDir: ".gemini/skills"
|
|
798
|
+
},
|
|
799
|
+
kiro: {
|
|
800
|
+
displayName: "Kiro CLI",
|
|
801
|
+
skillsDir: ".kiro/skills"
|
|
802
|
+
},
|
|
803
|
+
opencode: {
|
|
804
|
+
displayName: "OpenCode",
|
|
805
|
+
skillsDir: ".opencode/skills"
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
DEFAULT_AGENT_CONFIGS = {
|
|
809
|
+
"claude-code": { skillsDir: AGENT_INFO["claude-code"].skillsDir },
|
|
810
|
+
codex: { skillsDir: AGENT_INFO.codex.skillsDir },
|
|
811
|
+
cursor: { skillsDir: AGENT_INFO.cursor.skillsDir },
|
|
812
|
+
gemini: { skillsDir: AGENT_INFO.gemini.skillsDir },
|
|
813
|
+
kiro: { skillsDir: AGENT_INFO.kiro.skillsDir },
|
|
814
|
+
opencode: { skillsDir: AGENT_INFO.opencode.skillsDir }
|
|
815
|
+
};
|
|
816
|
+
ALL_AGENTS = [
|
|
817
|
+
"claude-code",
|
|
818
|
+
"codex",
|
|
819
|
+
"cursor",
|
|
820
|
+
"gemini",
|
|
821
|
+
"kiro",
|
|
822
|
+
"opencode"
|
|
823
|
+
];
|
|
824
|
+
DEFAULT_AGENTS = ALL_AGENTS;
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
function getGitHubHeaders() {
|
|
828
|
+
const headers = {
|
|
829
|
+
Accept: "application/vnd.github+json",
|
|
830
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
831
|
+
"User-Agent": "pspm-cli"
|
|
832
|
+
};
|
|
833
|
+
const token = process.env.GITHUB_TOKEN;
|
|
834
|
+
if (token) {
|
|
835
|
+
headers.Authorization = `Bearer ${token}`;
|
|
836
|
+
}
|
|
837
|
+
return headers;
|
|
838
|
+
}
|
|
839
|
+
async function resolveGitHubRef(owner, repo, ref) {
|
|
840
|
+
const headers = getGitHubHeaders();
|
|
841
|
+
if (!ref || ref === "latest") {
|
|
842
|
+
const repoUrl = `https://api.github.com/repos/${owner}/${repo}`;
|
|
843
|
+
const repoResponse = await fetch(repoUrl, { headers });
|
|
844
|
+
if (repoResponse.status === 404) {
|
|
845
|
+
throw new GitHubNotFoundError({ owner, repo });
|
|
846
|
+
}
|
|
847
|
+
if (repoResponse.status === 403) {
|
|
848
|
+
const remaining = repoResponse.headers.get("x-ratelimit-remaining");
|
|
849
|
+
if (remaining === "0") {
|
|
850
|
+
throw new GitHubRateLimitError();
|
|
851
|
+
}
|
|
642
852
|
}
|
|
643
|
-
if (!
|
|
644
|
-
|
|
645
|
-
process.exit(1);
|
|
853
|
+
if (!repoResponse.ok) {
|
|
854
|
+
throw new Error(`GitHub API error: ${repoResponse.status}`);
|
|
646
855
|
}
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
856
|
+
const repoData = await repoResponse.json();
|
|
857
|
+
ref = repoData.default_branch;
|
|
858
|
+
}
|
|
859
|
+
const commitUrl = `https://api.github.com/repos/${owner}/${repo}/commits/${ref}`;
|
|
860
|
+
const commitResponse = await fetch(commitUrl, { headers });
|
|
861
|
+
if (commitResponse.status === 404) {
|
|
862
|
+
throw new GitHubNotFoundError({ owner, repo, ref });
|
|
863
|
+
}
|
|
864
|
+
if (commitResponse.status === 403) {
|
|
865
|
+
const remaining = commitResponse.headers.get("x-ratelimit-remaining");
|
|
866
|
+
if (remaining === "0") {
|
|
867
|
+
throw new GitHubRateLimitError();
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
if (!commitResponse.ok) {
|
|
871
|
+
throw new Error(`GitHub API error: ${commitResponse.status}`);
|
|
872
|
+
}
|
|
873
|
+
const commitData = await commitResponse.json();
|
|
874
|
+
return commitData.sha;
|
|
875
|
+
}
|
|
876
|
+
async function downloadGitHubPackage(spec) {
|
|
877
|
+
const headers = getGitHubHeaders();
|
|
878
|
+
const commit = await resolveGitHubRef(spec.owner, spec.repo, spec.ref);
|
|
879
|
+
const tarballUrl = `https://api.github.com/repos/${spec.owner}/${spec.repo}/tarball/${commit}`;
|
|
880
|
+
const response = await fetch(tarballUrl, {
|
|
881
|
+
headers,
|
|
882
|
+
redirect: "follow"
|
|
883
|
+
});
|
|
884
|
+
if (response.status === 404) {
|
|
885
|
+
throw new GitHubNotFoundError(spec);
|
|
886
|
+
}
|
|
887
|
+
if (response.status === 403) {
|
|
888
|
+
const remaining = response.headers.get("x-ratelimit-remaining");
|
|
889
|
+
if (remaining === "0") {
|
|
890
|
+
throw new GitHubRateLimitError();
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
if (!response.ok) {
|
|
894
|
+
throw new Error(`Failed to download GitHub tarball: ${response.status}`);
|
|
895
|
+
}
|
|
896
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
897
|
+
const integrity = calculateIntegrity(buffer);
|
|
898
|
+
return { buffer, commit, integrity };
|
|
899
|
+
}
|
|
900
|
+
async function extractGitHubPackage(spec, buffer, skillsDir) {
|
|
901
|
+
const destPath = spec.path ? join(skillsDir, "_github", spec.owner, spec.repo, spec.path) : join(skillsDir, "_github", spec.owner, spec.repo);
|
|
902
|
+
const tempDir = join(skillsDir, "_github", ".temp", `${Date.now()}`);
|
|
903
|
+
await mkdir(tempDir, { recursive: true });
|
|
904
|
+
const tempFile = join(tempDir, "archive.tgz");
|
|
905
|
+
try {
|
|
906
|
+
await writeFile(tempFile, buffer);
|
|
907
|
+
const { exec: exec2 } = await import('child_process');
|
|
908
|
+
const { promisify: promisify2 } = await import('util');
|
|
909
|
+
const execAsync = promisify2(exec2);
|
|
910
|
+
await execAsync(`tar -xzf "${tempFile}" -C "${tempDir}"`);
|
|
911
|
+
const entries = await readdir(tempDir);
|
|
912
|
+
const extractedDir = entries.find(
|
|
913
|
+
(e) => e !== "archive.tgz" && !e.startsWith(".")
|
|
914
|
+
);
|
|
915
|
+
if (!extractedDir) {
|
|
916
|
+
throw new Error("Failed to find extracted directory in tarball");
|
|
917
|
+
}
|
|
918
|
+
const sourcePath = join(tempDir, extractedDir);
|
|
919
|
+
const copySource = spec.path ? join(sourcePath, spec.path) : sourcePath;
|
|
920
|
+
if (spec.path) {
|
|
921
|
+
const pathExists = await lstat(copySource).catch(() => null);
|
|
922
|
+
if (!pathExists) {
|
|
923
|
+
const rootEntries = await readdir(sourcePath);
|
|
924
|
+
const dirs = [];
|
|
925
|
+
for (const entry of rootEntries) {
|
|
926
|
+
const stat7 = await lstat(join(sourcePath, entry)).catch(() => null);
|
|
927
|
+
if (stat7?.isDirectory() && !entry.startsWith(".")) {
|
|
928
|
+
dirs.push(entry);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
throw new GitHubPathNotFoundError(spec, dirs);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
await rm(destPath, { recursive: true, force: true });
|
|
935
|
+
await mkdir(destPath, { recursive: true });
|
|
936
|
+
await cp(copySource, destPath, { recursive: true });
|
|
937
|
+
return spec.path ? `.pspm/skills/_github/${spec.owner}/${spec.repo}/${spec.path}` : `.pspm/skills/_github/${spec.owner}/${spec.repo}`;
|
|
938
|
+
} finally {
|
|
939
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
function getGitHubDisplayName(spec, commit) {
|
|
943
|
+
let name = `github:${spec.owner}/${spec.repo}`;
|
|
944
|
+
if (spec.path) {
|
|
945
|
+
name += `/${spec.path}`;
|
|
946
|
+
}
|
|
947
|
+
if (spec.ref || commit) {
|
|
948
|
+
const ref = spec.ref || "HEAD";
|
|
949
|
+
name += ` (${ref}${""})`;
|
|
950
|
+
}
|
|
951
|
+
return name;
|
|
952
|
+
}
|
|
953
|
+
var GitHubRateLimitError, GitHubNotFoundError, GitHubPathNotFoundError;
|
|
954
|
+
var init_github = __esm({
|
|
955
|
+
"src/github.ts"() {
|
|
956
|
+
init_src();
|
|
957
|
+
GitHubRateLimitError = class extends Error {
|
|
958
|
+
constructor() {
|
|
959
|
+
super(
|
|
960
|
+
"GitHub API rate limit exceeded. Set GITHUB_TOKEN environment variable for higher limits."
|
|
654
961
|
);
|
|
655
|
-
|
|
962
|
+
this.name = "GitHubRateLimitError";
|
|
656
963
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
join10(process.cwd(), "pspm.json"),
|
|
665
|
-
"utf-8"
|
|
964
|
+
};
|
|
965
|
+
GitHubNotFoundError = class extends Error {
|
|
966
|
+
constructor(spec) {
|
|
967
|
+
const path = spec.path ? `/${spec.path}` : "";
|
|
968
|
+
const ref = spec.ref ? `@${spec.ref}` : "";
|
|
969
|
+
super(
|
|
970
|
+
`GitHub repository not found: ${spec.owner}/${spec.repo}${path}${ref}`
|
|
666
971
|
);
|
|
667
|
-
|
|
668
|
-
} catch {
|
|
669
|
-
try {
|
|
670
|
-
const content = await readFile6(
|
|
671
|
-
join10(process.cwd(), "package.json"),
|
|
672
|
-
"utf-8"
|
|
673
|
-
);
|
|
674
|
-
manifest = JSON.parse(content);
|
|
675
|
-
} catch {
|
|
676
|
-
console.error(
|
|
677
|
-
"Error: No pspm.json or package.json found in current directory"
|
|
678
|
-
);
|
|
679
|
-
console.error(
|
|
680
|
-
"Either run this command in a package directory or specify a package name"
|
|
681
|
-
);
|
|
682
|
-
process.exit(1);
|
|
683
|
-
}
|
|
972
|
+
this.name = "GitHubNotFoundError";
|
|
684
973
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
974
|
+
};
|
|
975
|
+
GitHubPathNotFoundError = class extends Error {
|
|
976
|
+
constructor(spec, availablePaths) {
|
|
977
|
+
const pathInfo = availablePaths?.length ? `
|
|
978
|
+
Available paths in repository root:
|
|
979
|
+
${availablePaths.join("\n ")}` : "";
|
|
980
|
+
super(
|
|
981
|
+
`Path "${spec.path}" not found in ${spec.owner}/${spec.repo}${pathInfo}`
|
|
982
|
+
);
|
|
983
|
+
this.name = "GitHubPathNotFoundError";
|
|
688
984
|
}
|
|
689
|
-
|
|
690
|
-
}
|
|
691
|
-
configure2({ registryUrl, apiKey });
|
|
692
|
-
console.log(`Setting ${packageName} to ${visibility}...`);
|
|
693
|
-
const response = await changeSkillAccess(packageName, { visibility });
|
|
694
|
-
if (response.status !== 200 || !response.data) {
|
|
695
|
-
const errorMessage = response.error ?? "Failed to change visibility";
|
|
696
|
-
console.error(`Error: ${errorMessage}`);
|
|
697
|
-
process.exit(1);
|
|
698
|
-
}
|
|
699
|
-
const result = response.data;
|
|
700
|
-
console.log(
|
|
701
|
-
`+ @user/${result.username}/${result.name} is now ${result.visibility}`
|
|
702
|
-
);
|
|
703
|
-
if (visibility === "public") {
|
|
704
|
-
console.log("");
|
|
705
|
-
console.log(
|
|
706
|
-
"Note: This action is irreversible. Public packages cannot be made private."
|
|
707
|
-
);
|
|
708
|
-
}
|
|
709
|
-
} catch (error) {
|
|
710
|
-
const message = error instanceof Error ? error.message : "Unknown error";
|
|
711
|
-
console.error(`Error: ${message}`);
|
|
712
|
-
process.exit(1);
|
|
985
|
+
};
|
|
713
986
|
}
|
|
714
|
-
}
|
|
987
|
+
});
|
|
715
988
|
async function hasLegacyLockfile() {
|
|
716
989
|
try {
|
|
717
990
|
await stat(getLegacyLockfilePath());
|
|
@@ -783,17 +1056,20 @@ async function writeLockfile(lockfile) {
|
|
|
783
1056
|
const lockfilePath = getLockfilePath();
|
|
784
1057
|
await mkdir(dirname(lockfilePath), { recursive: true });
|
|
785
1058
|
const normalized = {
|
|
786
|
-
lockfileVersion:
|
|
1059
|
+
lockfileVersion: 3,
|
|
787
1060
|
registryUrl: lockfile.registryUrl,
|
|
788
1061
|
packages: lockfile.packages ?? lockfile.skills ?? {}
|
|
789
1062
|
};
|
|
1063
|
+
if (lockfile.githubPackages && Object.keys(lockfile.githubPackages).length > 0) {
|
|
1064
|
+
normalized.githubPackages = lockfile.githubPackages;
|
|
1065
|
+
}
|
|
790
1066
|
await writeFile(lockfilePath, `${JSON.stringify(normalized, null, 2)}
|
|
791
1067
|
`);
|
|
792
1068
|
}
|
|
793
1069
|
async function createEmptyLockfile() {
|
|
794
1070
|
const registryUrl = await getRegistryUrl();
|
|
795
1071
|
return {
|
|
796
|
-
lockfileVersion:
|
|
1072
|
+
lockfileVersion: 3,
|
|
797
1073
|
registryUrl,
|
|
798
1074
|
packages: {}
|
|
799
1075
|
};
|
|
@@ -836,125 +1112,596 @@ async function listLockfileSkills() {
|
|
|
836
1112
|
entry
|
|
837
1113
|
}));
|
|
838
1114
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1115
|
+
async function addGitHubToLockfile(specifier, entry) {
|
|
1116
|
+
let lockfile = await readLockfile();
|
|
1117
|
+
if (!lockfile) {
|
|
1118
|
+
lockfile = await createEmptyLockfile();
|
|
1119
|
+
}
|
|
1120
|
+
if (!lockfile.githubPackages) {
|
|
1121
|
+
lockfile.githubPackages = {};
|
|
1122
|
+
}
|
|
1123
|
+
lockfile.githubPackages[specifier] = entry;
|
|
1124
|
+
await writeLockfile(lockfile);
|
|
1125
|
+
}
|
|
1126
|
+
async function removeGitHubFromLockfile(specifier) {
|
|
1127
|
+
const lockfile = await readLockfile();
|
|
1128
|
+
if (!lockfile?.githubPackages?.[specifier]) {
|
|
1129
|
+
return false;
|
|
1130
|
+
}
|
|
1131
|
+
delete lockfile.githubPackages[specifier];
|
|
1132
|
+
await writeLockfile(lockfile);
|
|
1133
|
+
return true;
|
|
1134
|
+
}
|
|
1135
|
+
async function listLockfileGitHubPackages() {
|
|
1136
|
+
const lockfile = await readLockfile();
|
|
1137
|
+
if (!lockfile?.githubPackages) {
|
|
1138
|
+
return [];
|
|
1139
|
+
}
|
|
1140
|
+
return Object.entries(lockfile.githubPackages).map(([specifier, entry]) => ({
|
|
1141
|
+
specifier,
|
|
1142
|
+
entry
|
|
1143
|
+
}));
|
|
1144
|
+
}
|
|
1145
|
+
var init_lockfile = __esm({
|
|
1146
|
+
"src/lockfile.ts"() {
|
|
1147
|
+
init_config();
|
|
1148
|
+
}
|
|
1149
|
+
});
|
|
1150
|
+
function getManifestPath() {
|
|
1151
|
+
return join(process.cwd(), "pspm.json");
|
|
1152
|
+
}
|
|
1153
|
+
async function readManifest() {
|
|
842
1154
|
try {
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
1155
|
+
const content = await readFile(getManifestPath(), "utf-8");
|
|
1156
|
+
return JSON.parse(content);
|
|
1157
|
+
} catch {
|
|
1158
|
+
return null;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
async function writeManifest(manifest) {
|
|
1162
|
+
const content = JSON.stringify(manifest, null, 2);
|
|
1163
|
+
await writeFile(getManifestPath(), `${content}
|
|
1164
|
+
`);
|
|
1165
|
+
}
|
|
1166
|
+
async function createMinimalManifest() {
|
|
1167
|
+
return {
|
|
1168
|
+
dependencies: {}
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
async function ensureManifest() {
|
|
1172
|
+
let manifest = await readManifest();
|
|
1173
|
+
if (!manifest) {
|
|
1174
|
+
manifest = await createMinimalManifest();
|
|
1175
|
+
await writeManifest(manifest);
|
|
1176
|
+
}
|
|
1177
|
+
return manifest;
|
|
1178
|
+
}
|
|
1179
|
+
async function addDependency(skillName, versionRange) {
|
|
1180
|
+
const manifest = await ensureManifest();
|
|
1181
|
+
if (!manifest.dependencies) {
|
|
1182
|
+
manifest.dependencies = {};
|
|
1183
|
+
}
|
|
1184
|
+
manifest.dependencies[skillName] = versionRange;
|
|
1185
|
+
await writeManifest(manifest);
|
|
1186
|
+
}
|
|
1187
|
+
async function removeDependency(skillName) {
|
|
1188
|
+
const manifest = await readManifest();
|
|
1189
|
+
if (!manifest?.dependencies?.[skillName]) {
|
|
1190
|
+
return false;
|
|
1191
|
+
}
|
|
1192
|
+
delete manifest.dependencies[skillName];
|
|
1193
|
+
await writeManifest(manifest);
|
|
1194
|
+
return true;
|
|
1195
|
+
}
|
|
1196
|
+
async function getDependencies() {
|
|
1197
|
+
const manifest = await readManifest();
|
|
1198
|
+
return manifest?.dependencies ?? {};
|
|
1199
|
+
}
|
|
1200
|
+
async function getGitHubDependencies() {
|
|
1201
|
+
const manifest = await readManifest();
|
|
1202
|
+
return manifest?.githubDependencies ?? {};
|
|
1203
|
+
}
|
|
1204
|
+
async function addGitHubDependency(specifier, ref) {
|
|
1205
|
+
const manifest = await ensureManifest();
|
|
1206
|
+
if (!manifest.githubDependencies) {
|
|
1207
|
+
manifest.githubDependencies = {};
|
|
1208
|
+
}
|
|
1209
|
+
manifest.githubDependencies[specifier] = ref;
|
|
1210
|
+
await writeManifest(manifest);
|
|
1211
|
+
}
|
|
1212
|
+
async function removeGitHubDependency(specifier) {
|
|
1213
|
+
const manifest = await readManifest();
|
|
1214
|
+
if (!manifest?.githubDependencies?.[specifier]) {
|
|
1215
|
+
return false;
|
|
1216
|
+
}
|
|
1217
|
+
delete manifest.githubDependencies[specifier];
|
|
1218
|
+
await writeManifest(manifest);
|
|
1219
|
+
return true;
|
|
1220
|
+
}
|
|
1221
|
+
var init_manifest2 = __esm({
|
|
1222
|
+
"src/manifest.ts"() {
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
async function createAgentSymlinks(skills, options) {
|
|
1226
|
+
const { agents, projectRoot, agentConfigs } = options;
|
|
1227
|
+
if (agents.length === 1 && agents[0] === "none") {
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
for (const agentName of agents) {
|
|
1231
|
+
const config2 = resolveAgentConfig(agentName, agentConfigs);
|
|
1232
|
+
if (!config2) {
|
|
1233
|
+
console.warn(`Warning: Unknown agent "${agentName}", skipping symlinks`);
|
|
1234
|
+
continue;
|
|
852
1235
|
}
|
|
853
|
-
const
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1236
|
+
const agentSkillsDir = join(projectRoot, config2.skillsDir);
|
|
1237
|
+
await mkdir(agentSkillsDir, { recursive: true });
|
|
1238
|
+
for (const skill of skills) {
|
|
1239
|
+
const symlinkPath = join(agentSkillsDir, skill.name);
|
|
1240
|
+
const targetPath = join(projectRoot, skill.sourcePath);
|
|
1241
|
+
const relativeTarget = relative(dirname(symlinkPath), targetPath);
|
|
1242
|
+
await createSymlink(symlinkPath, relativeTarget, skill.name);
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
async function createSymlink(symlinkPath, target, skillName) {
|
|
1247
|
+
try {
|
|
1248
|
+
const stats = await lstat(symlinkPath).catch(() => null);
|
|
1249
|
+
if (stats) {
|
|
1250
|
+
if (stats.isSymbolicLink()) {
|
|
1251
|
+
const existingTarget = await readlink(symlinkPath);
|
|
1252
|
+
if (existingTarget === target) {
|
|
1253
|
+
return;
|
|
868
1254
|
}
|
|
869
|
-
|
|
1255
|
+
await rm(symlinkPath);
|
|
1256
|
+
} else {
|
|
1257
|
+
console.warn(
|
|
1258
|
+
`Warning: File exists at symlink path for "${skillName}", skipping: ${symlinkPath}`
|
|
1259
|
+
);
|
|
1260
|
+
return;
|
|
870
1261
|
}
|
|
871
|
-
const errorMessage = extractApiErrorMessage(
|
|
872
|
-
versionsResponse,
|
|
873
|
-
`Skill @user/${username}/${name} not found`
|
|
874
|
-
);
|
|
875
|
-
console.error(`Error: ${errorMessage}`);
|
|
876
|
-
process.exit(1);
|
|
877
1262
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1263
|
+
await symlink(target, symlinkPath);
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1266
|
+
console.warn(
|
|
1267
|
+
`Warning: Failed to create symlink for "${skillName}": ${message}`
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
async function removeAgentSymlinks(skillName, options) {
|
|
1272
|
+
const { agents, projectRoot, agentConfigs } = options;
|
|
1273
|
+
if (agents.length === 1 && agents[0] === "none") {
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
for (const agentName of agents) {
|
|
1277
|
+
const config2 = resolveAgentConfig(agentName, agentConfigs);
|
|
1278
|
+
if (!config2) {
|
|
1279
|
+
continue;
|
|
882
1280
|
}
|
|
883
|
-
const
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
1281
|
+
const symlinkPath = join(projectRoot, config2.skillsDir, skillName);
|
|
1282
|
+
try {
|
|
1283
|
+
const stats = await lstat(symlinkPath).catch(() => null);
|
|
1284
|
+
if (stats?.isSymbolicLink()) {
|
|
1285
|
+
await rm(symlinkPath);
|
|
1286
|
+
}
|
|
1287
|
+
} catch {
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
function getRegistrySkillPath(username, skillName) {
|
|
1292
|
+
return `.pspm/skills/${username}/${skillName}`;
|
|
1293
|
+
}
|
|
1294
|
+
function getGitHubSkillPath(owner, repo, path) {
|
|
1295
|
+
if (path) {
|
|
1296
|
+
return `.pspm/skills/_github/${owner}/${repo}/${path}`;
|
|
1297
|
+
}
|
|
1298
|
+
return `.pspm/skills/_github/${owner}/${repo}`;
|
|
1299
|
+
}
|
|
1300
|
+
async function getLinkedAgents(skillName, agents, projectRoot, agentConfigs) {
|
|
1301
|
+
const linkedAgents = [];
|
|
1302
|
+
for (const agentName of agents) {
|
|
1303
|
+
const config2 = resolveAgentConfig(agentName, agentConfigs);
|
|
1304
|
+
if (!config2) continue;
|
|
1305
|
+
const symlinkPath = join(projectRoot, config2.skillsDir, skillName);
|
|
1306
|
+
try {
|
|
1307
|
+
const stats = await lstat(symlinkPath);
|
|
1308
|
+
if (stats.isSymbolicLink()) {
|
|
1309
|
+
linkedAgents.push(agentName);
|
|
1310
|
+
}
|
|
1311
|
+
} catch {
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
return linkedAgents;
|
|
1315
|
+
}
|
|
1316
|
+
var init_symlinks = __esm({
|
|
1317
|
+
"src/symlinks.ts"() {
|
|
1318
|
+
init_agents();
|
|
1319
|
+
}
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
// src/commands/add.ts
|
|
1323
|
+
var add_exports = {};
|
|
1324
|
+
__export(add_exports, {
|
|
1325
|
+
add: () => add
|
|
1326
|
+
});
|
|
1327
|
+
async function add(specifiers, options) {
|
|
1328
|
+
console.log("Resolving packages...\n");
|
|
1329
|
+
const resolvedPackages = [];
|
|
1330
|
+
const validationErrors = [];
|
|
1331
|
+
for (const specifier of specifiers) {
|
|
1332
|
+
try {
|
|
1333
|
+
if (isGitHubSpecifier(specifier)) {
|
|
1334
|
+
const resolved = await validateGitHubPackage(specifier);
|
|
1335
|
+
resolvedPackages.push(resolved);
|
|
1336
|
+
} else {
|
|
1337
|
+
const resolved = await validateRegistryPackage(specifier);
|
|
1338
|
+
resolvedPackages.push(resolved);
|
|
1339
|
+
}
|
|
1340
|
+
} catch (error) {
|
|
1341
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1342
|
+
validationErrors.push({ specifier, error: message });
|
|
1343
|
+
console.error(`Failed to resolve ${specifier}: ${message}
|
|
1344
|
+
`);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
if (resolvedPackages.length === 0) {
|
|
1348
|
+
console.error("No packages could be resolved.");
|
|
1349
|
+
process.exit(1);
|
|
1350
|
+
}
|
|
1351
|
+
if (validationErrors.length > 0) {
|
|
1352
|
+
console.log(
|
|
1353
|
+
`Resolved ${resolvedPackages.length} of ${specifiers.length} packages.
|
|
1354
|
+
`
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
let agents;
|
|
1358
|
+
const manifest = await readManifest();
|
|
1359
|
+
if (options.agent) {
|
|
1360
|
+
agents = parseAgentArg(options.agent);
|
|
1361
|
+
} else if (manifest) {
|
|
1362
|
+
agents = parseAgentArg(void 0);
|
|
1363
|
+
} else if (options.yes) {
|
|
1364
|
+
agents = parseAgentArg(void 0);
|
|
1365
|
+
} else {
|
|
1366
|
+
console.log("No pspm.json found. Let's set up your project.\n");
|
|
1367
|
+
agents = await promptForAgents();
|
|
1368
|
+
console.log();
|
|
1369
|
+
}
|
|
1370
|
+
const results = [];
|
|
1371
|
+
for (const resolved of resolvedPackages) {
|
|
1372
|
+
try {
|
|
1373
|
+
if (resolved.type === "github") {
|
|
1374
|
+
await installGitHubPackage(resolved, {
|
|
1375
|
+
...options,
|
|
1376
|
+
resolvedAgents: agents
|
|
1377
|
+
});
|
|
1378
|
+
} else {
|
|
1379
|
+
await installRegistryPackage(resolved, {
|
|
1380
|
+
...options,
|
|
1381
|
+
resolvedAgents: agents
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
results.push({ specifier: resolved.specifier, success: true });
|
|
1385
|
+
} catch (error) {
|
|
1386
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1387
|
+
results.push({
|
|
1388
|
+
specifier: resolved.specifier,
|
|
1389
|
+
success: false,
|
|
1390
|
+
error: message
|
|
1391
|
+
});
|
|
1392
|
+
console.error(`Failed to install ${resolved.specifier}: ${message}
|
|
1393
|
+
`);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
if (specifiers.length > 1) {
|
|
1397
|
+
const succeeded = results.filter((r) => r.success).length;
|
|
1398
|
+
const failed = results.filter((r) => !r.success).length + validationErrors.length;
|
|
1399
|
+
console.log(`
|
|
1400
|
+
Summary: ${succeeded} added, ${failed} failed`);
|
|
1401
|
+
if (failed > 0) {
|
|
890
1402
|
process.exit(1);
|
|
891
1403
|
}
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
async function validateRegistryPackage(specifier) {
|
|
1407
|
+
const config2 = await resolveConfig();
|
|
1408
|
+
const registryUrl = config2.registryUrl;
|
|
1409
|
+
const apiKey = getTokenForRegistry(config2, registryUrl);
|
|
1410
|
+
const parsed = parseSkillSpecifier(specifier);
|
|
1411
|
+
if (!parsed) {
|
|
1412
|
+
throw new Error(
|
|
1413
|
+
`Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}[@{version}]`
|
|
1414
|
+
);
|
|
1415
|
+
}
|
|
1416
|
+
const { username, name, versionRange } = parsed;
|
|
1417
|
+
configure2({ registryUrl, apiKey: apiKey ?? "" });
|
|
1418
|
+
console.log(`Resolving ${specifier}...`);
|
|
1419
|
+
const versionsResponse = await listSkillVersions(username, name);
|
|
1420
|
+
if (versionsResponse.status !== 200) {
|
|
1421
|
+
if (versionsResponse.status === 401) {
|
|
1422
|
+
if (!apiKey) {
|
|
1423
|
+
throw new Error(
|
|
1424
|
+
`Package @user/${username}/${name} requires authentication. Please run 'pspm login' to authenticate`
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
throw new Error(
|
|
1428
|
+
`Access denied to @user/${username}/${name}. You may not have permission to access this private package.`
|
|
898
1429
|
);
|
|
899
|
-
console.error(`Error: ${errorMessage}`);
|
|
900
|
-
process.exit(1);
|
|
901
1430
|
}
|
|
902
|
-
const
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1431
|
+
const errorMessage = extractApiErrorMessage(
|
|
1432
|
+
versionsResponse,
|
|
1433
|
+
`Skill @user/${username}/${name} not found`
|
|
1434
|
+
);
|
|
1435
|
+
throw new Error(errorMessage);
|
|
1436
|
+
}
|
|
1437
|
+
const versions = versionsResponse.data;
|
|
1438
|
+
if (versions.length === 0) {
|
|
1439
|
+
throw new Error(`Skill @user/${username}/${name} not found`);
|
|
1440
|
+
}
|
|
1441
|
+
const versionStrings = versions.map((v) => v.version);
|
|
1442
|
+
const resolvedVersion = resolveVersion(versionRange || "*", versionStrings);
|
|
1443
|
+
if (!resolvedVersion) {
|
|
1444
|
+
throw new Error(
|
|
1445
|
+
`No version matching "${versionRange || "latest"}" found for @user/${username}/${name}. Available versions: ${versionStrings.join(", ")}`
|
|
1446
|
+
);
|
|
1447
|
+
}
|
|
1448
|
+
const versionResponse = await getSkillVersion(
|
|
1449
|
+
username,
|
|
1450
|
+
name,
|
|
1451
|
+
resolvedVersion
|
|
1452
|
+
);
|
|
1453
|
+
if (versionResponse.status !== 200 || !versionResponse.data) {
|
|
1454
|
+
const errorMessage = extractApiErrorMessage(
|
|
1455
|
+
versionResponse,
|
|
1456
|
+
`Version ${resolvedVersion} not found`
|
|
1457
|
+
);
|
|
1458
|
+
throw new Error(errorMessage);
|
|
1459
|
+
}
|
|
1460
|
+
console.log(`Resolved @user/${username}/${name}@${resolvedVersion}`);
|
|
1461
|
+
return {
|
|
1462
|
+
type: "registry",
|
|
1463
|
+
specifier,
|
|
1464
|
+
username,
|
|
1465
|
+
name,
|
|
1466
|
+
versionRange,
|
|
1467
|
+
resolvedVersion,
|
|
1468
|
+
versionInfo: {
|
|
1469
|
+
downloadUrl: versionResponse.data.downloadUrl,
|
|
1470
|
+
checksum: versionResponse.data.checksum
|
|
907
1471
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
async function validateGitHubPackage(specifier) {
|
|
1475
|
+
const parsed = parseGitHubSpecifier(specifier);
|
|
1476
|
+
if (!parsed) {
|
|
1477
|
+
throw new Error(
|
|
1478
|
+
`Invalid GitHub specifier "${specifier}". Use format: github:{owner}/{repo}[/{path}][@{ref}]`
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
const ref = parsed.ref || "HEAD";
|
|
1482
|
+
console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
|
|
1483
|
+
const result = await downloadGitHubPackage(parsed);
|
|
1484
|
+
console.log(`Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`);
|
|
1485
|
+
return {
|
|
1486
|
+
type: "github",
|
|
1487
|
+
specifier,
|
|
1488
|
+
parsed,
|
|
1489
|
+
ref,
|
|
1490
|
+
downloadResult: result
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
async function installRegistryPackage(resolved, options) {
|
|
1494
|
+
const { username, name, versionRange, resolvedVersion, versionInfo } = resolved;
|
|
1495
|
+
console.log(`Installing @user/${username}/${name}@${resolvedVersion}...`);
|
|
1496
|
+
const config2 = await resolveConfig();
|
|
1497
|
+
const apiKey = getTokenForRegistry(config2, config2.registryUrl);
|
|
1498
|
+
const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
|
|
1499
|
+
const downloadHeaders = {};
|
|
1500
|
+
if (!isPresignedUrl && apiKey) {
|
|
1501
|
+
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
1502
|
+
}
|
|
1503
|
+
const tarballResponse = await fetch(versionInfo.downloadUrl, {
|
|
1504
|
+
headers: downloadHeaders,
|
|
1505
|
+
redirect: "follow"
|
|
1506
|
+
});
|
|
1507
|
+
if (!tarballResponse.ok) {
|
|
1508
|
+
throw new Error(`Failed to download tarball (${tarballResponse.status})`);
|
|
1509
|
+
}
|
|
1510
|
+
const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
|
|
1511
|
+
const integrity = calculateIntegrity(tarballBuffer);
|
|
1512
|
+
const expectedIntegrity = `sha256-${Buffer.from(versionInfo.checksum, "hex").toString("base64")}`;
|
|
1513
|
+
if (integrity !== expectedIntegrity) {
|
|
1514
|
+
throw new Error("Checksum verification failed");
|
|
1515
|
+
}
|
|
1516
|
+
const skillsDir = getSkillsDir();
|
|
1517
|
+
const destDir = join(skillsDir, username, name);
|
|
1518
|
+
await mkdir(destDir, { recursive: true });
|
|
1519
|
+
const { writeFile: writeFile8 } = await import('fs/promises');
|
|
1520
|
+
const tempFile = join(destDir, ".temp.tgz");
|
|
1521
|
+
await writeFile8(tempFile, tarballBuffer);
|
|
1522
|
+
const { exec: exec2 } = await import('child_process');
|
|
1523
|
+
const { promisify: promisify2 } = await import('util');
|
|
1524
|
+
const execAsync = promisify2(exec2);
|
|
1525
|
+
try {
|
|
1526
|
+
await rm(destDir, { recursive: true, force: true });
|
|
1527
|
+
await mkdir(destDir, { recursive: true });
|
|
1528
|
+
await writeFile8(tempFile, tarballBuffer);
|
|
1529
|
+
await execAsync(
|
|
1530
|
+
`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
|
|
1531
|
+
);
|
|
1532
|
+
} finally {
|
|
1533
|
+
await rm(tempFile, { force: true });
|
|
1534
|
+
}
|
|
1535
|
+
const fullName = `@user/${username}/${name}`;
|
|
1536
|
+
await addToLockfile(fullName, {
|
|
1537
|
+
version: resolvedVersion,
|
|
1538
|
+
resolved: versionInfo.downloadUrl,
|
|
1539
|
+
integrity
|
|
1540
|
+
});
|
|
1541
|
+
const dependencyRange = versionRange || `^${resolvedVersion}`;
|
|
1542
|
+
await addDependency(fullName, dependencyRange);
|
|
1543
|
+
const agents = options.resolvedAgents;
|
|
1544
|
+
if (agents[0] !== "none") {
|
|
1545
|
+
const skillManifest = await readManifest();
|
|
1546
|
+
const skillInfo = {
|
|
1547
|
+
name,
|
|
1548
|
+
sourcePath: getRegistrySkillPath(username, name)
|
|
1549
|
+
};
|
|
1550
|
+
await createAgentSymlinks([skillInfo], {
|
|
1551
|
+
agents,
|
|
1552
|
+
projectRoot: process.cwd(),
|
|
1553
|
+
agentConfigs: skillManifest?.agents
|
|
911
1554
|
});
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1555
|
+
}
|
|
1556
|
+
console.log(`Installed @user/${username}/${name}@${resolvedVersion}`);
|
|
1557
|
+
console.log(`Location: ${destDir}`);
|
|
1558
|
+
}
|
|
1559
|
+
async function installGitHubPackage(resolved, options) {
|
|
1560
|
+
const { specifier, parsed, ref, downloadResult } = resolved;
|
|
1561
|
+
console.log(
|
|
1562
|
+
`Installing ${specifier} (${ref}@${downloadResult.commit.slice(0, 7)})...`
|
|
1563
|
+
);
|
|
1564
|
+
const skillsDir = getSkillsDir();
|
|
1565
|
+
const destPath = await extractGitHubPackage(
|
|
1566
|
+
parsed,
|
|
1567
|
+
downloadResult.buffer,
|
|
1568
|
+
skillsDir
|
|
1569
|
+
);
|
|
1570
|
+
const lockfileSpecifier = formatGitHubSpecifier({
|
|
1571
|
+
owner: parsed.owner,
|
|
1572
|
+
repo: parsed.repo,
|
|
1573
|
+
path: parsed.path
|
|
1574
|
+
// Don't include ref in the specifier key, it's stored in gitRef
|
|
1575
|
+
});
|
|
1576
|
+
const entry = {
|
|
1577
|
+
version: downloadResult.commit.slice(0, 7),
|
|
1578
|
+
resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
|
|
1579
|
+
integrity: downloadResult.integrity,
|
|
1580
|
+
gitCommit: downloadResult.commit,
|
|
1581
|
+
gitRef: ref
|
|
1582
|
+
};
|
|
1583
|
+
await addGitHubToLockfile(lockfileSpecifier, entry);
|
|
1584
|
+
await addGitHubDependency(lockfileSpecifier, ref);
|
|
1585
|
+
const agents = options.resolvedAgents;
|
|
1586
|
+
if (agents[0] !== "none") {
|
|
1587
|
+
const manifest = await readManifest();
|
|
1588
|
+
const skillName = getGitHubSkillName(parsed);
|
|
1589
|
+
const skillInfo = {
|
|
1590
|
+
name: skillName,
|
|
1591
|
+
sourcePath: getGitHubSkillPath(parsed.owner, parsed.repo, parsed.path)
|
|
1592
|
+
};
|
|
1593
|
+
await createAgentSymlinks([skillInfo], {
|
|
1594
|
+
agents,
|
|
1595
|
+
projectRoot: process.cwd(),
|
|
1596
|
+
agentConfigs: manifest?.agents
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
console.log(
|
|
1600
|
+
`Installed ${specifier} (${ref}@${downloadResult.commit.slice(0, 7)})`
|
|
1601
|
+
);
|
|
1602
|
+
console.log(`Location: ${destPath}`);
|
|
1603
|
+
}
|
|
1604
|
+
var init_add = __esm({
|
|
1605
|
+
"src/commands/add.ts"() {
|
|
1606
|
+
init_src();
|
|
1607
|
+
init_agents();
|
|
1608
|
+
init_api_client();
|
|
1609
|
+
init_config();
|
|
1610
|
+
init_errors();
|
|
1611
|
+
init_github();
|
|
1612
|
+
init_lockfile();
|
|
1613
|
+
init_manifest2();
|
|
1614
|
+
init_symlinks();
|
|
1615
|
+
}
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
// src/commands/access.ts
|
|
1619
|
+
init_src();
|
|
1620
|
+
init_api_client();
|
|
1621
|
+
init_config();
|
|
1622
|
+
async function access(specifier, options) {
|
|
1623
|
+
try {
|
|
1624
|
+
const apiKey = await requireApiKey();
|
|
1625
|
+
const registryUrl = await getRegistryUrl();
|
|
1626
|
+
if (options.public && options.private) {
|
|
1627
|
+
console.error("Error: Cannot specify both --public and --private");
|
|
916
1628
|
process.exit(1);
|
|
917
1629
|
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
const expectedIntegrity = `sha256-${Buffer.from(versionInfo.checksum, "hex").toString("base64")}`;
|
|
921
|
-
if (integrity !== expectedIntegrity) {
|
|
922
|
-
console.error("Error: Checksum verification failed");
|
|
1630
|
+
if (!options.public && !options.private) {
|
|
1631
|
+
console.error("Error: Must specify either --public or --private");
|
|
923
1632
|
process.exit(1);
|
|
924
1633
|
}
|
|
925
|
-
const
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
await
|
|
938
|
-
await
|
|
939
|
-
|
|
1634
|
+
const visibility = options.public ? "public" : "private";
|
|
1635
|
+
let packageName;
|
|
1636
|
+
if (specifier) {
|
|
1637
|
+
const parsed = parseSkillSpecifier(specifier);
|
|
1638
|
+
if (!parsed) {
|
|
1639
|
+
console.error(
|
|
1640
|
+
`Error: Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}`
|
|
1641
|
+
);
|
|
1642
|
+
process.exit(1);
|
|
1643
|
+
}
|
|
1644
|
+
packageName = parsed.name;
|
|
1645
|
+
} else {
|
|
1646
|
+
const { readFile: readFile7 } = await import('fs/promises');
|
|
1647
|
+
const { join: join13 } = await import('path');
|
|
1648
|
+
let manifest = null;
|
|
1649
|
+
try {
|
|
1650
|
+
const content = await readFile7(
|
|
1651
|
+
join13(process.cwd(), "pspm.json"),
|
|
1652
|
+
"utf-8"
|
|
1653
|
+
);
|
|
1654
|
+
manifest = JSON.parse(content);
|
|
1655
|
+
} catch {
|
|
1656
|
+
try {
|
|
1657
|
+
const content = await readFile7(
|
|
1658
|
+
join13(process.cwd(), "package.json"),
|
|
1659
|
+
"utf-8"
|
|
1660
|
+
);
|
|
1661
|
+
manifest = JSON.parse(content);
|
|
1662
|
+
} catch {
|
|
1663
|
+
console.error(
|
|
1664
|
+
"Error: No pspm.json or package.json found in current directory"
|
|
1665
|
+
);
|
|
1666
|
+
console.error(
|
|
1667
|
+
"Either run this command in a package directory or specify a package name"
|
|
1668
|
+
);
|
|
1669
|
+
process.exit(1);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
if (!manifest?.name) {
|
|
1673
|
+
console.error("Error: Package manifest is missing 'name' field");
|
|
1674
|
+
process.exit(1);
|
|
1675
|
+
}
|
|
1676
|
+
packageName = manifest.name;
|
|
1677
|
+
}
|
|
1678
|
+
configure2({ registryUrl, apiKey });
|
|
1679
|
+
console.log(`Setting ${packageName} to ${visibility}...`);
|
|
1680
|
+
const response = await changeSkillAccess(packageName, { visibility });
|
|
1681
|
+
if (response.status !== 200 || !response.data) {
|
|
1682
|
+
const errorMessage = response.error ?? "Failed to change visibility";
|
|
1683
|
+
console.error(`Error: ${errorMessage}`);
|
|
1684
|
+
process.exit(1);
|
|
1685
|
+
}
|
|
1686
|
+
const result = response.data;
|
|
1687
|
+
console.log(
|
|
1688
|
+
`+ @user/${result.username}/${result.name} is now ${result.visibility}`
|
|
1689
|
+
);
|
|
1690
|
+
if (visibility === "public") {
|
|
1691
|
+
console.log("");
|
|
1692
|
+
console.log(
|
|
1693
|
+
"Note: This action is irreversible. Public packages cannot be made private."
|
|
940
1694
|
);
|
|
941
|
-
} finally {
|
|
942
|
-
await rm(tempFile, { force: true });
|
|
943
1695
|
}
|
|
944
|
-
const fullName = `@user/${username}/${name}`;
|
|
945
|
-
await addToLockfile(fullName, {
|
|
946
|
-
version: resolved,
|
|
947
|
-
resolved: versionInfo.downloadUrl,
|
|
948
|
-
integrity
|
|
949
|
-
});
|
|
950
|
-
console.log(`Installed @user/${username}/${name}@${resolved}`);
|
|
951
|
-
console.log(`Location: ${destDir}`);
|
|
952
1696
|
} catch (error) {
|
|
953
1697
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
954
1698
|
console.error(`Error: ${message}`);
|
|
955
1699
|
process.exit(1);
|
|
956
1700
|
}
|
|
957
1701
|
}
|
|
1702
|
+
|
|
1703
|
+
// src/commands/index.ts
|
|
1704
|
+
init_add();
|
|
958
1705
|
async function configInit(options) {
|
|
959
1706
|
try {
|
|
960
1707
|
const configPath = join(process.cwd(), ".pspmrc");
|
|
@@ -987,6 +1734,7 @@ async function configInit(options) {
|
|
|
987
1734
|
}
|
|
988
1735
|
|
|
989
1736
|
// src/commands/config/show.ts
|
|
1737
|
+
init_config();
|
|
990
1738
|
async function configShow() {
|
|
991
1739
|
try {
|
|
992
1740
|
const resolved = await resolveConfig();
|
|
@@ -1016,6 +1764,9 @@ async function configShow() {
|
|
|
1016
1764
|
}
|
|
1017
1765
|
|
|
1018
1766
|
// src/commands/deprecate.ts
|
|
1767
|
+
init_src();
|
|
1768
|
+
init_api_client();
|
|
1769
|
+
init_config();
|
|
1019
1770
|
async function deprecate(specifier, message, options) {
|
|
1020
1771
|
try {
|
|
1021
1772
|
const apiKey = await requireApiKey();
|
|
@@ -1078,6 +1829,17 @@ async function deprecate(specifier, message, options) {
|
|
|
1078
1829
|
process.exit(1);
|
|
1079
1830
|
}
|
|
1080
1831
|
}
|
|
1832
|
+
|
|
1833
|
+
// src/commands/init.ts
|
|
1834
|
+
init_src();
|
|
1835
|
+
function prompt(rl, question, defaultValue) {
|
|
1836
|
+
return new Promise((resolve) => {
|
|
1837
|
+
const displayDefault = defaultValue ? ` (${defaultValue})` : "";
|
|
1838
|
+
rl.question(`${question}${displayDefault} `, (answer) => {
|
|
1839
|
+
resolve(answer.trim() || defaultValue);
|
|
1840
|
+
});
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1081
1843
|
async function readExistingPackageJson() {
|
|
1082
1844
|
try {
|
|
1083
1845
|
const content = await readFile(
|
|
@@ -1096,52 +1858,177 @@ async function readExistingPackageJson() {
|
|
|
1096
1858
|
return null;
|
|
1097
1859
|
}
|
|
1098
1860
|
}
|
|
1861
|
+
async function getGitAuthor() {
|
|
1862
|
+
try {
|
|
1863
|
+
const { exec: exec2 } = await import('child_process');
|
|
1864
|
+
const { promisify: promisify2 } = await import('util');
|
|
1865
|
+
const execAsync = promisify2(exec2);
|
|
1866
|
+
const [nameResult, emailResult] = await Promise.all([
|
|
1867
|
+
execAsync("git config user.name").catch(() => ({ stdout: "" })),
|
|
1868
|
+
execAsync("git config user.email").catch(() => ({ stdout: "" }))
|
|
1869
|
+
]);
|
|
1870
|
+
const name = nameResult.stdout.trim();
|
|
1871
|
+
const email = emailResult.stdout.trim();
|
|
1872
|
+
if (name && email) {
|
|
1873
|
+
return `${name} <${email}>`;
|
|
1874
|
+
}
|
|
1875
|
+
if (name) {
|
|
1876
|
+
return name;
|
|
1877
|
+
}
|
|
1878
|
+
return null;
|
|
1879
|
+
} catch {
|
|
1880
|
+
return null;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1099
1883
|
function sanitizeName(name) {
|
|
1100
1884
|
const withoutScope = name.replace(/^@[^/]+\//, "");
|
|
1101
1885
|
return withoutScope.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
|
|
1102
1886
|
}
|
|
1887
|
+
function isValidName(name) {
|
|
1888
|
+
return /^[a-z][a-z0-9_-]*$/.test(name);
|
|
1889
|
+
}
|
|
1890
|
+
function isValidVersion(version2) {
|
|
1891
|
+
return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version2);
|
|
1892
|
+
}
|
|
1103
1893
|
async function init(options) {
|
|
1104
1894
|
try {
|
|
1105
1895
|
const pspmJsonPath = join(process.cwd(), "pspm.json");
|
|
1896
|
+
let exists = false;
|
|
1106
1897
|
try {
|
|
1107
1898
|
await stat(pspmJsonPath);
|
|
1899
|
+
exists = true;
|
|
1900
|
+
} catch {
|
|
1901
|
+
}
|
|
1902
|
+
if (exists && !options.force) {
|
|
1108
1903
|
console.error("Error: pspm.json already exists in this directory.");
|
|
1109
|
-
console.error("Use --force to overwrite
|
|
1904
|
+
console.error("Use --force to overwrite.");
|
|
1110
1905
|
process.exit(1);
|
|
1111
|
-
} catch {
|
|
1112
1906
|
}
|
|
1113
|
-
const existingPkg = await readExistingPackageJson();
|
|
1114
|
-
const
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
const
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1907
|
+
const existingPkg = await readExistingPackageJson();
|
|
1908
|
+
const gitAuthor = await getGitAuthor();
|
|
1909
|
+
const defaultName = sanitizeName(
|
|
1910
|
+
options.name || existingPkg?.name || basename(process.cwd())
|
|
1911
|
+
);
|
|
1912
|
+
const defaultVersion = existingPkg?.version || "0.1.0";
|
|
1913
|
+
const defaultDescription = options.description || existingPkg?.description || "";
|
|
1914
|
+
const defaultAuthor = options.author || existingPkg?.author || gitAuthor || "";
|
|
1915
|
+
const defaultLicense = existingPkg?.license || "MIT";
|
|
1916
|
+
const defaultMain = "SKILL.md";
|
|
1917
|
+
const defaultCapabilities = "";
|
|
1918
|
+
let manifest;
|
|
1919
|
+
if (options.yes) {
|
|
1920
|
+
manifest = {
|
|
1921
|
+
$schema: PSPM_SCHEMA_URL,
|
|
1922
|
+
name: defaultName,
|
|
1923
|
+
version: defaultVersion,
|
|
1924
|
+
description: defaultDescription || void 0,
|
|
1925
|
+
author: defaultAuthor || void 0,
|
|
1926
|
+
license: defaultLicense,
|
|
1927
|
+
type: "skill",
|
|
1928
|
+
capabilities: [],
|
|
1929
|
+
main: defaultMain,
|
|
1930
|
+
requirements: {
|
|
1931
|
+
pspm: ">=0.1.0"
|
|
1932
|
+
},
|
|
1933
|
+
files: [...DEFAULT_SKILL_FILES],
|
|
1934
|
+
dependencies: {},
|
|
1935
|
+
private: false
|
|
1936
|
+
};
|
|
1937
|
+
} else {
|
|
1938
|
+
console.log(
|
|
1939
|
+
"This utility will walk you through creating a pspm.json file."
|
|
1940
|
+
);
|
|
1941
|
+
console.log(
|
|
1942
|
+
"It only covers the most common items, and tries to guess sensible defaults."
|
|
1943
|
+
);
|
|
1944
|
+
console.log("");
|
|
1945
|
+
console.log(
|
|
1946
|
+
"See `pspm init --help` for definitive documentation on these fields"
|
|
1947
|
+
);
|
|
1948
|
+
console.log("and exactly what they do.");
|
|
1949
|
+
console.log("");
|
|
1950
|
+
console.log("Press ^C at any time to quit.");
|
|
1951
|
+
const rl = createInterface({
|
|
1952
|
+
input: process.stdin,
|
|
1953
|
+
output: process.stdout
|
|
1954
|
+
});
|
|
1955
|
+
try {
|
|
1956
|
+
let name = await prompt(rl, "skill name:", defaultName);
|
|
1957
|
+
while (!isValidName(name)) {
|
|
1958
|
+
console.log(
|
|
1959
|
+
" Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores."
|
|
1960
|
+
);
|
|
1961
|
+
name = await prompt(rl, "skill name:", sanitizeName(name));
|
|
1962
|
+
}
|
|
1963
|
+
let version2 = await prompt(rl, "version:", defaultVersion);
|
|
1964
|
+
while (!isValidVersion(version2)) {
|
|
1965
|
+
console.log(" Version must be valid semver (e.g., 1.0.0)");
|
|
1966
|
+
version2 = await prompt(rl, "version:", "0.1.0");
|
|
1967
|
+
}
|
|
1968
|
+
const description = await prompt(
|
|
1969
|
+
rl,
|
|
1970
|
+
"description:",
|
|
1971
|
+
defaultDescription
|
|
1972
|
+
);
|
|
1973
|
+
const main = await prompt(rl, "entry point:", defaultMain);
|
|
1974
|
+
const capabilitiesStr = await prompt(
|
|
1975
|
+
rl,
|
|
1976
|
+
"capabilities (comma-separated):",
|
|
1977
|
+
defaultCapabilities
|
|
1978
|
+
);
|
|
1979
|
+
const author = await prompt(rl, "author:", defaultAuthor);
|
|
1980
|
+
const license = await prompt(rl, "license:", defaultLicense);
|
|
1981
|
+
rl.close();
|
|
1982
|
+
const capabilities = capabilitiesStr ? capabilitiesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
|
|
1983
|
+
manifest = {
|
|
1984
|
+
$schema: PSPM_SCHEMA_URL,
|
|
1985
|
+
name,
|
|
1986
|
+
version: version2,
|
|
1987
|
+
description: description || void 0,
|
|
1988
|
+
author: author || void 0,
|
|
1989
|
+
license,
|
|
1990
|
+
type: "skill",
|
|
1991
|
+
capabilities,
|
|
1992
|
+
main,
|
|
1993
|
+
requirements: {
|
|
1994
|
+
pspm: ">=0.1.0"
|
|
1995
|
+
},
|
|
1996
|
+
files: [...DEFAULT_SKILL_FILES],
|
|
1997
|
+
dependencies: {},
|
|
1998
|
+
private: false
|
|
1999
|
+
};
|
|
2000
|
+
} catch (error) {
|
|
2001
|
+
rl.close();
|
|
2002
|
+
if (error instanceof Error && error.message.includes("readline was closed")) {
|
|
2003
|
+
console.log("\nAborted.");
|
|
2004
|
+
process.exit(0);
|
|
2005
|
+
}
|
|
2006
|
+
throw error;
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
1136
2009
|
if (!manifest.description) delete manifest.description;
|
|
1137
2010
|
if (!manifest.author) delete manifest.author;
|
|
2011
|
+
if (manifest.capabilities?.length === 0) delete manifest.capabilities;
|
|
1138
2012
|
const content = JSON.stringify(manifest, null, 2);
|
|
1139
|
-
|
|
1140
|
-
`);
|
|
1141
|
-
console.log("Created pspm.json:");
|
|
2013
|
+
console.log("");
|
|
2014
|
+
console.log(`About to write to ${pspmJsonPath}:`);
|
|
1142
2015
|
console.log("");
|
|
1143
2016
|
console.log(content);
|
|
1144
2017
|
console.log("");
|
|
2018
|
+
if (!options.yes) {
|
|
2019
|
+
const rl = createInterface({
|
|
2020
|
+
input: process.stdin,
|
|
2021
|
+
output: process.stdout
|
|
2022
|
+
});
|
|
2023
|
+
const confirm = await prompt(rl, "Is this OK?", "yes");
|
|
2024
|
+
rl.close();
|
|
2025
|
+
if (confirm.toLowerCase() !== "yes" && confirm.toLowerCase() !== "y") {
|
|
2026
|
+
console.log("Aborted.");
|
|
2027
|
+
process.exit(0);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
await writeFile(pspmJsonPath, `${content}
|
|
2031
|
+
`);
|
|
1145
2032
|
try {
|
|
1146
2033
|
await stat(join(process.cwd(), "SKILL.md"));
|
|
1147
2034
|
} catch {
|
|
@@ -1160,6 +2047,17 @@ async function init(options) {
|
|
|
1160
2047
|
process.exit(1);
|
|
1161
2048
|
}
|
|
1162
2049
|
}
|
|
2050
|
+
|
|
2051
|
+
// src/commands/install.ts
|
|
2052
|
+
init_src();
|
|
2053
|
+
init_agents();
|
|
2054
|
+
init_api_client();
|
|
2055
|
+
init_config();
|
|
2056
|
+
init_errors();
|
|
2057
|
+
init_github();
|
|
2058
|
+
init_lockfile();
|
|
2059
|
+
init_manifest2();
|
|
2060
|
+
init_symlinks();
|
|
1163
2061
|
function getCacheFilePath(cacheDir, integrity) {
|
|
1164
2062
|
const match = integrity.match(/^sha256-(.+)$/);
|
|
1165
2063
|
if (!match) {
|
|
@@ -1191,120 +2089,537 @@ async function writeToCache(cacheDir, integrity, data) {
|
|
|
1191
2089
|
} catch {
|
|
1192
2090
|
}
|
|
1193
2091
|
}
|
|
1194
|
-
async function install(options) {
|
|
2092
|
+
async function install(specifiers, options) {
|
|
2093
|
+
if (specifiers.length > 0) {
|
|
2094
|
+
const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
|
|
2095
|
+
await add2(specifiers, {
|
|
2096
|
+
save: true,
|
|
2097
|
+
agent: options.agent,
|
|
2098
|
+
yes: options.yes
|
|
2099
|
+
});
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
2102
|
+
await installFromLockfile(options);
|
|
2103
|
+
}
|
|
2104
|
+
async function installFromLockfile(options) {
|
|
1195
2105
|
try {
|
|
1196
2106
|
const config2 = await resolveConfig();
|
|
1197
|
-
const
|
|
2107
|
+
const registryUrl = config2.registryUrl;
|
|
2108
|
+
const apiKey = getTokenForRegistry(config2, registryUrl);
|
|
1198
2109
|
const skillsDir = options.dir || getSkillsDir();
|
|
1199
2110
|
const cacheDir = getCacheDir();
|
|
1200
2111
|
await migrateLockfileIfNeeded();
|
|
1201
|
-
|
|
1202
|
-
|
|
2112
|
+
let lockfile = await readLockfile();
|
|
2113
|
+
const manifestDeps = await getDependencies();
|
|
2114
|
+
const manifestGitHubDeps = await getGitHubDependencies();
|
|
2115
|
+
const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
|
|
2116
|
+
const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
|
|
2117
|
+
const installedSkills = [];
|
|
2118
|
+
const missingDeps = [];
|
|
2119
|
+
for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
|
|
2120
|
+
if (!lockfilePackages[fullName]) {
|
|
2121
|
+
missingDeps.push({ fullName, versionRange });
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
if (missingDeps.length > 0) {
|
|
1203
2125
|
if (options.frozenLockfile) {
|
|
1204
2126
|
console.error(
|
|
1205
|
-
"Error:
|
|
2127
|
+
"Error: Dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
|
|
1206
2128
|
);
|
|
2129
|
+
console.error("Missing dependencies:");
|
|
2130
|
+
for (const dep of missingDeps) {
|
|
2131
|
+
console.error(` - ${dep.fullName}@${dep.versionRange}`);
|
|
2132
|
+
}
|
|
1207
2133
|
process.exit(1);
|
|
1208
2134
|
}
|
|
1209
|
-
console.log(
|
|
1210
|
-
return;
|
|
1211
|
-
}
|
|
1212
|
-
const skillCount = Object.keys(
|
|
1213
|
-
lockfile.packages ?? lockfile.skills ?? {}
|
|
1214
|
-
).length;
|
|
1215
|
-
if (skillCount === 0) {
|
|
1216
|
-
console.log("No skills in lockfile. Nothing to install.");
|
|
1217
|
-
return;
|
|
1218
|
-
}
|
|
1219
|
-
console.log(`Installing ${skillCount} skill(s)...
|
|
2135
|
+
console.log(`Resolving ${missingDeps.length} new dependency(ies)...
|
|
1220
2136
|
`);
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
2137
|
+
configure2({ registryUrl, apiKey: apiKey ?? "" });
|
|
2138
|
+
for (const { fullName, versionRange } of missingDeps) {
|
|
2139
|
+
const parsed = parseSkillSpecifier(fullName);
|
|
2140
|
+
if (!parsed) {
|
|
2141
|
+
console.error(`Error: Invalid dependency specifier: ${fullName}`);
|
|
2142
|
+
continue;
|
|
2143
|
+
}
|
|
2144
|
+
const { username, name } = parsed;
|
|
2145
|
+
console.log(`Resolving ${fullName}@${versionRange}...`);
|
|
2146
|
+
const versionsResponse = await listSkillVersions(username, name);
|
|
2147
|
+
if (versionsResponse.status !== 200) {
|
|
2148
|
+
const errorMessage = extractApiErrorMessage(
|
|
2149
|
+
versionsResponse,
|
|
2150
|
+
`Skill ${fullName} not found`
|
|
2151
|
+
);
|
|
2152
|
+
console.error(`Error: ${errorMessage}`);
|
|
2153
|
+
continue;
|
|
2154
|
+
}
|
|
2155
|
+
const versions = versionsResponse.data;
|
|
2156
|
+
if (versions.length === 0) {
|
|
2157
|
+
console.error(`Error: Skill ${fullName} not found`);
|
|
2158
|
+
continue;
|
|
2159
|
+
}
|
|
2160
|
+
const versionStrings = versions.map(
|
|
2161
|
+
(v) => v.version
|
|
2162
|
+
);
|
|
2163
|
+
const resolved = resolveVersion(versionRange || "*", versionStrings);
|
|
2164
|
+
if (!resolved) {
|
|
2165
|
+
console.error(
|
|
2166
|
+
`Error: No version matching "${versionRange}" for ${fullName}`
|
|
2167
|
+
);
|
|
2168
|
+
continue;
|
|
2169
|
+
}
|
|
2170
|
+
const versionResponse = await getSkillVersion(username, name, resolved);
|
|
2171
|
+
if (versionResponse.status !== 200 || !versionResponse.data) {
|
|
2172
|
+
const errorMessage = extractApiErrorMessage(
|
|
2173
|
+
versionResponse,
|
|
2174
|
+
`Version ${resolved} not found`
|
|
2175
|
+
);
|
|
2176
|
+
console.error(`Error: ${errorMessage}`);
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
const versionInfo = versionResponse.data;
|
|
2180
|
+
const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
|
|
1239
2181
|
const downloadHeaders = {};
|
|
1240
2182
|
if (!isPresignedUrl && apiKey) {
|
|
1241
2183
|
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
1242
2184
|
}
|
|
1243
|
-
const
|
|
2185
|
+
const tarballResponse = await fetch(versionInfo.downloadUrl, {
|
|
1244
2186
|
headers: downloadHeaders,
|
|
1245
2187
|
redirect: "follow"
|
|
1246
2188
|
});
|
|
1247
|
-
if (!
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
2189
|
+
if (!tarballResponse.ok) {
|
|
2190
|
+
console.error(
|
|
2191
|
+
`Error: Failed to download tarball for ${fullName} (${tarballResponse.status})`
|
|
2192
|
+
);
|
|
2193
|
+
continue;
|
|
2194
|
+
}
|
|
2195
|
+
const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
|
|
2196
|
+
const integrity = calculateIntegrity(tarballBuffer);
|
|
2197
|
+
await addToLockfile(fullName, {
|
|
2198
|
+
version: resolved,
|
|
2199
|
+
resolved: versionInfo.downloadUrl,
|
|
2200
|
+
integrity
|
|
2201
|
+
});
|
|
2202
|
+
await writeToCache(cacheDir, integrity, tarballBuffer);
|
|
2203
|
+
console.log(` Resolved ${fullName}@${resolved}`);
|
|
2204
|
+
}
|
|
2205
|
+
lockfile = await readLockfile();
|
|
2206
|
+
}
|
|
2207
|
+
const missingGitHubDeps = [];
|
|
2208
|
+
for (const [specifier, ref] of Object.entries(manifestGitHubDeps)) {
|
|
2209
|
+
if (!lockfileGitHubPackages[specifier]) {
|
|
2210
|
+
missingGitHubDeps.push({ specifier, ref });
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
if (missingGitHubDeps.length > 0) {
|
|
2214
|
+
if (options.frozenLockfile) {
|
|
2215
|
+
console.error(
|
|
2216
|
+
"Error: GitHub dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
|
|
2217
|
+
);
|
|
2218
|
+
console.error("Missing GitHub dependencies:");
|
|
2219
|
+
for (const dep of missingGitHubDeps) {
|
|
2220
|
+
console.error(` - ${dep.specifier}@${dep.ref}`);
|
|
2221
|
+
}
|
|
2222
|
+
process.exit(1);
|
|
2223
|
+
}
|
|
2224
|
+
console.log(
|
|
2225
|
+
`
|
|
2226
|
+
Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
|
|
2227
|
+
`
|
|
2228
|
+
);
|
|
2229
|
+
for (const { specifier, ref } of missingGitHubDeps) {
|
|
2230
|
+
const parsed = parseGitHubSpecifier(specifier);
|
|
2231
|
+
if (!parsed) {
|
|
2232
|
+
console.error(`Error: Invalid GitHub specifier: ${specifier}`);
|
|
2233
|
+
continue;
|
|
2234
|
+
}
|
|
2235
|
+
parsed.ref = parsed.ref || ref;
|
|
2236
|
+
console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
|
|
2237
|
+
try {
|
|
2238
|
+
const result = await downloadGitHubPackage(parsed);
|
|
2239
|
+
await extractGitHubPackage(parsed, result.buffer, skillsDir);
|
|
2240
|
+
const entry = {
|
|
2241
|
+
version: result.commit.slice(0, 7),
|
|
2242
|
+
resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
|
|
2243
|
+
integrity: result.integrity,
|
|
2244
|
+
gitCommit: result.commit,
|
|
2245
|
+
gitRef: ref || "HEAD"
|
|
2246
|
+
};
|
|
2247
|
+
await addGitHubToLockfile(specifier, entry);
|
|
2248
|
+
await writeToCache(cacheDir, result.integrity, result.buffer);
|
|
2249
|
+
console.log(
|
|
2250
|
+
` Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`
|
|
2251
|
+
);
|
|
2252
|
+
} catch (error) {
|
|
2253
|
+
if (error instanceof GitHubRateLimitError) {
|
|
2254
|
+
console.error(`Error: ${error.message}`);
|
|
2255
|
+
} else if (error instanceof GitHubPathNotFoundError) {
|
|
2256
|
+
console.error(`Error: ${error.message}`);
|
|
2257
|
+
} else if (error instanceof GitHubNotFoundError) {
|
|
2258
|
+
console.error(`Error: ${error.message}`);
|
|
2259
|
+
} else {
|
|
2260
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2261
|
+
console.error(`Error resolving ${specifier}: ${message}`);
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
lockfile = await readLockfile();
|
|
2266
|
+
}
|
|
2267
|
+
const manifest = await readManifest();
|
|
2268
|
+
const agentConfigs = manifest?.agents;
|
|
2269
|
+
let agents;
|
|
2270
|
+
if (options.agent) {
|
|
2271
|
+
agents = parseAgentArg(options.agent);
|
|
2272
|
+
} else if (manifest) {
|
|
2273
|
+
agents = parseAgentArg(void 0);
|
|
2274
|
+
} else if (options.yes) {
|
|
2275
|
+
agents = parseAgentArg(void 0);
|
|
2276
|
+
} else {
|
|
2277
|
+
console.log("\nNo pspm.json found. Let's set up your project.\n");
|
|
2278
|
+
agents = await promptForAgents();
|
|
2279
|
+
console.log();
|
|
2280
|
+
}
|
|
2281
|
+
const packages = lockfile?.packages ?? lockfile?.skills ?? {};
|
|
2282
|
+
const packageCount = Object.keys(packages).length;
|
|
2283
|
+
if (packageCount > 0) {
|
|
2284
|
+
console.log(`
|
|
2285
|
+
Installing ${packageCount} registry skill(s)...
|
|
2286
|
+
`);
|
|
2287
|
+
const entries = Object.entries(packages);
|
|
2288
|
+
for (const [fullName, entry] of entries) {
|
|
2289
|
+
const match = fullName.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
2290
|
+
if (!match) {
|
|
2291
|
+
console.warn(`Warning: Invalid skill name in lockfile: ${fullName}`);
|
|
2292
|
+
continue;
|
|
2293
|
+
}
|
|
2294
|
+
const [, username, name] = match;
|
|
2295
|
+
console.log(`Installing ${fullName}@${entry.version}...`);
|
|
2296
|
+
let tarballBuffer;
|
|
2297
|
+
let fromCache = false;
|
|
2298
|
+
const cachedTarball = await readFromCache(cacheDir, entry.integrity);
|
|
2299
|
+
if (cachedTarball) {
|
|
2300
|
+
tarballBuffer = cachedTarball;
|
|
2301
|
+
fromCache = true;
|
|
2302
|
+
} else {
|
|
2303
|
+
const isPresignedUrl = entry.resolved.includes(".r2.cloudflarestorage.com") || entry.resolved.includes("X-Amz-Signature");
|
|
2304
|
+
const downloadHeaders = {};
|
|
2305
|
+
if (!isPresignedUrl && apiKey) {
|
|
2306
|
+
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
2307
|
+
}
|
|
2308
|
+
const response = await fetch(entry.resolved, {
|
|
2309
|
+
headers: downloadHeaders,
|
|
2310
|
+
redirect: "follow"
|
|
2311
|
+
});
|
|
2312
|
+
if (!response.ok) {
|
|
2313
|
+
if (response.status === 401) {
|
|
2314
|
+
if (!apiKey) {
|
|
2315
|
+
console.error(
|
|
2316
|
+
` Error: ${fullName} requires authentication. Run 'pspm login' first.`
|
|
2317
|
+
);
|
|
2318
|
+
} else {
|
|
2319
|
+
console.error(
|
|
2320
|
+
` Error: Access denied to ${fullName}. You may not have permission to access this private package.`
|
|
2321
|
+
);
|
|
2322
|
+
}
|
|
1253
2323
|
} else {
|
|
1254
2324
|
console.error(
|
|
1255
|
-
` Error:
|
|
2325
|
+
` Error: Failed to download ${fullName} (${response.status})`
|
|
1256
2326
|
);
|
|
1257
2327
|
}
|
|
1258
|
-
|
|
2328
|
+
continue;
|
|
2329
|
+
}
|
|
2330
|
+
tarballBuffer = Buffer.from(await response.arrayBuffer());
|
|
2331
|
+
const actualIntegrity = `sha256-${createHash("sha256").update(tarballBuffer).digest("base64")}`;
|
|
2332
|
+
if (actualIntegrity !== entry.integrity) {
|
|
1259
2333
|
console.error(
|
|
1260
|
-
` Error:
|
|
2334
|
+
` Error: Checksum verification failed for ${fullName}`
|
|
1261
2335
|
);
|
|
2336
|
+
if (options.frozenLockfile) {
|
|
2337
|
+
process.exit(1);
|
|
2338
|
+
}
|
|
2339
|
+
continue;
|
|
1262
2340
|
}
|
|
1263
|
-
|
|
2341
|
+
await writeToCache(cacheDir, entry.integrity, tarballBuffer);
|
|
1264
2342
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
2343
|
+
const destDir = join(skillsDir, username, name);
|
|
2344
|
+
await rm(destDir, { recursive: true, force: true });
|
|
2345
|
+
await mkdir(destDir, { recursive: true });
|
|
2346
|
+
const tempFile = join(destDir, ".temp.tgz");
|
|
2347
|
+
await writeFile(tempFile, tarballBuffer);
|
|
2348
|
+
const { exec: exec2 } = await import('child_process');
|
|
2349
|
+
const { promisify: promisify2 } = await import('util');
|
|
2350
|
+
const execAsync = promisify2(exec2);
|
|
2351
|
+
try {
|
|
2352
|
+
await execAsync(
|
|
2353
|
+
`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
|
|
2354
|
+
);
|
|
2355
|
+
} finally {
|
|
2356
|
+
await rm(tempFile, { force: true });
|
|
2357
|
+
}
|
|
2358
|
+
console.log(
|
|
2359
|
+
` Installed to ${destDir}${fromCache ? " (from cache)" : ""}`
|
|
2360
|
+
);
|
|
2361
|
+
installedSkills.push({
|
|
2362
|
+
name,
|
|
2363
|
+
sourcePath: getRegistrySkillPath(username, name)
|
|
2364
|
+
});
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
const githubPackages = lockfile?.githubPackages ?? {};
|
|
2368
|
+
const githubCount = Object.keys(githubPackages).length;
|
|
2369
|
+
if (githubCount > 0) {
|
|
2370
|
+
console.log(`
|
|
2371
|
+
Installing ${githubCount} GitHub skill(s)...
|
|
2372
|
+
`);
|
|
2373
|
+
for (const [specifier, entry] of Object.entries(githubPackages)) {
|
|
2374
|
+
const parsed = parseGitHubSpecifier(specifier);
|
|
2375
|
+
if (!parsed) {
|
|
2376
|
+
console.warn(
|
|
2377
|
+
`Warning: Invalid GitHub specifier in lockfile: ${specifier}`
|
|
1270
2378
|
);
|
|
1271
|
-
if (options.frozenLockfile) {
|
|
1272
|
-
process.exit(1);
|
|
1273
|
-
}
|
|
1274
2379
|
continue;
|
|
1275
2380
|
}
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
2381
|
+
const ghEntry = entry;
|
|
2382
|
+
console.log(
|
|
2383
|
+
`Installing ${specifier} (${ghEntry.gitRef}@${ghEntry.gitCommit.slice(0, 7)})...`
|
|
2384
|
+
);
|
|
2385
|
+
let tarballBuffer;
|
|
2386
|
+
let fromCache = false;
|
|
2387
|
+
const cachedTarball = await readFromCache(cacheDir, ghEntry.integrity);
|
|
2388
|
+
if (cachedTarball) {
|
|
2389
|
+
tarballBuffer = cachedTarball;
|
|
2390
|
+
fromCache = true;
|
|
2391
|
+
} else {
|
|
2392
|
+
try {
|
|
2393
|
+
const specWithCommit = { ...parsed, ref: ghEntry.gitCommit };
|
|
2394
|
+
const result = await downloadGitHubPackage(specWithCommit);
|
|
2395
|
+
tarballBuffer = result.buffer;
|
|
2396
|
+
if (result.integrity !== ghEntry.integrity) {
|
|
2397
|
+
console.error(
|
|
2398
|
+
` Error: Checksum verification failed for ${specifier}`
|
|
2399
|
+
);
|
|
2400
|
+
if (options.frozenLockfile) {
|
|
2401
|
+
process.exit(1);
|
|
2402
|
+
}
|
|
2403
|
+
continue;
|
|
2404
|
+
}
|
|
2405
|
+
await writeToCache(cacheDir, ghEntry.integrity, tarballBuffer);
|
|
2406
|
+
} catch (error) {
|
|
2407
|
+
if (error instanceof GitHubRateLimitError) {
|
|
2408
|
+
console.error(` Error: ${error.message}`);
|
|
2409
|
+
} else if (error instanceof GitHubPathNotFoundError) {
|
|
2410
|
+
console.error(` Error: ${error.message}`);
|
|
2411
|
+
} else if (error instanceof GitHubNotFoundError) {
|
|
2412
|
+
console.error(` Error: ${error.message}`);
|
|
2413
|
+
} else {
|
|
2414
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2415
|
+
console.error(` Error downloading ${specifier}: ${message}`);
|
|
2416
|
+
}
|
|
2417
|
+
continue;
|
|
2418
|
+
}
|
|
2419
|
+
}
|
|
2420
|
+
try {
|
|
2421
|
+
const destPath = await extractGitHubPackage(
|
|
2422
|
+
parsed,
|
|
2423
|
+
tarballBuffer,
|
|
2424
|
+
skillsDir
|
|
2425
|
+
);
|
|
2426
|
+
console.log(
|
|
2427
|
+
` Installed to ${destPath}${fromCache ? " (from cache)" : ""}`
|
|
2428
|
+
);
|
|
2429
|
+
const skillName = getGitHubSkillName(parsed);
|
|
2430
|
+
installedSkills.push({
|
|
2431
|
+
name: skillName,
|
|
2432
|
+
sourcePath: getGitHubSkillPath(
|
|
2433
|
+
parsed.owner,
|
|
2434
|
+
parsed.repo,
|
|
2435
|
+
parsed.path
|
|
2436
|
+
)
|
|
2437
|
+
});
|
|
2438
|
+
} catch (error) {
|
|
2439
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2440
|
+
console.error(` Error extracting ${specifier}: ${message}`);
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
if (installedSkills.length > 0 && agents[0] !== "none") {
|
|
2445
|
+
console.log(`
|
|
2446
|
+
Creating symlinks for agent(s): ${agents.join(", ")}...`);
|
|
2447
|
+
await createAgentSymlinks(installedSkills, {
|
|
2448
|
+
agents,
|
|
2449
|
+
projectRoot: process.cwd(),
|
|
2450
|
+
agentConfigs
|
|
2451
|
+
});
|
|
2452
|
+
console.log(" Symlinks created.");
|
|
2453
|
+
}
|
|
2454
|
+
const totalCount = packageCount + githubCount;
|
|
2455
|
+
if (totalCount === 0) {
|
|
2456
|
+
console.log("No skills to install.");
|
|
2457
|
+
} else {
|
|
2458
|
+
console.log(`
|
|
2459
|
+
All ${totalCount} skill(s) installed.`);
|
|
2460
|
+
}
|
|
2461
|
+
} catch (error) {
|
|
2462
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
2463
|
+
console.error(`Error: ${message}`);
|
|
2464
|
+
process.exit(1);
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
// src/commands/link.ts
|
|
2469
|
+
init_src();
|
|
2470
|
+
init_agents();
|
|
2471
|
+
init_lockfile();
|
|
2472
|
+
init_manifest2();
|
|
2473
|
+
init_symlinks();
|
|
2474
|
+
async function link(options) {
|
|
2475
|
+
try {
|
|
2476
|
+
const manifest = await readManifest();
|
|
2477
|
+
const agentConfigs = manifest?.agents;
|
|
2478
|
+
let agents;
|
|
2479
|
+
if (options.agent) {
|
|
2480
|
+
agents = parseAgentArg(options.agent);
|
|
2481
|
+
} else if (manifest) {
|
|
2482
|
+
agents = parseAgentArg(void 0);
|
|
2483
|
+
} else if (options.yes) {
|
|
2484
|
+
agents = parseAgentArg(void 0);
|
|
2485
|
+
} else {
|
|
2486
|
+
console.log("No pspm.json found. Let's set up your project.\n");
|
|
2487
|
+
agents = await promptForAgents();
|
|
2488
|
+
}
|
|
2489
|
+
if (agents.length === 1 && agents[0] === "none") {
|
|
2490
|
+
console.log("Skipping symlink creation (--agent none)");
|
|
2491
|
+
return;
|
|
2492
|
+
}
|
|
2493
|
+
const skills = [];
|
|
2494
|
+
const registrySkills = await listLockfileSkills();
|
|
2495
|
+
for (const { name } of registrySkills) {
|
|
2496
|
+
const parsed = parseSkillSpecifier(name);
|
|
2497
|
+
if (!parsed) {
|
|
2498
|
+
console.warn(`Warning: Invalid skill name in lockfile: ${name}`);
|
|
2499
|
+
continue;
|
|
2500
|
+
}
|
|
2501
|
+
skills.push({
|
|
2502
|
+
name: parsed.name,
|
|
2503
|
+
sourcePath: getRegistrySkillPath(parsed.username, parsed.name)
|
|
2504
|
+
});
|
|
2505
|
+
}
|
|
2506
|
+
const githubSkills = await listLockfileGitHubPackages();
|
|
2507
|
+
for (const { specifier } of githubSkills) {
|
|
2508
|
+
const parsed = parseGitHubSpecifier(specifier);
|
|
2509
|
+
if (!parsed) {
|
|
2510
|
+
console.warn(
|
|
2511
|
+
`Warning: Invalid GitHub specifier in lockfile: ${specifier}`
|
|
1289
2512
|
);
|
|
1290
|
-
|
|
1291
|
-
await rm(tempFile, { force: true });
|
|
2513
|
+
continue;
|
|
1292
2514
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
2515
|
+
const skillName = getGitHubSkillName(parsed);
|
|
2516
|
+
skills.push({
|
|
2517
|
+
name: skillName,
|
|
2518
|
+
sourcePath: getGitHubSkillPath(parsed.owner, parsed.repo, parsed.path)
|
|
2519
|
+
});
|
|
2520
|
+
}
|
|
2521
|
+
if (skills.length === 0) {
|
|
2522
|
+
console.log("No skills found in lockfile. Nothing to link.");
|
|
2523
|
+
return;
|
|
2524
|
+
}
|
|
2525
|
+
console.log(
|
|
2526
|
+
`Creating symlinks for ${skills.length} skill(s) to agent(s): ${agents.join(", ")}...`
|
|
2527
|
+
);
|
|
2528
|
+
await createAgentSymlinks(skills, {
|
|
2529
|
+
agents,
|
|
2530
|
+
projectRoot: process.cwd(),
|
|
2531
|
+
agentConfigs
|
|
2532
|
+
});
|
|
2533
|
+
console.log("Symlinks created successfully.");
|
|
2534
|
+
console.log("\nLinked skills:");
|
|
2535
|
+
for (const skill of skills) {
|
|
2536
|
+
console.log(` ${skill.name} -> ${skill.sourcePath}`);
|
|
1296
2537
|
}
|
|
1297
|
-
console.log(`
|
|
1298
|
-
All ${skillCount} skill(s) installed.`);
|
|
1299
2538
|
} catch (error) {
|
|
1300
2539
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1301
2540
|
console.error(`Error: ${message}`);
|
|
1302
2541
|
process.exit(1);
|
|
1303
2542
|
}
|
|
1304
2543
|
}
|
|
2544
|
+
|
|
2545
|
+
// src/commands/list.ts
|
|
2546
|
+
init_src();
|
|
2547
|
+
init_agents();
|
|
2548
|
+
init_lockfile();
|
|
2549
|
+
init_manifest2();
|
|
2550
|
+
init_symlinks();
|
|
1305
2551
|
async function list(options) {
|
|
1306
2552
|
try {
|
|
1307
|
-
const
|
|
2553
|
+
const registrySkills = await listLockfileSkills();
|
|
2554
|
+
const githubSkills = await listLockfileGitHubPackages();
|
|
2555
|
+
const manifest = await readManifest();
|
|
2556
|
+
const agentConfigs = manifest?.agents;
|
|
2557
|
+
const availableAgents = getAvailableAgents(agentConfigs);
|
|
2558
|
+
const projectRoot = process.cwd();
|
|
2559
|
+
const skills = [];
|
|
2560
|
+
for (const { name: fullName, entry } of registrySkills) {
|
|
2561
|
+
const match = fullName.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
2562
|
+
if (!match) continue;
|
|
2563
|
+
const [, username, skillName] = match;
|
|
2564
|
+
const sourcePath = getRegistrySkillPath(username, skillName);
|
|
2565
|
+
const absolutePath = join(projectRoot, sourcePath);
|
|
2566
|
+
let status = "installed";
|
|
2567
|
+
try {
|
|
2568
|
+
await access$1(absolutePath);
|
|
2569
|
+
} catch {
|
|
2570
|
+
status = "missing";
|
|
2571
|
+
}
|
|
2572
|
+
const linkedAgents = await getLinkedAgents(
|
|
2573
|
+
skillName,
|
|
2574
|
+
availableAgents,
|
|
2575
|
+
projectRoot,
|
|
2576
|
+
agentConfigs
|
|
2577
|
+
);
|
|
2578
|
+
skills.push({
|
|
2579
|
+
name: skillName,
|
|
2580
|
+
fullName,
|
|
2581
|
+
version: entry.version,
|
|
2582
|
+
source: "registry",
|
|
2583
|
+
sourcePath,
|
|
2584
|
+
status,
|
|
2585
|
+
linkedAgents
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
for (const { specifier, entry } of githubSkills) {
|
|
2589
|
+
const parsed = parseGitHubSpecifier(specifier);
|
|
2590
|
+
if (!parsed) continue;
|
|
2591
|
+
const ghEntry = entry;
|
|
2592
|
+
const skillName = getGitHubSkillName(parsed);
|
|
2593
|
+
const sourcePath = getGitHubSkillPath(
|
|
2594
|
+
parsed.owner,
|
|
2595
|
+
parsed.repo,
|
|
2596
|
+
parsed.path
|
|
2597
|
+
);
|
|
2598
|
+
const absolutePath = join(projectRoot, sourcePath);
|
|
2599
|
+
let status = "installed";
|
|
2600
|
+
try {
|
|
2601
|
+
await access$1(absolutePath);
|
|
2602
|
+
} catch {
|
|
2603
|
+
status = "missing";
|
|
2604
|
+
}
|
|
2605
|
+
const linkedAgents = await getLinkedAgents(
|
|
2606
|
+
skillName,
|
|
2607
|
+
availableAgents,
|
|
2608
|
+
projectRoot,
|
|
2609
|
+
agentConfigs
|
|
2610
|
+
);
|
|
2611
|
+
skills.push({
|
|
2612
|
+
name: skillName,
|
|
2613
|
+
fullName: specifier,
|
|
2614
|
+
version: ghEntry.gitCommit.slice(0, 7),
|
|
2615
|
+
source: "github",
|
|
2616
|
+
sourcePath,
|
|
2617
|
+
status,
|
|
2618
|
+
linkedAgents,
|
|
2619
|
+
gitRef: ghEntry.gitRef,
|
|
2620
|
+
gitCommit: ghEntry.gitCommit
|
|
2621
|
+
});
|
|
2622
|
+
}
|
|
1308
2623
|
if (skills.length === 0) {
|
|
1309
2624
|
console.log("No skills installed.");
|
|
1310
2625
|
return;
|
|
@@ -1313,32 +2628,43 @@ async function list(options) {
|
|
|
1313
2628
|
console.log(JSON.stringify(skills, null, 2));
|
|
1314
2629
|
return;
|
|
1315
2630
|
}
|
|
1316
|
-
const skillsDir = getSkillsDir();
|
|
1317
2631
|
console.log("Installed skills:\n");
|
|
1318
|
-
for (const
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
try {
|
|
1325
|
-
await access$1(skillPath);
|
|
1326
|
-
} catch {
|
|
1327
|
-
status = "missing";
|
|
2632
|
+
for (const skill of skills) {
|
|
2633
|
+
if (skill.source === "registry") {
|
|
2634
|
+
console.log(` ${skill.fullName}@${skill.version} (registry)`);
|
|
2635
|
+
} else {
|
|
2636
|
+
const refInfo = skill.gitRef ? `${skill.gitRef}@${skill.gitCommit?.slice(0, 7)}` : skill.version;
|
|
2637
|
+
console.log(` ${skill.fullName} (${refInfo})`);
|
|
1328
2638
|
}
|
|
1329
|
-
|
|
1330
|
-
if (status === "missing") {
|
|
2639
|
+
if (skill.status === "missing") {
|
|
1331
2640
|
console.log(` Status: MISSING (run 'pspm install' to restore)`);
|
|
1332
2641
|
}
|
|
2642
|
+
if (skill.linkedAgents.length > 0) {
|
|
2643
|
+
for (const agent of skill.linkedAgents) {
|
|
2644
|
+
const config2 = resolveAgentConfig(agent, agentConfigs);
|
|
2645
|
+
if (config2) {
|
|
2646
|
+
console.log(` -> ${config2.skillsDir}/${skill.name}`);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
1333
2650
|
}
|
|
2651
|
+
const registryCount = skills.filter((s) => s.source === "registry").length;
|
|
2652
|
+
const githubCount = skills.filter((s) => s.source === "github").length;
|
|
2653
|
+
const parts = [];
|
|
2654
|
+
if (registryCount > 0) parts.push(`${registryCount} registry`);
|
|
2655
|
+
if (githubCount > 0) parts.push(`${githubCount} github`);
|
|
1334
2656
|
console.log(`
|
|
1335
|
-
Total: ${skills.length} skill(s)`);
|
|
2657
|
+
Total: ${skills.length} skill(s) (${parts.join(", ")})`);
|
|
1336
2658
|
} catch (error) {
|
|
1337
2659
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1338
2660
|
console.error(`Error: ${message}`);
|
|
1339
2661
|
process.exit(1);
|
|
1340
2662
|
}
|
|
1341
2663
|
}
|
|
2664
|
+
|
|
2665
|
+
// src/commands/login.ts
|
|
2666
|
+
init_api_client();
|
|
2667
|
+
init_config();
|
|
1342
2668
|
var DEFAULT_WEB_APP_URL = "https://pspm.dev";
|
|
1343
2669
|
function getWebAppUrl(registryUrl) {
|
|
1344
2670
|
if (process.env.PSPM_WEB_URL) {
|
|
@@ -1516,6 +2842,7 @@ async function login(options) {
|
|
|
1516
2842
|
}
|
|
1517
2843
|
|
|
1518
2844
|
// src/commands/logout.ts
|
|
2845
|
+
init_config();
|
|
1519
2846
|
async function logout() {
|
|
1520
2847
|
try {
|
|
1521
2848
|
const loggedIn = await isLoggedIn();
|
|
@@ -1531,6 +2858,10 @@ async function logout() {
|
|
|
1531
2858
|
process.exit(1);
|
|
1532
2859
|
}
|
|
1533
2860
|
}
|
|
2861
|
+
|
|
2862
|
+
// src/commands/migrate.ts
|
|
2863
|
+
init_config();
|
|
2864
|
+
init_lockfile();
|
|
1534
2865
|
async function migrate(options) {
|
|
1535
2866
|
try {
|
|
1536
2867
|
const legacySkillsDir = getLegacySkillsDir();
|
|
@@ -1630,6 +2961,12 @@ async function migrate(options) {
|
|
|
1630
2961
|
process.exit(1);
|
|
1631
2962
|
}
|
|
1632
2963
|
}
|
|
2964
|
+
|
|
2965
|
+
// src/commands/publish.ts
|
|
2966
|
+
init_src();
|
|
2967
|
+
init_api_client();
|
|
2968
|
+
init_config();
|
|
2969
|
+
init_errors();
|
|
1633
2970
|
var exec = promisify(exec$1);
|
|
1634
2971
|
async function detectManifest() {
|
|
1635
2972
|
const cwd = process.cwd();
|
|
@@ -1835,63 +3172,119 @@ Setting visibility to ${options.access}...`);
|
|
|
1835
3172
|
process.exit(1);
|
|
1836
3173
|
}
|
|
1837
3174
|
}
|
|
3175
|
+
|
|
3176
|
+
// src/commands/remove.ts
|
|
3177
|
+
init_src();
|
|
3178
|
+
init_agents();
|
|
3179
|
+
init_config();
|
|
3180
|
+
init_lockfile();
|
|
3181
|
+
init_manifest2();
|
|
3182
|
+
init_symlinks();
|
|
1838
3183
|
async function remove(nameOrSpecifier) {
|
|
1839
3184
|
try {
|
|
1840
|
-
await
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
console.error(`Error: Invalid skill specifier: ${nameOrSpecifier}`);
|
|
1848
|
-
process.exit(1);
|
|
1849
|
-
}
|
|
1850
|
-
fullName = `@user/${match[1]}/${match[2]}`;
|
|
1851
|
-
username = match[1];
|
|
1852
|
-
name = match[2];
|
|
3185
|
+
const manifest = await readManifest();
|
|
3186
|
+
const agentConfigs = manifest?.agents;
|
|
3187
|
+
const agents = getAvailableAgents(agentConfigs);
|
|
3188
|
+
if (isGitHubSpecifier(nameOrSpecifier)) {
|
|
3189
|
+
await removeGitHub(nameOrSpecifier, agents, agentConfigs);
|
|
3190
|
+
} else if (nameOrSpecifier.startsWith("@user/")) {
|
|
3191
|
+
await removeRegistry(nameOrSpecifier, agents, agentConfigs);
|
|
1853
3192
|
} else {
|
|
1854
|
-
|
|
1855
|
-
const found = skills.find((s) => {
|
|
1856
|
-
const match2 = s.name.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
1857
|
-
return match2 && match2[2] === nameOrSpecifier;
|
|
1858
|
-
});
|
|
1859
|
-
if (!found) {
|
|
1860
|
-
console.error(
|
|
1861
|
-
`Error: Skill "${nameOrSpecifier}" not found in lockfile`
|
|
1862
|
-
);
|
|
1863
|
-
process.exit(1);
|
|
1864
|
-
}
|
|
1865
|
-
fullName = found.name;
|
|
1866
|
-
const match = fullName.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
1867
|
-
if (!match) {
|
|
1868
|
-
console.error(`Error: Invalid skill name in lockfile: ${fullName}`);
|
|
1869
|
-
process.exit(1);
|
|
1870
|
-
}
|
|
1871
|
-
username = match[1];
|
|
1872
|
-
name = match[2];
|
|
1873
|
-
}
|
|
1874
|
-
console.log(`Removing ${fullName}...`);
|
|
1875
|
-
const removed = await removeFromLockfile(fullName);
|
|
1876
|
-
if (!removed) {
|
|
1877
|
-
console.error(`Error: ${fullName} not found in lockfile`);
|
|
1878
|
-
process.exit(1);
|
|
1879
|
-
}
|
|
1880
|
-
const skillsDir = getSkillsDir();
|
|
1881
|
-
const destDir = join(skillsDir, username, name);
|
|
1882
|
-
try {
|
|
1883
|
-
await rm(destDir, { recursive: true, force: true });
|
|
1884
|
-
} catch {
|
|
3193
|
+
await removeByShortName(nameOrSpecifier, agents, agentConfigs);
|
|
1885
3194
|
}
|
|
1886
|
-
console.log(`Removed ${fullName}`);
|
|
1887
3195
|
} catch (error) {
|
|
1888
3196
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1889
3197
|
console.error(`Error: ${message}`);
|
|
1890
3198
|
process.exit(1);
|
|
1891
3199
|
}
|
|
1892
3200
|
}
|
|
3201
|
+
async function removeRegistry(specifier, agents, agentConfigs) {
|
|
3202
|
+
const match = specifier.match(/^@user\/([^/]+)\/([^@/]+)/);
|
|
3203
|
+
if (!match) {
|
|
3204
|
+
console.error(`Error: Invalid skill specifier: ${specifier}`);
|
|
3205
|
+
process.exit(1);
|
|
3206
|
+
}
|
|
3207
|
+
const fullName = `@user/${match[1]}/${match[2]}`;
|
|
3208
|
+
const username = match[1];
|
|
3209
|
+
const name = match[2];
|
|
3210
|
+
console.log(`Removing ${fullName}...`);
|
|
3211
|
+
const removedFromLockfile = await removeFromLockfile(fullName);
|
|
3212
|
+
const removedFromManifest = await removeDependency(fullName);
|
|
3213
|
+
if (!removedFromLockfile && !removedFromManifest) {
|
|
3214
|
+
console.error(`Error: ${fullName} not found in lockfile or pspm.json`);
|
|
3215
|
+
process.exit(1);
|
|
3216
|
+
}
|
|
3217
|
+
await removeAgentSymlinks(name, {
|
|
3218
|
+
agents,
|
|
3219
|
+
projectRoot: process.cwd(),
|
|
3220
|
+
agentConfigs
|
|
3221
|
+
});
|
|
3222
|
+
const skillsDir = getSkillsDir();
|
|
3223
|
+
const destDir = join(skillsDir, username, name);
|
|
3224
|
+
try {
|
|
3225
|
+
await rm(destDir, { recursive: true, force: true });
|
|
3226
|
+
} catch {
|
|
3227
|
+
}
|
|
3228
|
+
console.log(`Removed ${fullName}`);
|
|
3229
|
+
}
|
|
3230
|
+
async function removeGitHub(specifier, agents, agentConfigs) {
|
|
3231
|
+
const parsed = parseGitHubSpecifier(specifier);
|
|
3232
|
+
if (!parsed) {
|
|
3233
|
+
console.error(`Error: Invalid GitHub specifier: ${specifier}`);
|
|
3234
|
+
process.exit(1);
|
|
3235
|
+
}
|
|
3236
|
+
const lockfileKey = parsed.path ? `github:${parsed.owner}/${parsed.repo}/${parsed.path}` : `github:${parsed.owner}/${parsed.repo}`;
|
|
3237
|
+
console.log(`Removing ${lockfileKey}...`);
|
|
3238
|
+
const removedFromLockfile = await removeGitHubFromLockfile(lockfileKey);
|
|
3239
|
+
const removedFromManifest = await removeGitHubDependency(lockfileKey);
|
|
3240
|
+
if (!removedFromLockfile && !removedFromManifest) {
|
|
3241
|
+
console.error(`Error: ${lockfileKey} not found in lockfile or pspm.json`);
|
|
3242
|
+
process.exit(1);
|
|
3243
|
+
}
|
|
3244
|
+
const skillName = getGitHubSkillName(parsed);
|
|
3245
|
+
await removeAgentSymlinks(skillName, {
|
|
3246
|
+
agents,
|
|
3247
|
+
projectRoot: process.cwd(),
|
|
3248
|
+
agentConfigs
|
|
3249
|
+
});
|
|
3250
|
+
const skillsDir = getSkillsDir();
|
|
3251
|
+
const destPath = getGitHubSkillPath(parsed.owner, parsed.repo, parsed.path);
|
|
3252
|
+
const destDir = join(skillsDir, "..", destPath);
|
|
3253
|
+
try {
|
|
3254
|
+
await rm(destDir, { recursive: true, force: true });
|
|
3255
|
+
} catch {
|
|
3256
|
+
}
|
|
3257
|
+
console.log(`Removed ${lockfileKey}`);
|
|
3258
|
+
}
|
|
3259
|
+
async function removeByShortName(shortName, agents, agentConfigs) {
|
|
3260
|
+
const registrySkills = await listLockfileSkills();
|
|
3261
|
+
const foundRegistry = registrySkills.find((s) => {
|
|
3262
|
+
const match = s.name.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
3263
|
+
return match && match[2] === shortName;
|
|
3264
|
+
});
|
|
3265
|
+
if (foundRegistry) {
|
|
3266
|
+
await removeRegistry(foundRegistry.name, agents, agentConfigs);
|
|
3267
|
+
return;
|
|
3268
|
+
}
|
|
3269
|
+
const githubSkills = await listLockfileGitHubPackages();
|
|
3270
|
+
const foundGitHub = githubSkills.find((s) => {
|
|
3271
|
+
const parsed = parseGitHubSpecifier(s.specifier);
|
|
3272
|
+
if (!parsed) return false;
|
|
3273
|
+
return getGitHubSkillName(parsed) === shortName;
|
|
3274
|
+
});
|
|
3275
|
+
if (foundGitHub) {
|
|
3276
|
+
await removeGitHub(foundGitHub.specifier, agents, agentConfigs);
|
|
3277
|
+
return;
|
|
3278
|
+
}
|
|
3279
|
+
console.error(`Error: Skill "${shortName}" not found in lockfile`);
|
|
3280
|
+
process.exit(1);
|
|
3281
|
+
}
|
|
1893
3282
|
|
|
1894
3283
|
// src/commands/unpublish.ts
|
|
3284
|
+
init_src();
|
|
3285
|
+
init_api_client();
|
|
3286
|
+
init_config();
|
|
3287
|
+
init_errors();
|
|
1895
3288
|
async function unpublish(specifier, options) {
|
|
1896
3289
|
try {
|
|
1897
3290
|
const apiKey = await requireApiKey();
|
|
@@ -1950,6 +3343,12 @@ async function unpublish(specifier, options) {
|
|
|
1950
3343
|
}
|
|
1951
3344
|
|
|
1952
3345
|
// src/commands/update.ts
|
|
3346
|
+
init_src();
|
|
3347
|
+
init_api_client();
|
|
3348
|
+
init_config();
|
|
3349
|
+
init_errors();
|
|
3350
|
+
init_lockfile();
|
|
3351
|
+
init_add();
|
|
1953
3352
|
async function update(options) {
|
|
1954
3353
|
try {
|
|
1955
3354
|
const apiKey = await requireApiKey();
|
|
@@ -2011,7 +3410,7 @@ async function update(options) {
|
|
|
2011
3410
|
if (!match) continue;
|
|
2012
3411
|
const [, username, skillName] = match;
|
|
2013
3412
|
const specifier = `@user/${username}/${skillName}@${latest}`;
|
|
2014
|
-
await add(specifier, {});
|
|
3413
|
+
await add([specifier], {});
|
|
2015
3414
|
}
|
|
2016
3415
|
console.log("\nAll skills updated.");
|
|
2017
3416
|
} catch (error) {
|
|
@@ -2022,6 +3421,8 @@ async function update(options) {
|
|
|
2022
3421
|
}
|
|
2023
3422
|
|
|
2024
3423
|
// src/commands/whoami.ts
|
|
3424
|
+
init_api_client();
|
|
3425
|
+
init_config();
|
|
2025
3426
|
async function whoami() {
|
|
2026
3427
|
try {
|
|
2027
3428
|
const resolved = await resolveConfig();
|
|
@@ -2075,12 +3476,13 @@ program.command("logout").description("Log out and clear stored credentials").ac
|
|
|
2075
3476
|
program.command("whoami").description("Show current user information").action(async () => {
|
|
2076
3477
|
await whoami();
|
|
2077
3478
|
});
|
|
2078
|
-
program.command("init").description("Create a new pspm.json manifest in the current directory").option("-n, --name <name>", "Skill name").option("-d, --description <desc>", "Skill description").option("-a, --author <author>", "Author name").option("-y, --yes", "Skip prompts and use defaults").action(async (options) => {
|
|
3479
|
+
program.command("init").description("Create a new pspm.json manifest in the current directory").option("-n, --name <name>", "Skill name").option("-d, --description <desc>", "Skill description").option("-a, --author <author>", "Author name").option("-y, --yes", "Skip prompts and use defaults").option("-f, --force", "Overwrite existing pspm.json").action(async (options) => {
|
|
2079
3480
|
await init({
|
|
2080
3481
|
name: options.name,
|
|
2081
3482
|
description: options.description,
|
|
2082
3483
|
author: options.author,
|
|
2083
|
-
yes: options.yes
|
|
3484
|
+
yes: options.yes,
|
|
3485
|
+
force: options.force
|
|
2084
3486
|
});
|
|
2085
3487
|
});
|
|
2086
3488
|
program.command("migrate").description(
|
|
@@ -2088,8 +3490,17 @@ program.command("migrate").description(
|
|
|
2088
3490
|
).option("--dry-run", "Show what would be migrated without making changes").action(async (options) => {
|
|
2089
3491
|
await migrate({ dryRun: options.dryRun });
|
|
2090
3492
|
});
|
|
2091
|
-
program.command("add <
|
|
2092
|
-
|
|
3493
|
+
program.command("add <specifiers...>").description(
|
|
3494
|
+
"Add one or more skills (e.g., @user/bsheng/vite_slides@^2.0.0 or github:owner/repo/path@ref)"
|
|
3495
|
+
).option("--save", "Save to lockfile (default)").option(
|
|
3496
|
+
"--agent <agents>",
|
|
3497
|
+
'Comma-separated agents for symlinks (default: all agents, use "none" to skip)'
|
|
3498
|
+
).option("-y, --yes", "Skip agent selection prompt and use defaults").action(async (specifiers, options) => {
|
|
3499
|
+
await add(specifiers, {
|
|
3500
|
+
save: options.save ?? true,
|
|
3501
|
+
agent: options.agent,
|
|
3502
|
+
yes: options.yes
|
|
3503
|
+
});
|
|
2093
3504
|
});
|
|
2094
3505
|
program.command("remove <name>").alias("rm").description("Remove an installed skill").action(async (name) => {
|
|
2095
3506
|
await remove(name);
|
|
@@ -2097,12 +3508,25 @@ program.command("remove <name>").alias("rm").description("Remove an installed sk
|
|
|
2097
3508
|
program.command("list").alias("ls").description("List installed skills").option("--json", "Output as JSON").action(async (options) => {
|
|
2098
3509
|
await list({ json: options.json });
|
|
2099
3510
|
});
|
|
2100
|
-
program.command("install").alias("i").description(
|
|
2101
|
-
|
|
3511
|
+
program.command("install [specifiers...]").alias("i").description(
|
|
3512
|
+
"Install skills from lockfile, or add and install specific packages"
|
|
3513
|
+
).option("--frozen-lockfile", "Fail if lockfile is missing or outdated").option("--dir <path>", "Install skills to a specific directory").option(
|
|
3514
|
+
"--agent <agents>",
|
|
3515
|
+
'Comma-separated agents for symlinks (default: all agents, use "none" to skip)'
|
|
3516
|
+
).option("-y, --yes", "Skip agent selection prompt and use defaults").action(async (specifiers, options) => {
|
|
3517
|
+
await install(specifiers, {
|
|
2102
3518
|
frozenLockfile: options.frozenLockfile,
|
|
2103
|
-
dir: options.dir
|
|
3519
|
+
dir: options.dir,
|
|
3520
|
+
agent: options.agent,
|
|
3521
|
+
yes: options.yes
|
|
2104
3522
|
});
|
|
2105
3523
|
});
|
|
3524
|
+
program.command("link").description("Recreate agent symlinks without reinstalling").option(
|
|
3525
|
+
"--agent <agents>",
|
|
3526
|
+
'Comma-separated agents for symlinks (default: all agents, use "none" to skip)'
|
|
3527
|
+
).option("-y, --yes", "Skip agent selection prompt and use defaults").action(async (options) => {
|
|
3528
|
+
await link({ agent: options.agent, yes: options.yes });
|
|
3529
|
+
});
|
|
2106
3530
|
program.command("update").description("Update all skills to latest compatible versions").option("--dry-run", "Show what would be updated without making changes").action(async (options) => {
|
|
2107
3531
|
await update({ dryRun: options.dryRun });
|
|
2108
3532
|
});
|