@anytio/pspm 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { createHash, randomBytes } from 'crypto';
8
8
  import * as semver2 from 'semver';
9
9
  import semver2__default from 'semver';
10
10
  import { checkbox } from '@inquirer/prompts';
11
- import { readFileSync } from 'fs';
11
+ import { readFileSync, writeFileSync } from 'fs';
12
12
  import { fileURLToPath, URL as URL$1 } from 'url';
13
13
  import { Command } from 'commander';
14
14
  import { createInterface } from 'readline';
@@ -37,6 +37,9 @@ function getConfig() {
37
37
  }
38
38
  return config;
39
39
  }
40
+ function isConfigured() {
41
+ return config !== null;
42
+ }
40
43
  async function customFetch(url, options) {
41
44
  const { baseUrl, apiKey } = getConfig();
42
45
  const fullUrl = `${baseUrl}${url}`;
@@ -66,18 +69,42 @@ async function customFetch(url, options) {
66
69
  headers: response.headers
67
70
  };
68
71
  }
69
- var config;
72
+ var config, SDKError;
70
73
  var init_fetcher = __esm({
71
74
  "src/sdk/fetcher.ts"() {
72
75
  config = null;
76
+ SDKError = class extends Error {
77
+ constructor(message, status, body) {
78
+ super(message);
79
+ this.status = status;
80
+ this.body = body;
81
+ this.name = "SDKError";
82
+ }
83
+ };
73
84
  }
74
85
  });
75
86
 
76
87
  // src/sdk/generated/index.ts
77
- var getMeUrl, me, getExplorePublicSkillsUrl, explorePublicSkills, getDeleteSkillUrl, deleteSkill, getListSkillVersionsUrl, listSkillVersions, getGetSkillVersionUrl, getSkillVersion, getDeleteSkillVersionUrl, deleteSkillVersion, getPublishSkillUrl, publishSkill, getListOrgSkillVersionsUrl, listOrgSkillVersions, getGetOrgSkillVersionUrl, getOrgSkillVersion, getPublishOrgSkillUrl, publishOrgSkill;
88
+ var getExchangeCliTokenUrl, exchangeCliToken, getMeUrl, me, getExplorePublicSkillsUrl, explorePublicSkills, getListMySkillsUrl, listMySkills, getListUserSkillsUrl, listUserSkills, getGetSkillUrl, getSkill, getDeleteSkillUrl, deleteSkill, getListSkillVersionsUrl, listSkillVersions, getGetSkillVersionUrl, getSkillVersion, getDeleteSkillVersionUrl, deleteSkillVersion, getPublishSkillUrl, publishSkill, getListOrgSkillVersionsUrl, listOrgSkillVersions, getGetOrgSkillVersionUrl, getOrgSkillVersion, getPublishOrgSkillUrl, publishOrgSkill;
78
89
  var init_generated = __esm({
79
90
  "src/sdk/generated/index.ts"() {
80
91
  init_fetcher();
92
+ getExchangeCliTokenUrl = () => {
93
+ return `/api/api-keys/cli-token-exchange`;
94
+ };
95
+ exchangeCliToken = async (exchangeCliTokenInput, options) => {
96
+ return customFetch(
97
+ getExchangeCliTokenUrl(),
98
+ {
99
+ ...options,
100
+ method: "POST",
101
+ headers: { "Content-Type": "application/json", ...options?.headers },
102
+ body: JSON.stringify(
103
+ exchangeCliTokenInput
104
+ )
105
+ }
106
+ );
107
+ };
81
108
  getMeUrl = () => {
82
109
  return `/api/skills/-/me`;
83
110
  };
@@ -109,6 +136,42 @@ var init_generated = __esm({
109
136
  }
110
137
  );
111
138
  };
139
+ getListMySkillsUrl = () => {
140
+ return `/api/skills/-/mine`;
141
+ };
142
+ listMySkills = async (options) => {
143
+ return customFetch(
144
+ getListMySkillsUrl(),
145
+ {
146
+ ...options,
147
+ method: "GET"
148
+ }
149
+ );
150
+ };
151
+ getListUserSkillsUrl = (username) => {
152
+ return `/api/skills/@user/${username}`;
153
+ };
154
+ listUserSkills = async (username, options) => {
155
+ return customFetch(
156
+ getListUserSkillsUrl(username),
157
+ {
158
+ ...options,
159
+ method: "GET"
160
+ }
161
+ );
162
+ };
163
+ getGetSkillUrl = (username, name) => {
164
+ return `/api/skills/@user/${username}/${name}`;
165
+ };
166
+ getSkill = async (username, name, options) => {
167
+ return customFetch(
168
+ getGetSkillUrl(username, name),
169
+ {
170
+ ...options,
171
+ method: "GET"
172
+ }
173
+ );
174
+ };
112
175
  getDeleteSkillUrl = (username, name) => {
113
176
  return `/api/skills/@user/${username}/${name}`;
114
177
  };
@@ -217,6 +280,33 @@ var init_generated = __esm({
217
280
  });
218
281
 
219
282
  // src/api-client.ts
283
+ var api_client_exports = {};
284
+ __export(api_client_exports, {
285
+ SDKError: () => SDKError,
286
+ changeSkillAccess: () => changeSkillAccess,
287
+ configure: () => configure2,
288
+ deleteSkill: () => deleteSkill,
289
+ deleteSkillVersion: () => deleteSkillVersion,
290
+ deprecateSkillVersion: () => deprecateSkillVersion,
291
+ exchangeCliToken: () => exchangeCliToken,
292
+ fetchSkillList: () => fetchSkillList,
293
+ getConfig: () => getConfig,
294
+ getGithubSkillVersion: () => getGithubSkillVersion,
295
+ getOrgSkillVersion: () => getOrgSkillVersion,
296
+ getSkill: () => getSkill,
297
+ getSkillVersion: () => getSkillVersion,
298
+ isConfigured: () => isConfigured,
299
+ listGithubSkillVersions: () => listGithubSkillVersions,
300
+ listMySkills: () => listMySkills,
301
+ listOrgSkillVersions: () => listOrgSkillVersions,
302
+ listSkillVersions: () => listSkillVersions,
303
+ listUserSkills: () => listUserSkills,
304
+ me: () => me,
305
+ publishOrgSkill: () => publishOrgSkill,
306
+ publishSkill: () => publishSkill,
307
+ undeprecateSkillVersion: () => undeprecateSkillVersion,
308
+ whoamiRequest: () => whoamiRequest
309
+ });
220
310
  function registryUrlToBaseUrl(registryUrl) {
221
311
  return registryUrl.replace(/\/api\/skills\/?$/, "");
222
312
  }
@@ -298,6 +388,32 @@ async function undeprecateSkillVersion(username, skillName, version3) {
298
388
  };
299
389
  }
300
390
  }
391
+ async function fetchSkillList(ownerType, ownerName, listName) {
392
+ const config2 = getConfig();
393
+ try {
394
+ const response = await fetch(
395
+ `${config2.baseUrl}/api/skill-lists/lists/@${ownerType}/${ownerName}/${listName}`,
396
+ {
397
+ method: "GET",
398
+ headers: {
399
+ "Content-Type": "application/json",
400
+ ...config2.apiKey ? { Authorization: `Bearer ${config2.apiKey}` } : {}
401
+ }
402
+ }
403
+ );
404
+ if (!response.ok) {
405
+ const error = await response.text();
406
+ return { status: response.status, error };
407
+ }
408
+ const data = await response.json();
409
+ return { status: response.status, data };
410
+ } catch (error) {
411
+ return {
412
+ status: 500,
413
+ error: error instanceof Error ? error.message : "Unknown error"
414
+ };
415
+ }
416
+ }
301
417
  async function listGithubSkillVersions(owner, repo, name) {
302
418
  const config2 = getConfig();
303
419
  try {
@@ -852,7 +968,9 @@ function validateManifest(manifest) {
852
968
  if (!manifest.version) {
853
969
  return { valid: false, error: "Manifest must have a 'version' field" };
854
970
  }
855
- if (!/^[a-z][a-z0-9_-]*$/.test(manifest.name)) {
971
+ const parts = manifest.name.split("/");
972
+ const bareName = parts[parts.length - 1];
973
+ if (!/^[a-z][a-z0-9_-]*$/.test(bareName)) {
856
974
  return {
857
975
  valid: false,
858
976
  error: "Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores"
@@ -3213,690 +3331,834 @@ var init_add = __esm({
3213
3331
  }
3214
3332
  });
3215
3333
 
3216
- // src/commands/access.ts
3217
- init_api_client();
3218
- init_config();
3219
- init_lib();
3220
- function isLocalSpecifier2(specifier) {
3221
- return specifier.startsWith("file:") || specifier.startsWith("./") || specifier.startsWith("../");
3334
+ // src/commands/install.ts
3335
+ var install_exports = {};
3336
+ __export(install_exports, {
3337
+ install: () => install
3338
+ });
3339
+ function getCacheFilePath(cacheDir, integrity) {
3340
+ const match = integrity.match(/^sha256-(.+)$/);
3341
+ if (!match) {
3342
+ throw new Error(`Invalid integrity format: ${integrity}`);
3343
+ }
3344
+ const base64Hash = match[1];
3345
+ const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
3346
+ return join(cacheDir, `sha256-${hexHash}.tgz`);
3222
3347
  }
3223
- async function access(specifier, options) {
3348
+ async function readFromCache(cacheDir, integrity) {
3224
3349
  try {
3225
- const apiKey = await requireApiKey();
3226
- const registryUrl = await getRegistryUrl();
3227
- if (options.public && options.private) {
3228
- console.error("Error: Cannot specify both --public and --private");
3229
- process.exit(1);
3350
+ const cachePath = getCacheFilePath(cacheDir, integrity);
3351
+ const data = await readFile(cachePath);
3352
+ const actualIntegrity = `sha256-${createHash("sha256").update(data).digest("base64")}`;
3353
+ if (actualIntegrity !== integrity) {
3354
+ await rm(cachePath, { force: true });
3355
+ return null;
3230
3356
  }
3231
- if (!options.public && !options.private) {
3232
- console.error("Error: Must specify either --public or --private");
3233
- process.exit(1);
3357
+ return data;
3358
+ } catch {
3359
+ return null;
3360
+ }
3361
+ }
3362
+ async function writeToCache(cacheDir, integrity, data) {
3363
+ try {
3364
+ await mkdir(cacheDir, { recursive: true });
3365
+ const cachePath = getCacheFilePath(cacheDir, integrity);
3366
+ await writeFile(cachePath, data);
3367
+ } catch {
3368
+ }
3369
+ }
3370
+ async function install(specifiers, options) {
3371
+ if (options.global) {
3372
+ const { setGlobalMode: setGlobalMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3373
+ setGlobalMode2(true);
3374
+ }
3375
+ if (options.list) {
3376
+ const listSpecifiers = await resolveListToSpecifiers(options.list);
3377
+ if (listSpecifiers.length === 0) {
3378
+ console.log("No skills in the list to install.");
3379
+ return;
3234
3380
  }
3235
- const visibility = options.public ? "public" : "private";
3236
- let packageName;
3237
- let packageUsername;
3238
- if (specifier) {
3239
- if (isGitHubSpecifier2(specifier)) {
3240
- const ghSpec = parseGitHubSpecifier2(specifier);
3241
- if (ghSpec) {
3242
- console.error(`Error: Cannot change visibility of GitHub packages.`);
3243
- console.error(
3244
- ` "${specifier}" is hosted on GitHub, not the PSPM registry.`
3245
- );
3246
- console.error(
3247
- ` Visibility can only be changed for packages published to the registry.`
3248
- );
3249
- } else {
3250
- console.error(`Error: Invalid GitHub specifier "${specifier}".`);
3251
- console.error(` Use format: github:{owner}/{repo}[/{path}][@{ref}]`);
3252
- }
3253
- process.exit(1);
3254
- }
3255
- if (isLocalSpecifier2(specifier)) {
3256
- console.error(`Error: Cannot change visibility of local packages.`);
3257
- console.error(
3258
- ` "${specifier}" is a local directory, not a registry package.`
3259
- );
3260
- console.error(
3261
- ` Visibility can only be changed for packages published to the registry.`
3262
- );
3263
- process.exit(1);
3381
+ const allSpecifiers = [...specifiers, ...listSpecifiers];
3382
+ const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
3383
+ await add2(allSpecifiers, {
3384
+ save: true,
3385
+ agent: options.agent,
3386
+ yes: options.yes,
3387
+ global: options.global
3388
+ });
3389
+ return;
3390
+ }
3391
+ if (specifiers.length > 0) {
3392
+ const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
3393
+ await add2(specifiers, {
3394
+ save: true,
3395
+ agent: options.agent,
3396
+ yes: options.yes,
3397
+ global: options.global
3398
+ });
3399
+ return;
3400
+ }
3401
+ await installFromLockfile(options);
3402
+ }
3403
+ async function resolveListToSpecifiers(listSpec) {
3404
+ const match = listSpec.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
3405
+ if (!match) {
3406
+ console.error(
3407
+ `Error: Invalid list specifier "${listSpec}". Use format: @user/{username}/{list-name} or @org/{orgname}/{list-name}`
3408
+ );
3409
+ process.exit(1);
3410
+ }
3411
+ const [, ownerType, ownerName, listName] = match;
3412
+ const config2 = await resolveConfig();
3413
+ configure2({
3414
+ registryUrl: config2.registryUrl,
3415
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
3416
+ });
3417
+ console.log(
3418
+ `Fetching skill list @${ownerType}/${ownerName}/${listName}...
3419
+ `
3420
+ );
3421
+ const { fetchSkillList: fetchSkillList2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
3422
+ const response = await fetchSkillList2(
3423
+ ownerType,
3424
+ ownerName,
3425
+ listName
3426
+ );
3427
+ if (response.status !== 200 || !response.data) {
3428
+ const errorMsg = response.status === 404 ? `List "@${ownerType}/${ownerName}/${listName}" not found or is private.` : response.error || "Failed to fetch list";
3429
+ console.error(`Error: ${errorMsg}`);
3430
+ process.exit(1);
3431
+ }
3432
+ const list2 = response.data;
3433
+ console.log(
3434
+ `List: ${list2.name}${list2.description ? ` \u2014 ${list2.description}` : ""}`
3435
+ );
3436
+ console.log(`Skills: ${list2.items.length}
3437
+ `);
3438
+ const specifiers = [];
3439
+ for (const item of list2.items) {
3440
+ const ns = item.namespace === "org" ? "org" : "user";
3441
+ let spec = `@${ns}/${item.ownerName}/${item.skillName}`;
3442
+ if (item.pinnedVersion) {
3443
+ spec += `@${item.pinnedVersion}`;
3444
+ }
3445
+ specifiers.push(spec);
3446
+ }
3447
+ return specifiers;
3448
+ }
3449
+ async function installFromLockfile(options) {
3450
+ try {
3451
+ const config2 = await resolveConfig();
3452
+ const registryUrl = config2.registryUrl;
3453
+ const apiKey = getTokenForRegistry(config2, registryUrl);
3454
+ const skillsDir = options.dir || getSkillsDir();
3455
+ const cacheDir = getCacheDir();
3456
+ await migrateLockfileIfNeeded();
3457
+ let lockfile = await readLockfile();
3458
+ const manifestDeps = await getDependencies();
3459
+ const manifestGitHubDeps = await getGitHubDependencies();
3460
+ const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
3461
+ const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
3462
+ const installedSkills = [];
3463
+ const missingDeps = [];
3464
+ for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
3465
+ if (!lockfilePackages[fullName]) {
3466
+ missingDeps.push({ fullName, versionRange });
3264
3467
  }
3265
- const parsed = parseRegistrySpecifier2(specifier);
3266
- if (!parsed) {
3267
- console.error(`Error: Invalid package specifier "${specifier}".`);
3268
- console.error(
3269
- ` Use format: @user/{username}/{name} or @org/{orgname}/{name}`
3270
- );
3271
- console.error(``);
3272
- console.error(` Examples:`);
3273
- console.error(` pspm access @user/myname/my-skill --public`);
3468
+ }
3469
+ if (missingDeps.length > 0) {
3470
+ if (options.frozenLockfile) {
3274
3471
  console.error(
3275
- ` pspm access --public (uses current directory's pspm.json)`
3276
- );
3277
- process.exit(1);
3278
- }
3279
- packageName = parsed.name;
3280
- packageUsername = parsed.owner;
3281
- } else {
3282
- const { readFile: readFile10 } = await import('fs/promises');
3283
- const { join: join18 } = await import('path');
3284
- let manifest = null;
3285
- try {
3286
- const content = await readFile10(
3287
- join18(process.cwd(), "pspm.json"),
3288
- "utf-8"
3472
+ "Error: Dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
3289
3473
  );
3290
- manifest = JSON.parse(content);
3291
- } catch {
3292
- try {
3293
- const content = await readFile10(
3294
- join18(process.cwd(), "package.json"),
3295
- "utf-8"
3296
- );
3297
- manifest = JSON.parse(content);
3298
- } catch {
3299
- console.error(
3300
- "Error: No pspm.json or package.json found in current directory"
3301
- );
3302
- console.error(
3303
- "Either run this command in a package directory or specify a package name"
3304
- );
3305
- process.exit(1);
3474
+ console.error("Missing dependencies:");
3475
+ for (const dep of missingDeps) {
3476
+ console.error(` - ${dep.fullName}@${dep.versionRange}`);
3306
3477
  }
3307
- }
3308
- if (!manifest?.name) {
3309
- console.error("Error: Package manifest is missing 'name' field");
3310
3478
  process.exit(1);
3311
3479
  }
3312
- packageName = manifest.name;
3313
- }
3314
- if (!packageUsername) {
3315
- const config2 = await resolveConfig();
3316
- packageUsername = config2.username;
3317
- }
3318
- if (!packageUsername) {
3319
- console.error(
3320
- "Error: Could not determine username. Please use the full specifier: @user/{username}/{name}"
3321
- );
3322
- process.exit(1);
3323
- }
3324
- configure2({ registryUrl, apiKey });
3325
- console.log(`Setting ${packageName} to ${visibility}...`);
3326
- const response = await changeSkillAccess(packageUsername, packageName, {
3327
- visibility
3328
- });
3329
- if (response.status !== 200 || !response.data) {
3330
- const errorMessage = response.error ?? "Failed to change visibility";
3331
- console.error(`Error: ${errorMessage}`);
3332
- process.exit(1);
3333
- }
3334
- const result = response.data;
3335
- console.log(
3336
- `+ @${result.namespace ?? "user"}/${result.username}/${result.name} is now ${result.visibility}`
3337
- );
3338
- if (visibility === "public") {
3339
- console.log("");
3340
- console.log(
3341
- "Note: This action is irreversible. Public packages cannot be made private."
3342
- );
3343
- }
3344
- } catch (error) {
3345
- const message = error instanceof Error ? error.message : "Unknown error";
3346
- console.error(`Error: ${message}`);
3347
- process.exit(1);
3348
- }
3349
- }
3350
-
3351
- // src/commands/index.ts
3352
- init_add();
3353
-
3354
- // src/commands/audit.ts
3355
- init_config();
3356
- init_lib();
3357
- init_lockfile3();
3358
- async function audit(options) {
3359
- try {
3360
- const lockfile = await readLockfile();
3361
- if (!lockfile) {
3362
- if (options.json) {
3363
- console.log(
3364
- JSON.stringify({
3365
- ok: false,
3366
- issues: [
3367
- {
3368
- name: "-",
3369
- source: "-",
3370
- severity: "error",
3371
- type: "no-lockfile",
3372
- message: "No lockfile found. Run 'pspm install' first."
3373
- }
3374
- ]
3375
- })
3480
+ console.log(`Resolving ${missingDeps.length} new dependency(ies)...
3481
+ `);
3482
+ configure2({ registryUrl, apiKey });
3483
+ for (const { fullName, versionRange } of missingDeps) {
3484
+ const parsed = parseRegistrySpecifier2(fullName);
3485
+ if (!parsed) {
3486
+ console.error(`Error: Invalid dependency specifier: ${fullName}`);
3487
+ continue;
3488
+ }
3489
+ const { owner, name } = parsed;
3490
+ console.log(`Resolving ${fullName}@${versionRange}...`);
3491
+ const versionsResponse = await listSkillVersions(owner, name);
3492
+ if (versionsResponse.status !== 200) {
3493
+ const errorMessage = extractApiErrorMessage(
3494
+ versionsResponse,
3495
+ `Skill ${fullName} not found`
3496
+ );
3497
+ console.error(`Error: ${errorMessage}`);
3498
+ continue;
3499
+ }
3500
+ const versions = versionsResponse.data;
3501
+ if (versions.length === 0) {
3502
+ console.error(`Error: Skill ${fullName} not found`);
3503
+ continue;
3504
+ }
3505
+ const versionStrings = versions.map(
3506
+ (v) => v.version
3376
3507
  );
3377
- } else {
3378
- console.error("No lockfile found. Run 'pspm install' to create one.");
3379
- }
3380
- process.exit(1);
3381
- }
3382
- const issues = [];
3383
- const skillsDir = getSkillsDir();
3384
- const projectRoot = process.cwd();
3385
- if (!options.json) {
3386
- console.log("Auditing installed skills...\n");
3387
- }
3388
- const registrySkills = await listLockfileSkills();
3389
- for (const { name: fullName, entry } of registrySkills) {
3390
- const match = fullName.match(/^@user\/([^/]+)\/([^/]+)$/);
3391
- if (!match) continue;
3392
- const [, username, skillName] = match;
3393
- const destDir = join(projectRoot, skillsDir, username, skillName);
3394
- const exists = await pathExists(destDir);
3395
- if (!exists) {
3396
- issues.push({
3397
- name: fullName,
3398
- source: "registry",
3399
- severity: "error",
3400
- type: "missing",
3401
- message: `Not installed on disk. Run 'pspm install' to restore.`
3402
- });
3403
- continue;
3404
- }
3405
- if (entry.deprecated) {
3406
- issues.push({
3407
- name: fullName,
3408
- source: "registry",
3409
- severity: "warning",
3410
- type: "deprecated",
3411
- message: `Deprecated: ${entry.deprecated}`
3508
+ const resolved = resolveVersion2(versionRange || "*", versionStrings);
3509
+ if (!resolved) {
3510
+ console.error(
3511
+ `Error: No version matching "${versionRange}" for ${fullName}`
3512
+ );
3513
+ continue;
3514
+ }
3515
+ const versionResponse = await getSkillVersion(owner, name, resolved);
3516
+ if (versionResponse.status !== 200 || !versionResponse.data) {
3517
+ const errorMessage = extractApiErrorMessage(
3518
+ versionResponse,
3519
+ `Version ${resolved} not found`
3520
+ );
3521
+ console.error(`Error: ${errorMessage}`);
3522
+ continue;
3523
+ }
3524
+ const versionInfo = versionResponse.data;
3525
+ const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
3526
+ const downloadHeaders = {};
3527
+ if (!isPresignedUrl && apiKey) {
3528
+ downloadHeaders.Authorization = `Bearer ${apiKey}`;
3529
+ }
3530
+ const tarballResponse = await fetch(versionInfo.downloadUrl, {
3531
+ headers: downloadHeaders,
3532
+ redirect: "follow"
3412
3533
  });
3413
- }
3414
- const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
3415
- if (!skillMdExists) {
3416
- issues.push({
3417
- name: fullName,
3418
- source: "registry",
3419
- severity: "warning",
3420
- type: "integrity",
3421
- message: "Missing SKILL.md in installed directory. Package may be corrupted."
3534
+ if (!tarballResponse.ok) {
3535
+ console.error(
3536
+ `Error: Failed to download tarball for ${fullName} (${tarballResponse.status})`
3537
+ );
3538
+ continue;
3539
+ }
3540
+ const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
3541
+ const integrity = calculateIntegrity(tarballBuffer);
3542
+ await addToLockfile(fullName, {
3543
+ version: resolved,
3544
+ resolved: versionInfo.downloadUrl,
3545
+ integrity
3422
3546
  });
3547
+ await writeToCache(cacheDir, integrity, tarballBuffer);
3548
+ console.log(` Resolved ${fullName}@${resolved}`);
3423
3549
  }
3550
+ lockfile = await readLockfile();
3424
3551
  }
3425
- const githubSkills = await listLockfileGitHubPackages();
3426
- for (const { specifier } of githubSkills) {
3427
- const parsed = parseGitHubSpecifier2(specifier);
3428
- if (!parsed) continue;
3429
- const destDir = parsed.path ? join(
3430
- projectRoot,
3431
- skillsDir,
3432
- "_github",
3433
- parsed.owner,
3434
- parsed.repo,
3435
- parsed.path
3436
- ) : join(projectRoot, skillsDir, "_github", parsed.owner, parsed.repo);
3437
- const exists = await pathExists(destDir);
3438
- if (!exists) {
3439
- issues.push({
3440
- name: specifier,
3441
- source: "github",
3442
- severity: "error",
3443
- type: "missing",
3444
- message: `Not installed on disk. Run 'pspm install' to restore.`
3445
- });
3446
- continue;
3447
- }
3448
- const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
3449
- if (!skillMdExists) {
3450
- issues.push({
3451
- name: specifier,
3452
- source: "github",
3453
- severity: "warning",
3454
- type: "integrity",
3455
- message: "Missing SKILL.md in installed directory. Package may be corrupted."
3456
- });
3552
+ const missingGitHubDeps = [];
3553
+ for (const [specifier, ref] of Object.entries(manifestGitHubDeps)) {
3554
+ if (!lockfileGitHubPackages[specifier]) {
3555
+ missingGitHubDeps.push({ specifier, ref });
3457
3556
  }
3458
3557
  }
3459
- const wellKnownSkills = await listLockfileWellKnownPackages();
3460
- for (const { specifier, entry } of wellKnownSkills) {
3461
- const wkEntry = entry;
3462
- const destDir = join(
3463
- projectRoot,
3464
- skillsDir,
3465
- "_wellknown",
3466
- wkEntry.hostname,
3467
- wkEntry.name
3468
- );
3469
- const exists = await pathExists(destDir);
3470
- if (!exists) {
3471
- issues.push({
3472
- name: specifier,
3473
- source: "well-known",
3474
- severity: "error",
3475
- type: "missing",
3476
- message: `Not installed on disk. Run 'pspm install' to restore.`
3477
- });
3478
- continue;
3479
- }
3480
- const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
3481
- if (!skillMdExists) {
3482
- issues.push({
3483
- name: specifier,
3484
- source: "well-known",
3485
- severity: "warning",
3486
- type: "integrity",
3487
- message: "Missing SKILL.md in installed directory. Package may be corrupted."
3488
- });
3558
+ if (missingGitHubDeps.length > 0) {
3559
+ if (options.frozenLockfile) {
3560
+ console.error(
3561
+ "Error: GitHub dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
3562
+ );
3563
+ console.error("Missing GitHub dependencies:");
3564
+ for (const dep of missingGitHubDeps) {
3565
+ console.error(` - ${dep.specifier}@${dep.ref}`);
3566
+ }
3567
+ process.exit(1);
3489
3568
  }
3490
- }
3491
- const errorCount = issues.filter((i) => i.severity === "error").length;
3492
- const warningCount = issues.filter((i) => i.severity === "warning").length;
3493
- if (options.json) {
3494
3569
  console.log(
3495
- JSON.stringify(
3496
- {
3497
- ok: errorCount === 0,
3498
- totalPackages: registrySkills.length + githubSkills.length + wellKnownSkills.length,
3499
- issues
3500
- },
3501
- null,
3502
- 2
3503
- )
3570
+ `
3571
+ Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
3572
+ `
3504
3573
  );
3505
- if (errorCount > 0) process.exit(1);
3506
- return;
3574
+ for (const { specifier, ref } of missingGitHubDeps) {
3575
+ const parsed = parseGitHubSpecifier2(specifier);
3576
+ if (!parsed) {
3577
+ console.error(`Error: Invalid GitHub specifier: ${specifier}`);
3578
+ continue;
3579
+ }
3580
+ parsed.ref = parsed.ref || ref;
3581
+ console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
3582
+ try {
3583
+ const result = await downloadGitHubPackage(parsed);
3584
+ await extractGitHubPackage(parsed, result.buffer, skillsDir);
3585
+ const entry = {
3586
+ version: result.commit.slice(0, 7),
3587
+ resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
3588
+ integrity: result.integrity,
3589
+ gitCommit: result.commit,
3590
+ gitRef: ref || "HEAD"
3591
+ };
3592
+ await addGitHubToLockfile(specifier, entry);
3593
+ await writeToCache(cacheDir, result.integrity, result.buffer);
3594
+ console.log(
3595
+ ` Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`
3596
+ );
3597
+ } catch (error) {
3598
+ if (error instanceof GitHubRateLimitError) {
3599
+ console.error(`Error: ${error.message}`);
3600
+ } else if (error instanceof GitHubPathNotFoundError) {
3601
+ console.error(`Error: ${error.message}`);
3602
+ } else if (error instanceof GitHubNotFoundError) {
3603
+ console.error(`Error: ${error.message}`);
3604
+ } else {
3605
+ const message = error instanceof Error ? error.message : String(error);
3606
+ console.error(`Error resolving ${specifier}: ${message}`);
3607
+ }
3608
+ }
3609
+ }
3610
+ lockfile = await readLockfile();
3507
3611
  }
3508
- if (issues.length === 0) {
3509
- const totalPackages2 = registrySkills.length + githubSkills.length + wellKnownSkills.length;
3510
- console.log(`Audited ${totalPackages2} package(s). No issues found.`);
3511
- return;
3612
+ const manifest = await readManifest();
3613
+ const agentConfigs = manifest?.agents;
3614
+ let agents;
3615
+ if (options.agent) {
3616
+ agents = parseAgentArg(options.agent);
3617
+ } else if (manifest) {
3618
+ agents = parseAgentArg(void 0);
3619
+ } else if (options.yes) {
3620
+ agents = parseAgentArg(void 0);
3621
+ } else {
3622
+ console.log("\nNo pspm.json found. Let's set up your project.\n");
3623
+ agents = await promptForAgents();
3624
+ console.log();
3512
3625
  }
3513
- const errors = issues.filter((i) => i.severity === "error");
3514
- const warnings = issues.filter((i) => i.severity === "warning");
3515
- if (errors.length > 0) {
3516
- console.log("Errors:");
3517
- for (const issue of errors) {
3626
+ const packages = lockfile?.packages ?? lockfile?.skills ?? {};
3627
+ const packageCount = Object.keys(packages).length;
3628
+ if (packageCount > 0) {
3629
+ console.log(`
3630
+ Installing ${packageCount} registry skill(s)...
3631
+ `);
3632
+ const installOrder = computeInstallOrder(packages);
3633
+ const entries = installOrder.filter((name) => packages[name]).map((name) => [name, packages[name]]);
3634
+ for (const [fullName, entry] of entries) {
3635
+ const parsedName = parseRegistrySpecifier2(fullName);
3636
+ if (!parsedName) {
3637
+ console.warn(`Warning: Invalid skill name in lockfile: ${fullName}`);
3638
+ continue;
3639
+ }
3640
+ const { namespace: ns, owner: pkgOwner, name, subname } = parsedName;
3641
+ console.log(`Installing ${fullName}@${entry.version}...`);
3642
+ let tarballBuffer;
3643
+ let fromCache = false;
3644
+ const cachedTarball = await readFromCache(cacheDir, entry.integrity);
3645
+ if (cachedTarball) {
3646
+ tarballBuffer = cachedTarball;
3647
+ fromCache = true;
3648
+ } else {
3649
+ const isPresignedUrl = entry.resolved.includes(".r2.cloudflarestorage.com") || entry.resolved.includes("X-Amz-Signature");
3650
+ const downloadHeaders = {};
3651
+ if (!isPresignedUrl && apiKey) {
3652
+ downloadHeaders.Authorization = `Bearer ${apiKey}`;
3653
+ }
3654
+ const response = await fetch(entry.resolved, {
3655
+ headers: downloadHeaders,
3656
+ redirect: "follow"
3657
+ });
3658
+ if (!response.ok) {
3659
+ if (response.status === 401) {
3660
+ if (!apiKey) {
3661
+ console.error(
3662
+ ` Error: ${fullName} requires authentication. Run 'pspm login' first.`
3663
+ );
3664
+ } else {
3665
+ console.error(
3666
+ ` Error: Access denied to ${fullName}. You may not have permission to access this private package.`
3667
+ );
3668
+ }
3669
+ } else {
3670
+ console.error(
3671
+ ` Error: Failed to download ${fullName} (${response.status})`
3672
+ );
3673
+ }
3674
+ continue;
3675
+ }
3676
+ tarballBuffer = Buffer.from(await response.arrayBuffer());
3677
+ const actualIntegrity = `sha256-${createHash("sha256").update(tarballBuffer).digest("base64")}`;
3678
+ if (actualIntegrity !== entry.integrity) {
3679
+ console.error(
3680
+ ` Error: Checksum verification failed for ${fullName}`
3681
+ );
3682
+ if (options.frozenLockfile) {
3683
+ process.exit(1);
3684
+ }
3685
+ continue;
3686
+ }
3687
+ await writeToCache(cacheDir, entry.integrity, tarballBuffer);
3688
+ }
3689
+ const effectiveSkillName = subname ?? name;
3690
+ let destDir;
3691
+ if (ns === "org") {
3692
+ destDir = join(skillsDir, "_org", pkgOwner, effectiveSkillName);
3693
+ } else if (ns === "github" && subname) {
3694
+ destDir = join(
3695
+ skillsDir,
3696
+ "_github-registry",
3697
+ pkgOwner,
3698
+ name,
3699
+ subname
3700
+ );
3701
+ } else {
3702
+ destDir = join(skillsDir, pkgOwner, effectiveSkillName);
3703
+ }
3704
+ await rm(destDir, { recursive: true, force: true });
3705
+ await mkdir(destDir, { recursive: true });
3706
+ const tempFile = join(destDir, ".temp.tgz");
3707
+ await writeFile(tempFile, tarballBuffer);
3708
+ const { exec: exec2 } = await import('child_process');
3709
+ const { promisify: promisify2 } = await import('util');
3710
+ const execAsync = promisify2(exec2);
3711
+ try {
3712
+ await execAsync(
3713
+ `tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
3714
+ );
3715
+ } finally {
3716
+ await rm(tempFile, { force: true });
3717
+ }
3518
3718
  console.log(
3519
- ` [${issue.type.toUpperCase()}] ${issue.name} (${issue.source})`
3719
+ ` Installed to ${destDir}${fromCache ? " (from cache)" : ""}`
3520
3720
  );
3521
- console.log(` ${issue.message}`);
3721
+ const pathSkillName = ns === "github" && subname ? `${name}/${subname}` : name;
3722
+ installedSkills.push({
3723
+ name: effectiveSkillName,
3724
+ sourcePath: getRegistrySkillPath(ns, pkgOwner, pathSkillName)
3725
+ });
3522
3726
  }
3523
- console.log();
3524
3727
  }
3525
- if (warnings.length > 0) {
3526
- console.log("Warnings:");
3527
- for (const issue of warnings) {
3728
+ const githubPackages = lockfile?.githubPackages ?? {};
3729
+ const githubCount = Object.keys(githubPackages).length;
3730
+ if (githubCount > 0) {
3731
+ console.log(`
3732
+ Installing ${githubCount} GitHub skill(s)...
3733
+ `);
3734
+ for (const [specifier, entry] of Object.entries(githubPackages)) {
3735
+ const parsed = parseGitHubSpecifier2(specifier);
3736
+ if (!parsed) {
3737
+ console.warn(
3738
+ `Warning: Invalid GitHub specifier in lockfile: ${specifier}`
3739
+ );
3740
+ continue;
3741
+ }
3742
+ const ghEntry = entry;
3528
3743
  console.log(
3529
- ` [${issue.type.toUpperCase()}] ${issue.name} (${issue.source})`
3744
+ `Installing ${specifier} (${ghEntry.gitRef}@${ghEntry.gitCommit.slice(0, 7)})...`
3530
3745
  );
3531
- console.log(` ${issue.message}`);
3746
+ let tarballBuffer;
3747
+ let fromCache = false;
3748
+ const cachedTarball = await readFromCache(cacheDir, ghEntry.integrity);
3749
+ if (cachedTarball) {
3750
+ tarballBuffer = cachedTarball;
3751
+ fromCache = true;
3752
+ } else {
3753
+ try {
3754
+ const specWithCommit = { ...parsed, ref: ghEntry.gitCommit };
3755
+ const result = await downloadGitHubPackage(specWithCommit);
3756
+ tarballBuffer = result.buffer;
3757
+ if (result.integrity !== ghEntry.integrity) {
3758
+ console.error(
3759
+ ` Error: Checksum verification failed for ${specifier}`
3760
+ );
3761
+ if (options.frozenLockfile) {
3762
+ process.exit(1);
3763
+ }
3764
+ continue;
3765
+ }
3766
+ await writeToCache(cacheDir, ghEntry.integrity, tarballBuffer);
3767
+ } catch (error) {
3768
+ if (error instanceof GitHubRateLimitError) {
3769
+ console.error(` Error: ${error.message}`);
3770
+ } else if (error instanceof GitHubPathNotFoundError) {
3771
+ console.error(` Error: ${error.message}`);
3772
+ } else if (error instanceof GitHubNotFoundError) {
3773
+ console.error(` Error: ${error.message}`);
3774
+ } else {
3775
+ const message = error instanceof Error ? error.message : String(error);
3776
+ console.error(` Error downloading ${specifier}: ${message}`);
3777
+ }
3778
+ continue;
3779
+ }
3780
+ }
3781
+ try {
3782
+ const destPath = await extractGitHubPackage(
3783
+ parsed,
3784
+ tarballBuffer,
3785
+ skillsDir
3786
+ );
3787
+ console.log(
3788
+ ` Installed to ${destPath}${fromCache ? " (from cache)" : ""}`
3789
+ );
3790
+ const skillName = getGitHubSkillName2(parsed);
3791
+ installedSkills.push({
3792
+ name: skillName,
3793
+ sourcePath: getGitHubSkillPath(
3794
+ parsed.owner,
3795
+ parsed.repo,
3796
+ parsed.path
3797
+ )
3798
+ });
3799
+ } catch (error) {
3800
+ const message = error instanceof Error ? error.message : String(error);
3801
+ console.error(` Error extracting ${specifier}: ${message}`);
3802
+ }
3532
3803
  }
3533
- console.log();
3534
- }
3535
- const totalPackages = registrySkills.length + githubSkills.length + wellKnownSkills.length;
3536
- console.log(
3537
- `Audited ${totalPackages} package(s): ${errorCount} error(s), ${warningCount} warning(s).`
3538
- );
3539
- if (errorCount > 0) {
3540
- process.exit(1);
3541
3804
  }
3542
- } catch (error) {
3543
- const message = error instanceof Error ? error.message : "Unknown error";
3544
- console.error(`Error: ${message}`);
3545
- process.exit(1);
3546
- }
3547
- }
3548
- async function pathExists(path) {
3549
- try {
3550
- await stat(path);
3551
- return true;
3552
- } catch {
3553
- return false;
3554
- }
3555
- }
3556
- async function configInit(options) {
3557
- try {
3558
- const configPath = join(process.cwd(), ".pspmrc");
3559
- try {
3560
- await stat(configPath);
3561
- console.error("Error: .pspmrc already exists in this directory.");
3562
- process.exit(1);
3563
- } catch {
3805
+ if (installedSkills.length > 0 && agents[0] !== "none") {
3806
+ const globalMode = isGlobalMode();
3807
+ console.log(
3808
+ `
3809
+ Creating symlinks for agent(s): ${agents.join(", ")}${globalMode ? " (global)" : ""}...`
3810
+ );
3811
+ await createAgentSymlinks(installedSkills, {
3812
+ agents,
3813
+ projectRoot: globalMode ? homedir() : process.cwd(),
3814
+ agentConfigs,
3815
+ global: globalMode
3816
+ });
3817
+ console.log(" Symlinks created.");
3564
3818
  }
3565
- const lines = ["; Project-specific PSPM configuration", ""];
3566
- if (options.registry) {
3567
- lines.push(`registry = ${options.registry}`);
3819
+ const totalCount = packageCount + githubCount;
3820
+ if (totalCount === 0) {
3821
+ console.log("No skills to install.");
3568
3822
  } else {
3569
- lines.push("; Uncomment to use a custom registry:");
3570
- lines.push("; registry = https://custom-registry.example.com");
3823
+ console.log(`
3824
+ All ${totalCount} skill(s) installed.`);
3571
3825
  }
3572
- lines.push("");
3573
- await writeFile(configPath, lines.join("\n"));
3574
- console.log("Created .pspmrc");
3575
- console.log("");
3576
- console.log("Contents:");
3577
- console.log(lines.join("\n"));
3578
- console.log("Note: .pspmrc should be committed to version control.");
3579
- console.log("API keys should NOT be stored here - use pspm login instead.");
3580
3826
  } catch (error) {
3581
3827
  const message = error instanceof Error ? error.message : "Unknown error";
3582
3828
  console.error(`Error: ${message}`);
3583
3829
  process.exit(1);
3584
3830
  }
3585
3831
  }
3586
-
3587
- // src/commands/config/show.ts
3588
- init_config();
3589
- async function configShow() {
3590
- try {
3591
- const resolved = await resolveConfig();
3592
- const projectConfig = await findProjectConfig();
3593
- const configPath = getConfigPath();
3594
- console.log("Resolved Configuration:\n");
3595
- console.log(` Registry URL: ${resolved.registryUrl}`);
3596
- console.log(` API Key: ${resolved.apiKey ? "***" : "(not set)"}`);
3597
- console.log(` Username: ${resolved.username || "(not set)"}`);
3598
- console.log("");
3599
- console.log("Config Locations:");
3600
- console.log(` User config: ${configPath}`);
3601
- console.log(` Project config: ${projectConfig ? ".pspmrc" : "(none)"}`);
3602
- console.log("");
3603
- console.log("Environment Variables:");
3604
- console.log(
3605
- ` PSPM_REGISTRY_URL: ${process.env.PSPM_REGISTRY_URL || "(not set)"}`
3606
- );
3607
- console.log(
3608
- ` PSPM_API_KEY: ${process.env.PSPM_API_KEY ? "***" : "(not set)"}`
3609
- );
3610
- } catch (error) {
3611
- const message = error instanceof Error ? error.message : "Unknown error";
3612
- console.error(`Error: ${message}`);
3613
- process.exit(1);
3832
+ var init_install = __esm({
3833
+ "src/commands/install.ts"() {
3834
+ init_agents();
3835
+ init_api_client();
3836
+ init_config();
3837
+ init_errors();
3838
+ init_github();
3839
+ init_lib();
3840
+ init_lockfile3();
3841
+ init_manifest3();
3842
+ init_symlinks();
3614
3843
  }
3615
- }
3844
+ });
3616
3845
 
3617
- // src/commands/deprecate.ts
3846
+ // src/commands/access.ts
3618
3847
  init_api_client();
3619
3848
  init_config();
3620
3849
  init_lib();
3621
- async function deprecate(specifier, message, options) {
3850
+ function isLocalSpecifier2(specifier) {
3851
+ return specifier.startsWith("file:") || specifier.startsWith("./") || specifier.startsWith("../");
3852
+ }
3853
+ async function access(specifier, options) {
3622
3854
  try {
3623
3855
  const apiKey = await requireApiKey();
3624
3856
  const registryUrl = await getRegistryUrl();
3625
- const parsed = parseRegistrySpecifier2(specifier);
3626
- if (!parsed) {
3627
- console.error(
3628
- `Error: Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}@{version} or @org/{orgname}/{name}@{version}`
3629
- );
3857
+ if (options.public && options.private) {
3858
+ console.error("Error: Cannot specify both --public and --private");
3630
3859
  process.exit(1);
3631
3860
  }
3632
- const { owner, name, versionRange } = parsed;
3633
- const fullName = generateRegistryIdentifier2({
3634
- namespace: parsed.namespace,
3635
- owner,
3636
- name
3637
- });
3638
- if (!versionRange) {
3639
- console.error(
3640
- "Error: Version is required for deprecation. Use format: @user/{username}/{name}@{version}"
3641
- );
3861
+ if (!options.public && !options.private) {
3862
+ console.error("Error: Must specify either --public or --private");
3642
3863
  process.exit(1);
3643
3864
  }
3644
- configure2({ registryUrl, apiKey });
3645
- if (options.undo) {
3646
- console.log(`Removing deprecation from ${fullName}@${versionRange}...`);
3647
- const response = await undeprecateSkillVersion(owner, name, versionRange);
3648
- if (response.status !== 200) {
3865
+ const visibility = options.public ? "public" : "private";
3866
+ let packageName;
3867
+ let packageUsername;
3868
+ if (specifier) {
3869
+ if (isGitHubSpecifier2(specifier)) {
3870
+ const ghSpec = parseGitHubSpecifier2(specifier);
3871
+ if (ghSpec) {
3872
+ console.error(`Error: Cannot change visibility of GitHub packages.`);
3873
+ console.error(
3874
+ ` "${specifier}" is hosted on GitHub, not the PSPM registry.`
3875
+ );
3876
+ console.error(
3877
+ ` Visibility can only be changed for packages published to the registry.`
3878
+ );
3879
+ } else {
3880
+ console.error(`Error: Invalid GitHub specifier "${specifier}".`);
3881
+ console.error(` Use format: github:{owner}/{repo}[/{path}][@{ref}]`);
3882
+ }
3883
+ process.exit(1);
3884
+ }
3885
+ if (isLocalSpecifier2(specifier)) {
3886
+ console.error(`Error: Cannot change visibility of local packages.`);
3649
3887
  console.error(
3650
- `Error: ${response.error || "Failed to remove deprecation"}`
3888
+ ` "${specifier}" is a local directory, not a registry package.`
3889
+ );
3890
+ console.error(
3891
+ ` Visibility can only be changed for packages published to the registry.`
3651
3892
  );
3652
3893
  process.exit(1);
3653
3894
  }
3654
- console.log(`Removed deprecation from ${fullName}@${versionRange}`);
3655
- } else {
3656
- if (!message) {
3895
+ const parsed = parseRegistrySpecifier2(specifier);
3896
+ if (!parsed) {
3897
+ console.error(`Error: Invalid package specifier "${specifier}".`);
3657
3898
  console.error(
3658
- "Error: Deprecation message is required. Usage: pspm deprecate <specifier> <message>"
3899
+ ` Use format: @user/{username}/{name} or @org/{orgname}/{name}`
3900
+ );
3901
+ console.error(``);
3902
+ console.error(` Examples:`);
3903
+ console.error(` pspm access @user/myname/my-skill --public`);
3904
+ console.error(
3905
+ ` pspm access --public (uses current directory's pspm.json)`
3659
3906
  );
3660
3907
  process.exit(1);
3661
3908
  }
3662
- console.log(`Deprecating ${fullName}@${versionRange}...`);
3663
- const response = await deprecateSkillVersion(
3664
- owner,
3665
- name,
3666
- versionRange,
3667
- message
3668
- );
3669
- if (response.status !== 200) {
3670
- console.error(
3671
- `Error: ${response.error || "Failed to deprecate version"}`
3909
+ packageName = parsed.name;
3910
+ packageUsername = parsed.owner;
3911
+ } else {
3912
+ const { readFile: readFile10 } = await import('fs/promises');
3913
+ const { join: join18 } = await import('path');
3914
+ let manifest = null;
3915
+ try {
3916
+ const content = await readFile10(
3917
+ join18(process.cwd(), "pspm.json"),
3918
+ "utf-8"
3672
3919
  );
3920
+ manifest = JSON.parse(content);
3921
+ } catch {
3922
+ try {
3923
+ const content = await readFile10(
3924
+ join18(process.cwd(), "package.json"),
3925
+ "utf-8"
3926
+ );
3927
+ manifest = JSON.parse(content);
3928
+ } catch {
3929
+ console.error(
3930
+ "Error: No pspm.json or package.json found in current directory"
3931
+ );
3932
+ console.error(
3933
+ "Either run this command in a package directory or specify a package name"
3934
+ );
3935
+ process.exit(1);
3936
+ }
3937
+ }
3938
+ if (!manifest?.name) {
3939
+ console.error("Error: Package manifest is missing 'name' field");
3673
3940
  process.exit(1);
3674
3941
  }
3675
- console.log(`Deprecated ${fullName}@${versionRange}`);
3676
- console.log(`Message: ${message}`);
3942
+ packageName = manifest.name;
3943
+ }
3944
+ if (!packageUsername) {
3945
+ const config2 = await resolveConfig();
3946
+ packageUsername = config2.username;
3947
+ }
3948
+ if (!packageUsername) {
3949
+ console.error(
3950
+ "Error: Could not determine username. Please use the full specifier: @user/{username}/{name}"
3951
+ );
3952
+ process.exit(1);
3953
+ }
3954
+ configure2({ registryUrl, apiKey });
3955
+ console.log(`Setting ${packageName} to ${visibility}...`);
3956
+ const response = await changeSkillAccess(packageUsername, packageName, {
3957
+ visibility
3958
+ });
3959
+ if (response.status !== 200 || !response.data) {
3960
+ const errorMessage = response.error ?? "Failed to change visibility";
3961
+ console.error(`Error: ${errorMessage}`);
3962
+ process.exit(1);
3963
+ }
3964
+ const result = response.data;
3965
+ console.log(
3966
+ `+ @${result.namespace ?? "user"}/${result.username}/${result.name} is now ${result.visibility}`
3967
+ );
3968
+ if (visibility === "public") {
3677
3969
  console.log("");
3678
3970
  console.log(
3679
- "Users installing this version will see a deprecation warning."
3971
+ "Note: This action is irreversible. Public packages cannot be made private."
3680
3972
  );
3681
- console.log("The package is still available for download.");
3682
3973
  }
3683
3974
  } catch (error) {
3684
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
3685
- console.error(`Error: ${errorMessage}`);
3975
+ const message = error instanceof Error ? error.message : "Unknown error";
3976
+ console.error(`Error: ${message}`);
3686
3977
  process.exit(1);
3687
3978
  }
3688
3979
  }
3689
3980
 
3690
- // src/commands/init.ts
3981
+ // src/commands/index.ts
3982
+ init_add();
3983
+
3984
+ // src/commands/audit.ts
3985
+ init_config();
3691
3986
  init_lib();
3692
- function prompt(rl, question, defaultValue) {
3693
- return new Promise((resolve2) => {
3694
- const displayDefault = defaultValue ? ` (${defaultValue})` : "";
3695
- rl.question(`${question}${displayDefault} `, (answer) => {
3696
- resolve2(answer.trim() || defaultValue);
3697
- });
3698
- });
3699
- }
3700
- async function readExistingPackageJson() {
3987
+ init_lockfile3();
3988
+ async function audit(options) {
3701
3989
  try {
3702
- const content = await readFile(
3703
- join(process.cwd(), "package.json"),
3704
- "utf-8"
3705
- );
3706
- const pkg = JSON.parse(content);
3707
- return {
3708
- name: pkg.name,
3709
- version: pkg.version,
3710
- description: pkg.description,
3711
- author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name,
3712
- license: pkg.license
3713
- };
3714
- } catch {
3715
- return null;
3716
- }
3717
- }
3718
- async function getGitAuthor() {
3719
- try {
3720
- const { exec: exec2 } = await import('child_process');
3721
- const { promisify: promisify2 } = await import('util');
3722
- const execAsync = promisify2(exec2);
3723
- const [nameResult, emailResult] = await Promise.all([
3724
- execAsync("git config user.name").catch(() => ({ stdout: "" })),
3725
- execAsync("git config user.email").catch(() => ({ stdout: "" }))
3726
- ]);
3727
- const name = nameResult.stdout.trim();
3728
- const email = emailResult.stdout.trim();
3729
- if (name && email) {
3730
- return `${name} <${email}>`;
3990
+ const lockfile = await readLockfile();
3991
+ if (!lockfile) {
3992
+ if (options.json) {
3993
+ console.log(
3994
+ JSON.stringify({
3995
+ ok: false,
3996
+ issues: [
3997
+ {
3998
+ name: "-",
3999
+ source: "-",
4000
+ severity: "error",
4001
+ type: "no-lockfile",
4002
+ message: "No lockfile found. Run 'pspm install' first."
4003
+ }
4004
+ ]
4005
+ })
4006
+ );
4007
+ } else {
4008
+ console.error("No lockfile found. Run 'pspm install' to create one.");
4009
+ }
4010
+ process.exit(1);
3731
4011
  }
3732
- if (name) {
3733
- return name;
4012
+ const issues = [];
4013
+ const skillsDir = getSkillsDir();
4014
+ if (!options.json) {
4015
+ console.log("Auditing installed skills...\n");
3734
4016
  }
3735
- return null;
3736
- } catch {
3737
- return null;
3738
- }
3739
- }
3740
- function sanitizeName(name) {
3741
- const withoutScope = name.replace(/^@[^/]+\//, "");
3742
- return withoutScope.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
3743
- }
3744
- function isValidName(name) {
3745
- return /^[a-z][a-z0-9_-]*$/.test(name);
3746
- }
3747
- function isValidVersion(version3) {
3748
- return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version3);
3749
- }
3750
- async function init(options) {
3751
- try {
3752
- const pspmJsonPath = join(process.cwd(), "pspm.json");
3753
- let exists = false;
3754
- try {
3755
- await stat(pspmJsonPath);
3756
- exists = true;
3757
- } catch {
4017
+ const registrySkills = await listLockfileSkills();
4018
+ for (const { name: fullName, entry } of registrySkills) {
4019
+ const match = fullName.match(/^@user\/([^/]+)\/([^/]+)$/);
4020
+ if (!match) continue;
4021
+ const [, username, skillName] = match;
4022
+ const destDir = join(skillsDir, username, skillName);
4023
+ const exists = await pathExists(destDir);
4024
+ if (!exists) {
4025
+ issues.push({
4026
+ name: fullName,
4027
+ source: "registry",
4028
+ severity: "error",
4029
+ type: "missing",
4030
+ message: `Not installed on disk. Run 'pspm install' to restore.`
4031
+ });
4032
+ continue;
4033
+ }
4034
+ if (entry.deprecated) {
4035
+ issues.push({
4036
+ name: fullName,
4037
+ source: "registry",
4038
+ severity: "warning",
4039
+ type: "deprecated",
4040
+ message: `Deprecated: ${entry.deprecated}`
4041
+ });
4042
+ }
4043
+ const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
4044
+ if (!skillMdExists) {
4045
+ issues.push({
4046
+ name: fullName,
4047
+ source: "registry",
4048
+ severity: "warning",
4049
+ type: "integrity",
4050
+ message: "Missing SKILL.md in installed directory. Package may be corrupted."
4051
+ });
4052
+ }
3758
4053
  }
3759
- if (exists && !options.force) {
3760
- console.error("Error: pspm.json already exists in this directory.");
3761
- console.error("Use --force to overwrite.");
3762
- process.exit(1);
4054
+ const githubSkills = await listLockfileGitHubPackages();
4055
+ for (const { specifier } of githubSkills) {
4056
+ const parsed = parseGitHubSpecifier2(specifier);
4057
+ if (!parsed) continue;
4058
+ const destDir = parsed.path ? join(skillsDir, "_github", parsed.owner, parsed.repo, parsed.path) : join(skillsDir, "_github", parsed.owner, parsed.repo);
4059
+ const exists = await pathExists(destDir);
4060
+ if (!exists) {
4061
+ issues.push({
4062
+ name: specifier,
4063
+ source: "github",
4064
+ severity: "error",
4065
+ type: "missing",
4066
+ message: `Not installed on disk. Run 'pspm install' to restore.`
4067
+ });
4068
+ continue;
4069
+ }
4070
+ const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
4071
+ if (!skillMdExists) {
4072
+ issues.push({
4073
+ name: specifier,
4074
+ source: "github",
4075
+ severity: "warning",
4076
+ type: "integrity",
4077
+ message: "Missing SKILL.md in installed directory. Package may be corrupted."
4078
+ });
4079
+ }
3763
4080
  }
3764
- const existingPkg = await readExistingPackageJson();
3765
- const gitAuthor = await getGitAuthor();
3766
- const defaultName = sanitizeName(
3767
- options.name || existingPkg?.name || basename(process.cwd())
3768
- );
3769
- const defaultVersion = existingPkg?.version || "0.1.0";
3770
- const defaultDescription = options.description || existingPkg?.description || "";
3771
- const defaultAuthor = options.author || existingPkg?.author || gitAuthor || "";
3772
- const defaultLicense = existingPkg?.license || "MIT";
3773
- const defaultMain = "SKILL.md";
3774
- const defaultCapabilities = "";
3775
- let manifest;
3776
- if (options.yes) {
3777
- manifest = {
3778
- $schema: PSPM_SCHEMA_URL,
3779
- name: defaultName,
3780
- version: defaultVersion,
3781
- description: defaultDescription || void 0,
3782
- author: defaultAuthor || void 0,
3783
- license: defaultLicense,
3784
- type: "skill",
3785
- capabilities: [],
3786
- main: defaultMain,
3787
- requirements: {
3788
- pspm: ">=0.1.0"
3789
- },
3790
- files: [...DEFAULT_SKILL_FILES],
3791
- dependencies: {},
3792
- private: false
3793
- };
3794
- } else {
3795
- console.log(
3796
- "This utility will walk you through creating a pspm.json file."
3797
- );
3798
- console.log(
3799
- "It only covers the most common items, and tries to guess sensible defaults."
3800
- );
3801
- console.log("");
3802
- console.log(
3803
- "See `pspm init --help` for definitive documentation on these fields"
4081
+ const wellKnownSkills = await listLockfileWellKnownPackages();
4082
+ for (const { specifier, entry } of wellKnownSkills) {
4083
+ const wkEntry = entry;
4084
+ const destDir = join(
4085
+ skillsDir,
4086
+ "_wellknown",
4087
+ wkEntry.hostname,
4088
+ wkEntry.name
3804
4089
  );
3805
- console.log("and exactly what they do.");
3806
- console.log("");
3807
- console.log("Press ^C at any time to quit.");
3808
- const rl = createInterface({
3809
- input: process.stdin,
3810
- output: process.stdout
3811
- });
3812
- try {
3813
- let name = await prompt(rl, "skill name:", defaultName);
3814
- while (!isValidName(name)) {
3815
- console.log(
3816
- " Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores."
3817
- );
3818
- name = await prompt(rl, "skill name:", sanitizeName(name));
3819
- }
3820
- let version3 = await prompt(rl, "version:", defaultVersion);
3821
- while (!isValidVersion(version3)) {
3822
- console.log(" Version must be valid semver (e.g., 1.0.0)");
3823
- version3 = await prompt(rl, "version:", "0.1.0");
3824
- }
3825
- const description = await prompt(
3826
- rl,
3827
- "description:",
3828
- defaultDescription
3829
- );
3830
- const main = await prompt(rl, "entry point:", defaultMain);
3831
- const capabilitiesStr = await prompt(
3832
- rl,
3833
- "capabilities (comma-separated):",
3834
- defaultCapabilities
3835
- );
3836
- const author = await prompt(rl, "author:", defaultAuthor);
3837
- const license = await prompt(rl, "license:", defaultLicense);
3838
- rl.close();
3839
- const capabilities = capabilitiesStr ? capabilitiesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
3840
- manifest = {
3841
- $schema: PSPM_SCHEMA_URL,
3842
- name,
3843
- version: version3,
3844
- description: description || void 0,
3845
- author: author || void 0,
3846
- license,
3847
- type: "skill",
3848
- capabilities,
3849
- main,
3850
- requirements: {
3851
- pspm: ">=0.1.0"
3852
- },
3853
- files: [...DEFAULT_SKILL_FILES],
3854
- dependencies: {},
3855
- private: false
3856
- };
3857
- } catch (error) {
3858
- rl.close();
3859
- if (error instanceof Error && error.message.includes("readline was closed")) {
3860
- console.log("\nAborted.");
3861
- process.exit(0);
3862
- }
3863
- throw error;
4090
+ const exists = await pathExists(destDir);
4091
+ if (!exists) {
4092
+ issues.push({
4093
+ name: specifier,
4094
+ source: "well-known",
4095
+ severity: "error",
4096
+ type: "missing",
4097
+ message: `Not installed on disk. Run 'pspm install' to restore.`
4098
+ });
4099
+ continue;
4100
+ }
4101
+ const skillMdExists = await pathExists(join(destDir, "SKILL.md"));
4102
+ if (!skillMdExists) {
4103
+ issues.push({
4104
+ name: specifier,
4105
+ source: "well-known",
4106
+ severity: "warning",
4107
+ type: "integrity",
4108
+ message: "Missing SKILL.md in installed directory. Package may be corrupted."
4109
+ });
3864
4110
  }
3865
4111
  }
3866
- if (!manifest.description) manifest.description = void 0;
3867
- if (!manifest.author) manifest.author = void 0;
3868
- if (manifest.capabilities?.length === 0) manifest.capabilities = void 0;
3869
- const content = JSON.stringify(manifest, null, 2);
3870
- console.log("");
3871
- console.log(`About to write to ${pspmJsonPath}:`);
3872
- console.log("");
3873
- console.log(content);
3874
- console.log("");
3875
- if (!options.yes) {
3876
- const rl = createInterface({
3877
- input: process.stdin,
3878
- output: process.stdout
3879
- });
3880
- const confirm2 = await prompt(rl, "Is this OK?", "yes");
3881
- rl.close();
3882
- if (confirm2.toLowerCase() !== "yes" && confirm2.toLowerCase() !== "y") {
3883
- console.log("Aborted.");
3884
- process.exit(0);
4112
+ const errorCount = issues.filter((i) => i.severity === "error").length;
4113
+ const warningCount = issues.filter((i) => i.severity === "warning").length;
4114
+ if (options.json) {
4115
+ console.log(
4116
+ JSON.stringify(
4117
+ {
4118
+ ok: errorCount === 0,
4119
+ totalPackages: registrySkills.length + githubSkills.length + wellKnownSkills.length,
4120
+ issues
4121
+ },
4122
+ null,
4123
+ 2
4124
+ )
4125
+ );
4126
+ if (errorCount > 0) process.exit(1);
4127
+ return;
4128
+ }
4129
+ if (issues.length === 0) {
4130
+ const totalPackages2 = registrySkills.length + githubSkills.length + wellKnownSkills.length;
4131
+ console.log(`Audited ${totalPackages2} package(s). No issues found.`);
4132
+ return;
4133
+ }
4134
+ const errors = issues.filter((i) => i.severity === "error");
4135
+ const warnings = issues.filter((i) => i.severity === "warning");
4136
+ if (errors.length > 0) {
4137
+ console.log("Errors:");
4138
+ for (const issue of errors) {
4139
+ console.log(
4140
+ ` [${issue.type.toUpperCase()}] ${issue.name} (${issue.source})`
4141
+ );
4142
+ console.log(` ${issue.message}`);
3885
4143
  }
4144
+ console.log();
3886
4145
  }
3887
- await writeFile(pspmJsonPath, `${content}
3888
- `);
3889
- try {
3890
- await stat(join(process.cwd(), "SKILL.md"));
3891
- } catch {
3892
- console.log(
3893
- "Note: Create a SKILL.md file with your skill's prompt content."
3894
- );
4146
+ if (warnings.length > 0) {
4147
+ console.log("Warnings:");
4148
+ for (const issue of warnings) {
4149
+ console.log(
4150
+ ` [${issue.type.toUpperCase()}] ${issue.name} (${issue.source})`
4151
+ );
4152
+ console.log(` ${issue.message}`);
4153
+ }
4154
+ console.log();
3895
4155
  }
3896
- if (existingPkg) {
3897
- console.log("Note: Values were derived from existing package.json.");
3898
- console.log(" pspm.json is for publishing to PSPM registry.");
3899
- console.log(" package.json can still be used for npm dependencies.");
4156
+ const totalPackages = registrySkills.length + githubSkills.length + wellKnownSkills.length;
4157
+ console.log(
4158
+ `Audited ${totalPackages} package(s): ${errorCount} error(s), ${warningCount} warning(s).`
4159
+ );
4160
+ if (errorCount > 0) {
4161
+ process.exit(1);
3900
4162
  }
3901
4163
  } catch (error) {
3902
4164
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -3904,441 +4166,358 @@ async function init(options) {
3904
4166
  process.exit(1);
3905
4167
  }
3906
4168
  }
3907
-
3908
- // src/commands/install.ts
3909
- init_agents();
3910
- init_api_client();
3911
- init_config();
3912
- init_errors();
3913
- init_github();
3914
- init_lib();
3915
- init_lockfile3();
3916
- init_manifest3();
3917
- init_symlinks();
3918
- function getCacheFilePath(cacheDir, integrity) {
3919
- const match = integrity.match(/^sha256-(.+)$/);
3920
- if (!match) {
3921
- throw new Error(`Invalid integrity format: ${integrity}`);
3922
- }
3923
- const base64Hash = match[1];
3924
- const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
3925
- return join(cacheDir, `sha256-${hexHash}.tgz`);
3926
- }
3927
- async function readFromCache(cacheDir, integrity) {
4169
+ async function pathExists(path) {
3928
4170
  try {
3929
- const cachePath = getCacheFilePath(cacheDir, integrity);
3930
- const data = await readFile(cachePath);
3931
- const actualIntegrity = `sha256-${createHash("sha256").update(data).digest("base64")}`;
3932
- if (actualIntegrity !== integrity) {
3933
- await rm(cachePath, { force: true });
3934
- return null;
3935
- }
3936
- return data;
4171
+ await stat(path);
4172
+ return true;
3937
4173
  } catch {
3938
- return null;
4174
+ return false;
3939
4175
  }
3940
4176
  }
3941
- async function writeToCache(cacheDir, integrity, data) {
4177
+ async function configInit(options) {
3942
4178
  try {
3943
- await mkdir(cacheDir, { recursive: true });
3944
- const cachePath = getCacheFilePath(cacheDir, integrity);
3945
- await writeFile(cachePath, data);
3946
- } catch {
3947
- }
3948
- }
3949
- async function install(specifiers, options) {
3950
- if (options.global) {
3951
- const { setGlobalMode: setGlobalMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3952
- setGlobalMode2(true);
3953
- }
3954
- if (specifiers.length > 0) {
3955
- const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
3956
- await add2(specifiers, {
3957
- save: true,
3958
- agent: options.agent,
3959
- yes: options.yes,
3960
- global: options.global
3961
- });
3962
- return;
4179
+ const configPath = join(process.cwd(), ".pspmrc");
4180
+ try {
4181
+ await stat(configPath);
4182
+ console.error("Error: .pspmrc already exists in this directory.");
4183
+ process.exit(1);
4184
+ } catch {
4185
+ }
4186
+ const lines = ["; Project-specific PSPM configuration", ""];
4187
+ if (options.registry) {
4188
+ lines.push(`registry = ${options.registry}`);
4189
+ } else {
4190
+ lines.push("; Uncomment to use a custom registry:");
4191
+ lines.push("; registry = https://custom-registry.example.com");
4192
+ }
4193
+ lines.push("");
4194
+ await writeFile(configPath, lines.join("\n"));
4195
+ console.log("Created .pspmrc");
4196
+ console.log("");
4197
+ console.log("Contents:");
4198
+ console.log(lines.join("\n"));
4199
+ console.log("Note: .pspmrc should be committed to version control.");
4200
+ console.log("API keys should NOT be stored here - use pspm login instead.");
4201
+ } catch (error) {
4202
+ const message = error instanceof Error ? error.message : "Unknown error";
4203
+ console.error(`Error: ${message}`);
4204
+ process.exit(1);
3963
4205
  }
3964
- await installFromLockfile(options);
3965
4206
  }
3966
- async function installFromLockfile(options) {
4207
+
4208
+ // src/commands/config/show.ts
4209
+ init_config();
4210
+ async function configShow() {
3967
4211
  try {
3968
- const config2 = await resolveConfig();
3969
- const registryUrl = config2.registryUrl;
3970
- const apiKey = getTokenForRegistry(config2, registryUrl);
3971
- const skillsDir = options.dir || getSkillsDir();
3972
- const cacheDir = getCacheDir();
3973
- await migrateLockfileIfNeeded();
3974
- let lockfile = await readLockfile();
3975
- const manifestDeps = await getDependencies();
3976
- const manifestGitHubDeps = await getGitHubDependencies();
3977
- const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
3978
- const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
3979
- const installedSkills = [];
3980
- const missingDeps = [];
3981
- for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
3982
- if (!lockfilePackages[fullName]) {
3983
- missingDeps.push({ fullName, versionRange });
3984
- }
3985
- }
3986
- if (missingDeps.length > 0) {
3987
- if (options.frozenLockfile) {
3988
- console.error(
3989
- "Error: Dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
3990
- );
3991
- console.error("Missing dependencies:");
3992
- for (const dep of missingDeps) {
3993
- console.error(` - ${dep.fullName}@${dep.versionRange}`);
3994
- }
3995
- process.exit(1);
3996
- }
3997
- console.log(`Resolving ${missingDeps.length} new dependency(ies)...
3998
- `);
3999
- configure2({ registryUrl, apiKey });
4000
- for (const { fullName, versionRange } of missingDeps) {
4001
- const parsed = parseRegistrySpecifier2(fullName);
4002
- if (!parsed) {
4003
- console.error(`Error: Invalid dependency specifier: ${fullName}`);
4004
- continue;
4005
- }
4006
- const { owner, name } = parsed;
4007
- console.log(`Resolving ${fullName}@${versionRange}...`);
4008
- const versionsResponse = await listSkillVersions(owner, name);
4009
- if (versionsResponse.status !== 200) {
4010
- const errorMessage = extractApiErrorMessage(
4011
- versionsResponse,
4012
- `Skill ${fullName} not found`
4013
- );
4014
- console.error(`Error: ${errorMessage}`);
4015
- continue;
4016
- }
4017
- const versions = versionsResponse.data;
4018
- if (versions.length === 0) {
4019
- console.error(`Error: Skill ${fullName} not found`);
4020
- continue;
4021
- }
4022
- const versionStrings = versions.map(
4023
- (v) => v.version
4024
- );
4025
- const resolved = resolveVersion2(versionRange || "*", versionStrings);
4026
- if (!resolved) {
4027
- console.error(
4028
- `Error: No version matching "${versionRange}" for ${fullName}`
4029
- );
4030
- continue;
4031
- }
4032
- const versionResponse = await getSkillVersion(owner, name, resolved);
4033
- if (versionResponse.status !== 200 || !versionResponse.data) {
4034
- const errorMessage = extractApiErrorMessage(
4035
- versionResponse,
4036
- `Version ${resolved} not found`
4037
- );
4038
- console.error(`Error: ${errorMessage}`);
4039
- continue;
4040
- }
4041
- const versionInfo = versionResponse.data;
4042
- const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
4043
- const downloadHeaders = {};
4044
- if (!isPresignedUrl && apiKey) {
4045
- downloadHeaders.Authorization = `Bearer ${apiKey}`;
4046
- }
4047
- const tarballResponse = await fetch(versionInfo.downloadUrl, {
4048
- headers: downloadHeaders,
4049
- redirect: "follow"
4050
- });
4051
- if (!tarballResponse.ok) {
4052
- console.error(
4053
- `Error: Failed to download tarball for ${fullName} (${tarballResponse.status})`
4054
- );
4055
- continue;
4056
- }
4057
- const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
4058
- const integrity = calculateIntegrity(tarballBuffer);
4059
- await addToLockfile(fullName, {
4060
- version: resolved,
4061
- resolved: versionInfo.downloadUrl,
4062
- integrity
4063
- });
4064
- await writeToCache(cacheDir, integrity, tarballBuffer);
4065
- console.log(` Resolved ${fullName}@${resolved}`);
4066
- }
4067
- lockfile = await readLockfile();
4212
+ const resolved = await resolveConfig();
4213
+ const projectConfig = await findProjectConfig();
4214
+ const configPath = getConfigPath();
4215
+ console.log("Resolved Configuration:\n");
4216
+ console.log(` Registry URL: ${resolved.registryUrl}`);
4217
+ console.log(` API Key: ${resolved.apiKey ? "***" : "(not set)"}`);
4218
+ console.log(` Username: ${resolved.username || "(not set)"}`);
4219
+ console.log("");
4220
+ console.log("Config Locations:");
4221
+ console.log(` User config: ${configPath}`);
4222
+ console.log(` Project config: ${projectConfig ? ".pspmrc" : "(none)"}`);
4223
+ console.log("");
4224
+ console.log("Environment Variables:");
4225
+ console.log(
4226
+ ` PSPM_REGISTRY_URL: ${process.env.PSPM_REGISTRY_URL || "(not set)"}`
4227
+ );
4228
+ console.log(
4229
+ ` PSPM_API_KEY: ${process.env.PSPM_API_KEY ? "***" : "(not set)"}`
4230
+ );
4231
+ } catch (error) {
4232
+ const message = error instanceof Error ? error.message : "Unknown error";
4233
+ console.error(`Error: ${message}`);
4234
+ process.exit(1);
4235
+ }
4236
+ }
4237
+
4238
+ // src/commands/deprecate.ts
4239
+ init_api_client();
4240
+ init_config();
4241
+ init_lib();
4242
+ async function deprecate(specifier, message, options) {
4243
+ try {
4244
+ const apiKey = await requireApiKey();
4245
+ const registryUrl = await getRegistryUrl();
4246
+ const parsed = parseRegistrySpecifier2(specifier);
4247
+ if (!parsed) {
4248
+ console.error(
4249
+ `Error: Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}@{version} or @org/{orgname}/{name}@{version}`
4250
+ );
4251
+ process.exit(1);
4068
4252
  }
4069
- const missingGitHubDeps = [];
4070
- for (const [specifier, ref] of Object.entries(manifestGitHubDeps)) {
4071
- if (!lockfileGitHubPackages[specifier]) {
4072
- missingGitHubDeps.push({ specifier, ref });
4073
- }
4253
+ const { owner, name, versionRange } = parsed;
4254
+ const fullName = generateRegistryIdentifier2({
4255
+ namespace: parsed.namespace,
4256
+ owner,
4257
+ name
4258
+ });
4259
+ if (!versionRange) {
4260
+ console.error(
4261
+ "Error: Version is required for deprecation. Use format: @user/{username}/{name}@{version}"
4262
+ );
4263
+ process.exit(1);
4074
4264
  }
4075
- if (missingGitHubDeps.length > 0) {
4076
- if (options.frozenLockfile) {
4265
+ configure2({ registryUrl, apiKey });
4266
+ if (options.undo) {
4267
+ console.log(`Removing deprecation from ${fullName}@${versionRange}...`);
4268
+ const response = await undeprecateSkillVersion(owner, name, versionRange);
4269
+ if (response.status !== 200) {
4077
4270
  console.error(
4078
- "Error: GitHub dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
4271
+ `Error: ${response.error || "Failed to remove deprecation"}`
4079
4272
  );
4080
- console.error("Missing GitHub dependencies:");
4081
- for (const dep of missingGitHubDeps) {
4082
- console.error(` - ${dep.specifier}@${dep.ref}`);
4083
- }
4084
4273
  process.exit(1);
4085
4274
  }
4086
- console.log(
4087
- `
4088
- Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
4089
- `
4275
+ console.log(`Removed deprecation from ${fullName}@${versionRange}`);
4276
+ } else {
4277
+ if (!message) {
4278
+ console.error(
4279
+ "Error: Deprecation message is required. Usage: pspm deprecate <specifier> <message>"
4280
+ );
4281
+ process.exit(1);
4282
+ }
4283
+ console.log(`Deprecating ${fullName}@${versionRange}...`);
4284
+ const response = await deprecateSkillVersion(
4285
+ owner,
4286
+ name,
4287
+ versionRange,
4288
+ message
4090
4289
  );
4091
- for (const { specifier, ref } of missingGitHubDeps) {
4092
- const parsed = parseGitHubSpecifier2(specifier);
4093
- if (!parsed) {
4094
- console.error(`Error: Invalid GitHub specifier: ${specifier}`);
4095
- continue;
4096
- }
4097
- parsed.ref = parsed.ref || ref;
4098
- console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
4099
- try {
4100
- const result = await downloadGitHubPackage(parsed);
4101
- await extractGitHubPackage(parsed, result.buffer, skillsDir);
4102
- const entry = {
4103
- version: result.commit.slice(0, 7),
4104
- resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
4105
- integrity: result.integrity,
4106
- gitCommit: result.commit,
4107
- gitRef: ref || "HEAD"
4108
- };
4109
- await addGitHubToLockfile(specifier, entry);
4110
- await writeToCache(cacheDir, result.integrity, result.buffer);
4111
- console.log(
4112
- ` Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`
4113
- );
4114
- } catch (error) {
4115
- if (error instanceof GitHubRateLimitError) {
4116
- console.error(`Error: ${error.message}`);
4117
- } else if (error instanceof GitHubPathNotFoundError) {
4118
- console.error(`Error: ${error.message}`);
4119
- } else if (error instanceof GitHubNotFoundError) {
4120
- console.error(`Error: ${error.message}`);
4121
- } else {
4122
- const message = error instanceof Error ? error.message : String(error);
4123
- console.error(`Error resolving ${specifier}: ${message}`);
4124
- }
4125
- }
4290
+ if (response.status !== 200) {
4291
+ console.error(
4292
+ `Error: ${response.error || "Failed to deprecate version"}`
4293
+ );
4294
+ process.exit(1);
4126
4295
  }
4127
- lockfile = await readLockfile();
4296
+ console.log(`Deprecated ${fullName}@${versionRange}`);
4297
+ console.log(`Message: ${message}`);
4298
+ console.log("");
4299
+ console.log(
4300
+ "Users installing this version will see a deprecation warning."
4301
+ );
4302
+ console.log("The package is still available for download.");
4128
4303
  }
4129
- const manifest = await readManifest();
4130
- const agentConfigs = manifest?.agents;
4131
- let agents;
4132
- if (options.agent) {
4133
- agents = parseAgentArg(options.agent);
4134
- } else if (manifest) {
4135
- agents = parseAgentArg(void 0);
4136
- } else if (options.yes) {
4137
- agents = parseAgentArg(void 0);
4138
- } else {
4139
- console.log("\nNo pspm.json found. Let's set up your project.\n");
4140
- agents = await promptForAgents();
4141
- console.log();
4304
+ } catch (error) {
4305
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
4306
+ console.error(`Error: ${errorMessage}`);
4307
+ process.exit(1);
4308
+ }
4309
+ }
4310
+
4311
+ // src/commands/init.ts
4312
+ init_lib();
4313
+ function prompt(rl, question, defaultValue) {
4314
+ return new Promise((resolve3) => {
4315
+ const displayDefault = defaultValue ? ` (${defaultValue})` : "";
4316
+ rl.question(`${question}${displayDefault} `, (answer) => {
4317
+ resolve3(answer.trim() || defaultValue);
4318
+ });
4319
+ });
4320
+ }
4321
+ async function readExistingPackageJson() {
4322
+ try {
4323
+ const content = await readFile(
4324
+ join(process.cwd(), "package.json"),
4325
+ "utf-8"
4326
+ );
4327
+ const pkg = JSON.parse(content);
4328
+ return {
4329
+ name: pkg.name,
4330
+ version: pkg.version,
4331
+ description: pkg.description,
4332
+ author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name,
4333
+ license: pkg.license
4334
+ };
4335
+ } catch {
4336
+ return null;
4337
+ }
4338
+ }
4339
+ async function getGitAuthor() {
4340
+ try {
4341
+ const { exec: exec2 } = await import('child_process');
4342
+ const { promisify: promisify2 } = await import('util');
4343
+ const execAsync = promisify2(exec2);
4344
+ const [nameResult, emailResult] = await Promise.all([
4345
+ execAsync("git config user.name").catch(() => ({ stdout: "" })),
4346
+ execAsync("git config user.email").catch(() => ({ stdout: "" }))
4347
+ ]);
4348
+ const name = nameResult.stdout.trim();
4349
+ const email = emailResult.stdout.trim();
4350
+ if (name && email) {
4351
+ return `${name} <${email}>`;
4142
4352
  }
4143
- const packages = lockfile?.packages ?? lockfile?.skills ?? {};
4144
- const packageCount = Object.keys(packages).length;
4145
- if (packageCount > 0) {
4146
- console.log(`
4147
- Installing ${packageCount} registry skill(s)...
4148
- `);
4149
- const installOrder = computeInstallOrder(packages);
4150
- const entries = installOrder.filter((name) => packages[name]).map((name) => [name, packages[name]]);
4151
- for (const [fullName, entry] of entries) {
4152
- const parsedName = parseRegistrySpecifier2(fullName);
4153
- if (!parsedName) {
4154
- console.warn(`Warning: Invalid skill name in lockfile: ${fullName}`);
4155
- continue;
4156
- }
4157
- const { namespace: ns, owner: pkgOwner, name, subname } = parsedName;
4158
- console.log(`Installing ${fullName}@${entry.version}...`);
4159
- let tarballBuffer;
4160
- let fromCache = false;
4161
- const cachedTarball = await readFromCache(cacheDir, entry.integrity);
4162
- if (cachedTarball) {
4163
- tarballBuffer = cachedTarball;
4164
- fromCache = true;
4165
- } else {
4166
- const isPresignedUrl = entry.resolved.includes(".r2.cloudflarestorage.com") || entry.resolved.includes("X-Amz-Signature");
4167
- const downloadHeaders = {};
4168
- if (!isPresignedUrl && apiKey) {
4169
- downloadHeaders.Authorization = `Bearer ${apiKey}`;
4170
- }
4171
- const response = await fetch(entry.resolved, {
4172
- headers: downloadHeaders,
4173
- redirect: "follow"
4174
- });
4175
- if (!response.ok) {
4176
- if (response.status === 401) {
4177
- if (!apiKey) {
4178
- console.error(
4179
- ` Error: ${fullName} requires authentication. Run 'pspm login' first.`
4180
- );
4181
- } else {
4182
- console.error(
4183
- ` Error: Access denied to ${fullName}. You may not have permission to access this private package.`
4184
- );
4185
- }
4186
- } else {
4187
- console.error(
4188
- ` Error: Failed to download ${fullName} (${response.status})`
4189
- );
4190
- }
4191
- continue;
4192
- }
4193
- tarballBuffer = Buffer.from(await response.arrayBuffer());
4194
- const actualIntegrity = `sha256-${createHash("sha256").update(tarballBuffer).digest("base64")}`;
4195
- if (actualIntegrity !== entry.integrity) {
4196
- console.error(
4197
- ` Error: Checksum verification failed for ${fullName}`
4198
- );
4199
- if (options.frozenLockfile) {
4200
- process.exit(1);
4201
- }
4202
- continue;
4203
- }
4204
- await writeToCache(cacheDir, entry.integrity, tarballBuffer);
4205
- }
4206
- const effectiveSkillName = subname ?? name;
4207
- let destDir;
4208
- if (ns === "org") {
4209
- destDir = join(skillsDir, "_org", pkgOwner, effectiveSkillName);
4210
- } else if (ns === "github" && subname) {
4211
- destDir = join(
4212
- skillsDir,
4213
- "_github-registry",
4214
- pkgOwner,
4215
- name,
4216
- subname
4353
+ if (name) {
4354
+ return name;
4355
+ }
4356
+ return null;
4357
+ } catch {
4358
+ return null;
4359
+ }
4360
+ }
4361
+ function sanitizeName(name) {
4362
+ const withoutScope = name.replace(/^@[^/]+\//, "");
4363
+ return withoutScope.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
4364
+ }
4365
+ function isValidName(name) {
4366
+ return /^[a-z][a-z0-9_-]*$/.test(name);
4367
+ }
4368
+ function isValidVersion(version3) {
4369
+ return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version3);
4370
+ }
4371
+ async function init(options) {
4372
+ try {
4373
+ const pspmJsonPath = join(process.cwd(), "pspm.json");
4374
+ let exists = false;
4375
+ try {
4376
+ await stat(pspmJsonPath);
4377
+ exists = true;
4378
+ } catch {
4379
+ }
4380
+ if (exists && !options.force) {
4381
+ console.error("Error: pspm.json already exists in this directory.");
4382
+ console.error("Use --force to overwrite.");
4383
+ process.exit(1);
4384
+ }
4385
+ const existingPkg = await readExistingPackageJson();
4386
+ const gitAuthor = await getGitAuthor();
4387
+ const defaultName = sanitizeName(
4388
+ options.name || existingPkg?.name || basename(process.cwd())
4389
+ );
4390
+ const defaultVersion = existingPkg?.version || "0.1.0";
4391
+ const defaultDescription = options.description || existingPkg?.description || "";
4392
+ const defaultAuthor = options.author || existingPkg?.author || gitAuthor || "";
4393
+ const defaultLicense = existingPkg?.license || "MIT";
4394
+ const defaultMain = "SKILL.md";
4395
+ const defaultCapabilities = "";
4396
+ let manifest;
4397
+ if (options.yes) {
4398
+ manifest = {
4399
+ $schema: PSPM_SCHEMA_URL,
4400
+ name: defaultName,
4401
+ version: defaultVersion,
4402
+ description: defaultDescription || void 0,
4403
+ author: defaultAuthor || void 0,
4404
+ license: defaultLicense,
4405
+ type: "skill",
4406
+ capabilities: [],
4407
+ main: defaultMain,
4408
+ requirements: {
4409
+ pspm: ">=0.1.0"
4410
+ },
4411
+ files: [...DEFAULT_SKILL_FILES],
4412
+ dependencies: {},
4413
+ private: false
4414
+ };
4415
+ } else {
4416
+ console.log(
4417
+ "This utility will walk you through creating a pspm.json file."
4418
+ );
4419
+ console.log(
4420
+ "It only covers the most common items, and tries to guess sensible defaults."
4421
+ );
4422
+ console.log("");
4423
+ console.log(
4424
+ "See `pspm init --help` for definitive documentation on these fields"
4425
+ );
4426
+ console.log("and exactly what they do.");
4427
+ console.log("");
4428
+ console.log("Press ^C at any time to quit.");
4429
+ const rl = createInterface({
4430
+ input: process.stdin,
4431
+ output: process.stdout
4432
+ });
4433
+ try {
4434
+ let name = await prompt(rl, "skill name:", defaultName);
4435
+ while (!isValidName(name)) {
4436
+ console.log(
4437
+ " Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores."
4217
4438
  );
4218
- } else {
4219
- destDir = join(skillsDir, pkgOwner, effectiveSkillName);
4439
+ name = await prompt(rl, "skill name:", sanitizeName(name));
4220
4440
  }
4221
- await rm(destDir, { recursive: true, force: true });
4222
- await mkdir(destDir, { recursive: true });
4223
- const tempFile = join(destDir, ".temp.tgz");
4224
- await writeFile(tempFile, tarballBuffer);
4225
- const { exec: exec2 } = await import('child_process');
4226
- const { promisify: promisify2 } = await import('util');
4227
- const execAsync = promisify2(exec2);
4228
- try {
4229
- await execAsync(
4230
- `tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
4231
- );
4232
- } finally {
4233
- await rm(tempFile, { force: true });
4441
+ let version3 = await prompt(rl, "version:", defaultVersion);
4442
+ while (!isValidVersion(version3)) {
4443
+ console.log(" Version must be valid semver (e.g., 1.0.0)");
4444
+ version3 = await prompt(rl, "version:", "0.1.0");
4234
4445
  }
4235
- console.log(
4236
- ` Installed to ${destDir}${fromCache ? " (from cache)" : ""}`
4446
+ const description = await prompt(
4447
+ rl,
4448
+ "description:",
4449
+ defaultDescription
4237
4450
  );
4238
- const pathSkillName = ns === "github" && subname ? `${name}/${subname}` : name;
4239
- installedSkills.push({
4240
- name: effectiveSkillName,
4241
- sourcePath: getRegistrySkillPath(ns, pkgOwner, pathSkillName)
4242
- });
4243
- }
4244
- }
4245
- const githubPackages = lockfile?.githubPackages ?? {};
4246
- const githubCount = Object.keys(githubPackages).length;
4247
- if (githubCount > 0) {
4248
- console.log(`
4249
- Installing ${githubCount} GitHub skill(s)...
4250
- `);
4251
- for (const [specifier, entry] of Object.entries(githubPackages)) {
4252
- const parsed = parseGitHubSpecifier2(specifier);
4253
- if (!parsed) {
4254
- console.warn(
4255
- `Warning: Invalid GitHub specifier in lockfile: ${specifier}`
4256
- );
4257
- continue;
4258
- }
4259
- const ghEntry = entry;
4260
- console.log(
4261
- `Installing ${specifier} (${ghEntry.gitRef}@${ghEntry.gitCommit.slice(0, 7)})...`
4451
+ const main = await prompt(rl, "entry point:", defaultMain);
4452
+ const capabilitiesStr = await prompt(
4453
+ rl,
4454
+ "capabilities (comma-separated):",
4455
+ defaultCapabilities
4262
4456
  );
4263
- let tarballBuffer;
4264
- let fromCache = false;
4265
- const cachedTarball = await readFromCache(cacheDir, ghEntry.integrity);
4266
- if (cachedTarball) {
4267
- tarballBuffer = cachedTarball;
4268
- fromCache = true;
4269
- } else {
4270
- try {
4271
- const specWithCommit = { ...parsed, ref: ghEntry.gitCommit };
4272
- const result = await downloadGitHubPackage(specWithCommit);
4273
- tarballBuffer = result.buffer;
4274
- if (result.integrity !== ghEntry.integrity) {
4275
- console.error(
4276
- ` Error: Checksum verification failed for ${specifier}`
4277
- );
4278
- if (options.frozenLockfile) {
4279
- process.exit(1);
4280
- }
4281
- continue;
4282
- }
4283
- await writeToCache(cacheDir, ghEntry.integrity, tarballBuffer);
4284
- } catch (error) {
4285
- if (error instanceof GitHubRateLimitError) {
4286
- console.error(` Error: ${error.message}`);
4287
- } else if (error instanceof GitHubPathNotFoundError) {
4288
- console.error(` Error: ${error.message}`);
4289
- } else if (error instanceof GitHubNotFoundError) {
4290
- console.error(` Error: ${error.message}`);
4291
- } else {
4292
- const message = error instanceof Error ? error.message : String(error);
4293
- console.error(` Error downloading ${specifier}: ${message}`);
4294
- }
4295
- continue;
4296
- }
4297
- }
4298
- try {
4299
- const destPath = await extractGitHubPackage(
4300
- parsed,
4301
- tarballBuffer,
4302
- skillsDir
4303
- );
4304
- console.log(
4305
- ` Installed to ${destPath}${fromCache ? " (from cache)" : ""}`
4306
- );
4307
- const skillName = getGitHubSkillName2(parsed);
4308
- installedSkills.push({
4309
- name: skillName,
4310
- sourcePath: getGitHubSkillPath(
4311
- parsed.owner,
4312
- parsed.repo,
4313
- parsed.path
4314
- )
4315
- });
4316
- } catch (error) {
4317
- const message = error instanceof Error ? error.message : String(error);
4318
- console.error(` Error extracting ${specifier}: ${message}`);
4457
+ const author = await prompt(rl, "author:", defaultAuthor);
4458
+ const license = await prompt(rl, "license:", defaultLicense);
4459
+ rl.close();
4460
+ const capabilities = capabilitiesStr ? capabilitiesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
4461
+ manifest = {
4462
+ $schema: PSPM_SCHEMA_URL,
4463
+ name,
4464
+ version: version3,
4465
+ description: description || void 0,
4466
+ author: author || void 0,
4467
+ license,
4468
+ type: "skill",
4469
+ capabilities,
4470
+ main,
4471
+ requirements: {
4472
+ pspm: ">=0.1.0"
4473
+ },
4474
+ files: [...DEFAULT_SKILL_FILES],
4475
+ dependencies: {},
4476
+ private: false
4477
+ };
4478
+ } catch (error) {
4479
+ rl.close();
4480
+ if (error instanceof Error && error.message.includes("readline was closed")) {
4481
+ console.log("\nAborted.");
4482
+ process.exit(0);
4319
4483
  }
4484
+ throw error;
4320
4485
  }
4321
4486
  }
4322
- if (installedSkills.length > 0 && agents[0] !== "none") {
4323
- const globalMode = isGlobalMode();
4324
- console.log(
4325
- `
4326
- Creating symlinks for agent(s): ${agents.join(", ")}${globalMode ? " (global)" : ""}...`
4327
- );
4328
- await createAgentSymlinks(installedSkills, {
4329
- agents,
4330
- projectRoot: globalMode ? homedir() : process.cwd(),
4331
- agentConfigs,
4332
- global: globalMode
4487
+ if (!manifest.description) manifest.description = void 0;
4488
+ if (!manifest.author) manifest.author = void 0;
4489
+ if (manifest.capabilities?.length === 0) manifest.capabilities = void 0;
4490
+ const content = JSON.stringify(manifest, null, 2);
4491
+ console.log("");
4492
+ console.log(`About to write to ${pspmJsonPath}:`);
4493
+ console.log("");
4494
+ console.log(content);
4495
+ console.log("");
4496
+ if (!options.yes) {
4497
+ const rl = createInterface({
4498
+ input: process.stdin,
4499
+ output: process.stdout
4333
4500
  });
4334
- console.log(" Symlinks created.");
4501
+ const confirm2 = await prompt(rl, "Is this OK?", "yes");
4502
+ rl.close();
4503
+ if (confirm2.toLowerCase() !== "yes" && confirm2.toLowerCase() !== "y") {
4504
+ console.log("Aborted.");
4505
+ process.exit(0);
4506
+ }
4335
4507
  }
4336
- const totalCount = packageCount + githubCount;
4337
- if (totalCount === 0) {
4338
- console.log("No skills to install.");
4339
- } else {
4340
- console.log(`
4341
- All ${totalCount} skill(s) installed.`);
4508
+ await writeFile(pspmJsonPath, `${content}
4509
+ `);
4510
+ try {
4511
+ await stat(join(process.cwd(), "SKILL.md"));
4512
+ } catch {
4513
+ console.log(
4514
+ "Note: Create a SKILL.md file with your skill's prompt content."
4515
+ );
4516
+ }
4517
+ if (existingPkg) {
4518
+ console.log("Note: Values were derived from existing package.json.");
4519
+ console.log(" pspm.json is for publishing to PSPM registry.");
4520
+ console.log(" package.json can still be used for npm dependencies.");
4342
4521
  }
4343
4522
  } catch (error) {
4344
4523
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -4347,6 +4526,9 @@ All ${totalCount} skill(s) installed.`);
4347
4526
  }
4348
4527
  }
4349
4528
 
4529
+ // src/commands/index.ts
4530
+ init_install();
4531
+
4350
4532
  // src/commands/link.ts
4351
4533
  init_agents();
4352
4534
  init_lib();
@@ -4652,8 +4834,8 @@ function startCallbackServer(expectedState) {
4652
4834
  let resolveToken;
4653
4835
  let rejectToken;
4654
4836
  let timeoutId;
4655
- const tokenPromise = new Promise((resolve2, reject) => {
4656
- resolveToken = resolve2;
4837
+ const tokenPromise = new Promise((resolve3, reject) => {
4838
+ resolveToken = resolve3;
4657
4839
  rejectToken = reject;
4658
4840
  });
4659
4841
  const server = http.createServer((req, res) => {
@@ -4907,6 +5089,92 @@ async function migrate(options) {
4907
5089
  process.exit(1);
4908
5090
  }
4909
5091
  }
5092
+
5093
+ // src/commands/notebook.ts
5094
+ init_config();
5095
+ async function fetchApi(path, options = {}) {
5096
+ const config2 = await resolveConfig();
5097
+ const apiKey = await requireApiKey();
5098
+ const baseUrl = config2.registryUrl.replace(/\/api\/skills\/?$/, "");
5099
+ const headers = {
5100
+ "Content-Type": "application/json",
5101
+ ...options.headers
5102
+ };
5103
+ if (apiKey) {
5104
+ headers.Authorization = `Bearer ${apiKey}`;
5105
+ }
5106
+ return fetch(`${baseUrl}${path}`, { ...options, headers });
5107
+ }
5108
+ async function notebookUpload(filePath, options) {
5109
+ const absPath = resolve(filePath);
5110
+ const content = readFileSync(absPath, "utf-8");
5111
+ const name = basename(filePath).replace(/\.anyt\.md$|\.anyt$|\.md$/, "");
5112
+ const body = {
5113
+ name,
5114
+ content,
5115
+ description: options.description,
5116
+ visibility: options.visibility || "private"
5117
+ };
5118
+ const path = options.org ? `/api/notebooks/org/${options.org}` : "/api/notebooks";
5119
+ const response = await fetchApi(path, {
5120
+ method: "POST",
5121
+ body: JSON.stringify(body)
5122
+ });
5123
+ if (!response.ok) {
5124
+ const err = await response.json().catch(() => ({}));
5125
+ throw new Error(
5126
+ err.message || `Upload failed (${response.status})`
5127
+ );
5128
+ }
5129
+ const notebook = await response.json();
5130
+ console.log(
5131
+ `Uploaded: ${notebook.name} (${notebook.id})${options.org ? ` to org ${options.org}` : ""}`
5132
+ );
5133
+ }
5134
+ async function notebookList(options) {
5135
+ const path = options.org ? `/api/notebooks/org/${options.org}` : "/api/notebooks/-/mine";
5136
+ const response = await fetchApi(path);
5137
+ if (!response.ok) {
5138
+ throw new Error(`Failed to list notebooks (${response.status})`);
5139
+ }
5140
+ const notebooks = await response.json();
5141
+ if (notebooks.length === 0) {
5142
+ console.log("No notebooks found");
5143
+ return;
5144
+ }
5145
+ console.log(
5146
+ `
5147
+ ${"Name".padEnd(30)} ${"Cells".padEnd(8)} ${"Visibility".padEnd(12)} ${"Updated".padEnd(12)} ID`
5148
+ );
5149
+ console.log("-".repeat(90));
5150
+ for (const nb of notebooks) {
5151
+ const updated = new Date(nb.updatedAt).toLocaleDateString();
5152
+ console.log(
5153
+ `${nb.name.slice(0, 28).padEnd(30)} ${String(nb.cellCount).padEnd(8)} ${nb.visibility.padEnd(12)} ${updated.padEnd(12)} ${nb.id}`
5154
+ );
5155
+ }
5156
+ console.log(`
5157
+ ${notebooks.length} notebook(s)`);
5158
+ }
5159
+ async function notebookDownload(id, output) {
5160
+ const response = await fetchApi(`/api/notebooks/${id}`);
5161
+ if (!response.ok) {
5162
+ throw new Error(`Notebook not found (${response.status})`);
5163
+ }
5164
+ const notebook = await response.json();
5165
+ const outPath = output || `${notebook.slug}.anyt.md`;
5166
+ writeFileSync(outPath, notebook.content, "utf-8");
5167
+ console.log(`Downloaded: ${notebook.name} -> ${outPath}`);
5168
+ }
5169
+ async function notebookDelete(id) {
5170
+ const response = await fetchApi(`/api/notebooks/${id}`, {
5171
+ method: "DELETE"
5172
+ });
5173
+ if (!response.ok) {
5174
+ throw new Error(`Failed to delete notebook (${response.status})`);
5175
+ }
5176
+ console.log(`Notebook ${id} deleted`);
5177
+ }
4910
5178
  function resolveVersion3(range, availableVersions) {
4911
5179
  const sorted = availableVersions.filter((v) => semver2.valid(v)).sort((a, b) => semver2.rcompare(a, b));
4912
5180
  if (range === "latest") {
@@ -4923,7 +5191,7 @@ function getLatestVersion3(versions) {
4923
5191
  return valid5.sort((a, b) => semver2.rcompare(a, b))[0];
4924
5192
  }
4925
5193
 
4926
- // ../../packages/server/skill-registry/src/client/outdated.ts
5194
+ // ../../packages/features/skill-registry/src/client/outdated.ts
4927
5195
  function createOutdatedChecker(config2) {
4928
5196
  const { registryUrl, apiKey, githubToken } = config2;
4929
5197
  async function fetchWithAuth(url, token) {
@@ -4943,14 +5211,26 @@ function createOutdatedChecker(config2) {
4943
5211
  const response = await fetchWithAuth(url, apiKey);
4944
5212
  return await response.json();
4945
5213
  }
5214
+ async function fetchGithubRegistryVersions(owner, repo, skillname) {
5215
+ const url = `${registryUrl}/@github/${owner}/${repo}/${skillname}/versions`;
5216
+ const response = await fetchWithAuth(url, apiKey);
5217
+ return await response.json();
5218
+ }
4946
5219
  async function checkRegistryPackage(specifier, entry, versionRange) {
4947
- const match = specifier.match(/^@user\/([^/]+)\/([^/]+)$/);
4948
- if (!match) {
5220
+ const userMatch = specifier.match(/^@(?:user|org)\/([^/]+)\/([^/]+)$/);
5221
+ const githubMatch = specifier.match(/^@github\/([^/]+)\/([^/]+)\/([^/]+)$/);
5222
+ if (!userMatch && !githubMatch) {
4949
5223
  throw new Error(`Invalid registry specifier: ${specifier}`);
4950
5224
  }
4951
- const [, username, name] = match;
5225
+ const isGithubRegistry = !!githubMatch;
5226
+ const username = userMatch ? userMatch[1] : "";
5227
+ const name = userMatch ? userMatch[2] : "";
4952
5228
  try {
4953
- const versions = await fetchRegistryVersions(username, name);
5229
+ const versions = isGithubRegistry && githubMatch ? await fetchGithubRegistryVersions(
5230
+ githubMatch[1],
5231
+ githubMatch[2],
5232
+ githubMatch[3]
5233
+ ) : await fetchRegistryVersions(username, name);
4954
5234
  const versionStrings = versions.map((v) => v.version);
4955
5235
  const range = versionRange || "*";
4956
5236
  const wanted = resolveVersion3(range, versionStrings);
@@ -5176,7 +5456,7 @@ init_errors();
5176
5456
  init_lib();
5177
5457
  var exec = promisify(exec$1);
5178
5458
  function confirm(question) {
5179
- return new Promise((resolve2) => {
5459
+ return new Promise((resolve3) => {
5180
5460
  const rl = createInterface({
5181
5461
  input: process.stdin,
5182
5462
  output: process.stdout
@@ -5184,7 +5464,7 @@ function confirm(question) {
5184
5464
  rl.question(`${question} (y/N) `, (answer) => {
5185
5465
  rl.close();
5186
5466
  const normalized = answer.trim().toLowerCase();
5187
- resolve2(normalized === "y" || normalized === "yes");
5467
+ resolve3(normalized === "y" || normalized === "yes");
5188
5468
  });
5189
5469
  });
5190
5470
  }
@@ -5294,8 +5574,10 @@ async function publishCommand(options) {
5294
5574
  }
5295
5575
  console.log("");
5296
5576
  }
5577
+ const nameParts = manifest.name.split("/");
5578
+ const bareName = nameParts[nameParts.length - 1];
5297
5579
  const packageJson2 = {
5298
- name: manifest.name,
5580
+ name: bareName,
5299
5581
  version: manifest.version,
5300
5582
  description: manifest.description,
5301
5583
  files: manifest.files,
@@ -5500,8 +5782,8 @@ async function removeRegistry(specifier, agents, agentConfigs) {
5500
5782
  console.error(`Error: Invalid skill specifier: ${specifier}`);
5501
5783
  process.exit(1);
5502
5784
  }
5503
- const { namespace, owner, name } = parsed;
5504
- const fullName = `@${namespace}/${owner}/${name}`;
5785
+ const { namespace, owner, name, subname } = parsed;
5786
+ const fullName = namespace === "github" && subname ? `@github/${owner}/${name}/${subname}` : `@${namespace}/${owner}/${name}`;
5505
5787
  console.log(`Removing ${fullName}...`);
5506
5788
  const removedFromLockfile = await removeFromLockfile(fullName);
5507
5789
  const removedFromManifest = await removeDependency(fullName);
@@ -5509,13 +5791,21 @@ async function removeRegistry(specifier, agents, agentConfigs) {
5509
5791
  console.error(`Error: ${fullName} not found in lockfile or pspm.json`);
5510
5792
  process.exit(1);
5511
5793
  }
5512
- await removeAgentSymlinks(name, {
5794
+ const symlinkName = subname ?? name;
5795
+ await removeAgentSymlinks(symlinkName, {
5513
5796
  agents,
5514
5797
  projectRoot: process.cwd(),
5515
5798
  agentConfigs
5516
5799
  });
5517
5800
  const skillsDir = getSkillsDir();
5518
- const destDir = namespace === "org" ? join(skillsDir, "_org", owner, name) : join(skillsDir, owner, name);
5801
+ let destDir;
5802
+ if (namespace === "github" && subname) {
5803
+ destDir = join(skillsDir, "_github-registry", owner, name, subname);
5804
+ } else if (namespace === "org") {
5805
+ destDir = join(skillsDir, "_org", owner, name);
5806
+ } else {
5807
+ destDir = join(skillsDir, owner, name);
5808
+ }
5519
5809
  try {
5520
5810
  await rm(destDir, { recursive: true, force: true });
5521
5811
  } catch {
@@ -5555,7 +5845,9 @@ async function removeByShortName(shortName, agents, agentConfigs) {
5555
5845
  const registrySkills = await listLockfileSkills();
5556
5846
  const foundRegistry = registrySkills.find((s) => {
5557
5847
  const parsed = parseRegistrySpecifier2(s.name);
5558
- return parsed && parsed.name === shortName;
5848
+ if (!parsed) return false;
5849
+ const effectiveName = parsed.subname ?? parsed.name;
5850
+ return effectiveName === shortName;
5559
5851
  });
5560
5852
  if (foundRegistry) {
5561
5853
  await removeRegistry(foundRegistry.name, agents, agentConfigs);
@@ -5657,6 +5949,396 @@ function formatDownloads(count) {
5657
5949
  return String(count);
5658
5950
  }
5659
5951
 
5952
+ // src/commands/skill-list.ts
5953
+ init_api_client();
5954
+ init_config();
5955
+ function parseListSpecifier(specifier) {
5956
+ const match = specifier.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
5957
+ if (!match) return null;
5958
+ return {
5959
+ ownerType: match[1],
5960
+ ownerName: match[2],
5961
+ listName: match[3]
5962
+ };
5963
+ }
5964
+ function extractErrorMessage(text) {
5965
+ try {
5966
+ const json = JSON.parse(text);
5967
+ if (typeof json.message === "string") return json.message;
5968
+ if (typeof json.error === "string") return json.error;
5969
+ if (typeof json.error?.message === "string") return json.error.message;
5970
+ } catch {
5971
+ }
5972
+ return text;
5973
+ }
5974
+ async function getBaseUrl() {
5975
+ const config2 = await resolveConfig();
5976
+ return config2.registryUrl.replace(/\/api\/skills\/?$/, "");
5977
+ }
5978
+ async function getAuthHeaders() {
5979
+ const apiKey = await requireApiKey();
5980
+ return {
5981
+ "Content-Type": "application/json",
5982
+ Authorization: `Bearer ${apiKey}`
5983
+ };
5984
+ }
5985
+ async function getOptionalAuthHeaders() {
5986
+ const config2 = await resolveConfig();
5987
+ const apiKey = getTokenForRegistry(config2, config2.registryUrl);
5988
+ const headers = {
5989
+ "Content-Type": "application/json"
5990
+ };
5991
+ if (apiKey) {
5992
+ headers.Authorization = `Bearer ${apiKey}`;
5993
+ }
5994
+ return headers;
5995
+ }
5996
+ async function skillListList(options) {
5997
+ const baseUrl = await getBaseUrl();
5998
+ const config2 = await resolveConfig();
5999
+ let path;
6000
+ if (options.org) {
6001
+ path = `/api/skill-lists/lists/@org/${options.org}`;
6002
+ } else {
6003
+ if (!config2.username) {
6004
+ console.error("Error: Not logged in. Run `pspm login` first.");
6005
+ process.exit(1);
6006
+ }
6007
+ path = `/api/skill-lists/lists/@user/${config2.username}`;
6008
+ }
6009
+ const headers = await getOptionalAuthHeaders();
6010
+ const response = await fetch(`${baseUrl}${path}`, { headers });
6011
+ if (!response.ok) {
6012
+ const errorText = await response.text();
6013
+ console.error(
6014
+ `Error: Failed to list skill lists (${response.status}): ${extractErrorMessage(errorText)}`
6015
+ );
6016
+ process.exit(1);
6017
+ }
6018
+ const lists = await response.json();
6019
+ if (options.json) {
6020
+ console.log(JSON.stringify(lists, null, 2));
6021
+ return;
6022
+ }
6023
+ if (lists.length === 0) {
6024
+ console.log("No skill lists found.");
6025
+ return;
6026
+ }
6027
+ const ownerLabel = options.org ? `@org/${options.org}` : `@user/${config2.username}`;
6028
+ console.log(`
6029
+ Skill lists for ${ownerLabel}:
6030
+ `);
6031
+ console.log(
6032
+ `${"Name".padEnd(35)} ${"Skills".padEnd(8)} ${"Visibility".padEnd(12)} Description`
6033
+ );
6034
+ console.log("-".repeat(90));
6035
+ for (const list2 of lists) {
6036
+ const desc = list2.description ? list2.description.slice(0, 30) : "";
6037
+ console.log(
6038
+ `${list2.name.slice(0, 33).padEnd(35)} ${String(list2.itemCount).padEnd(8)} ${list2.visibility.padEnd(12)} ${desc}`
6039
+ );
6040
+ }
6041
+ console.log(`
6042
+ ${lists.length} list(s)`);
6043
+ }
6044
+ async function skillListCreate(name, options) {
6045
+ const baseUrl = await getBaseUrl();
6046
+ const headers = await getAuthHeaders();
6047
+ const config2 = await resolveConfig();
6048
+ const visibility = options.visibility || "private";
6049
+ if (visibility !== "public" && visibility !== "private") {
6050
+ console.error('Error: --visibility must be "public" or "private"');
6051
+ process.exit(1);
6052
+ }
6053
+ let path;
6054
+ if (options.org) {
6055
+ path = `/api/skill-lists/lists/@org/${options.org}`;
6056
+ } else {
6057
+ if (!config2.username) {
6058
+ console.error("Error: Not logged in. Run `pspm login` first.");
6059
+ process.exit(1);
6060
+ }
6061
+ path = `/api/skill-lists/lists/@user/${config2.username}`;
6062
+ }
6063
+ const body = { name, visibility };
6064
+ if (options.description) {
6065
+ body.description = options.description;
6066
+ }
6067
+ const response = await fetch(`${baseUrl}${path}`, {
6068
+ method: "POST",
6069
+ headers,
6070
+ body: JSON.stringify(body)
6071
+ });
6072
+ if (!response.ok) {
6073
+ const errorText = await response.text();
6074
+ console.error(
6075
+ `Error: Failed to create list (${response.status}): ${extractErrorMessage(errorText)}`
6076
+ );
6077
+ process.exit(1);
6078
+ }
6079
+ const list2 = await response.json();
6080
+ const ownerLabel = options.org ? `@org/${options.org}` : `@user/${config2.username}`;
6081
+ console.log(`Created list: ${ownerLabel}/${list2.name} (${list2.visibility})`);
6082
+ console.log(
6083
+ `
6084
+ Install command: pspm skill-list install ${ownerLabel}/${list2.name}`
6085
+ );
6086
+ }
6087
+ async function skillListShow(specifier, options) {
6088
+ const parsed = parseListSpecifier(specifier);
6089
+ if (!parsed) {
6090
+ console.error(
6091
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6092
+ );
6093
+ process.exit(1);
6094
+ }
6095
+ const config2 = await resolveConfig();
6096
+ configure2({
6097
+ registryUrl: config2.registryUrl,
6098
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
6099
+ });
6100
+ const response = await fetchSkillList(
6101
+ parsed.ownerType,
6102
+ parsed.ownerName,
6103
+ parsed.listName
6104
+ );
6105
+ if (response.status !== 200 || !response.data) {
6106
+ const errorMsg = response.status === 404 ? `List "${specifier}" not found or is private.` : response.error || "Failed to fetch list";
6107
+ console.error(`Error: ${errorMsg}`);
6108
+ process.exit(1);
6109
+ }
6110
+ const list2 = response.data;
6111
+ if (options.json) {
6112
+ console.log(JSON.stringify(list2, null, 2));
6113
+ return;
6114
+ }
6115
+ console.log(`
6116
+ ${list2.name}`);
6117
+ if (list2.description) {
6118
+ console.log(` ${list2.description}`);
6119
+ }
6120
+ console.log(` Visibility: ${list2.visibility}`);
6121
+ console.log(` Owner: @${list2.ownerType}/${list2.ownerName}`);
6122
+ console.log(` Skills: ${list2.items.length}`);
6123
+ if (list2.items.length > 0) {
6124
+ console.log("");
6125
+ for (const item of list2.items) {
6126
+ const ns = item.namespace === "org" ? "org" : "user";
6127
+ const spec = `@${ns}/${item.ownerName}/${item.skillName}`;
6128
+ const ver = item.pinnedVersion ? `@${item.pinnedVersion}` : "";
6129
+ console.log(` - ${spec}${ver}`);
6130
+ }
6131
+ }
6132
+ console.log(`
6133
+ Install all: pspm skill-list install ${specifier}`);
6134
+ }
6135
+ async function skillListDelete(specifier) {
6136
+ const parsed = parseListSpecifier(specifier);
6137
+ if (!parsed) {
6138
+ console.error(
6139
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6140
+ );
6141
+ process.exit(1);
6142
+ }
6143
+ const baseUrl = await getBaseUrl();
6144
+ const headers = await getAuthHeaders();
6145
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}`;
6146
+ const response = await fetch(`${baseUrl}${path}`, {
6147
+ method: "DELETE",
6148
+ headers
6149
+ });
6150
+ if (!response.ok) {
6151
+ const errorText = await response.text();
6152
+ if (response.status === 404) {
6153
+ console.error(`Error: List "${specifier}" not found.`);
6154
+ } else {
6155
+ console.error(
6156
+ `Error: Failed to delete list (${response.status}): ${extractErrorMessage(errorText)}`
6157
+ );
6158
+ }
6159
+ process.exit(1);
6160
+ }
6161
+ console.log(`Deleted list: ${specifier}`);
6162
+ }
6163
+ async function skillListUpdate(specifier, options) {
6164
+ const parsed = parseListSpecifier(specifier);
6165
+ if (!parsed) {
6166
+ console.error(
6167
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6168
+ );
6169
+ process.exit(1);
6170
+ }
6171
+ if (!options.description && !options.visibility) {
6172
+ console.error(
6173
+ "Error: Provide at least one of --description or --visibility"
6174
+ );
6175
+ process.exit(1);
6176
+ }
6177
+ if (options.visibility && options.visibility !== "public" && options.visibility !== "private") {
6178
+ console.error('Error: --visibility must be "public" or "private"');
6179
+ process.exit(1);
6180
+ }
6181
+ const baseUrl = await getBaseUrl();
6182
+ const headers = await getAuthHeaders();
6183
+ const body = {};
6184
+ if (options.description !== void 0) {
6185
+ body.description = options.description;
6186
+ }
6187
+ if (options.visibility) {
6188
+ body.visibility = options.visibility;
6189
+ }
6190
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}`;
6191
+ const response = await fetch(`${baseUrl}${path}`, {
6192
+ method: "PUT",
6193
+ headers,
6194
+ body: JSON.stringify(body)
6195
+ });
6196
+ if (!response.ok) {
6197
+ const errorText = await response.text();
6198
+ console.error(
6199
+ `Error: Failed to update list (${response.status}): ${extractErrorMessage(errorText)}`
6200
+ );
6201
+ process.exit(1);
6202
+ }
6203
+ console.log(`Updated list: ${specifier}`);
6204
+ }
6205
+ async function skillListAddSkill(specifier, skillSpecifiers, options) {
6206
+ const parsed = parseListSpecifier(specifier);
6207
+ if (!parsed) {
6208
+ console.error(
6209
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6210
+ );
6211
+ process.exit(1);
6212
+ }
6213
+ const baseUrl = await getBaseUrl();
6214
+ const headers = await getAuthHeaders();
6215
+ const config2 = await resolveConfig();
6216
+ configure2({
6217
+ registryUrl: config2.registryUrl,
6218
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
6219
+ });
6220
+ for (const skillSpec of skillSpecifiers) {
6221
+ const skillId = await resolveSkillId(baseUrl, headers, skillSpec);
6222
+ if (!skillId) {
6223
+ console.error(`Error: Skill "${skillSpec}" not found.`);
6224
+ continue;
6225
+ }
6226
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}/items`;
6227
+ const addBody = { skillId };
6228
+ if (options.note) {
6229
+ addBody.note = options.note;
6230
+ }
6231
+ const response = await fetch(`${baseUrl}${path}`, {
6232
+ method: "POST",
6233
+ headers,
6234
+ body: JSON.stringify(addBody)
6235
+ });
6236
+ if (!response.ok) {
6237
+ const errorText = await response.text();
6238
+ if (response.status === 409) {
6239
+ console.log(`Already in list: ${skillSpec}`);
6240
+ } else {
6241
+ console.error(
6242
+ `Error: Failed to add "${skillSpec}" (${response.status}): ${extractErrorMessage(errorText)}`
6243
+ );
6244
+ }
6245
+ continue;
6246
+ }
6247
+ console.log(`Added: ${skillSpec}`);
6248
+ }
6249
+ }
6250
+ async function skillListRemoveSkill(specifier, skillSpecifier) {
6251
+ const parsed = parseListSpecifier(specifier);
6252
+ if (!parsed) {
6253
+ console.error(
6254
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6255
+ );
6256
+ process.exit(1);
6257
+ }
6258
+ const config2 = await resolveConfig();
6259
+ configure2({
6260
+ registryUrl: config2.registryUrl,
6261
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
6262
+ });
6263
+ const listResponse = await fetchSkillList(
6264
+ parsed.ownerType,
6265
+ parsed.ownerName,
6266
+ parsed.listName
6267
+ );
6268
+ if (listResponse.status !== 200 || !listResponse.data) {
6269
+ console.error(`Error: List "${specifier}" not found.`);
6270
+ process.exit(1);
6271
+ }
6272
+ const item = listResponse.data.items.find((i) => {
6273
+ const ns = i.namespace === "org" ? "org" : "user";
6274
+ const fullSpec = `@${ns}/${i.ownerName}/${i.skillName}`;
6275
+ return fullSpec === skillSpecifier || i.skillName === skillSpecifier || `${i.ownerName}/${i.skillName}` === skillSpecifier;
6276
+ });
6277
+ if (!item) {
6278
+ console.error(
6279
+ `Error: Skill "${skillSpecifier}" not found in list "${specifier}".`
6280
+ );
6281
+ process.exit(1);
6282
+ }
6283
+ const baseUrl = await getBaseUrl();
6284
+ const headers = await getAuthHeaders();
6285
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}/items/${item.id}`;
6286
+ const response = await fetch(`${baseUrl}${path}`, {
6287
+ method: "DELETE",
6288
+ headers
6289
+ });
6290
+ if (!response.ok) {
6291
+ const errorText = await response.text();
6292
+ console.error(
6293
+ `Error: Failed to remove skill (${response.status}): ${extractErrorMessage(errorText)}`
6294
+ );
6295
+ process.exit(1);
6296
+ }
6297
+ console.log(`Removed: ${skillSpecifier} from ${specifier}`);
6298
+ }
6299
+ async function skillListInstall(specifier, options) {
6300
+ const { install: install2 } = await Promise.resolve().then(() => (init_install(), install_exports));
6301
+ await install2([], {
6302
+ list: specifier,
6303
+ agent: options.agent,
6304
+ yes: options.yes,
6305
+ global: options.global,
6306
+ dir: options.dir
6307
+ });
6308
+ }
6309
+ async function resolveSkillId(baseUrl, headers, specifier) {
6310
+ const match = specifier.match(/^@(user|org|github)\/(.+)$/);
6311
+ let search2;
6312
+ let namespace;
6313
+ if (match) {
6314
+ namespace = match[1];
6315
+ const parts = match[2].split("/");
6316
+ search2 = parts[parts.length - 1];
6317
+ } else {
6318
+ search2 = specifier;
6319
+ }
6320
+ const params = new URLSearchParams({ search: search2, limit: "5" });
6321
+ if (namespace) {
6322
+ params.set("namespace", namespace);
6323
+ }
6324
+ const response = await fetch(`${baseUrl}/api/skills/-/explore?${params}`, {
6325
+ headers
6326
+ });
6327
+ if (!response.ok) return null;
6328
+ const data = await response.json();
6329
+ if (!data.skills || data.skills.length === 0) return null;
6330
+ if (match) {
6331
+ const parts = match[2].split("/");
6332
+ const skillName = parts[parts.length - 1];
6333
+ const ownerName = parts.length >= 2 ? parts[parts.length - 2] : void 0;
6334
+ const exact = data.skills.find(
6335
+ (s) => s.name === skillName && (!ownerName || s.username === ownerName) && (!namespace || s.namespace === namespace)
6336
+ );
6337
+ if (exact) return exact.id;
6338
+ }
6339
+ return data.skills[0].id;
6340
+ }
6341
+
5660
6342
  // src/commands/unpublish.ts
5661
6343
  init_api_client();
5662
6344
  init_config();
@@ -6100,11 +6782,15 @@ program.command("install [specifiers...]").alias("i").description(
6100
6782
  ).option("--frozen-lockfile", "Fail if lockfile is missing or outdated").option("--dir <path>", "Install skills to a specific directory").option(
6101
6783
  "--agent <agents>",
6102
6784
  'Comma-separated agents for symlinks (default: all agents, use "none" to skip)'
6785
+ ).option(
6786
+ "--list <specifier>",
6787
+ "Install all skills from a skill list (e.g. @user/username/list-name)"
6103
6788
  ).option("-g, --global", "Install to user home directory instead of project").option("-y, --yes", "Skip agent selection prompt and use defaults").action(async (specifiers, options) => {
6104
6789
  await install(specifiers, {
6105
6790
  frozenLockfile: options.frozenLockfile,
6106
6791
  dir: options.dir,
6107
6792
  agent: options.agent,
6793
+ list: options.list,
6108
6794
  yes: options.yes,
6109
6795
  global: options.global
6110
6796
  });
@@ -6185,6 +6871,73 @@ program.command("deprecate <specifier> [message]").description(
6185
6871
  ).option("--undo", "Remove deprecation status").action(async (specifier, message, options) => {
6186
6872
  await deprecate(specifier, message, { undo: options.undo });
6187
6873
  });
6874
+ var skillListCmd = program.command("skill-list").description("Manage skill lists (collections of skills)");
6875
+ skillListCmd.command("list").description("List your skill lists").option("--org <orgname>", "List organization skill lists").option("--json", "Output as JSON").action(async (options) => {
6876
+ await skillListList({ org: options.org, json: options.json });
6877
+ });
6878
+ skillListCmd.command("create <name>").description("Create a new skill list").option("-d, --description <description>", "List description").option(
6879
+ "--visibility <visibility>",
6880
+ "Visibility: private or public",
6881
+ "private"
6882
+ ).option("--org <orgname>", "Create under an organization").action(async (name, options) => {
6883
+ await skillListCreate(name, {
6884
+ description: options.description,
6885
+ visibility: options.visibility,
6886
+ org: options.org
6887
+ });
6888
+ });
6889
+ skillListCmd.command("show <specifier>").description("Show skill list details (e.g. @user/alice/my-tools)").option("--json", "Output as JSON").action(async (specifier, options) => {
6890
+ await skillListShow(specifier, { json: options.json });
6891
+ });
6892
+ skillListCmd.command("delete <specifier>").description("Delete a skill list").action(async (specifier) => {
6893
+ await skillListDelete(specifier);
6894
+ });
6895
+ skillListCmd.command("update <specifier>").description("Update skill list metadata").option("-d, --description <description>", "New description").option("--visibility <visibility>", "New visibility: private or public").action(async (specifier, options) => {
6896
+ await skillListUpdate(specifier, {
6897
+ description: options.description,
6898
+ visibility: options.visibility
6899
+ });
6900
+ });
6901
+ skillListCmd.command("add-skill <specifier> <skills...>").description(
6902
+ "Add skills to a list (e.g. pspm skill-list add-skill @user/me/my-list @user/alice/tool)"
6903
+ ).option("--note <note>", "Note for the added skill").action(async (specifier, skills, options) => {
6904
+ await skillListAddSkill(specifier, skills, {
6905
+ note: options.note
6906
+ });
6907
+ });
6908
+ skillListCmd.command("remove-skill <specifier> <skill>").description("Remove a skill from a list").action(async (specifier, skill) => {
6909
+ await skillListRemoveSkill(specifier, skill);
6910
+ });
6911
+ skillListCmd.command("install <specifier>").description(
6912
+ "Install all skills from a list (e.g. pspm skill-list install @user/alice/my-tools)"
6913
+ ).option(
6914
+ "--agent <agents>",
6915
+ 'Comma-separated agents for symlinks (default: all agents, use "none" to skip)'
6916
+ ).option("--dir <path>", "Install skills to a specific directory").option("-g, --global", "Install to user home directory instead of project").option("-y, --yes", "Skip agent selection prompt and use defaults").action(async (specifier, options) => {
6917
+ await skillListInstall(specifier, {
6918
+ agent: options.agent,
6919
+ yes: options.yes,
6920
+ global: options.global,
6921
+ dir: options.dir
6922
+ });
6923
+ });
6924
+ var notebookCmd = program.command("notebook").description("Manage AnyT notebooks");
6925
+ notebookCmd.command("upload <file>").description("Upload a .anyt notebook").option("--org <orgname>", "Upload to an organization").option(
6926
+ "--visibility <visibility>",
6927
+ "Visibility: private, team, or public",
6928
+ "private"
6929
+ ).option("--description <description>", "Notebook description").action(async (file, options) => {
6930
+ await notebookUpload(file, options);
6931
+ });
6932
+ notebookCmd.command("list").description("List notebooks").option("--org <orgname>", "List organization notebooks").action(async (options) => {
6933
+ await notebookList(options);
6934
+ });
6935
+ notebookCmd.command("download <id>").description("Download a notebook by ID").option("-o, --output <path>", "Output file path").action(async (id, options) => {
6936
+ await notebookDownload(id, options.output);
6937
+ });
6938
+ notebookCmd.command("delete <id>").description("Delete a notebook by ID").action(async (id) => {
6939
+ await notebookDelete(id);
6940
+ });
6188
6941
  await program.parseAsync();
6189
6942
  var executedCommand = program.args[0];
6190
6943
  if (executedCommand !== "upgrade") {