@anytio/pspm 0.11.0 → 0.13.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
@@ -3,12 +3,12 @@ import { stat, writeFile, readdir, mkdir, rm, rename, access as access$1, readFi
3
3
  import { homedir } from 'os';
4
4
  import { join, dirname, basename, resolve, relative } from 'path';
5
5
  import * as ini from 'ini';
6
+ import { createHash, randomBytes, createCipheriv, createDecipheriv, scryptSync } from 'crypto';
6
7
  import ignore from 'ignore';
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';
@@ -603,6 +603,8 @@ __export(config_exports, {
603
603
  findProjectConfig: () => findProjectConfig,
604
604
  getCacheDir: () => getCacheDir,
605
605
  getConfigPath: () => getConfigPath,
606
+ getEncryptionKey: () => getEncryptionKey,
607
+ getEncryptionScope: () => getEncryptionScope,
606
608
  getLegacyLockfilePath: () => getLegacyLockfilePath,
607
609
  getLegacySkillsDir: () => getLegacySkillsDir,
608
610
  getLockfilePath: () => getLockfilePath,
@@ -613,9 +615,11 @@ __export(config_exports, {
613
615
  isGlobalMode: () => isGlobalMode,
614
616
  isLoggedIn: () => isLoggedIn,
615
617
  readUserConfig: () => readUserConfig,
618
+ removeEncryptionKey: () => removeEncryptionKey,
616
619
  requireApiKey: () => requireApiKey,
617
620
  resolveConfig: () => resolveConfig,
618
621
  setCredentials: () => setCredentials,
622
+ setEncryptionKey: () => setEncryptionKey,
619
623
  setGlobalMode: () => setGlobalMode,
620
624
  writeUserConfig: () => writeUserConfig
621
625
  });
@@ -685,12 +689,21 @@ async function readUserConfig() {
685
689
  }
686
690
  }
687
691
  }
692
+ const encryptionKeys = {};
693
+ for (const key of Object.keys(parsed)) {
694
+ const encKeyMatch = key.match(/^encryption-key:(.+)$/);
695
+ if (encKeyMatch) {
696
+ const scope = encKeyMatch[1];
697
+ encryptionKeys[scope] = parsed[key];
698
+ }
699
+ }
688
700
  return {
689
701
  registry: parsed.registry,
690
702
  authToken: parsed.authToken,
691
703
  username: parsed.username,
692
704
  scopedRegistries: Object.keys(scopedRegistries).length > 0 ? scopedRegistries : void 0,
693
- registryTokens: Object.keys(registryTokens).length > 0 ? registryTokens : void 0
705
+ registryTokens: Object.keys(registryTokens).length > 0 ? registryTokens : void 0,
706
+ encryptionKeys: Object.keys(encryptionKeys).length > 0 ? encryptionKeys : void 0
694
707
  };
695
708
  } catch (error) {
696
709
  if (process.env.PSPM_DEBUG) {
@@ -713,6 +726,12 @@ async function writeUserConfig(config2) {
713
726
  if (config2.username) {
714
727
  lines.push(`username = ${config2.username}`);
715
728
  }
729
+ if (config2.encryptionKeys && Object.keys(config2.encryptionKeys).length > 0) {
730
+ lines.push("; Encryption keys (scope -> passphrase)");
731
+ for (const [scope, key] of Object.entries(config2.encryptionKeys)) {
732
+ lines.push(`encryption-key:${scope} = ${key}`);
733
+ }
734
+ }
716
735
  lines.push("");
717
736
  await mkdir(dirname(configPath), { recursive: true });
718
737
  await writeFile(configPath, lines.join("\n"));
@@ -896,6 +915,36 @@ async function getRegistryUrl() {
896
915
  const resolved = await resolveConfig();
897
916
  return resolved.registryUrl;
898
917
  }
918
+ async function getEncryptionKey(scope) {
919
+ const envSuffix = scope.replace(/^@/, "").replace(/\//g, "_").toUpperCase();
920
+ const envKey = process.env[`PSPM_ENCRYPTION_KEY_${envSuffix}`];
921
+ if (envKey) {
922
+ return envKey;
923
+ }
924
+ const config2 = await readUserConfig();
925
+ return config2.encryptionKeys?.[scope];
926
+ }
927
+ async function setEncryptionKey(scope, key) {
928
+ const config2 = await readUserConfig();
929
+ if (!config2.encryptionKeys) {
930
+ config2.encryptionKeys = {};
931
+ }
932
+ config2.encryptionKeys[scope] = key;
933
+ await writeUserConfig(config2);
934
+ }
935
+ async function removeEncryptionKey(scope) {
936
+ const config2 = await readUserConfig();
937
+ if (config2.encryptionKeys) {
938
+ delete config2.encryptionKeys[scope];
939
+ if (Object.keys(config2.encryptionKeys).length === 0) {
940
+ config2.encryptionKeys = void 0;
941
+ }
942
+ }
943
+ await writeUserConfig(config2);
944
+ }
945
+ function getEncryptionScope(namespace, owner) {
946
+ return `@${namespace}/${owner}`;
947
+ }
899
948
  var DEFAULT_REGISTRY_URL, _globalMode;
900
949
  var init_config = __esm({
901
950
  "src/config.ts"() {
@@ -904,6 +953,62 @@ var init_config = __esm({
904
953
  _globalMode = false;
905
954
  }
906
955
  });
956
+ function deriveKey(passphrase, salt) {
957
+ return scryptSync(passphrase, salt, KEY_LENGTH, {
958
+ N: SCRYPT_COST,
959
+ r: SCRYPT_BLOCK_SIZE,
960
+ p: SCRYPT_PARALLELISM
961
+ });
962
+ }
963
+ function encryptBuffer(data, passphrase, scope) {
964
+ const salt = randomBytes(SALT_LENGTH);
965
+ const iv = randomBytes(IV_LENGTH);
966
+ const key = deriveKey(passphrase, salt);
967
+ const cipher = createCipheriv(ALGORITHM, key, iv, {
968
+ authTagLength: AUTH_TAG_LENGTH
969
+ });
970
+ const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
971
+ const authTag = cipher.getAuthTag();
972
+ return {
973
+ encrypted,
974
+ metadata: {
975
+ algorithm: ALGORITHM,
976
+ kdf: "scrypt",
977
+ salt: salt.toString("hex"),
978
+ iv: iv.toString("hex"),
979
+ authTag: authTag.toString("hex"),
980
+ scope
981
+ }
982
+ };
983
+ }
984
+ function decryptBuffer(encrypted, passphrase, metadata) {
985
+ const salt = Buffer.from(metadata.salt, "hex");
986
+ const iv = Buffer.from(metadata.iv, "hex");
987
+ const authTag = Buffer.from(metadata.authTag, "hex");
988
+ const key = deriveKey(passphrase, salt);
989
+ const decipher = createDecipheriv(ALGORITHM, key, iv, {
990
+ authTagLength: AUTH_TAG_LENGTH
991
+ });
992
+ decipher.setAuthTag(authTag);
993
+ const decrypted = Buffer.concat([
994
+ decipher.update(encrypted),
995
+ decipher.final()
996
+ ]);
997
+ return decrypted;
998
+ }
999
+ var ALGORITHM, KEY_LENGTH, IV_LENGTH, SALT_LENGTH, SCRYPT_COST, SCRYPT_BLOCK_SIZE, SCRYPT_PARALLELISM, AUTH_TAG_LENGTH;
1000
+ var init_encryption = __esm({
1001
+ "src/lib/encryption.ts"() {
1002
+ ALGORITHM = "aes-256-gcm";
1003
+ KEY_LENGTH = 32;
1004
+ IV_LENGTH = 16;
1005
+ SALT_LENGTH = 32;
1006
+ SCRYPT_COST = 16384;
1007
+ SCRYPT_BLOCK_SIZE = 8;
1008
+ SCRYPT_PARALLELISM = 1;
1009
+ AUTH_TAG_LENGTH = 16;
1010
+ }
1011
+ });
907
1012
  async function loadIgnorePatterns(cwd = process.cwd()) {
908
1013
  const ig = ignore();
909
1014
  ig.add(ALWAYS_IGNORED);
@@ -968,7 +1073,9 @@ function validateManifest(manifest) {
968
1073
  if (!manifest.version) {
969
1074
  return { valid: false, error: "Manifest must have a 'version' field" };
970
1075
  }
971
- if (!/^[a-z][a-z0-9_-]*$/.test(manifest.name)) {
1076
+ const parts = manifest.name.split("/");
1077
+ const bareName = parts[parts.length - 1];
1078
+ if (!/^[a-z][a-z0-9_-]*$/.test(bareName)) {
972
1079
  return {
973
1080
  valid: false,
974
1081
  error: "Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores"
@@ -1624,6 +1731,7 @@ var init_specifier2 = __esm({
1624
1731
  // src/lib/index.ts
1625
1732
  var init_lib = __esm({
1626
1733
  "src/lib/index.ts"() {
1734
+ init_encryption();
1627
1735
  init_ignore();
1628
1736
  init_integrity();
1629
1737
  init_lockfile();
@@ -3329,110 +3437,649 @@ var init_add = __esm({
3329
3437
  }
3330
3438
  });
3331
3439
 
3332
- // src/commands/access.ts
3333
- init_api_client();
3334
- init_config();
3335
- init_lib();
3336
- function isLocalSpecifier2(specifier) {
3337
- return specifier.startsWith("file:") || specifier.startsWith("./") || specifier.startsWith("../");
3440
+ // src/commands/install.ts
3441
+ var install_exports = {};
3442
+ __export(install_exports, {
3443
+ install: () => install
3444
+ });
3445
+ function getCacheFilePath(cacheDir, integrity) {
3446
+ const match = integrity.match(/^sha256-(.+)$/);
3447
+ if (!match) {
3448
+ throw new Error(`Invalid integrity format: ${integrity}`);
3449
+ }
3450
+ const base64Hash = match[1];
3451
+ const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
3452
+ return join(cacheDir, `sha256-${hexHash}.tgz`);
3338
3453
  }
3339
- async function access(specifier, options) {
3454
+ async function readFromCache(cacheDir, integrity) {
3340
3455
  try {
3341
- const apiKey = await requireApiKey();
3342
- const registryUrl = await getRegistryUrl();
3343
- if (options.public && options.private) {
3344
- console.error("Error: Cannot specify both --public and --private");
3345
- process.exit(1);
3456
+ const cachePath = getCacheFilePath(cacheDir, integrity);
3457
+ const data = await readFile(cachePath);
3458
+ const actualIntegrity = `sha256-${createHash("sha256").update(data).digest("base64")}`;
3459
+ if (actualIntegrity !== integrity) {
3460
+ await rm(cachePath, { force: true });
3461
+ return null;
3346
3462
  }
3347
- if (!options.public && !options.private) {
3348
- console.error("Error: Must specify either --public or --private");
3349
- process.exit(1);
3463
+ return data;
3464
+ } catch {
3465
+ return null;
3466
+ }
3467
+ }
3468
+ async function writeToCache(cacheDir, integrity, data) {
3469
+ try {
3470
+ await mkdir(cacheDir, { recursive: true });
3471
+ const cachePath = getCacheFilePath(cacheDir, integrity);
3472
+ await writeFile(cachePath, data);
3473
+ } catch {
3474
+ }
3475
+ }
3476
+ async function install(specifiers, options) {
3477
+ if (options.global) {
3478
+ const { setGlobalMode: setGlobalMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3479
+ setGlobalMode2(true);
3480
+ }
3481
+ if (options.list) {
3482
+ const listSpecifiers = await resolveListToSpecifiers(options.list);
3483
+ if (listSpecifiers.length === 0) {
3484
+ console.log("No skills in the list to install.");
3485
+ return;
3350
3486
  }
3351
- const visibility = options.public ? "public" : "private";
3352
- let packageName;
3353
- let packageUsername;
3354
- if (specifier) {
3355
- if (isGitHubSpecifier2(specifier)) {
3356
- const ghSpec = parseGitHubSpecifier2(specifier);
3357
- if (ghSpec) {
3358
- console.error(`Error: Cannot change visibility of GitHub packages.`);
3359
- console.error(
3360
- ` "${specifier}" is hosted on GitHub, not the PSPM registry.`
3361
- );
3362
- console.error(
3363
- ` Visibility can only be changed for packages published to the registry.`
3364
- );
3365
- } else {
3366
- console.error(`Error: Invalid GitHub specifier "${specifier}".`);
3367
- console.error(` Use format: github:{owner}/{repo}[/{path}][@{ref}]`);
3368
- }
3369
- process.exit(1);
3370
- }
3371
- if (isLocalSpecifier2(specifier)) {
3372
- console.error(`Error: Cannot change visibility of local packages.`);
3373
- console.error(
3374
- ` "${specifier}" is a local directory, not a registry package.`
3375
- );
3376
- console.error(
3377
- ` Visibility can only be changed for packages published to the registry.`
3378
- );
3379
- process.exit(1);
3487
+ const allSpecifiers = [...specifiers, ...listSpecifiers];
3488
+ const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
3489
+ await add2(allSpecifiers, {
3490
+ save: true,
3491
+ agent: options.agent,
3492
+ yes: options.yes,
3493
+ global: options.global
3494
+ });
3495
+ return;
3496
+ }
3497
+ if (specifiers.length > 0) {
3498
+ const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
3499
+ await add2(specifiers, {
3500
+ save: true,
3501
+ agent: options.agent,
3502
+ yes: options.yes,
3503
+ global: options.global
3504
+ });
3505
+ return;
3506
+ }
3507
+ await installFromLockfile(options);
3508
+ }
3509
+ async function resolveListToSpecifiers(listSpec) {
3510
+ const match = listSpec.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
3511
+ if (!match) {
3512
+ console.error(
3513
+ `Error: Invalid list specifier "${listSpec}". Use format: @user/{username}/{list-name} or @org/{orgname}/{list-name}`
3514
+ );
3515
+ process.exit(1);
3516
+ }
3517
+ const [, ownerType, ownerName, listName] = match;
3518
+ const config2 = await resolveConfig();
3519
+ configure2({
3520
+ registryUrl: config2.registryUrl,
3521
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
3522
+ });
3523
+ console.log(
3524
+ `Fetching skill list @${ownerType}/${ownerName}/${listName}...
3525
+ `
3526
+ );
3527
+ const { fetchSkillList: fetchSkillList2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
3528
+ const response = await fetchSkillList2(
3529
+ ownerType,
3530
+ ownerName,
3531
+ listName
3532
+ );
3533
+ if (response.status !== 200 || !response.data) {
3534
+ const errorMsg = response.status === 404 ? `List "@${ownerType}/${ownerName}/${listName}" not found or is private.` : response.error || "Failed to fetch list";
3535
+ console.error(`Error: ${errorMsg}`);
3536
+ process.exit(1);
3537
+ }
3538
+ const list2 = response.data;
3539
+ console.log(
3540
+ `List: ${list2.name}${list2.description ? ` \u2014 ${list2.description}` : ""}`
3541
+ );
3542
+ console.log(`Skills: ${list2.items.length}
3543
+ `);
3544
+ const specifiers = [];
3545
+ for (const item of list2.items) {
3546
+ const ns = item.namespace === "org" ? "org" : "user";
3547
+ let spec = `@${ns}/${item.ownerName}/${item.skillName}`;
3548
+ if (item.pinnedVersion) {
3549
+ spec += `@${item.pinnedVersion}`;
3550
+ }
3551
+ specifiers.push(spec);
3552
+ }
3553
+ return specifiers;
3554
+ }
3555
+ async function installFromLockfile(options) {
3556
+ try {
3557
+ const config2 = await resolveConfig();
3558
+ const registryUrl = config2.registryUrl;
3559
+ const apiKey = getTokenForRegistry(config2, registryUrl);
3560
+ const skillsDir = options.dir || getSkillsDir();
3561
+ const cacheDir = getCacheDir();
3562
+ await migrateLockfileIfNeeded();
3563
+ let lockfile = await readLockfile();
3564
+ const manifestDeps = await getDependencies();
3565
+ const manifestGitHubDeps = await getGitHubDependencies();
3566
+ const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
3567
+ const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
3568
+ const installedSkills = [];
3569
+ const missingDeps = [];
3570
+ for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
3571
+ if (!lockfilePackages[fullName]) {
3572
+ missingDeps.push({ fullName, versionRange });
3380
3573
  }
3381
- const parsed = parseRegistrySpecifier2(specifier);
3382
- if (!parsed) {
3383
- console.error(`Error: Invalid package specifier "${specifier}".`);
3384
- console.error(
3385
- ` Use format: @user/{username}/{name} or @org/{orgname}/{name}`
3386
- );
3387
- console.error(``);
3388
- console.error(` Examples:`);
3389
- console.error(` pspm access @user/myname/my-skill --public`);
3574
+ }
3575
+ if (missingDeps.length > 0) {
3576
+ if (options.frozenLockfile) {
3390
3577
  console.error(
3391
- ` pspm access --public (uses current directory's pspm.json)`
3392
- );
3393
- process.exit(1);
3394
- }
3395
- packageName = parsed.name;
3396
- packageUsername = parsed.owner;
3397
- } else {
3398
- const { readFile: readFile10 } = await import('fs/promises');
3399
- const { join: join18 } = await import('path');
3400
- let manifest = null;
3401
- try {
3402
- const content = await readFile10(
3403
- join18(process.cwd(), "pspm.json"),
3404
- "utf-8"
3578
+ "Error: Dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
3405
3579
  );
3406
- manifest = JSON.parse(content);
3407
- } catch {
3408
- try {
3409
- const content = await readFile10(
3410
- join18(process.cwd(), "package.json"),
3411
- "utf-8"
3412
- );
3413
- manifest = JSON.parse(content);
3414
- } catch {
3415
- console.error(
3416
- "Error: No pspm.json or package.json found in current directory"
3417
- );
3418
- console.error(
3419
- "Either run this command in a package directory or specify a package name"
3420
- );
3421
- process.exit(1);
3580
+ console.error("Missing dependencies:");
3581
+ for (const dep of missingDeps) {
3582
+ console.error(` - ${dep.fullName}@${dep.versionRange}`);
3422
3583
  }
3423
- }
3424
- if (!manifest?.name) {
3425
- console.error("Error: Package manifest is missing 'name' field");
3426
3584
  process.exit(1);
3427
3585
  }
3428
- packageName = manifest.name;
3429
- }
3430
- if (!packageUsername) {
3431
- const config2 = await resolveConfig();
3432
- packageUsername = config2.username;
3433
- }
3434
- if (!packageUsername) {
3435
- console.error(
3586
+ console.log(`Resolving ${missingDeps.length} new dependency(ies)...
3587
+ `);
3588
+ configure2({ registryUrl, apiKey });
3589
+ for (const { fullName, versionRange } of missingDeps) {
3590
+ const parsed = parseRegistrySpecifier2(fullName);
3591
+ if (!parsed) {
3592
+ console.error(`Error: Invalid dependency specifier: ${fullName}`);
3593
+ continue;
3594
+ }
3595
+ const { owner, name } = parsed;
3596
+ console.log(`Resolving ${fullName}@${versionRange}...`);
3597
+ const versionsResponse = await listSkillVersions(owner, name);
3598
+ if (versionsResponse.status !== 200) {
3599
+ const errorMessage = extractApiErrorMessage(
3600
+ versionsResponse,
3601
+ `Skill ${fullName} not found`
3602
+ );
3603
+ console.error(`Error: ${errorMessage}`);
3604
+ continue;
3605
+ }
3606
+ const versions = versionsResponse.data;
3607
+ if (versions.length === 0) {
3608
+ console.error(`Error: Skill ${fullName} not found`);
3609
+ continue;
3610
+ }
3611
+ const versionStrings = versions.map(
3612
+ (v) => v.version
3613
+ );
3614
+ const resolved = resolveVersion2(versionRange || "*", versionStrings);
3615
+ if (!resolved) {
3616
+ console.error(
3617
+ `Error: No version matching "${versionRange}" for ${fullName}`
3618
+ );
3619
+ continue;
3620
+ }
3621
+ const versionResponse = await getSkillVersion(owner, name, resolved);
3622
+ if (versionResponse.status !== 200 || !versionResponse.data) {
3623
+ const errorMessage = extractApiErrorMessage(
3624
+ versionResponse,
3625
+ `Version ${resolved} not found`
3626
+ );
3627
+ console.error(`Error: ${errorMessage}`);
3628
+ continue;
3629
+ }
3630
+ const versionInfo = versionResponse.data;
3631
+ const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
3632
+ const downloadHeaders = {};
3633
+ if (!isPresignedUrl && apiKey) {
3634
+ downloadHeaders.Authorization = `Bearer ${apiKey}`;
3635
+ }
3636
+ const tarballResponse = await fetch(versionInfo.downloadUrl, {
3637
+ headers: downloadHeaders,
3638
+ redirect: "follow"
3639
+ });
3640
+ if (!tarballResponse.ok) {
3641
+ console.error(
3642
+ `Error: Failed to download tarball for ${fullName} (${tarballResponse.status})`
3643
+ );
3644
+ continue;
3645
+ }
3646
+ const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
3647
+ const integrity = calculateIntegrity(tarballBuffer);
3648
+ const lockfileEntry = {
3649
+ version: resolved,
3650
+ resolved: versionInfo.downloadUrl,
3651
+ integrity
3652
+ };
3653
+ if (versionInfo.manifest?.encryption) {
3654
+ lockfileEntry.encryption = versionInfo.manifest.encryption;
3655
+ }
3656
+ await addToLockfile(fullName, lockfileEntry);
3657
+ await writeToCache(cacheDir, integrity, tarballBuffer);
3658
+ console.log(` Resolved ${fullName}@${resolved}`);
3659
+ }
3660
+ lockfile = await readLockfile();
3661
+ }
3662
+ const missingGitHubDeps = [];
3663
+ for (const [specifier, ref] of Object.entries(manifestGitHubDeps)) {
3664
+ if (!lockfileGitHubPackages[specifier]) {
3665
+ missingGitHubDeps.push({ specifier, ref });
3666
+ }
3667
+ }
3668
+ if (missingGitHubDeps.length > 0) {
3669
+ if (options.frozenLockfile) {
3670
+ console.error(
3671
+ "Error: GitHub dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
3672
+ );
3673
+ console.error("Missing GitHub dependencies:");
3674
+ for (const dep of missingGitHubDeps) {
3675
+ console.error(` - ${dep.specifier}@${dep.ref}`);
3676
+ }
3677
+ process.exit(1);
3678
+ }
3679
+ console.log(
3680
+ `
3681
+ Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
3682
+ `
3683
+ );
3684
+ for (const { specifier, ref } of missingGitHubDeps) {
3685
+ const parsed = parseGitHubSpecifier2(specifier);
3686
+ if (!parsed) {
3687
+ console.error(`Error: Invalid GitHub specifier: ${specifier}`);
3688
+ continue;
3689
+ }
3690
+ parsed.ref = parsed.ref || ref;
3691
+ console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
3692
+ try {
3693
+ const result = await downloadGitHubPackage(parsed);
3694
+ await extractGitHubPackage(parsed, result.buffer, skillsDir);
3695
+ const entry = {
3696
+ version: result.commit.slice(0, 7),
3697
+ resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
3698
+ integrity: result.integrity,
3699
+ gitCommit: result.commit,
3700
+ gitRef: ref || "HEAD"
3701
+ };
3702
+ await addGitHubToLockfile(specifier, entry);
3703
+ await writeToCache(cacheDir, result.integrity, result.buffer);
3704
+ console.log(
3705
+ ` Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`
3706
+ );
3707
+ } catch (error) {
3708
+ if (error instanceof GitHubRateLimitError) {
3709
+ console.error(`Error: ${error.message}`);
3710
+ } else if (error instanceof GitHubPathNotFoundError) {
3711
+ console.error(`Error: ${error.message}`);
3712
+ } else if (error instanceof GitHubNotFoundError) {
3713
+ console.error(`Error: ${error.message}`);
3714
+ } else {
3715
+ const message = error instanceof Error ? error.message : String(error);
3716
+ console.error(`Error resolving ${specifier}: ${message}`);
3717
+ }
3718
+ }
3719
+ }
3720
+ lockfile = await readLockfile();
3721
+ }
3722
+ const manifest = await readManifest();
3723
+ const agentConfigs = manifest?.agents;
3724
+ let agents;
3725
+ if (options.agent) {
3726
+ agents = parseAgentArg(options.agent);
3727
+ } else if (manifest) {
3728
+ agents = parseAgentArg(void 0);
3729
+ } else if (options.yes) {
3730
+ agents = parseAgentArg(void 0);
3731
+ } else {
3732
+ console.log("\nNo pspm.json found. Let's set up your project.\n");
3733
+ agents = await promptForAgents();
3734
+ console.log();
3735
+ }
3736
+ const packages = lockfile?.packages ?? lockfile?.skills ?? {};
3737
+ const packageCount = Object.keys(packages).length;
3738
+ if (packageCount > 0) {
3739
+ console.log(`
3740
+ Installing ${packageCount} registry skill(s)...
3741
+ `);
3742
+ const installOrder = computeInstallOrder(packages);
3743
+ const entries = installOrder.filter((name) => packages[name]).map((name) => [name, packages[name]]);
3744
+ for (const [fullName, entry] of entries) {
3745
+ const parsedName = parseRegistrySpecifier2(fullName);
3746
+ if (!parsedName) {
3747
+ console.warn(`Warning: Invalid skill name in lockfile: ${fullName}`);
3748
+ continue;
3749
+ }
3750
+ const { namespace: ns, owner: pkgOwner, name, subname } = parsedName;
3751
+ console.log(`Installing ${fullName}@${entry.version}...`);
3752
+ let tarballBuffer;
3753
+ let fromCache = false;
3754
+ const cachedTarball = await readFromCache(cacheDir, entry.integrity);
3755
+ if (cachedTarball) {
3756
+ tarballBuffer = cachedTarball;
3757
+ fromCache = true;
3758
+ } else {
3759
+ const isPresignedUrl = entry.resolved.includes(".r2.cloudflarestorage.com") || entry.resolved.includes("X-Amz-Signature");
3760
+ const downloadHeaders = {};
3761
+ if (!isPresignedUrl && apiKey) {
3762
+ downloadHeaders.Authorization = `Bearer ${apiKey}`;
3763
+ }
3764
+ const response = await fetch(entry.resolved, {
3765
+ headers: downloadHeaders,
3766
+ redirect: "follow"
3767
+ });
3768
+ if (!response.ok) {
3769
+ if (response.status === 401) {
3770
+ if (!apiKey) {
3771
+ console.error(
3772
+ ` Error: ${fullName} requires authentication. Run 'pspm login' first.`
3773
+ );
3774
+ } else {
3775
+ console.error(
3776
+ ` Error: Access denied to ${fullName}. You may not have permission to access this private package.`
3777
+ );
3778
+ }
3779
+ } else {
3780
+ console.error(
3781
+ ` Error: Failed to download ${fullName} (${response.status})`
3782
+ );
3783
+ }
3784
+ continue;
3785
+ }
3786
+ tarballBuffer = Buffer.from(await response.arrayBuffer());
3787
+ const actualIntegrity = `sha256-${createHash("sha256").update(tarballBuffer).digest("base64")}`;
3788
+ if (actualIntegrity !== entry.integrity) {
3789
+ console.error(
3790
+ ` Error: Checksum verification failed for ${fullName}`
3791
+ );
3792
+ if (options.frozenLockfile) {
3793
+ process.exit(1);
3794
+ }
3795
+ continue;
3796
+ }
3797
+ await writeToCache(cacheDir, entry.integrity, tarballBuffer);
3798
+ }
3799
+ if (entry.encryption) {
3800
+ const scope = entry.encryption.scope;
3801
+ const encKey = await getEncryptionKey(scope);
3802
+ if (!encKey) {
3803
+ console.error(
3804
+ ` Error: Package ${fullName} is encrypted. Set the key: pspm config set-encryption-key ${scope} <passphrase>`
3805
+ );
3806
+ continue;
3807
+ }
3808
+ try {
3809
+ tarballBuffer = decryptBuffer(
3810
+ tarballBuffer,
3811
+ encKey,
3812
+ entry.encryption
3813
+ );
3814
+ } catch {
3815
+ console.error(
3816
+ ` Error: Failed to decrypt ${fullName}. Wrong encryption key for scope "${scope}".`
3817
+ );
3818
+ continue;
3819
+ }
3820
+ }
3821
+ const effectiveSkillName = subname ?? name;
3822
+ let destDir;
3823
+ if (ns === "org") {
3824
+ destDir = join(skillsDir, "_org", pkgOwner, effectiveSkillName);
3825
+ } else if (ns === "github" && subname) {
3826
+ destDir = join(
3827
+ skillsDir,
3828
+ "_github-registry",
3829
+ pkgOwner,
3830
+ name,
3831
+ subname
3832
+ );
3833
+ } else {
3834
+ destDir = join(skillsDir, pkgOwner, effectiveSkillName);
3835
+ }
3836
+ await rm(destDir, { recursive: true, force: true });
3837
+ await mkdir(destDir, { recursive: true });
3838
+ const tempFile = join(destDir, ".temp.tgz");
3839
+ await writeFile(tempFile, tarballBuffer);
3840
+ const { exec: exec2 } = await import('child_process');
3841
+ const { promisify: promisify2 } = await import('util');
3842
+ const execAsync = promisify2(exec2);
3843
+ try {
3844
+ await execAsync(
3845
+ `tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
3846
+ );
3847
+ } finally {
3848
+ await rm(tempFile, { force: true });
3849
+ }
3850
+ console.log(
3851
+ ` Installed to ${destDir}${fromCache ? " (from cache)" : ""}`
3852
+ );
3853
+ const pathSkillName = ns === "github" && subname ? `${name}/${subname}` : name;
3854
+ installedSkills.push({
3855
+ name: effectiveSkillName,
3856
+ sourcePath: getRegistrySkillPath(ns, pkgOwner, pathSkillName)
3857
+ });
3858
+ }
3859
+ }
3860
+ const githubPackages = lockfile?.githubPackages ?? {};
3861
+ const githubCount = Object.keys(githubPackages).length;
3862
+ if (githubCount > 0) {
3863
+ console.log(`
3864
+ Installing ${githubCount} GitHub skill(s)...
3865
+ `);
3866
+ for (const [specifier, entry] of Object.entries(githubPackages)) {
3867
+ const parsed = parseGitHubSpecifier2(specifier);
3868
+ if (!parsed) {
3869
+ console.warn(
3870
+ `Warning: Invalid GitHub specifier in lockfile: ${specifier}`
3871
+ );
3872
+ continue;
3873
+ }
3874
+ const ghEntry = entry;
3875
+ console.log(
3876
+ `Installing ${specifier} (${ghEntry.gitRef}@${ghEntry.gitCommit.slice(0, 7)})...`
3877
+ );
3878
+ let tarballBuffer;
3879
+ let fromCache = false;
3880
+ const cachedTarball = await readFromCache(cacheDir, ghEntry.integrity);
3881
+ if (cachedTarball) {
3882
+ tarballBuffer = cachedTarball;
3883
+ fromCache = true;
3884
+ } else {
3885
+ try {
3886
+ const specWithCommit = { ...parsed, ref: ghEntry.gitCommit };
3887
+ const result = await downloadGitHubPackage(specWithCommit);
3888
+ tarballBuffer = result.buffer;
3889
+ if (result.integrity !== ghEntry.integrity) {
3890
+ console.error(
3891
+ ` Error: Checksum verification failed for ${specifier}`
3892
+ );
3893
+ if (options.frozenLockfile) {
3894
+ process.exit(1);
3895
+ }
3896
+ continue;
3897
+ }
3898
+ await writeToCache(cacheDir, ghEntry.integrity, tarballBuffer);
3899
+ } catch (error) {
3900
+ if (error instanceof GitHubRateLimitError) {
3901
+ console.error(` Error: ${error.message}`);
3902
+ } else if (error instanceof GitHubPathNotFoundError) {
3903
+ console.error(` Error: ${error.message}`);
3904
+ } else if (error instanceof GitHubNotFoundError) {
3905
+ console.error(` Error: ${error.message}`);
3906
+ } else {
3907
+ const message = error instanceof Error ? error.message : String(error);
3908
+ console.error(` Error downloading ${specifier}: ${message}`);
3909
+ }
3910
+ continue;
3911
+ }
3912
+ }
3913
+ try {
3914
+ const destPath = await extractGitHubPackage(
3915
+ parsed,
3916
+ tarballBuffer,
3917
+ skillsDir
3918
+ );
3919
+ console.log(
3920
+ ` Installed to ${destPath}${fromCache ? " (from cache)" : ""}`
3921
+ );
3922
+ const skillName = getGitHubSkillName2(parsed);
3923
+ installedSkills.push({
3924
+ name: skillName,
3925
+ sourcePath: getGitHubSkillPath(
3926
+ parsed.owner,
3927
+ parsed.repo,
3928
+ parsed.path
3929
+ )
3930
+ });
3931
+ } catch (error) {
3932
+ const message = error instanceof Error ? error.message : String(error);
3933
+ console.error(` Error extracting ${specifier}: ${message}`);
3934
+ }
3935
+ }
3936
+ }
3937
+ if (installedSkills.length > 0 && agents[0] !== "none") {
3938
+ const globalMode = isGlobalMode();
3939
+ console.log(
3940
+ `
3941
+ Creating symlinks for agent(s): ${agents.join(", ")}${globalMode ? " (global)" : ""}...`
3942
+ );
3943
+ await createAgentSymlinks(installedSkills, {
3944
+ agents,
3945
+ projectRoot: globalMode ? homedir() : process.cwd(),
3946
+ agentConfigs,
3947
+ global: globalMode
3948
+ });
3949
+ console.log(" Symlinks created.");
3950
+ }
3951
+ const totalCount = packageCount + githubCount;
3952
+ if (totalCount === 0) {
3953
+ console.log("No skills to install.");
3954
+ } else {
3955
+ console.log(`
3956
+ All ${totalCount} skill(s) installed.`);
3957
+ }
3958
+ } catch (error) {
3959
+ const message = error instanceof Error ? error.message : "Unknown error";
3960
+ console.error(`Error: ${message}`);
3961
+ process.exit(1);
3962
+ }
3963
+ }
3964
+ var init_install = __esm({
3965
+ "src/commands/install.ts"() {
3966
+ init_agents();
3967
+ init_api_client();
3968
+ init_config();
3969
+ init_errors();
3970
+ init_github();
3971
+ init_encryption();
3972
+ init_lib();
3973
+ init_lockfile3();
3974
+ init_manifest3();
3975
+ init_symlinks();
3976
+ }
3977
+ });
3978
+
3979
+ // src/commands/access.ts
3980
+ init_api_client();
3981
+ init_config();
3982
+ init_lib();
3983
+ function isLocalSpecifier2(specifier) {
3984
+ return specifier.startsWith("file:") || specifier.startsWith("./") || specifier.startsWith("../");
3985
+ }
3986
+ async function access(specifier, options) {
3987
+ try {
3988
+ const apiKey = await requireApiKey();
3989
+ const registryUrl = await getRegistryUrl();
3990
+ if (options.public && options.private) {
3991
+ console.error("Error: Cannot specify both --public and --private");
3992
+ process.exit(1);
3993
+ }
3994
+ if (!options.public && !options.private) {
3995
+ console.error("Error: Must specify either --public or --private");
3996
+ process.exit(1);
3997
+ }
3998
+ const visibility = options.public ? "public" : "private";
3999
+ let packageName;
4000
+ let packageUsername;
4001
+ if (specifier) {
4002
+ if (isGitHubSpecifier2(specifier)) {
4003
+ const ghSpec = parseGitHubSpecifier2(specifier);
4004
+ if (ghSpec) {
4005
+ console.error(`Error: Cannot change visibility of GitHub packages.`);
4006
+ console.error(
4007
+ ` "${specifier}" is hosted on GitHub, not the PSPM registry.`
4008
+ );
4009
+ console.error(
4010
+ ` Visibility can only be changed for packages published to the registry.`
4011
+ );
4012
+ } else {
4013
+ console.error(`Error: Invalid GitHub specifier "${specifier}".`);
4014
+ console.error(` Use format: github:{owner}/{repo}[/{path}][@{ref}]`);
4015
+ }
4016
+ process.exit(1);
4017
+ }
4018
+ if (isLocalSpecifier2(specifier)) {
4019
+ console.error(`Error: Cannot change visibility of local packages.`);
4020
+ console.error(
4021
+ ` "${specifier}" is a local directory, not a registry package.`
4022
+ );
4023
+ console.error(
4024
+ ` Visibility can only be changed for packages published to the registry.`
4025
+ );
4026
+ process.exit(1);
4027
+ }
4028
+ const parsed = parseRegistrySpecifier2(specifier);
4029
+ if (!parsed) {
4030
+ console.error(`Error: Invalid package specifier "${specifier}".`);
4031
+ console.error(
4032
+ ` Use format: @user/{username}/{name} or @org/{orgname}/{name}`
4033
+ );
4034
+ console.error(``);
4035
+ console.error(` Examples:`);
4036
+ console.error(` pspm access @user/myname/my-skill --public`);
4037
+ console.error(
4038
+ ` pspm access --public (uses current directory's pspm.json)`
4039
+ );
4040
+ process.exit(1);
4041
+ }
4042
+ packageName = parsed.name;
4043
+ packageUsername = parsed.owner;
4044
+ } else {
4045
+ const { readFile: readFile10 } = await import('fs/promises');
4046
+ const { join: join18 } = await import('path');
4047
+ let manifest = null;
4048
+ try {
4049
+ const content = await readFile10(
4050
+ join18(process.cwd(), "pspm.json"),
4051
+ "utf-8"
4052
+ );
4053
+ manifest = JSON.parse(content);
4054
+ } catch {
4055
+ try {
4056
+ const content = await readFile10(
4057
+ join18(process.cwd(), "package.json"),
4058
+ "utf-8"
4059
+ );
4060
+ manifest = JSON.parse(content);
4061
+ } catch {
4062
+ console.error(
4063
+ "Error: No pspm.json or package.json found in current directory"
4064
+ );
4065
+ console.error(
4066
+ "Either run this command in a package directory or specify a package name"
4067
+ );
4068
+ process.exit(1);
4069
+ }
4070
+ }
4071
+ if (!manifest?.name) {
4072
+ console.error("Error: Package manifest is missing 'name' field");
4073
+ process.exit(1);
4074
+ }
4075
+ packageName = manifest.name;
4076
+ }
4077
+ if (!packageUsername) {
4078
+ const config2 = await resolveConfig();
4079
+ packageUsername = config2.username;
4080
+ }
4081
+ if (!packageUsername) {
4082
+ console.error(
3436
4083
  "Error: Could not determine username. Please use the full specifier: @user/{username}/{name}"
3437
4084
  );
3438
4085
  process.exit(1);
@@ -3497,7 +4144,6 @@ async function audit(options) {
3497
4144
  }
3498
4145
  const issues = [];
3499
4146
  const skillsDir = getSkillsDir();
3500
- const projectRoot = process.cwd();
3501
4147
  if (!options.json) {
3502
4148
  console.log("Auditing installed skills...\n");
3503
4149
  }
@@ -3506,7 +4152,7 @@ async function audit(options) {
3506
4152
  const match = fullName.match(/^@user\/([^/]+)\/([^/]+)$/);
3507
4153
  if (!match) continue;
3508
4154
  const [, username, skillName] = match;
3509
- const destDir = join(projectRoot, skillsDir, username, skillName);
4155
+ const destDir = join(skillsDir, username, skillName);
3510
4156
  const exists = await pathExists(destDir);
3511
4157
  if (!exists) {
3512
4158
  issues.push({
@@ -3542,14 +4188,7 @@ async function audit(options) {
3542
4188
  for (const { specifier } of githubSkills) {
3543
4189
  const parsed = parseGitHubSpecifier2(specifier);
3544
4190
  if (!parsed) continue;
3545
- const destDir = parsed.path ? join(
3546
- projectRoot,
3547
- skillsDir,
3548
- "_github",
3549
- parsed.owner,
3550
- parsed.repo,
3551
- parsed.path
3552
- ) : join(projectRoot, skillsDir, "_github", parsed.owner, parsed.repo);
4191
+ const destDir = parsed.path ? join(skillsDir, "_github", parsed.owner, parsed.repo, parsed.path) : join(skillsDir, "_github", parsed.owner, parsed.repo);
3553
4192
  const exists = await pathExists(destDir);
3554
4193
  if (!exists) {
3555
4194
  issues.push({
@@ -3576,7 +4215,6 @@ async function audit(options) {
3576
4215
  for (const { specifier, entry } of wellKnownSkills) {
3577
4216
  const wkEntry = entry;
3578
4217
  const destDir = join(
3579
- projectRoot,
3580
4218
  skillsDir,
3581
4219
  "_wellknown",
3582
4220
  wkEntry.hostname,
@@ -3644,376 +4282,85 @@ async function audit(options) {
3644
4282
  console.log(
3645
4283
  ` [${issue.type.toUpperCase()}] ${issue.name} (${issue.source})`
3646
4284
  );
3647
- console.log(` ${issue.message}`);
3648
- }
3649
- console.log();
3650
- }
3651
- const totalPackages = registrySkills.length + githubSkills.length + wellKnownSkills.length;
3652
- console.log(
3653
- `Audited ${totalPackages} package(s): ${errorCount} error(s), ${warningCount} warning(s).`
3654
- );
3655
- if (errorCount > 0) {
3656
- process.exit(1);
3657
- }
3658
- } catch (error) {
3659
- const message = error instanceof Error ? error.message : "Unknown error";
3660
- console.error(`Error: ${message}`);
3661
- process.exit(1);
3662
- }
3663
- }
3664
- async function pathExists(path) {
3665
- try {
3666
- await stat(path);
3667
- return true;
3668
- } catch {
3669
- return false;
3670
- }
3671
- }
3672
- async function configInit(options) {
3673
- try {
3674
- const configPath = join(process.cwd(), ".pspmrc");
3675
- try {
3676
- await stat(configPath);
3677
- console.error("Error: .pspmrc already exists in this directory.");
3678
- process.exit(1);
3679
- } catch {
3680
- }
3681
- const lines = ["; Project-specific PSPM configuration", ""];
3682
- if (options.registry) {
3683
- lines.push(`registry = ${options.registry}`);
3684
- } else {
3685
- lines.push("; Uncomment to use a custom registry:");
3686
- lines.push("; registry = https://custom-registry.example.com");
3687
- }
3688
- lines.push("");
3689
- await writeFile(configPath, lines.join("\n"));
3690
- console.log("Created .pspmrc");
3691
- console.log("");
3692
- console.log("Contents:");
3693
- console.log(lines.join("\n"));
3694
- console.log("Note: .pspmrc should be committed to version control.");
3695
- console.log("API keys should NOT be stored here - use pspm login instead.");
3696
- } catch (error) {
3697
- const message = error instanceof Error ? error.message : "Unknown error";
3698
- console.error(`Error: ${message}`);
3699
- process.exit(1);
3700
- }
3701
- }
3702
-
3703
- // src/commands/config/show.ts
3704
- init_config();
3705
- async function configShow() {
3706
- try {
3707
- const resolved = await resolveConfig();
3708
- const projectConfig = await findProjectConfig();
3709
- const configPath = getConfigPath();
3710
- console.log("Resolved Configuration:\n");
3711
- console.log(` Registry URL: ${resolved.registryUrl}`);
3712
- console.log(` API Key: ${resolved.apiKey ? "***" : "(not set)"}`);
3713
- console.log(` Username: ${resolved.username || "(not set)"}`);
3714
- console.log("");
3715
- console.log("Config Locations:");
3716
- console.log(` User config: ${configPath}`);
3717
- console.log(` Project config: ${projectConfig ? ".pspmrc" : "(none)"}`);
3718
- console.log("");
3719
- console.log("Environment Variables:");
3720
- console.log(
3721
- ` PSPM_REGISTRY_URL: ${process.env.PSPM_REGISTRY_URL || "(not set)"}`
3722
- );
3723
- console.log(
3724
- ` PSPM_API_KEY: ${process.env.PSPM_API_KEY ? "***" : "(not set)"}`
3725
- );
3726
- } catch (error) {
3727
- const message = error instanceof Error ? error.message : "Unknown error";
3728
- console.error(`Error: ${message}`);
3729
- process.exit(1);
3730
- }
3731
- }
3732
-
3733
- // src/commands/deprecate.ts
3734
- init_api_client();
3735
- init_config();
3736
- init_lib();
3737
- async function deprecate(specifier, message, options) {
3738
- try {
3739
- const apiKey = await requireApiKey();
3740
- const registryUrl = await getRegistryUrl();
3741
- const parsed = parseRegistrySpecifier2(specifier);
3742
- if (!parsed) {
3743
- console.error(
3744
- `Error: Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}@{version} or @org/{orgname}/{name}@{version}`
3745
- );
3746
- process.exit(1);
3747
- }
3748
- const { owner, name, versionRange } = parsed;
3749
- const fullName = generateRegistryIdentifier2({
3750
- namespace: parsed.namespace,
3751
- owner,
3752
- name
3753
- });
3754
- if (!versionRange) {
3755
- console.error(
3756
- "Error: Version is required for deprecation. Use format: @user/{username}/{name}@{version}"
3757
- );
3758
- process.exit(1);
3759
- }
3760
- configure2({ registryUrl, apiKey });
3761
- if (options.undo) {
3762
- console.log(`Removing deprecation from ${fullName}@${versionRange}...`);
3763
- const response = await undeprecateSkillVersion(owner, name, versionRange);
3764
- if (response.status !== 200) {
3765
- console.error(
3766
- `Error: ${response.error || "Failed to remove deprecation"}`
3767
- );
3768
- process.exit(1);
3769
- }
3770
- console.log(`Removed deprecation from ${fullName}@${versionRange}`);
3771
- } else {
3772
- if (!message) {
3773
- console.error(
3774
- "Error: Deprecation message is required. Usage: pspm deprecate <specifier> <message>"
3775
- );
3776
- process.exit(1);
3777
- }
3778
- console.log(`Deprecating ${fullName}@${versionRange}...`);
3779
- const response = await deprecateSkillVersion(
3780
- owner,
3781
- name,
3782
- versionRange,
3783
- message
3784
- );
3785
- if (response.status !== 200) {
3786
- console.error(
3787
- `Error: ${response.error || "Failed to deprecate version"}`
3788
- );
3789
- process.exit(1);
4285
+ console.log(` ${issue.message}`);
3790
4286
  }
3791
- console.log(`Deprecated ${fullName}@${versionRange}`);
3792
- console.log(`Message: ${message}`);
3793
- console.log("");
3794
- console.log(
3795
- "Users installing this version will see a deprecation warning."
3796
- );
3797
- console.log("The package is still available for download.");
4287
+ console.log();
4288
+ }
4289
+ const totalPackages = registrySkills.length + githubSkills.length + wellKnownSkills.length;
4290
+ console.log(
4291
+ `Audited ${totalPackages} package(s): ${errorCount} error(s), ${warningCount} warning(s).`
4292
+ );
4293
+ if (errorCount > 0) {
4294
+ process.exit(1);
3798
4295
  }
3799
4296
  } catch (error) {
3800
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
3801
- console.error(`Error: ${errorMessage}`);
4297
+ const message = error instanceof Error ? error.message : "Unknown error";
4298
+ console.error(`Error: ${message}`);
3802
4299
  process.exit(1);
3803
4300
  }
3804
4301
  }
3805
-
3806
- // src/commands/init.ts
3807
- init_lib();
3808
- function prompt(rl, question, defaultValue) {
3809
- return new Promise((resolve2) => {
3810
- const displayDefault = defaultValue ? ` (${defaultValue})` : "";
3811
- rl.question(`${question}${displayDefault} `, (answer) => {
3812
- resolve2(answer.trim() || defaultValue);
3813
- });
3814
- });
3815
- }
3816
- async function readExistingPackageJson() {
3817
- try {
3818
- const content = await readFile(
3819
- join(process.cwd(), "package.json"),
3820
- "utf-8"
3821
- );
3822
- const pkg = JSON.parse(content);
3823
- return {
3824
- name: pkg.name,
3825
- version: pkg.version,
3826
- description: pkg.description,
3827
- author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name,
3828
- license: pkg.license
3829
- };
3830
- } catch {
3831
- return null;
3832
- }
3833
- }
3834
- async function getGitAuthor() {
4302
+ async function pathExists(path) {
3835
4303
  try {
3836
- const { exec: exec2 } = await import('child_process');
3837
- const { promisify: promisify2 } = await import('util');
3838
- const execAsync = promisify2(exec2);
3839
- const [nameResult, emailResult] = await Promise.all([
3840
- execAsync("git config user.name").catch(() => ({ stdout: "" })),
3841
- execAsync("git config user.email").catch(() => ({ stdout: "" }))
3842
- ]);
3843
- const name = nameResult.stdout.trim();
3844
- const email = emailResult.stdout.trim();
3845
- if (name && email) {
3846
- return `${name} <${email}>`;
3847
- }
3848
- if (name) {
3849
- return name;
3850
- }
3851
- return null;
4304
+ await stat(path);
4305
+ return true;
3852
4306
  } catch {
3853
- return null;
4307
+ return false;
3854
4308
  }
3855
4309
  }
3856
- function sanitizeName(name) {
3857
- const withoutScope = name.replace(/^@[^/]+\//, "");
3858
- return withoutScope.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
3859
- }
3860
- function isValidName(name) {
3861
- return /^[a-z][a-z0-9_-]*$/.test(name);
3862
- }
3863
- function isValidVersion(version3) {
3864
- return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version3);
3865
- }
3866
- async function init(options) {
4310
+ async function configInit(options) {
3867
4311
  try {
3868
- const pspmJsonPath = join(process.cwd(), "pspm.json");
3869
- let exists = false;
4312
+ const configPath = join(process.cwd(), ".pspmrc");
3870
4313
  try {
3871
- await stat(pspmJsonPath);
3872
- exists = true;
3873
- } catch {
3874
- }
3875
- if (exists && !options.force) {
3876
- console.error("Error: pspm.json already exists in this directory.");
3877
- console.error("Use --force to overwrite.");
4314
+ await stat(configPath);
4315
+ console.error("Error: .pspmrc already exists in this directory.");
3878
4316
  process.exit(1);
4317
+ } catch {
3879
4318
  }
3880
- const existingPkg = await readExistingPackageJson();
3881
- const gitAuthor = await getGitAuthor();
3882
- const defaultName = sanitizeName(
3883
- options.name || existingPkg?.name || basename(process.cwd())
3884
- );
3885
- const defaultVersion = existingPkg?.version || "0.1.0";
3886
- const defaultDescription = options.description || existingPkg?.description || "";
3887
- const defaultAuthor = options.author || existingPkg?.author || gitAuthor || "";
3888
- const defaultLicense = existingPkg?.license || "MIT";
3889
- const defaultMain = "SKILL.md";
3890
- const defaultCapabilities = "";
3891
- let manifest;
3892
- if (options.yes) {
3893
- manifest = {
3894
- $schema: PSPM_SCHEMA_URL,
3895
- name: defaultName,
3896
- version: defaultVersion,
3897
- description: defaultDescription || void 0,
3898
- author: defaultAuthor || void 0,
3899
- license: defaultLicense,
3900
- type: "skill",
3901
- capabilities: [],
3902
- main: defaultMain,
3903
- requirements: {
3904
- pspm: ">=0.1.0"
3905
- },
3906
- files: [...DEFAULT_SKILL_FILES],
3907
- dependencies: {},
3908
- private: false
3909
- };
4319
+ const lines = ["; Project-specific PSPM configuration", ""];
4320
+ if (options.registry) {
4321
+ lines.push(`registry = ${options.registry}`);
3910
4322
  } else {
3911
- console.log(
3912
- "This utility will walk you through creating a pspm.json file."
3913
- );
3914
- console.log(
3915
- "It only covers the most common items, and tries to guess sensible defaults."
3916
- );
3917
- console.log("");
3918
- console.log(
3919
- "See `pspm init --help` for definitive documentation on these fields"
3920
- );
3921
- console.log("and exactly what they do.");
3922
- console.log("");
3923
- console.log("Press ^C at any time to quit.");
3924
- const rl = createInterface({
3925
- input: process.stdin,
3926
- output: process.stdout
3927
- });
3928
- try {
3929
- let name = await prompt(rl, "skill name:", defaultName);
3930
- while (!isValidName(name)) {
3931
- console.log(
3932
- " Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores."
3933
- );
3934
- name = await prompt(rl, "skill name:", sanitizeName(name));
3935
- }
3936
- let version3 = await prompt(rl, "version:", defaultVersion);
3937
- while (!isValidVersion(version3)) {
3938
- console.log(" Version must be valid semver (e.g., 1.0.0)");
3939
- version3 = await prompt(rl, "version:", "0.1.0");
3940
- }
3941
- const description = await prompt(
3942
- rl,
3943
- "description:",
3944
- defaultDescription
3945
- );
3946
- const main = await prompt(rl, "entry point:", defaultMain);
3947
- const capabilitiesStr = await prompt(
3948
- rl,
3949
- "capabilities (comma-separated):",
3950
- defaultCapabilities
3951
- );
3952
- const author = await prompt(rl, "author:", defaultAuthor);
3953
- const license = await prompt(rl, "license:", defaultLicense);
3954
- rl.close();
3955
- const capabilities = capabilitiesStr ? capabilitiesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
3956
- manifest = {
3957
- $schema: PSPM_SCHEMA_URL,
3958
- name,
3959
- version: version3,
3960
- description: description || void 0,
3961
- author: author || void 0,
3962
- license,
3963
- type: "skill",
3964
- capabilities,
3965
- main,
3966
- requirements: {
3967
- pspm: ">=0.1.0"
3968
- },
3969
- files: [...DEFAULT_SKILL_FILES],
3970
- dependencies: {},
3971
- private: false
3972
- };
3973
- } catch (error) {
3974
- rl.close();
3975
- if (error instanceof Error && error.message.includes("readline was closed")) {
3976
- console.log("\nAborted.");
3977
- process.exit(0);
3978
- }
3979
- throw error;
3980
- }
4323
+ lines.push("; Uncomment to use a custom registry:");
4324
+ lines.push("; registry = https://custom-registry.example.com");
3981
4325
  }
3982
- if (!manifest.description) manifest.description = void 0;
3983
- if (!manifest.author) manifest.author = void 0;
3984
- if (manifest.capabilities?.length === 0) manifest.capabilities = void 0;
3985
- const content = JSON.stringify(manifest, null, 2);
3986
- console.log("");
3987
- console.log(`About to write to ${pspmJsonPath}:`);
3988
- console.log("");
3989
- console.log(content);
4326
+ lines.push("");
4327
+ await writeFile(configPath, lines.join("\n"));
4328
+ console.log("Created .pspmrc");
3990
4329
  console.log("");
3991
- if (!options.yes) {
3992
- const rl = createInterface({
3993
- input: process.stdin,
3994
- output: process.stdout
3995
- });
3996
- const confirm2 = await prompt(rl, "Is this OK?", "yes");
3997
- rl.close();
3998
- if (confirm2.toLowerCase() !== "yes" && confirm2.toLowerCase() !== "y") {
3999
- console.log("Aborted.");
4000
- process.exit(0);
4001
- }
4002
- }
4003
- await writeFile(pspmJsonPath, `${content}
4004
- `);
4005
- try {
4006
- await stat(join(process.cwd(), "SKILL.md"));
4007
- } catch {
4008
- console.log(
4009
- "Note: Create a SKILL.md file with your skill's prompt content."
4010
- );
4011
- }
4012
- if (existingPkg) {
4013
- console.log("Note: Values were derived from existing package.json.");
4014
- console.log(" pspm.json is for publishing to PSPM registry.");
4015
- console.log(" package.json can still be used for npm dependencies.");
4016
- }
4330
+ console.log("Contents:");
4331
+ console.log(lines.join("\n"));
4332
+ console.log("Note: .pspmrc should be committed to version control.");
4333
+ console.log("API keys should NOT be stored here - use pspm login instead.");
4334
+ } catch (error) {
4335
+ const message = error instanceof Error ? error.message : "Unknown error";
4336
+ console.error(`Error: ${message}`);
4337
+ process.exit(1);
4338
+ }
4339
+ }
4340
+
4341
+ // src/commands/config/show.ts
4342
+ init_config();
4343
+ async function configShow() {
4344
+ try {
4345
+ const resolved = await resolveConfig();
4346
+ const projectConfig = await findProjectConfig();
4347
+ const configPath = getConfigPath();
4348
+ console.log("Resolved Configuration:\n");
4349
+ console.log(` Registry URL: ${resolved.registryUrl}`);
4350
+ console.log(` API Key: ${resolved.apiKey ? "***" : "(not set)"}`);
4351
+ console.log(` Username: ${resolved.username || "(not set)"}`);
4352
+ console.log("");
4353
+ console.log("Config Locations:");
4354
+ console.log(` User config: ${configPath}`);
4355
+ console.log(` Project config: ${projectConfig ? ".pspmrc" : "(none)"}`);
4356
+ console.log("");
4357
+ console.log("Environment Variables:");
4358
+ console.log(
4359
+ ` PSPM_REGISTRY_URL: ${process.env.PSPM_REGISTRY_URL || "(not set)"}`
4360
+ );
4361
+ console.log(
4362
+ ` PSPM_API_KEY: ${process.env.PSPM_API_KEY ? "***" : "(not set)"}`
4363
+ );
4017
4364
  } catch (error) {
4018
4365
  const message = error instanceof Error ? error.message : "Unknown error";
4019
4366
  console.error(`Error: ${message}`);
@@ -4021,502 +4368,289 @@ async function init(options) {
4021
4368
  }
4022
4369
  }
4023
4370
 
4024
- // src/commands/install.ts
4025
- init_agents();
4371
+ // src/commands/deprecate.ts
4026
4372
  init_api_client();
4027
4373
  init_config();
4028
- init_errors();
4029
- init_github();
4030
4374
  init_lib();
4031
- init_lockfile3();
4032
- init_manifest3();
4033
- init_symlinks();
4034
- function getCacheFilePath(cacheDir, integrity) {
4035
- const match = integrity.match(/^sha256-(.+)$/);
4036
- if (!match) {
4037
- throw new Error(`Invalid integrity format: ${integrity}`);
4038
- }
4039
- const base64Hash = match[1];
4040
- const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
4041
- return join(cacheDir, `sha256-${hexHash}.tgz`);
4042
- }
4043
- async function readFromCache(cacheDir, integrity) {
4044
- try {
4045
- const cachePath = getCacheFilePath(cacheDir, integrity);
4046
- const data = await readFile(cachePath);
4047
- const actualIntegrity = `sha256-${createHash("sha256").update(data).digest("base64")}`;
4048
- if (actualIntegrity !== integrity) {
4049
- await rm(cachePath, { force: true });
4050
- return null;
4051
- }
4052
- return data;
4053
- } catch {
4054
- return null;
4055
- }
4056
- }
4057
- async function writeToCache(cacheDir, integrity, data) {
4375
+ async function deprecate(specifier, message, options) {
4058
4376
  try {
4059
- await mkdir(cacheDir, { recursive: true });
4060
- const cachePath = getCacheFilePath(cacheDir, integrity);
4061
- await writeFile(cachePath, data);
4062
- } catch {
4063
- }
4064
- }
4065
- async function install(specifiers, options) {
4066
- if (options.global) {
4067
- const { setGlobalMode: setGlobalMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4068
- setGlobalMode2(true);
4069
- }
4070
- if (options.list) {
4071
- const listSpecifiers = await resolveListToSpecifiers(options.list);
4072
- if (listSpecifiers.length === 0) {
4073
- console.log("No skills in the list to install.");
4074
- return;
4377
+ const apiKey = await requireApiKey();
4378
+ const registryUrl = await getRegistryUrl();
4379
+ const parsed = parseRegistrySpecifier2(specifier);
4380
+ if (!parsed) {
4381
+ console.error(
4382
+ `Error: Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}@{version} or @org/{orgname}/{name}@{version}`
4383
+ );
4384
+ process.exit(1);
4075
4385
  }
4076
- const allSpecifiers = [...specifiers, ...listSpecifiers];
4077
- const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
4078
- await add2(allSpecifiers, {
4079
- save: true,
4080
- agent: options.agent,
4081
- yes: options.yes,
4082
- global: options.global
4083
- });
4084
- return;
4085
- }
4086
- if (specifiers.length > 0) {
4087
- const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
4088
- await add2(specifiers, {
4089
- save: true,
4090
- agent: options.agent,
4091
- yes: options.yes,
4092
- global: options.global
4386
+ const { owner, name, versionRange } = parsed;
4387
+ const fullName = generateRegistryIdentifier2({
4388
+ namespace: parsed.namespace,
4389
+ owner,
4390
+ name
4093
4391
  });
4094
- return;
4095
- }
4096
- await installFromLockfile(options);
4097
- }
4098
- async function resolveListToSpecifiers(listSpec) {
4099
- const match = listSpec.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
4100
- if (!match) {
4101
- console.error(
4102
- `Error: Invalid list specifier "${listSpec}". Use format: @user/{username}/{list-name} or @org/{orgname}/{list-name}`
4103
- );
4104
- process.exit(1);
4105
- }
4106
- const [, ownerType, ownerName, listName] = match;
4107
- const config2 = await resolveConfig();
4108
- configure2({
4109
- registryUrl: config2.registryUrl,
4110
- apiKey: getTokenForRegistry(config2, config2.registryUrl)
4111
- });
4112
- console.log(
4113
- `Fetching skill list @${ownerType}/${ownerName}/${listName}...
4114
- `
4115
- );
4116
- const { fetchSkillList: fetchSkillList2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
4117
- const response = await fetchSkillList2(
4118
- ownerType,
4119
- ownerName,
4120
- listName
4121
- );
4122
- if (response.status !== 200 || !response.data) {
4123
- const errorMsg = response.status === 404 ? `List "@${ownerType}/${ownerName}/${listName}" not found or is private.` : response.error || "Failed to fetch list";
4124
- console.error(`Error: ${errorMsg}`);
4125
- process.exit(1);
4126
- }
4127
- const list2 = response.data;
4128
- console.log(
4129
- `List: ${list2.name}${list2.description ? ` \u2014 ${list2.description}` : ""}`
4130
- );
4131
- console.log(`Skills: ${list2.items.length}
4132
- `);
4133
- const specifiers = [];
4134
- for (const item of list2.items) {
4135
- const ns = item.namespace === "org" ? "org" : "user";
4136
- let spec = `@${ns}/${item.ownerName}/${item.skillName}`;
4137
- if (item.pinnedVersion) {
4138
- spec += `@${item.pinnedVersion}`;
4139
- }
4140
- specifiers.push(spec);
4141
- }
4142
- return specifiers;
4143
- }
4144
- async function installFromLockfile(options) {
4145
- try {
4146
- const config2 = await resolveConfig();
4147
- const registryUrl = config2.registryUrl;
4148
- const apiKey = getTokenForRegistry(config2, registryUrl);
4149
- const skillsDir = options.dir || getSkillsDir();
4150
- const cacheDir = getCacheDir();
4151
- await migrateLockfileIfNeeded();
4152
- let lockfile = await readLockfile();
4153
- const manifestDeps = await getDependencies();
4154
- const manifestGitHubDeps = await getGitHubDependencies();
4155
- const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
4156
- const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
4157
- const installedSkills = [];
4158
- const missingDeps = [];
4159
- for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
4160
- if (!lockfilePackages[fullName]) {
4161
- missingDeps.push({ fullName, versionRange });
4162
- }
4392
+ if (!versionRange) {
4393
+ console.error(
4394
+ "Error: Version is required for deprecation. Use format: @user/{username}/{name}@{version}"
4395
+ );
4396
+ process.exit(1);
4163
4397
  }
4164
- if (missingDeps.length > 0) {
4165
- if (options.frozenLockfile) {
4398
+ configure2({ registryUrl, apiKey });
4399
+ if (options.undo) {
4400
+ console.log(`Removing deprecation from ${fullName}@${versionRange}...`);
4401
+ const response = await undeprecateSkillVersion(owner, name, versionRange);
4402
+ if (response.status !== 200) {
4166
4403
  console.error(
4167
- "Error: Dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
4168
- );
4169
- console.error("Missing dependencies:");
4170
- for (const dep of missingDeps) {
4171
- console.error(` - ${dep.fullName}@${dep.versionRange}`);
4172
- }
4173
- process.exit(1);
4174
- }
4175
- console.log(`Resolving ${missingDeps.length} new dependency(ies)...
4176
- `);
4177
- configure2({ registryUrl, apiKey });
4178
- for (const { fullName, versionRange } of missingDeps) {
4179
- const parsed = parseRegistrySpecifier2(fullName);
4180
- if (!parsed) {
4181
- console.error(`Error: Invalid dependency specifier: ${fullName}`);
4182
- continue;
4183
- }
4184
- const { owner, name } = parsed;
4185
- console.log(`Resolving ${fullName}@${versionRange}...`);
4186
- const versionsResponse = await listSkillVersions(owner, name);
4187
- if (versionsResponse.status !== 200) {
4188
- const errorMessage = extractApiErrorMessage(
4189
- versionsResponse,
4190
- `Skill ${fullName} not found`
4191
- );
4192
- console.error(`Error: ${errorMessage}`);
4193
- continue;
4194
- }
4195
- const versions = versionsResponse.data;
4196
- if (versions.length === 0) {
4197
- console.error(`Error: Skill ${fullName} not found`);
4198
- continue;
4199
- }
4200
- const versionStrings = versions.map(
4201
- (v) => v.version
4202
- );
4203
- const resolved = resolveVersion2(versionRange || "*", versionStrings);
4204
- if (!resolved) {
4205
- console.error(
4206
- `Error: No version matching "${versionRange}" for ${fullName}`
4207
- );
4208
- continue;
4209
- }
4210
- const versionResponse = await getSkillVersion(owner, name, resolved);
4211
- if (versionResponse.status !== 200 || !versionResponse.data) {
4212
- const errorMessage = extractApiErrorMessage(
4213
- versionResponse,
4214
- `Version ${resolved} not found`
4215
- );
4216
- console.error(`Error: ${errorMessage}`);
4217
- continue;
4218
- }
4219
- const versionInfo = versionResponse.data;
4220
- const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
4221
- const downloadHeaders = {};
4222
- if (!isPresignedUrl && apiKey) {
4223
- downloadHeaders.Authorization = `Bearer ${apiKey}`;
4224
- }
4225
- const tarballResponse = await fetch(versionInfo.downloadUrl, {
4226
- headers: downloadHeaders,
4227
- redirect: "follow"
4228
- });
4229
- if (!tarballResponse.ok) {
4230
- console.error(
4231
- `Error: Failed to download tarball for ${fullName} (${tarballResponse.status})`
4232
- );
4233
- continue;
4234
- }
4235
- const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
4236
- const integrity = calculateIntegrity(tarballBuffer);
4237
- await addToLockfile(fullName, {
4238
- version: resolved,
4239
- resolved: versionInfo.downloadUrl,
4240
- integrity
4241
- });
4242
- await writeToCache(cacheDir, integrity, tarballBuffer);
4243
- console.log(` Resolved ${fullName}@${resolved}`);
4244
- }
4245
- lockfile = await readLockfile();
4246
- }
4247
- const missingGitHubDeps = [];
4248
- for (const [specifier, ref] of Object.entries(manifestGitHubDeps)) {
4249
- if (!lockfileGitHubPackages[specifier]) {
4250
- missingGitHubDeps.push({ specifier, ref });
4404
+ `Error: ${response.error || "Failed to remove deprecation"}`
4405
+ );
4406
+ process.exit(1);
4251
4407
  }
4252
- }
4253
- if (missingGitHubDeps.length > 0) {
4254
- if (options.frozenLockfile) {
4408
+ console.log(`Removed deprecation from ${fullName}@${versionRange}`);
4409
+ } else {
4410
+ if (!message) {
4255
4411
  console.error(
4256
- "Error: GitHub dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
4412
+ "Error: Deprecation message is required. Usage: pspm deprecate <specifier> <message>"
4257
4413
  );
4258
- console.error("Missing GitHub dependencies:");
4259
- for (const dep of missingGitHubDeps) {
4260
- console.error(` - ${dep.specifier}@${dep.ref}`);
4261
- }
4262
4414
  process.exit(1);
4263
4415
  }
4264
- console.log(
4265
- `
4266
- Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
4267
- `
4416
+ console.log(`Deprecating ${fullName}@${versionRange}...`);
4417
+ const response = await deprecateSkillVersion(
4418
+ owner,
4419
+ name,
4420
+ versionRange,
4421
+ message
4268
4422
  );
4269
- for (const { specifier, ref } of missingGitHubDeps) {
4270
- const parsed = parseGitHubSpecifier2(specifier);
4271
- if (!parsed) {
4272
- console.error(`Error: Invalid GitHub specifier: ${specifier}`);
4273
- continue;
4274
- }
4275
- parsed.ref = parsed.ref || ref;
4276
- console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
4277
- try {
4278
- const result = await downloadGitHubPackage(parsed);
4279
- await extractGitHubPackage(parsed, result.buffer, skillsDir);
4280
- const entry = {
4281
- version: result.commit.slice(0, 7),
4282
- resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
4283
- integrity: result.integrity,
4284
- gitCommit: result.commit,
4285
- gitRef: ref || "HEAD"
4286
- };
4287
- await addGitHubToLockfile(specifier, entry);
4288
- await writeToCache(cacheDir, result.integrity, result.buffer);
4289
- console.log(
4290
- ` Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`
4291
- );
4292
- } catch (error) {
4293
- if (error instanceof GitHubRateLimitError) {
4294
- console.error(`Error: ${error.message}`);
4295
- } else if (error instanceof GitHubPathNotFoundError) {
4296
- console.error(`Error: ${error.message}`);
4297
- } else if (error instanceof GitHubNotFoundError) {
4298
- console.error(`Error: ${error.message}`);
4299
- } else {
4300
- const message = error instanceof Error ? error.message : String(error);
4301
- console.error(`Error resolving ${specifier}: ${message}`);
4302
- }
4303
- }
4423
+ if (response.status !== 200) {
4424
+ console.error(
4425
+ `Error: ${response.error || "Failed to deprecate version"}`
4426
+ );
4427
+ process.exit(1);
4304
4428
  }
4305
- lockfile = await readLockfile();
4429
+ console.log(`Deprecated ${fullName}@${versionRange}`);
4430
+ console.log(`Message: ${message}`);
4431
+ console.log("");
4432
+ console.log(
4433
+ "Users installing this version will see a deprecation warning."
4434
+ );
4435
+ console.log("The package is still available for download.");
4306
4436
  }
4307
- const manifest = await readManifest();
4308
- const agentConfigs = manifest?.agents;
4309
- let agents;
4310
- if (options.agent) {
4311
- agents = parseAgentArg(options.agent);
4312
- } else if (manifest) {
4313
- agents = parseAgentArg(void 0);
4314
- } else if (options.yes) {
4315
- agents = parseAgentArg(void 0);
4316
- } else {
4317
- console.log("\nNo pspm.json found. Let's set up your project.\n");
4318
- agents = await promptForAgents();
4319
- console.log();
4437
+ } catch (error) {
4438
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
4439
+ console.error(`Error: ${errorMessage}`);
4440
+ process.exit(1);
4441
+ }
4442
+ }
4443
+
4444
+ // src/commands/init.ts
4445
+ init_lib();
4446
+ function prompt(rl, question, defaultValue) {
4447
+ return new Promise((resolve3) => {
4448
+ const displayDefault = defaultValue ? ` (${defaultValue})` : "";
4449
+ rl.question(`${question}${displayDefault} `, (answer) => {
4450
+ resolve3(answer.trim() || defaultValue);
4451
+ });
4452
+ });
4453
+ }
4454
+ async function readExistingPackageJson() {
4455
+ try {
4456
+ const content = await readFile(
4457
+ join(process.cwd(), "package.json"),
4458
+ "utf-8"
4459
+ );
4460
+ const pkg = JSON.parse(content);
4461
+ return {
4462
+ name: pkg.name,
4463
+ version: pkg.version,
4464
+ description: pkg.description,
4465
+ author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name,
4466
+ license: pkg.license
4467
+ };
4468
+ } catch {
4469
+ return null;
4470
+ }
4471
+ }
4472
+ async function getGitAuthor() {
4473
+ try {
4474
+ const { exec: exec2 } = await import('child_process');
4475
+ const { promisify: promisify2 } = await import('util');
4476
+ const execAsync = promisify2(exec2);
4477
+ const [nameResult, emailResult] = await Promise.all([
4478
+ execAsync("git config user.name").catch(() => ({ stdout: "" })),
4479
+ execAsync("git config user.email").catch(() => ({ stdout: "" }))
4480
+ ]);
4481
+ const name = nameResult.stdout.trim();
4482
+ const email = emailResult.stdout.trim();
4483
+ if (name && email) {
4484
+ return `${name} <${email}>`;
4320
4485
  }
4321
- const packages = lockfile?.packages ?? lockfile?.skills ?? {};
4322
- const packageCount = Object.keys(packages).length;
4323
- if (packageCount > 0) {
4324
- console.log(`
4325
- Installing ${packageCount} registry skill(s)...
4326
- `);
4327
- const installOrder = computeInstallOrder(packages);
4328
- const entries = installOrder.filter((name) => packages[name]).map((name) => [name, packages[name]]);
4329
- for (const [fullName, entry] of entries) {
4330
- const parsedName = parseRegistrySpecifier2(fullName);
4331
- if (!parsedName) {
4332
- console.warn(`Warning: Invalid skill name in lockfile: ${fullName}`);
4333
- continue;
4334
- }
4335
- const { namespace: ns, owner: pkgOwner, name, subname } = parsedName;
4336
- console.log(`Installing ${fullName}@${entry.version}...`);
4337
- let tarballBuffer;
4338
- let fromCache = false;
4339
- const cachedTarball = await readFromCache(cacheDir, entry.integrity);
4340
- if (cachedTarball) {
4341
- tarballBuffer = cachedTarball;
4342
- fromCache = true;
4343
- } else {
4344
- const isPresignedUrl = entry.resolved.includes(".r2.cloudflarestorage.com") || entry.resolved.includes("X-Amz-Signature");
4345
- const downloadHeaders = {};
4346
- if (!isPresignedUrl && apiKey) {
4347
- downloadHeaders.Authorization = `Bearer ${apiKey}`;
4348
- }
4349
- const response = await fetch(entry.resolved, {
4350
- headers: downloadHeaders,
4351
- redirect: "follow"
4352
- });
4353
- if (!response.ok) {
4354
- if (response.status === 401) {
4355
- if (!apiKey) {
4356
- console.error(
4357
- ` Error: ${fullName} requires authentication. Run 'pspm login' first.`
4358
- );
4359
- } else {
4360
- console.error(
4361
- ` Error: Access denied to ${fullName}. You may not have permission to access this private package.`
4362
- );
4363
- }
4364
- } else {
4365
- console.error(
4366
- ` Error: Failed to download ${fullName} (${response.status})`
4367
- );
4368
- }
4369
- continue;
4370
- }
4371
- tarballBuffer = Buffer.from(await response.arrayBuffer());
4372
- const actualIntegrity = `sha256-${createHash("sha256").update(tarballBuffer).digest("base64")}`;
4373
- if (actualIntegrity !== entry.integrity) {
4374
- console.error(
4375
- ` Error: Checksum verification failed for ${fullName}`
4376
- );
4377
- if (options.frozenLockfile) {
4378
- process.exit(1);
4379
- }
4380
- continue;
4381
- }
4382
- await writeToCache(cacheDir, entry.integrity, tarballBuffer);
4383
- }
4384
- const effectiveSkillName = subname ?? name;
4385
- let destDir;
4386
- if (ns === "org") {
4387
- destDir = join(skillsDir, "_org", pkgOwner, effectiveSkillName);
4388
- } else if (ns === "github" && subname) {
4389
- destDir = join(
4390
- skillsDir,
4391
- "_github-registry",
4392
- pkgOwner,
4393
- name,
4394
- subname
4486
+ if (name) {
4487
+ return name;
4488
+ }
4489
+ return null;
4490
+ } catch {
4491
+ return null;
4492
+ }
4493
+ }
4494
+ function sanitizeName(name) {
4495
+ const withoutScope = name.replace(/^@[^/]+\//, "");
4496
+ return withoutScope.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
4497
+ }
4498
+ function isValidName(name) {
4499
+ return /^[a-z][a-z0-9_-]*$/.test(name);
4500
+ }
4501
+ function isValidVersion(version3) {
4502
+ return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version3);
4503
+ }
4504
+ async function init(options) {
4505
+ try {
4506
+ const pspmJsonPath = join(process.cwd(), "pspm.json");
4507
+ let exists = false;
4508
+ try {
4509
+ await stat(pspmJsonPath);
4510
+ exists = true;
4511
+ } catch {
4512
+ }
4513
+ if (exists && !options.force) {
4514
+ console.error("Error: pspm.json already exists in this directory.");
4515
+ console.error("Use --force to overwrite.");
4516
+ process.exit(1);
4517
+ }
4518
+ const existingPkg = await readExistingPackageJson();
4519
+ const gitAuthor = await getGitAuthor();
4520
+ const defaultName = sanitizeName(
4521
+ options.name || existingPkg?.name || basename(process.cwd())
4522
+ );
4523
+ const defaultVersion = existingPkg?.version || "0.1.0";
4524
+ const defaultDescription = options.description || existingPkg?.description || "";
4525
+ const defaultAuthor = options.author || existingPkg?.author || gitAuthor || "";
4526
+ const defaultLicense = existingPkg?.license || "MIT";
4527
+ const defaultMain = "SKILL.md";
4528
+ const defaultCapabilities = "";
4529
+ let manifest;
4530
+ if (options.yes) {
4531
+ manifest = {
4532
+ $schema: PSPM_SCHEMA_URL,
4533
+ name: defaultName,
4534
+ version: defaultVersion,
4535
+ description: defaultDescription || void 0,
4536
+ author: defaultAuthor || void 0,
4537
+ license: defaultLicense,
4538
+ type: "skill",
4539
+ capabilities: [],
4540
+ main: defaultMain,
4541
+ requirements: {
4542
+ pspm: ">=0.1.0"
4543
+ },
4544
+ files: [...DEFAULT_SKILL_FILES],
4545
+ dependencies: {},
4546
+ private: false
4547
+ };
4548
+ } else {
4549
+ console.log(
4550
+ "This utility will walk you through creating a pspm.json file."
4551
+ );
4552
+ console.log(
4553
+ "It only covers the most common items, and tries to guess sensible defaults."
4554
+ );
4555
+ console.log("");
4556
+ console.log(
4557
+ "See `pspm init --help` for definitive documentation on these fields"
4558
+ );
4559
+ console.log("and exactly what they do.");
4560
+ console.log("");
4561
+ console.log("Press ^C at any time to quit.");
4562
+ const rl = createInterface({
4563
+ input: process.stdin,
4564
+ output: process.stdout
4565
+ });
4566
+ try {
4567
+ let name = await prompt(rl, "skill name:", defaultName);
4568
+ while (!isValidName(name)) {
4569
+ console.log(
4570
+ " Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores."
4395
4571
  );
4396
- } else {
4397
- destDir = join(skillsDir, pkgOwner, effectiveSkillName);
4572
+ name = await prompt(rl, "skill name:", sanitizeName(name));
4398
4573
  }
4399
- await rm(destDir, { recursive: true, force: true });
4400
- await mkdir(destDir, { recursive: true });
4401
- const tempFile = join(destDir, ".temp.tgz");
4402
- await writeFile(tempFile, tarballBuffer);
4403
- const { exec: exec2 } = await import('child_process');
4404
- const { promisify: promisify2 } = await import('util');
4405
- const execAsync = promisify2(exec2);
4406
- try {
4407
- await execAsync(
4408
- `tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
4409
- );
4410
- } finally {
4411
- await rm(tempFile, { force: true });
4574
+ let version3 = await prompt(rl, "version:", defaultVersion);
4575
+ while (!isValidVersion(version3)) {
4576
+ console.log(" Version must be valid semver (e.g., 1.0.0)");
4577
+ version3 = await prompt(rl, "version:", "0.1.0");
4412
4578
  }
4413
- console.log(
4414
- ` Installed to ${destDir}${fromCache ? " (from cache)" : ""}`
4579
+ const description = await prompt(
4580
+ rl,
4581
+ "description:",
4582
+ defaultDescription
4415
4583
  );
4416
- const pathSkillName = ns === "github" && subname ? `${name}/${subname}` : name;
4417
- installedSkills.push({
4418
- name: effectiveSkillName,
4419
- sourcePath: getRegistrySkillPath(ns, pkgOwner, pathSkillName)
4420
- });
4421
- }
4422
- }
4423
- const githubPackages = lockfile?.githubPackages ?? {};
4424
- const githubCount = Object.keys(githubPackages).length;
4425
- if (githubCount > 0) {
4426
- console.log(`
4427
- Installing ${githubCount} GitHub skill(s)...
4428
- `);
4429
- for (const [specifier, entry] of Object.entries(githubPackages)) {
4430
- const parsed = parseGitHubSpecifier2(specifier);
4431
- if (!parsed) {
4432
- console.warn(
4433
- `Warning: Invalid GitHub specifier in lockfile: ${specifier}`
4434
- );
4435
- continue;
4436
- }
4437
- const ghEntry = entry;
4438
- console.log(
4439
- `Installing ${specifier} (${ghEntry.gitRef}@${ghEntry.gitCommit.slice(0, 7)})...`
4584
+ const main = await prompt(rl, "entry point:", defaultMain);
4585
+ const capabilitiesStr = await prompt(
4586
+ rl,
4587
+ "capabilities (comma-separated):",
4588
+ defaultCapabilities
4440
4589
  );
4441
- let tarballBuffer;
4442
- let fromCache = false;
4443
- const cachedTarball = await readFromCache(cacheDir, ghEntry.integrity);
4444
- if (cachedTarball) {
4445
- tarballBuffer = cachedTarball;
4446
- fromCache = true;
4447
- } else {
4448
- try {
4449
- const specWithCommit = { ...parsed, ref: ghEntry.gitCommit };
4450
- const result = await downloadGitHubPackage(specWithCommit);
4451
- tarballBuffer = result.buffer;
4452
- if (result.integrity !== ghEntry.integrity) {
4453
- console.error(
4454
- ` Error: Checksum verification failed for ${specifier}`
4455
- );
4456
- if (options.frozenLockfile) {
4457
- process.exit(1);
4458
- }
4459
- continue;
4460
- }
4461
- await writeToCache(cacheDir, ghEntry.integrity, tarballBuffer);
4462
- } catch (error) {
4463
- if (error instanceof GitHubRateLimitError) {
4464
- console.error(` Error: ${error.message}`);
4465
- } else if (error instanceof GitHubPathNotFoundError) {
4466
- console.error(` Error: ${error.message}`);
4467
- } else if (error instanceof GitHubNotFoundError) {
4468
- console.error(` Error: ${error.message}`);
4469
- } else {
4470
- const message = error instanceof Error ? error.message : String(error);
4471
- console.error(` Error downloading ${specifier}: ${message}`);
4472
- }
4473
- continue;
4474
- }
4475
- }
4476
- try {
4477
- const destPath = await extractGitHubPackage(
4478
- parsed,
4479
- tarballBuffer,
4480
- skillsDir
4481
- );
4482
- console.log(
4483
- ` Installed to ${destPath}${fromCache ? " (from cache)" : ""}`
4484
- );
4485
- const skillName = getGitHubSkillName2(parsed);
4486
- installedSkills.push({
4487
- name: skillName,
4488
- sourcePath: getGitHubSkillPath(
4489
- parsed.owner,
4490
- parsed.repo,
4491
- parsed.path
4492
- )
4493
- });
4494
- } catch (error) {
4495
- const message = error instanceof Error ? error.message : String(error);
4496
- console.error(` Error extracting ${specifier}: ${message}`);
4590
+ const author = await prompt(rl, "author:", defaultAuthor);
4591
+ const license = await prompt(rl, "license:", defaultLicense);
4592
+ rl.close();
4593
+ const capabilities = capabilitiesStr ? capabilitiesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
4594
+ manifest = {
4595
+ $schema: PSPM_SCHEMA_URL,
4596
+ name,
4597
+ version: version3,
4598
+ description: description || void 0,
4599
+ author: author || void 0,
4600
+ license,
4601
+ type: "skill",
4602
+ capabilities,
4603
+ main,
4604
+ requirements: {
4605
+ pspm: ">=0.1.0"
4606
+ },
4607
+ files: [...DEFAULT_SKILL_FILES],
4608
+ dependencies: {},
4609
+ private: false
4610
+ };
4611
+ } catch (error) {
4612
+ rl.close();
4613
+ if (error instanceof Error && error.message.includes("readline was closed")) {
4614
+ console.log("\nAborted.");
4615
+ process.exit(0);
4497
4616
  }
4617
+ throw error;
4498
4618
  }
4499
4619
  }
4500
- if (installedSkills.length > 0 && agents[0] !== "none") {
4501
- const globalMode = isGlobalMode();
4620
+ if (!manifest.description) manifest.description = void 0;
4621
+ if (!manifest.author) manifest.author = void 0;
4622
+ if (manifest.capabilities?.length === 0) manifest.capabilities = void 0;
4623
+ const content = JSON.stringify(manifest, null, 2);
4624
+ console.log("");
4625
+ console.log(`About to write to ${pspmJsonPath}:`);
4626
+ console.log("");
4627
+ console.log(content);
4628
+ console.log("");
4629
+ if (!options.yes) {
4630
+ const rl = createInterface({
4631
+ input: process.stdin,
4632
+ output: process.stdout
4633
+ });
4634
+ const confirm2 = await prompt(rl, "Is this OK?", "yes");
4635
+ rl.close();
4636
+ if (confirm2.toLowerCase() !== "yes" && confirm2.toLowerCase() !== "y") {
4637
+ console.log("Aborted.");
4638
+ process.exit(0);
4639
+ }
4640
+ }
4641
+ await writeFile(pspmJsonPath, `${content}
4642
+ `);
4643
+ try {
4644
+ await stat(join(process.cwd(), "SKILL.md"));
4645
+ } catch {
4502
4646
  console.log(
4503
- `
4504
- Creating symlinks for agent(s): ${agents.join(", ")}${globalMode ? " (global)" : ""}...`
4647
+ "Note: Create a SKILL.md file with your skill's prompt content."
4505
4648
  );
4506
- await createAgentSymlinks(installedSkills, {
4507
- agents,
4508
- projectRoot: globalMode ? homedir() : process.cwd(),
4509
- agentConfigs,
4510
- global: globalMode
4511
- });
4512
- console.log(" Symlinks created.");
4513
4649
  }
4514
- const totalCount = packageCount + githubCount;
4515
- if (totalCount === 0) {
4516
- console.log("No skills to install.");
4517
- } else {
4518
- console.log(`
4519
- All ${totalCount} skill(s) installed.`);
4650
+ if (existingPkg) {
4651
+ console.log("Note: Values were derived from existing package.json.");
4652
+ console.log(" pspm.json is for publishing to PSPM registry.");
4653
+ console.log(" package.json can still be used for npm dependencies.");
4520
4654
  }
4521
4655
  } catch (error) {
4522
4656
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -4525,6 +4659,9 @@ All ${totalCount} skill(s) installed.`);
4525
4659
  }
4526
4660
  }
4527
4661
 
4662
+ // src/commands/index.ts
4663
+ init_install();
4664
+
4528
4665
  // src/commands/link.ts
4529
4666
  init_agents();
4530
4667
  init_lib();
@@ -4830,8 +4967,8 @@ function startCallbackServer(expectedState) {
4830
4967
  let resolveToken;
4831
4968
  let rejectToken;
4832
4969
  let timeoutId;
4833
- const tokenPromise = new Promise((resolve2, reject) => {
4834
- resolveToken = resolve2;
4970
+ const tokenPromise = new Promise((resolve3, reject) => {
4971
+ resolveToken = resolve3;
4835
4972
  rejectToken = reject;
4836
4973
  });
4837
4974
  const server = http.createServer((req, res) => {
@@ -5085,6 +5222,92 @@ async function migrate(options) {
5085
5222
  process.exit(1);
5086
5223
  }
5087
5224
  }
5225
+
5226
+ // src/commands/notebook.ts
5227
+ init_config();
5228
+ async function fetchApi(path, options = {}) {
5229
+ const config2 = await resolveConfig();
5230
+ const apiKey = await requireApiKey();
5231
+ const baseUrl = config2.registryUrl.replace(/\/api\/skills\/?$/, "");
5232
+ const headers = {
5233
+ "Content-Type": "application/json",
5234
+ ...options.headers
5235
+ };
5236
+ if (apiKey) {
5237
+ headers.Authorization = `Bearer ${apiKey}`;
5238
+ }
5239
+ return fetch(`${baseUrl}${path}`, { ...options, headers });
5240
+ }
5241
+ async function notebookUpload(filePath, options) {
5242
+ const absPath = resolve(filePath);
5243
+ const content = readFileSync(absPath, "utf-8");
5244
+ const name = basename(filePath).replace(/\.anyt\.md$|\.anyt$|\.md$/, "");
5245
+ const body = {
5246
+ name,
5247
+ content,
5248
+ description: options.description,
5249
+ visibility: options.visibility || "private"
5250
+ };
5251
+ const path = options.org ? `/api/notebooks/org/${options.org}` : "/api/notebooks";
5252
+ const response = await fetchApi(path, {
5253
+ method: "POST",
5254
+ body: JSON.stringify(body)
5255
+ });
5256
+ if (!response.ok) {
5257
+ const err = await response.json().catch(() => ({}));
5258
+ throw new Error(
5259
+ err.message || `Upload failed (${response.status})`
5260
+ );
5261
+ }
5262
+ const notebook = await response.json();
5263
+ console.log(
5264
+ `Uploaded: ${notebook.name} (${notebook.id})${options.org ? ` to org ${options.org}` : ""}`
5265
+ );
5266
+ }
5267
+ async function notebookList(options) {
5268
+ const path = options.org ? `/api/notebooks/org/${options.org}` : "/api/notebooks/-/mine";
5269
+ const response = await fetchApi(path);
5270
+ if (!response.ok) {
5271
+ throw new Error(`Failed to list notebooks (${response.status})`);
5272
+ }
5273
+ const notebooks = await response.json();
5274
+ if (notebooks.length === 0) {
5275
+ console.log("No notebooks found");
5276
+ return;
5277
+ }
5278
+ console.log(
5279
+ `
5280
+ ${"Name".padEnd(30)} ${"Cells".padEnd(8)} ${"Visibility".padEnd(12)} ${"Updated".padEnd(12)} ID`
5281
+ );
5282
+ console.log("-".repeat(90));
5283
+ for (const nb of notebooks) {
5284
+ const updated = new Date(nb.updatedAt).toLocaleDateString();
5285
+ console.log(
5286
+ `${nb.name.slice(0, 28).padEnd(30)} ${String(nb.cellCount).padEnd(8)} ${nb.visibility.padEnd(12)} ${updated.padEnd(12)} ${nb.id}`
5287
+ );
5288
+ }
5289
+ console.log(`
5290
+ ${notebooks.length} notebook(s)`);
5291
+ }
5292
+ async function notebookDownload(id, output) {
5293
+ const response = await fetchApi(`/api/notebooks/${id}`);
5294
+ if (!response.ok) {
5295
+ throw new Error(`Notebook not found (${response.status})`);
5296
+ }
5297
+ const notebook = await response.json();
5298
+ const outPath = output || `${notebook.slug}.anyt.md`;
5299
+ writeFileSync(outPath, notebook.content, "utf-8");
5300
+ console.log(`Downloaded: ${notebook.name} -> ${outPath}`);
5301
+ }
5302
+ async function notebookDelete(id) {
5303
+ const response = await fetchApi(`/api/notebooks/${id}`, {
5304
+ method: "DELETE"
5305
+ });
5306
+ if (!response.ok) {
5307
+ throw new Error(`Failed to delete notebook (${response.status})`);
5308
+ }
5309
+ console.log(`Notebook ${id} deleted`);
5310
+ }
5088
5311
  function resolveVersion3(range, availableVersions) {
5089
5312
  const sorted = availableVersions.filter((v) => semver2.valid(v)).sort((a, b) => semver2.rcompare(a, b));
5090
5313
  if (range === "latest") {
@@ -5101,7 +5324,7 @@ function getLatestVersion3(versions) {
5101
5324
  return valid5.sort((a, b) => semver2.rcompare(a, b))[0];
5102
5325
  }
5103
5326
 
5104
- // ../../packages/server/skill-registry/src/client/outdated.ts
5327
+ // ../../packages/features/skill-registry/src/client/outdated.ts
5105
5328
  function createOutdatedChecker(config2) {
5106
5329
  const { registryUrl, apiKey, githubToken } = config2;
5107
5330
  async function fetchWithAuth(url, token) {
@@ -5121,14 +5344,26 @@ function createOutdatedChecker(config2) {
5121
5344
  const response = await fetchWithAuth(url, apiKey);
5122
5345
  return await response.json();
5123
5346
  }
5347
+ async function fetchGithubRegistryVersions(owner, repo, skillname) {
5348
+ const url = `${registryUrl}/@github/${owner}/${repo}/${skillname}/versions`;
5349
+ const response = await fetchWithAuth(url, apiKey);
5350
+ return await response.json();
5351
+ }
5124
5352
  async function checkRegistryPackage(specifier, entry, versionRange) {
5125
- const match = specifier.match(/^@user\/([^/]+)\/([^/]+)$/);
5126
- if (!match) {
5353
+ const userMatch = specifier.match(/^@(?:user|org)\/([^/]+)\/([^/]+)$/);
5354
+ const githubMatch = specifier.match(/^@github\/([^/]+)\/([^/]+)\/([^/]+)$/);
5355
+ if (!userMatch && !githubMatch) {
5127
5356
  throw new Error(`Invalid registry specifier: ${specifier}`);
5128
5357
  }
5129
- const [, username, name] = match;
5358
+ const isGithubRegistry = !!githubMatch;
5359
+ const username = userMatch ? userMatch[1] : "";
5360
+ const name = userMatch ? userMatch[2] : "";
5130
5361
  try {
5131
- const versions = await fetchRegistryVersions(username, name);
5362
+ const versions = isGithubRegistry && githubMatch ? await fetchGithubRegistryVersions(
5363
+ githubMatch[1],
5364
+ githubMatch[2],
5365
+ githubMatch[3]
5366
+ ) : await fetchRegistryVersions(username, name);
5132
5367
  const versionStrings = versions.map((v) => v.version);
5133
5368
  const range = versionRange || "*";
5134
5369
  const wanted = resolveVersion3(range, versionStrings);
@@ -5351,10 +5586,11 @@ function printTable(results) {
5351
5586
  init_api_client();
5352
5587
  init_config();
5353
5588
  init_errors();
5589
+ init_encryption();
5354
5590
  init_lib();
5355
5591
  var exec = promisify(exec$1);
5356
5592
  function confirm(question) {
5357
- return new Promise((resolve2) => {
5593
+ return new Promise((resolve3) => {
5358
5594
  const rl = createInterface({
5359
5595
  input: process.stdin,
5360
5596
  output: process.stdout
@@ -5362,7 +5598,7 @@ function confirm(question) {
5362
5598
  rl.question(`${question} (y/N) `, (answer) => {
5363
5599
  rl.close();
5364
5600
  const normalized = answer.trim().toLowerCase();
5365
- resolve2(normalized === "y" || normalized === "yes");
5601
+ resolve3(normalized === "y" || normalized === "yes");
5366
5602
  });
5367
5603
  });
5368
5604
  }
@@ -5472,8 +5708,10 @@ async function publishCommand(options) {
5472
5708
  }
5473
5709
  console.log("");
5474
5710
  }
5711
+ const nameParts = manifest.name.split("/");
5712
+ const bareName = nameParts[nameParts.length - 1];
5475
5713
  const packageJson2 = {
5476
- name: manifest.name,
5714
+ name: bareName,
5477
5715
  version: manifest.version,
5478
5716
  description: manifest.description,
5479
5717
  files: manifest.files,
@@ -5589,19 +5827,53 @@ async function publishCommand(options) {
5589
5827
  process.exit(0);
5590
5828
  }
5591
5829
  console.log("");
5830
+ let finalTarballBase64 = tarballBase64;
5831
+ if (options.access === "private" || options.access === "team") {
5832
+ const config2 = await Promise.resolve().then(() => (init_config(), config_exports)).then((m) => m.resolveConfig());
5833
+ const namespace2 = options.org ? "org" : "user";
5834
+ const owner2 = options.org ?? config2.username;
5835
+ if (!owner2) {
5836
+ console.error(
5837
+ "Error: Cannot determine package owner. Run 'pspm login' first."
5838
+ );
5839
+ process.exit(1);
5840
+ }
5841
+ const scope = getEncryptionScope(namespace2, owner2);
5842
+ const encryptionKey = await getEncryptionKey(scope);
5843
+ if (encryptionKey) {
5844
+ console.log(`pspm notice Encrypting package (scope: ${scope})`);
5845
+ const { encrypted, metadata } = encryptBuffer(
5846
+ tarballBuffer,
5847
+ encryptionKey,
5848
+ scope
5849
+ );
5850
+ finalTarballBase64 = encrypted.toString("base64");
5851
+ packageJson2.encryption = metadata;
5852
+ console.log(
5853
+ "pspm notice Package encrypted with client-side encryption"
5854
+ );
5855
+ } else {
5856
+ console.log(
5857
+ `pspm warn No encryption key found for scope "${scope}". Publishing without encryption.`
5858
+ );
5859
+ console.log(
5860
+ `pspm warn To encrypt, run: pspm config set-encryption-key ${scope} <your-passphrase>`
5861
+ );
5862
+ }
5863
+ }
5592
5864
  console.log(`pspm notice Publishing to ${registryUrl} with tag latest`);
5593
5865
  configure2({ registryUrl, apiKey });
5594
5866
  let response;
5595
5867
  if (options.org) {
5596
5868
  response = await publishOrgSkill(options.org, {
5597
5869
  manifest: packageJson2,
5598
- tarballBase64,
5870
+ tarballBase64: finalTarballBase64,
5599
5871
  visibility: options.access
5600
5872
  });
5601
5873
  } else {
5602
5874
  response = await publishSkill({
5603
5875
  manifest: packageJson2,
5604
- tarballBase64,
5876
+ tarballBase64: finalTarballBase64,
5605
5877
  visibility: options.access
5606
5878
  });
5607
5879
  }
@@ -5627,7 +5899,9 @@ async function publishCommand(options) {
5627
5899
  `+ @${namespace}/${owner}/${result.skill.name}@${result.version.version}`
5628
5900
  );
5629
5901
  console.log(`Checksum: ${result.version.checksum}`);
5630
- console.log(`Visibility: ${visibilityIcon} ${visibility}`);
5902
+ console.log(
5903
+ `Visibility: ${visibilityIcon} ${visibility}${packageJson2.encryption ? " (encrypted)" : ""}`
5904
+ );
5631
5905
  if (visibility === "public") {
5632
5906
  console.log(
5633
5907
  "Note: Public packages cannot be made private. This is irreversible."
@@ -5678,8 +5952,8 @@ async function removeRegistry(specifier, agents, agentConfigs) {
5678
5952
  console.error(`Error: Invalid skill specifier: ${specifier}`);
5679
5953
  process.exit(1);
5680
5954
  }
5681
- const { namespace, owner, name } = parsed;
5682
- const fullName = `@${namespace}/${owner}/${name}`;
5955
+ const { namespace, owner, name, subname } = parsed;
5956
+ const fullName = namespace === "github" && subname ? `@github/${owner}/${name}/${subname}` : `@${namespace}/${owner}/${name}`;
5683
5957
  console.log(`Removing ${fullName}...`);
5684
5958
  const removedFromLockfile = await removeFromLockfile(fullName);
5685
5959
  const removedFromManifest = await removeDependency(fullName);
@@ -5687,13 +5961,21 @@ async function removeRegistry(specifier, agents, agentConfigs) {
5687
5961
  console.error(`Error: ${fullName} not found in lockfile or pspm.json`);
5688
5962
  process.exit(1);
5689
5963
  }
5690
- await removeAgentSymlinks(name, {
5964
+ const symlinkName = subname ?? name;
5965
+ await removeAgentSymlinks(symlinkName, {
5691
5966
  agents,
5692
5967
  projectRoot: process.cwd(),
5693
5968
  agentConfigs
5694
5969
  });
5695
5970
  const skillsDir = getSkillsDir();
5696
- const destDir = namespace === "org" ? join(skillsDir, "_org", owner, name) : join(skillsDir, owner, name);
5971
+ let destDir;
5972
+ if (namespace === "github" && subname) {
5973
+ destDir = join(skillsDir, "_github-registry", owner, name, subname);
5974
+ } else if (namespace === "org") {
5975
+ destDir = join(skillsDir, "_org", owner, name);
5976
+ } else {
5977
+ destDir = join(skillsDir, owner, name);
5978
+ }
5697
5979
  try {
5698
5980
  await rm(destDir, { recursive: true, force: true });
5699
5981
  } catch {
@@ -5733,7 +6015,9 @@ async function removeByShortName(shortName, agents, agentConfigs) {
5733
6015
  const registrySkills = await listLockfileSkills();
5734
6016
  const foundRegistry = registrySkills.find((s) => {
5735
6017
  const parsed = parseRegistrySpecifier2(s.name);
5736
- return parsed && parsed.name === shortName;
6018
+ if (!parsed) return false;
6019
+ const effectiveName = parsed.subname ?? parsed.name;
6020
+ return effectiveName === shortName;
5737
6021
  });
5738
6022
  if (foundRegistry) {
5739
6023
  await removeRegistry(foundRegistry.name, agents, agentConfigs);
@@ -5835,6 +6119,396 @@ function formatDownloads(count) {
5835
6119
  return String(count);
5836
6120
  }
5837
6121
 
6122
+ // src/commands/skill-list.ts
6123
+ init_api_client();
6124
+ init_config();
6125
+ function parseListSpecifier(specifier) {
6126
+ const match = specifier.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
6127
+ if (!match) return null;
6128
+ return {
6129
+ ownerType: match[1],
6130
+ ownerName: match[2],
6131
+ listName: match[3]
6132
+ };
6133
+ }
6134
+ function extractErrorMessage(text) {
6135
+ try {
6136
+ const json = JSON.parse(text);
6137
+ if (typeof json.message === "string") return json.message;
6138
+ if (typeof json.error === "string") return json.error;
6139
+ if (typeof json.error?.message === "string") return json.error.message;
6140
+ } catch {
6141
+ }
6142
+ return text;
6143
+ }
6144
+ async function getBaseUrl() {
6145
+ const config2 = await resolveConfig();
6146
+ return config2.registryUrl.replace(/\/api\/skills\/?$/, "");
6147
+ }
6148
+ async function getAuthHeaders() {
6149
+ const apiKey = await requireApiKey();
6150
+ return {
6151
+ "Content-Type": "application/json",
6152
+ Authorization: `Bearer ${apiKey}`
6153
+ };
6154
+ }
6155
+ async function getOptionalAuthHeaders() {
6156
+ const config2 = await resolveConfig();
6157
+ const apiKey = getTokenForRegistry(config2, config2.registryUrl);
6158
+ const headers = {
6159
+ "Content-Type": "application/json"
6160
+ };
6161
+ if (apiKey) {
6162
+ headers.Authorization = `Bearer ${apiKey}`;
6163
+ }
6164
+ return headers;
6165
+ }
6166
+ async function skillListList(options) {
6167
+ const baseUrl = await getBaseUrl();
6168
+ const config2 = await resolveConfig();
6169
+ let path;
6170
+ if (options.org) {
6171
+ path = `/api/skill-lists/lists/@org/${options.org}`;
6172
+ } else {
6173
+ if (!config2.username) {
6174
+ console.error("Error: Not logged in. Run `pspm login` first.");
6175
+ process.exit(1);
6176
+ }
6177
+ path = `/api/skill-lists/lists/@user/${config2.username}`;
6178
+ }
6179
+ const headers = await getOptionalAuthHeaders();
6180
+ const response = await fetch(`${baseUrl}${path}`, { headers });
6181
+ if (!response.ok) {
6182
+ const errorText = await response.text();
6183
+ console.error(
6184
+ `Error: Failed to list skill lists (${response.status}): ${extractErrorMessage(errorText)}`
6185
+ );
6186
+ process.exit(1);
6187
+ }
6188
+ const lists = await response.json();
6189
+ if (options.json) {
6190
+ console.log(JSON.stringify(lists, null, 2));
6191
+ return;
6192
+ }
6193
+ if (lists.length === 0) {
6194
+ console.log("No skill lists found.");
6195
+ return;
6196
+ }
6197
+ const ownerLabel = options.org ? `@org/${options.org}` : `@user/${config2.username}`;
6198
+ console.log(`
6199
+ Skill lists for ${ownerLabel}:
6200
+ `);
6201
+ console.log(
6202
+ `${"Name".padEnd(35)} ${"Skills".padEnd(8)} ${"Visibility".padEnd(12)} Description`
6203
+ );
6204
+ console.log("-".repeat(90));
6205
+ for (const list2 of lists) {
6206
+ const desc = list2.description ? list2.description.slice(0, 30) : "";
6207
+ console.log(
6208
+ `${list2.name.slice(0, 33).padEnd(35)} ${String(list2.itemCount).padEnd(8)} ${list2.visibility.padEnd(12)} ${desc}`
6209
+ );
6210
+ }
6211
+ console.log(`
6212
+ ${lists.length} list(s)`);
6213
+ }
6214
+ async function skillListCreate(name, options) {
6215
+ const baseUrl = await getBaseUrl();
6216
+ const headers = await getAuthHeaders();
6217
+ const config2 = await resolveConfig();
6218
+ const visibility = options.visibility || "private";
6219
+ if (visibility !== "public" && visibility !== "private") {
6220
+ console.error('Error: --visibility must be "public" or "private"');
6221
+ process.exit(1);
6222
+ }
6223
+ let path;
6224
+ if (options.org) {
6225
+ path = `/api/skill-lists/lists/@org/${options.org}`;
6226
+ } else {
6227
+ if (!config2.username) {
6228
+ console.error("Error: Not logged in. Run `pspm login` first.");
6229
+ process.exit(1);
6230
+ }
6231
+ path = `/api/skill-lists/lists/@user/${config2.username}`;
6232
+ }
6233
+ const body = { name, visibility };
6234
+ if (options.description) {
6235
+ body.description = options.description;
6236
+ }
6237
+ const response = await fetch(`${baseUrl}${path}`, {
6238
+ method: "POST",
6239
+ headers,
6240
+ body: JSON.stringify(body)
6241
+ });
6242
+ if (!response.ok) {
6243
+ const errorText = await response.text();
6244
+ console.error(
6245
+ `Error: Failed to create list (${response.status}): ${extractErrorMessage(errorText)}`
6246
+ );
6247
+ process.exit(1);
6248
+ }
6249
+ const list2 = await response.json();
6250
+ const ownerLabel = options.org ? `@org/${options.org}` : `@user/${config2.username}`;
6251
+ console.log(`Created list: ${ownerLabel}/${list2.name} (${list2.visibility})`);
6252
+ console.log(
6253
+ `
6254
+ Install command: pspm skill-list install ${ownerLabel}/${list2.name}`
6255
+ );
6256
+ }
6257
+ async function skillListShow(specifier, options) {
6258
+ const parsed = parseListSpecifier(specifier);
6259
+ if (!parsed) {
6260
+ console.error(
6261
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6262
+ );
6263
+ process.exit(1);
6264
+ }
6265
+ const config2 = await resolveConfig();
6266
+ configure2({
6267
+ registryUrl: config2.registryUrl,
6268
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
6269
+ });
6270
+ const response = await fetchSkillList(
6271
+ parsed.ownerType,
6272
+ parsed.ownerName,
6273
+ parsed.listName
6274
+ );
6275
+ if (response.status !== 200 || !response.data) {
6276
+ const errorMsg = response.status === 404 ? `List "${specifier}" not found or is private.` : response.error || "Failed to fetch list";
6277
+ console.error(`Error: ${errorMsg}`);
6278
+ process.exit(1);
6279
+ }
6280
+ const list2 = response.data;
6281
+ if (options.json) {
6282
+ console.log(JSON.stringify(list2, null, 2));
6283
+ return;
6284
+ }
6285
+ console.log(`
6286
+ ${list2.name}`);
6287
+ if (list2.description) {
6288
+ console.log(` ${list2.description}`);
6289
+ }
6290
+ console.log(` Visibility: ${list2.visibility}`);
6291
+ console.log(` Owner: @${list2.ownerType}/${list2.ownerName}`);
6292
+ console.log(` Skills: ${list2.items.length}`);
6293
+ if (list2.items.length > 0) {
6294
+ console.log("");
6295
+ for (const item of list2.items) {
6296
+ const ns = item.namespace === "org" ? "org" : "user";
6297
+ const spec = `@${ns}/${item.ownerName}/${item.skillName}`;
6298
+ const ver = item.pinnedVersion ? `@${item.pinnedVersion}` : "";
6299
+ console.log(` - ${spec}${ver}`);
6300
+ }
6301
+ }
6302
+ console.log(`
6303
+ Install all: pspm skill-list install ${specifier}`);
6304
+ }
6305
+ async function skillListDelete(specifier) {
6306
+ const parsed = parseListSpecifier(specifier);
6307
+ if (!parsed) {
6308
+ console.error(
6309
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6310
+ );
6311
+ process.exit(1);
6312
+ }
6313
+ const baseUrl = await getBaseUrl();
6314
+ const headers = await getAuthHeaders();
6315
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}`;
6316
+ const response = await fetch(`${baseUrl}${path}`, {
6317
+ method: "DELETE",
6318
+ headers
6319
+ });
6320
+ if (!response.ok) {
6321
+ const errorText = await response.text();
6322
+ if (response.status === 404) {
6323
+ console.error(`Error: List "${specifier}" not found.`);
6324
+ } else {
6325
+ console.error(
6326
+ `Error: Failed to delete list (${response.status}): ${extractErrorMessage(errorText)}`
6327
+ );
6328
+ }
6329
+ process.exit(1);
6330
+ }
6331
+ console.log(`Deleted list: ${specifier}`);
6332
+ }
6333
+ async function skillListUpdate(specifier, options) {
6334
+ const parsed = parseListSpecifier(specifier);
6335
+ if (!parsed) {
6336
+ console.error(
6337
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6338
+ );
6339
+ process.exit(1);
6340
+ }
6341
+ if (!options.description && !options.visibility) {
6342
+ console.error(
6343
+ "Error: Provide at least one of --description or --visibility"
6344
+ );
6345
+ process.exit(1);
6346
+ }
6347
+ if (options.visibility && options.visibility !== "public" && options.visibility !== "private") {
6348
+ console.error('Error: --visibility must be "public" or "private"');
6349
+ process.exit(1);
6350
+ }
6351
+ const baseUrl = await getBaseUrl();
6352
+ const headers = await getAuthHeaders();
6353
+ const body = {};
6354
+ if (options.description !== void 0) {
6355
+ body.description = options.description;
6356
+ }
6357
+ if (options.visibility) {
6358
+ body.visibility = options.visibility;
6359
+ }
6360
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}`;
6361
+ const response = await fetch(`${baseUrl}${path}`, {
6362
+ method: "PUT",
6363
+ headers,
6364
+ body: JSON.stringify(body)
6365
+ });
6366
+ if (!response.ok) {
6367
+ const errorText = await response.text();
6368
+ console.error(
6369
+ `Error: Failed to update list (${response.status}): ${extractErrorMessage(errorText)}`
6370
+ );
6371
+ process.exit(1);
6372
+ }
6373
+ console.log(`Updated list: ${specifier}`);
6374
+ }
6375
+ async function skillListAddSkill(specifier, skillSpecifiers, options) {
6376
+ const parsed = parseListSpecifier(specifier);
6377
+ if (!parsed) {
6378
+ console.error(
6379
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6380
+ );
6381
+ process.exit(1);
6382
+ }
6383
+ const baseUrl = await getBaseUrl();
6384
+ const headers = await getAuthHeaders();
6385
+ const config2 = await resolveConfig();
6386
+ configure2({
6387
+ registryUrl: config2.registryUrl,
6388
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
6389
+ });
6390
+ for (const skillSpec of skillSpecifiers) {
6391
+ const skillId = await resolveSkillId(baseUrl, headers, skillSpec);
6392
+ if (!skillId) {
6393
+ console.error(`Error: Skill "${skillSpec}" not found.`);
6394
+ continue;
6395
+ }
6396
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}/items`;
6397
+ const addBody = { skillId };
6398
+ if (options.note) {
6399
+ addBody.note = options.note;
6400
+ }
6401
+ const response = await fetch(`${baseUrl}${path}`, {
6402
+ method: "POST",
6403
+ headers,
6404
+ body: JSON.stringify(addBody)
6405
+ });
6406
+ if (!response.ok) {
6407
+ const errorText = await response.text();
6408
+ if (response.status === 409) {
6409
+ console.log(`Already in list: ${skillSpec}`);
6410
+ } else {
6411
+ console.error(
6412
+ `Error: Failed to add "${skillSpec}" (${response.status}): ${extractErrorMessage(errorText)}`
6413
+ );
6414
+ }
6415
+ continue;
6416
+ }
6417
+ console.log(`Added: ${skillSpec}`);
6418
+ }
6419
+ }
6420
+ async function skillListRemoveSkill(specifier, skillSpecifier) {
6421
+ const parsed = parseListSpecifier(specifier);
6422
+ if (!parsed) {
6423
+ console.error(
6424
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6425
+ );
6426
+ process.exit(1);
6427
+ }
6428
+ const config2 = await resolveConfig();
6429
+ configure2({
6430
+ registryUrl: config2.registryUrl,
6431
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
6432
+ });
6433
+ const listResponse = await fetchSkillList(
6434
+ parsed.ownerType,
6435
+ parsed.ownerName,
6436
+ parsed.listName
6437
+ );
6438
+ if (listResponse.status !== 200 || !listResponse.data) {
6439
+ console.error(`Error: List "${specifier}" not found.`);
6440
+ process.exit(1);
6441
+ }
6442
+ const item = listResponse.data.items.find((i) => {
6443
+ const ns = i.namespace === "org" ? "org" : "user";
6444
+ const fullSpec = `@${ns}/${i.ownerName}/${i.skillName}`;
6445
+ return fullSpec === skillSpecifier || i.skillName === skillSpecifier || `${i.ownerName}/${i.skillName}` === skillSpecifier;
6446
+ });
6447
+ if (!item) {
6448
+ console.error(
6449
+ `Error: Skill "${skillSpecifier}" not found in list "${specifier}".`
6450
+ );
6451
+ process.exit(1);
6452
+ }
6453
+ const baseUrl = await getBaseUrl();
6454
+ const headers = await getAuthHeaders();
6455
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}/items/${item.id}`;
6456
+ const response = await fetch(`${baseUrl}${path}`, {
6457
+ method: "DELETE",
6458
+ headers
6459
+ });
6460
+ if (!response.ok) {
6461
+ const errorText = await response.text();
6462
+ console.error(
6463
+ `Error: Failed to remove skill (${response.status}): ${extractErrorMessage(errorText)}`
6464
+ );
6465
+ process.exit(1);
6466
+ }
6467
+ console.log(`Removed: ${skillSpecifier} from ${specifier}`);
6468
+ }
6469
+ async function skillListInstall(specifier, options) {
6470
+ const { install: install2 } = await Promise.resolve().then(() => (init_install(), install_exports));
6471
+ await install2([], {
6472
+ list: specifier,
6473
+ agent: options.agent,
6474
+ yes: options.yes,
6475
+ global: options.global,
6476
+ dir: options.dir
6477
+ });
6478
+ }
6479
+ async function resolveSkillId(baseUrl, headers, specifier) {
6480
+ const match = specifier.match(/^@(user|org|github)\/(.+)$/);
6481
+ let search2;
6482
+ let namespace;
6483
+ if (match) {
6484
+ namespace = match[1];
6485
+ const parts = match[2].split("/");
6486
+ search2 = parts[parts.length - 1];
6487
+ } else {
6488
+ search2 = specifier;
6489
+ }
6490
+ const params = new URLSearchParams({ search: search2, limit: "5" });
6491
+ if (namespace) {
6492
+ params.set("namespace", namespace);
6493
+ }
6494
+ const response = await fetch(`${baseUrl}/api/skills/-/explore?${params}`, {
6495
+ headers
6496
+ });
6497
+ if (!response.ok) return null;
6498
+ const data = await response.json();
6499
+ if (!data.skills || data.skills.length === 0) return null;
6500
+ if (match) {
6501
+ const parts = match[2].split("/");
6502
+ const skillName = parts[parts.length - 1];
6503
+ const ownerName = parts.length >= 2 ? parts[parts.length - 2] : void 0;
6504
+ const exact = data.skills.find(
6505
+ (s) => s.name === skillName && (!ownerName || s.username === ownerName) && (!namespace || s.namespace === namespace)
6506
+ );
6507
+ if (exact) return exact.id;
6508
+ }
6509
+ return data.skills[0].id;
6510
+ }
6511
+
5838
6512
  // src/commands/unpublish.ts
5839
6513
  init_api_client();
5840
6514
  init_config();
@@ -6225,6 +6899,28 @@ configCmd.command("init").description("Create a .pspmrc file in the current dire
6225
6899
  registry: options.registry
6226
6900
  });
6227
6901
  });
6902
+ configCmd.command("set-encryption-key <scope> <passphrase>").description(
6903
+ "Set encryption key for a scope (e.g., pspm config set-encryption-key @user/alice my-secret)"
6904
+ ).action(async (scope, passphrase) => {
6905
+ const { setEncryptionKey: setEncryptionKey2 } = await Promise.resolve().then(() => (init_config(), config_exports));
6906
+ await setEncryptionKey2(scope, passphrase);
6907
+ console.log(`Encryption key set for scope "${scope}"`);
6908
+ });
6909
+ configCmd.command("remove-encryption-key <scope>").description("Remove encryption key for a scope").action(async (scope) => {
6910
+ const { removeEncryptionKey: removeEncryptionKey2 } = await Promise.resolve().then(() => (init_config(), config_exports));
6911
+ await removeEncryptionKey2(scope);
6912
+ console.log(`Encryption key removed for scope "${scope}"`);
6913
+ });
6914
+ configCmd.command("get-encryption-key <scope>").description("Check if an encryption key is set for a scope").action(async (scope) => {
6915
+ const { getEncryptionKey: getEncryptionKey2 } = await Promise.resolve().then(() => (init_config(), config_exports));
6916
+ const key = await getEncryptionKey2(scope);
6917
+ if (key) {
6918
+ const masked = `${key.substring(0, 4)}***`;
6919
+ console.log(`Encryption key for "${scope}": ${masked} (set)`);
6920
+ } else {
6921
+ console.log(`No encryption key set for "${scope}"`);
6922
+ }
6923
+ });
6228
6924
  program.command("upgrade").description("Upgrade pspm to the latest version").action(async () => {
6229
6925
  await upgrade();
6230
6926
  });
@@ -6367,6 +7063,73 @@ program.command("deprecate <specifier> [message]").description(
6367
7063
  ).option("--undo", "Remove deprecation status").action(async (specifier, message, options) => {
6368
7064
  await deprecate(specifier, message, { undo: options.undo });
6369
7065
  });
7066
+ var skillListCmd = program.command("skill-list").description("Manage skill lists (collections of skills)");
7067
+ skillListCmd.command("list").description("List your skill lists").option("--org <orgname>", "List organization skill lists").option("--json", "Output as JSON").action(async (options) => {
7068
+ await skillListList({ org: options.org, json: options.json });
7069
+ });
7070
+ skillListCmd.command("create <name>").description("Create a new skill list").option("-d, --description <description>", "List description").option(
7071
+ "--visibility <visibility>",
7072
+ "Visibility: private or public",
7073
+ "private"
7074
+ ).option("--org <orgname>", "Create under an organization").action(async (name, options) => {
7075
+ await skillListCreate(name, {
7076
+ description: options.description,
7077
+ visibility: options.visibility,
7078
+ org: options.org
7079
+ });
7080
+ });
7081
+ skillListCmd.command("show <specifier>").description("Show skill list details (e.g. @user/alice/my-tools)").option("--json", "Output as JSON").action(async (specifier, options) => {
7082
+ await skillListShow(specifier, { json: options.json });
7083
+ });
7084
+ skillListCmd.command("delete <specifier>").description("Delete a skill list").action(async (specifier) => {
7085
+ await skillListDelete(specifier);
7086
+ });
7087
+ 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) => {
7088
+ await skillListUpdate(specifier, {
7089
+ description: options.description,
7090
+ visibility: options.visibility
7091
+ });
7092
+ });
7093
+ skillListCmd.command("add-skill <specifier> <skills...>").description(
7094
+ "Add skills to a list (e.g. pspm skill-list add-skill @user/me/my-list @user/alice/tool)"
7095
+ ).option("--note <note>", "Note for the added skill").action(async (specifier, skills, options) => {
7096
+ await skillListAddSkill(specifier, skills, {
7097
+ note: options.note
7098
+ });
7099
+ });
7100
+ skillListCmd.command("remove-skill <specifier> <skill>").description("Remove a skill from a list").action(async (specifier, skill) => {
7101
+ await skillListRemoveSkill(specifier, skill);
7102
+ });
7103
+ skillListCmd.command("install <specifier>").description(
7104
+ "Install all skills from a list (e.g. pspm skill-list install @user/alice/my-tools)"
7105
+ ).option(
7106
+ "--agent <agents>",
7107
+ 'Comma-separated agents for symlinks (default: all agents, use "none" to skip)'
7108
+ ).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) => {
7109
+ await skillListInstall(specifier, {
7110
+ agent: options.agent,
7111
+ yes: options.yes,
7112
+ global: options.global,
7113
+ dir: options.dir
7114
+ });
7115
+ });
7116
+ var notebookCmd = program.command("notebook").description("Manage AnyT notebooks");
7117
+ notebookCmd.command("upload <file>").description("Upload a .anyt notebook").option("--org <orgname>", "Upload to an organization").option(
7118
+ "--visibility <visibility>",
7119
+ "Visibility: private, team, or public",
7120
+ "private"
7121
+ ).option("--description <description>", "Notebook description").action(async (file, options) => {
7122
+ await notebookUpload(file, options);
7123
+ });
7124
+ notebookCmd.command("list").description("List notebooks").option("--org <orgname>", "List organization notebooks").action(async (options) => {
7125
+ await notebookList(options);
7126
+ });
7127
+ notebookCmd.command("download <id>").description("Download a notebook by ID").option("-o, --output <path>", "Output file path").action(async (id, options) => {
7128
+ await notebookDownload(id, options.output);
7129
+ });
7130
+ notebookCmd.command("delete <id>").description("Delete a notebook by ID").action(async (id) => {
7131
+ await notebookDelete(id);
7132
+ });
6370
7133
  await program.parseAsync();
6371
7134
  var executedCommand = program.args[0];
6372
7135
  if (executedCommand !== "upgrade") {