@anytio/pspm 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { createHash, randomBytes } from 'crypto';
8
8
  import * as semver2 from 'semver';
9
9
  import semver2__default from 'semver';
10
10
  import { checkbox } from '@inquirer/prompts';
11
- import { readFileSync } from 'fs';
11
+ import { readFileSync, writeFileSync } from 'fs';
12
12
  import { fileURLToPath, URL as URL$1 } from 'url';
13
13
  import { Command } from 'commander';
14
14
  import { createInterface } from 'readline';
@@ -968,7 +968,9 @@ function validateManifest(manifest) {
968
968
  if (!manifest.version) {
969
969
  return { valid: false, error: "Manifest must have a 'version' field" };
970
970
  }
971
- if (!/^[a-z][a-z0-9_-]*$/.test(manifest.name)) {
971
+ const parts = manifest.name.split("/");
972
+ const bareName = parts[parts.length - 1];
973
+ if (!/^[a-z][a-z0-9_-]*$/.test(bareName)) {
972
974
  return {
973
975
  valid: false,
974
976
  error: "Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores"
@@ -3329,103 +3331,615 @@ var init_add = __esm({
3329
3331
  }
3330
3332
  });
3331
3333
 
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("../");
3334
+ // src/commands/install.ts
3335
+ var install_exports = {};
3336
+ __export(install_exports, {
3337
+ install: () => install
3338
+ });
3339
+ function getCacheFilePath(cacheDir, integrity) {
3340
+ const match = integrity.match(/^sha256-(.+)$/);
3341
+ if (!match) {
3342
+ throw new Error(`Invalid integrity format: ${integrity}`);
3343
+ }
3344
+ const base64Hash = match[1];
3345
+ const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
3346
+ return join(cacheDir, `sha256-${hexHash}.tgz`);
3338
3347
  }
3339
- async function access(specifier, options) {
3348
+ async function readFromCache(cacheDir, integrity) {
3340
3349
  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);
3350
+ const cachePath = getCacheFilePath(cacheDir, integrity);
3351
+ const data = await readFile(cachePath);
3352
+ const actualIntegrity = `sha256-${createHash("sha256").update(data).digest("base64")}`;
3353
+ if (actualIntegrity !== integrity) {
3354
+ await rm(cachePath, { force: true });
3355
+ return null;
3346
3356
  }
3347
- if (!options.public && !options.private) {
3348
- console.error("Error: Must specify either --public or --private");
3349
- process.exit(1);
3357
+ return data;
3358
+ } catch {
3359
+ return null;
3360
+ }
3361
+ }
3362
+ async function writeToCache(cacheDir, integrity, data) {
3363
+ try {
3364
+ await mkdir(cacheDir, { recursive: true });
3365
+ const cachePath = getCacheFilePath(cacheDir, integrity);
3366
+ await writeFile(cachePath, data);
3367
+ } catch {
3368
+ }
3369
+ }
3370
+ async function install(specifiers, options) {
3371
+ if (options.global) {
3372
+ const { setGlobalMode: setGlobalMode2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3373
+ setGlobalMode2(true);
3374
+ }
3375
+ if (options.list) {
3376
+ const listSpecifiers = await resolveListToSpecifiers(options.list);
3377
+ if (listSpecifiers.length === 0) {
3378
+ console.log("No skills in the list to install.");
3379
+ return;
3350
3380
  }
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);
3381
+ const allSpecifiers = [...specifiers, ...listSpecifiers];
3382
+ const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
3383
+ await add2(allSpecifiers, {
3384
+ save: true,
3385
+ agent: options.agent,
3386
+ yes: options.yes,
3387
+ global: options.global
3388
+ });
3389
+ return;
3390
+ }
3391
+ if (specifiers.length > 0) {
3392
+ const { add: add2 } = await Promise.resolve().then(() => (init_add(), add_exports));
3393
+ await add2(specifiers, {
3394
+ save: true,
3395
+ agent: options.agent,
3396
+ yes: options.yes,
3397
+ global: options.global
3398
+ });
3399
+ return;
3400
+ }
3401
+ await installFromLockfile(options);
3402
+ }
3403
+ async function resolveListToSpecifiers(listSpec) {
3404
+ const match = listSpec.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
3405
+ if (!match) {
3406
+ console.error(
3407
+ `Error: Invalid list specifier "${listSpec}". Use format: @user/{username}/{list-name} or @org/{orgname}/{list-name}`
3408
+ );
3409
+ process.exit(1);
3410
+ }
3411
+ const [, ownerType, ownerName, listName] = match;
3412
+ const config2 = await resolveConfig();
3413
+ configure2({
3414
+ registryUrl: config2.registryUrl,
3415
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
3416
+ });
3417
+ console.log(
3418
+ `Fetching skill list @${ownerType}/${ownerName}/${listName}...
3419
+ `
3420
+ );
3421
+ const { fetchSkillList: fetchSkillList2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
3422
+ const response = await fetchSkillList2(
3423
+ ownerType,
3424
+ ownerName,
3425
+ listName
3426
+ );
3427
+ if (response.status !== 200 || !response.data) {
3428
+ const errorMsg = response.status === 404 ? `List "@${ownerType}/${ownerName}/${listName}" not found or is private.` : response.error || "Failed to fetch list";
3429
+ console.error(`Error: ${errorMsg}`);
3430
+ process.exit(1);
3431
+ }
3432
+ const list2 = response.data;
3433
+ console.log(
3434
+ `List: ${list2.name}${list2.description ? ` \u2014 ${list2.description}` : ""}`
3435
+ );
3436
+ console.log(`Skills: ${list2.items.length}
3437
+ `);
3438
+ const specifiers = [];
3439
+ for (const item of list2.items) {
3440
+ const ns = item.namespace === "org" ? "org" : "user";
3441
+ let spec = `@${ns}/${item.ownerName}/${item.skillName}`;
3442
+ if (item.pinnedVersion) {
3443
+ spec += `@${item.pinnedVersion}`;
3444
+ }
3445
+ specifiers.push(spec);
3446
+ }
3447
+ return specifiers;
3448
+ }
3449
+ async function installFromLockfile(options) {
3450
+ try {
3451
+ const config2 = await resolveConfig();
3452
+ const registryUrl = config2.registryUrl;
3453
+ const apiKey = getTokenForRegistry(config2, registryUrl);
3454
+ const skillsDir = options.dir || getSkillsDir();
3455
+ const cacheDir = getCacheDir();
3456
+ await migrateLockfileIfNeeded();
3457
+ let lockfile = await readLockfile();
3458
+ const manifestDeps = await getDependencies();
3459
+ const manifestGitHubDeps = await getGitHubDependencies();
3460
+ const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
3461
+ const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
3462
+ const installedSkills = [];
3463
+ const missingDeps = [];
3464
+ for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
3465
+ if (!lockfilePackages[fullName]) {
3466
+ missingDeps.push({ fullName, versionRange });
3380
3467
  }
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`);
3468
+ }
3469
+ if (missingDeps.length > 0) {
3470
+ if (options.frozenLockfile) {
3390
3471
  console.error(
3391
- ` pspm access --public (uses current directory's pspm.json)`
3472
+ "Error: Dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
3392
3473
  );
3474
+ console.error("Missing dependencies:");
3475
+ for (const dep of missingDeps) {
3476
+ console.error(` - ${dep.fullName}@${dep.versionRange}`);
3477
+ }
3393
3478
  process.exit(1);
3394
3479
  }
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"
3405
- );
3406
- manifest = JSON.parse(content);
3407
- } catch {
3408
- try {
3409
- const content = await readFile10(
3410
- join18(process.cwd(), "package.json"),
3411
- "utf-8"
3480
+ console.log(`Resolving ${missingDeps.length} new dependency(ies)...
3481
+ `);
3482
+ configure2({ registryUrl, apiKey });
3483
+ for (const { fullName, versionRange } of missingDeps) {
3484
+ const parsed = parseRegistrySpecifier2(fullName);
3485
+ if (!parsed) {
3486
+ console.error(`Error: Invalid dependency specifier: ${fullName}`);
3487
+ continue;
3488
+ }
3489
+ const { owner, name } = parsed;
3490
+ console.log(`Resolving ${fullName}@${versionRange}...`);
3491
+ const versionsResponse = await listSkillVersions(owner, name);
3492
+ if (versionsResponse.status !== 200) {
3493
+ const errorMessage = extractApiErrorMessage(
3494
+ versionsResponse,
3495
+ `Skill ${fullName} not found`
3412
3496
  );
3413
- manifest = JSON.parse(content);
3414
- } catch {
3497
+ console.error(`Error: ${errorMessage}`);
3498
+ continue;
3499
+ }
3500
+ const versions = versionsResponse.data;
3501
+ if (versions.length === 0) {
3502
+ console.error(`Error: Skill ${fullName} not found`);
3503
+ continue;
3504
+ }
3505
+ const versionStrings = versions.map(
3506
+ (v) => v.version
3507
+ );
3508
+ const resolved = resolveVersion2(versionRange || "*", versionStrings);
3509
+ if (!resolved) {
3415
3510
  console.error(
3416
- "Error: No pspm.json or package.json found in current directory"
3511
+ `Error: No version matching "${versionRange}" for ${fullName}`
3417
3512
  );
3418
- console.error(
3419
- "Either run this command in a package directory or specify a package name"
3513
+ continue;
3514
+ }
3515
+ const versionResponse = await getSkillVersion(owner, name, resolved);
3516
+ if (versionResponse.status !== 200 || !versionResponse.data) {
3517
+ const errorMessage = extractApiErrorMessage(
3518
+ versionResponse,
3519
+ `Version ${resolved} not found`
3420
3520
  );
3421
- process.exit(1);
3521
+ console.error(`Error: ${errorMessage}`);
3522
+ continue;
3422
3523
  }
3423
- }
3424
- if (!manifest?.name) {
3425
- console.error("Error: Package manifest is missing 'name' field");
3426
- process.exit(1);
3427
- }
3428
- packageName = manifest.name;
3524
+ const versionInfo = versionResponse.data;
3525
+ const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
3526
+ const downloadHeaders = {};
3527
+ if (!isPresignedUrl && apiKey) {
3528
+ downloadHeaders.Authorization = `Bearer ${apiKey}`;
3529
+ }
3530
+ const tarballResponse = await fetch(versionInfo.downloadUrl, {
3531
+ headers: downloadHeaders,
3532
+ redirect: "follow"
3533
+ });
3534
+ if (!tarballResponse.ok) {
3535
+ console.error(
3536
+ `Error: Failed to download tarball for ${fullName} (${tarballResponse.status})`
3537
+ );
3538
+ continue;
3539
+ }
3540
+ const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
3541
+ const integrity = calculateIntegrity(tarballBuffer);
3542
+ await addToLockfile(fullName, {
3543
+ version: resolved,
3544
+ resolved: versionInfo.downloadUrl,
3545
+ integrity
3546
+ });
3547
+ await writeToCache(cacheDir, integrity, tarballBuffer);
3548
+ console.log(` Resolved ${fullName}@${resolved}`);
3549
+ }
3550
+ lockfile = await readLockfile();
3551
+ }
3552
+ const missingGitHubDeps = [];
3553
+ for (const [specifier, ref] of Object.entries(manifestGitHubDeps)) {
3554
+ if (!lockfileGitHubPackages[specifier]) {
3555
+ missingGitHubDeps.push({ specifier, ref });
3556
+ }
3557
+ }
3558
+ if (missingGitHubDeps.length > 0) {
3559
+ if (options.frozenLockfile) {
3560
+ console.error(
3561
+ "Error: GitHub dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
3562
+ );
3563
+ console.error("Missing GitHub dependencies:");
3564
+ for (const dep of missingGitHubDeps) {
3565
+ console.error(` - ${dep.specifier}@${dep.ref}`);
3566
+ }
3567
+ process.exit(1);
3568
+ }
3569
+ console.log(
3570
+ `
3571
+ Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
3572
+ `
3573
+ );
3574
+ for (const { specifier, ref } of missingGitHubDeps) {
3575
+ const parsed = parseGitHubSpecifier2(specifier);
3576
+ if (!parsed) {
3577
+ console.error(`Error: Invalid GitHub specifier: ${specifier}`);
3578
+ continue;
3579
+ }
3580
+ parsed.ref = parsed.ref || ref;
3581
+ console.log(`Resolving ${getGitHubDisplayName(parsed)}...`);
3582
+ try {
3583
+ const result = await downloadGitHubPackage(parsed);
3584
+ await extractGitHubPackage(parsed, result.buffer, skillsDir);
3585
+ const entry = {
3586
+ version: result.commit.slice(0, 7),
3587
+ resolved: `https://github.com/${parsed.owner}/${parsed.repo}`,
3588
+ integrity: result.integrity,
3589
+ gitCommit: result.commit,
3590
+ gitRef: ref || "HEAD"
3591
+ };
3592
+ await addGitHubToLockfile(specifier, entry);
3593
+ await writeToCache(cacheDir, result.integrity, result.buffer);
3594
+ console.log(
3595
+ ` Resolved ${specifier} (${ref}@${result.commit.slice(0, 7)})`
3596
+ );
3597
+ } catch (error) {
3598
+ if (error instanceof GitHubRateLimitError) {
3599
+ console.error(`Error: ${error.message}`);
3600
+ } else if (error instanceof GitHubPathNotFoundError) {
3601
+ console.error(`Error: ${error.message}`);
3602
+ } else if (error instanceof GitHubNotFoundError) {
3603
+ console.error(`Error: ${error.message}`);
3604
+ } else {
3605
+ const message = error instanceof Error ? error.message : String(error);
3606
+ console.error(`Error resolving ${specifier}: ${message}`);
3607
+ }
3608
+ }
3609
+ }
3610
+ lockfile = await readLockfile();
3611
+ }
3612
+ const manifest = await readManifest();
3613
+ const agentConfigs = manifest?.agents;
3614
+ let agents;
3615
+ if (options.agent) {
3616
+ agents = parseAgentArg(options.agent);
3617
+ } else if (manifest) {
3618
+ agents = parseAgentArg(void 0);
3619
+ } else if (options.yes) {
3620
+ agents = parseAgentArg(void 0);
3621
+ } else {
3622
+ console.log("\nNo pspm.json found. Let's set up your project.\n");
3623
+ agents = await promptForAgents();
3624
+ console.log();
3625
+ }
3626
+ const packages = lockfile?.packages ?? lockfile?.skills ?? {};
3627
+ const packageCount = Object.keys(packages).length;
3628
+ if (packageCount > 0) {
3629
+ console.log(`
3630
+ Installing ${packageCount} registry skill(s)...
3631
+ `);
3632
+ const installOrder = computeInstallOrder(packages);
3633
+ const entries = installOrder.filter((name) => packages[name]).map((name) => [name, packages[name]]);
3634
+ for (const [fullName, entry] of entries) {
3635
+ const parsedName = parseRegistrySpecifier2(fullName);
3636
+ if (!parsedName) {
3637
+ console.warn(`Warning: Invalid skill name in lockfile: ${fullName}`);
3638
+ continue;
3639
+ }
3640
+ const { namespace: ns, owner: pkgOwner, name, subname } = parsedName;
3641
+ console.log(`Installing ${fullName}@${entry.version}...`);
3642
+ let tarballBuffer;
3643
+ let fromCache = false;
3644
+ const cachedTarball = await readFromCache(cacheDir, entry.integrity);
3645
+ if (cachedTarball) {
3646
+ tarballBuffer = cachedTarball;
3647
+ fromCache = true;
3648
+ } else {
3649
+ const isPresignedUrl = entry.resolved.includes(".r2.cloudflarestorage.com") || entry.resolved.includes("X-Amz-Signature");
3650
+ const downloadHeaders = {};
3651
+ if (!isPresignedUrl && apiKey) {
3652
+ downloadHeaders.Authorization = `Bearer ${apiKey}`;
3653
+ }
3654
+ const response = await fetch(entry.resolved, {
3655
+ headers: downloadHeaders,
3656
+ redirect: "follow"
3657
+ });
3658
+ if (!response.ok) {
3659
+ if (response.status === 401) {
3660
+ if (!apiKey) {
3661
+ console.error(
3662
+ ` Error: ${fullName} requires authentication. Run 'pspm login' first.`
3663
+ );
3664
+ } else {
3665
+ console.error(
3666
+ ` Error: Access denied to ${fullName}. You may not have permission to access this private package.`
3667
+ );
3668
+ }
3669
+ } else {
3670
+ console.error(
3671
+ ` Error: Failed to download ${fullName} (${response.status})`
3672
+ );
3673
+ }
3674
+ continue;
3675
+ }
3676
+ tarballBuffer = Buffer.from(await response.arrayBuffer());
3677
+ const actualIntegrity = `sha256-${createHash("sha256").update(tarballBuffer).digest("base64")}`;
3678
+ if (actualIntegrity !== entry.integrity) {
3679
+ console.error(
3680
+ ` Error: Checksum verification failed for ${fullName}`
3681
+ );
3682
+ if (options.frozenLockfile) {
3683
+ process.exit(1);
3684
+ }
3685
+ continue;
3686
+ }
3687
+ await writeToCache(cacheDir, entry.integrity, tarballBuffer);
3688
+ }
3689
+ const effectiveSkillName = subname ?? name;
3690
+ let destDir;
3691
+ if (ns === "org") {
3692
+ destDir = join(skillsDir, "_org", pkgOwner, effectiveSkillName);
3693
+ } else if (ns === "github" && subname) {
3694
+ destDir = join(
3695
+ skillsDir,
3696
+ "_github-registry",
3697
+ pkgOwner,
3698
+ name,
3699
+ subname
3700
+ );
3701
+ } else {
3702
+ destDir = join(skillsDir, pkgOwner, effectiveSkillName);
3703
+ }
3704
+ await rm(destDir, { recursive: true, force: true });
3705
+ await mkdir(destDir, { recursive: true });
3706
+ const tempFile = join(destDir, ".temp.tgz");
3707
+ await writeFile(tempFile, tarballBuffer);
3708
+ const { exec: exec2 } = await import('child_process');
3709
+ const { promisify: promisify2 } = await import('util');
3710
+ const execAsync = promisify2(exec2);
3711
+ try {
3712
+ await execAsync(
3713
+ `tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
3714
+ );
3715
+ } finally {
3716
+ await rm(tempFile, { force: true });
3717
+ }
3718
+ console.log(
3719
+ ` Installed to ${destDir}${fromCache ? " (from cache)" : ""}`
3720
+ );
3721
+ const pathSkillName = ns === "github" && subname ? `${name}/${subname}` : name;
3722
+ installedSkills.push({
3723
+ name: effectiveSkillName,
3724
+ sourcePath: getRegistrySkillPath(ns, pkgOwner, pathSkillName)
3725
+ });
3726
+ }
3727
+ }
3728
+ const githubPackages = lockfile?.githubPackages ?? {};
3729
+ const githubCount = Object.keys(githubPackages).length;
3730
+ if (githubCount > 0) {
3731
+ console.log(`
3732
+ Installing ${githubCount} GitHub skill(s)...
3733
+ `);
3734
+ for (const [specifier, entry] of Object.entries(githubPackages)) {
3735
+ const parsed = parseGitHubSpecifier2(specifier);
3736
+ if (!parsed) {
3737
+ console.warn(
3738
+ `Warning: Invalid GitHub specifier in lockfile: ${specifier}`
3739
+ );
3740
+ continue;
3741
+ }
3742
+ const ghEntry = entry;
3743
+ console.log(
3744
+ `Installing ${specifier} (${ghEntry.gitRef}@${ghEntry.gitCommit.slice(0, 7)})...`
3745
+ );
3746
+ let tarballBuffer;
3747
+ let fromCache = false;
3748
+ const cachedTarball = await readFromCache(cacheDir, ghEntry.integrity);
3749
+ if (cachedTarball) {
3750
+ tarballBuffer = cachedTarball;
3751
+ fromCache = true;
3752
+ } else {
3753
+ try {
3754
+ const specWithCommit = { ...parsed, ref: ghEntry.gitCommit };
3755
+ const result = await downloadGitHubPackage(specWithCommit);
3756
+ tarballBuffer = result.buffer;
3757
+ if (result.integrity !== ghEntry.integrity) {
3758
+ console.error(
3759
+ ` Error: Checksum verification failed for ${specifier}`
3760
+ );
3761
+ if (options.frozenLockfile) {
3762
+ process.exit(1);
3763
+ }
3764
+ continue;
3765
+ }
3766
+ await writeToCache(cacheDir, ghEntry.integrity, tarballBuffer);
3767
+ } catch (error) {
3768
+ if (error instanceof GitHubRateLimitError) {
3769
+ console.error(` Error: ${error.message}`);
3770
+ } else if (error instanceof GitHubPathNotFoundError) {
3771
+ console.error(` Error: ${error.message}`);
3772
+ } else if (error instanceof GitHubNotFoundError) {
3773
+ console.error(` Error: ${error.message}`);
3774
+ } else {
3775
+ const message = error instanceof Error ? error.message : String(error);
3776
+ console.error(` Error downloading ${specifier}: ${message}`);
3777
+ }
3778
+ continue;
3779
+ }
3780
+ }
3781
+ try {
3782
+ const destPath = await extractGitHubPackage(
3783
+ parsed,
3784
+ tarballBuffer,
3785
+ skillsDir
3786
+ );
3787
+ console.log(
3788
+ ` Installed to ${destPath}${fromCache ? " (from cache)" : ""}`
3789
+ );
3790
+ const skillName = getGitHubSkillName2(parsed);
3791
+ installedSkills.push({
3792
+ name: skillName,
3793
+ sourcePath: getGitHubSkillPath(
3794
+ parsed.owner,
3795
+ parsed.repo,
3796
+ parsed.path
3797
+ )
3798
+ });
3799
+ } catch (error) {
3800
+ const message = error instanceof Error ? error.message : String(error);
3801
+ console.error(` Error extracting ${specifier}: ${message}`);
3802
+ }
3803
+ }
3804
+ }
3805
+ if (installedSkills.length > 0 && agents[0] !== "none") {
3806
+ const globalMode = isGlobalMode();
3807
+ console.log(
3808
+ `
3809
+ Creating symlinks for agent(s): ${agents.join(", ")}${globalMode ? " (global)" : ""}...`
3810
+ );
3811
+ await createAgentSymlinks(installedSkills, {
3812
+ agents,
3813
+ projectRoot: globalMode ? homedir() : process.cwd(),
3814
+ agentConfigs,
3815
+ global: globalMode
3816
+ });
3817
+ console.log(" Symlinks created.");
3818
+ }
3819
+ const totalCount = packageCount + githubCount;
3820
+ if (totalCount === 0) {
3821
+ console.log("No skills to install.");
3822
+ } else {
3823
+ console.log(`
3824
+ All ${totalCount} skill(s) installed.`);
3825
+ }
3826
+ } catch (error) {
3827
+ const message = error instanceof Error ? error.message : "Unknown error";
3828
+ console.error(`Error: ${message}`);
3829
+ process.exit(1);
3830
+ }
3831
+ }
3832
+ var init_install = __esm({
3833
+ "src/commands/install.ts"() {
3834
+ init_agents();
3835
+ init_api_client();
3836
+ init_config();
3837
+ init_errors();
3838
+ init_github();
3839
+ init_lib();
3840
+ init_lockfile3();
3841
+ init_manifest3();
3842
+ init_symlinks();
3843
+ }
3844
+ });
3845
+
3846
+ // src/commands/access.ts
3847
+ init_api_client();
3848
+ init_config();
3849
+ init_lib();
3850
+ function isLocalSpecifier2(specifier) {
3851
+ return specifier.startsWith("file:") || specifier.startsWith("./") || specifier.startsWith("../");
3852
+ }
3853
+ async function access(specifier, options) {
3854
+ try {
3855
+ const apiKey = await requireApiKey();
3856
+ const registryUrl = await getRegistryUrl();
3857
+ if (options.public && options.private) {
3858
+ console.error("Error: Cannot specify both --public and --private");
3859
+ process.exit(1);
3860
+ }
3861
+ if (!options.public && !options.private) {
3862
+ console.error("Error: Must specify either --public or --private");
3863
+ process.exit(1);
3864
+ }
3865
+ const visibility = options.public ? "public" : "private";
3866
+ let packageName;
3867
+ let packageUsername;
3868
+ if (specifier) {
3869
+ if (isGitHubSpecifier2(specifier)) {
3870
+ const ghSpec = parseGitHubSpecifier2(specifier);
3871
+ if (ghSpec) {
3872
+ console.error(`Error: Cannot change visibility of GitHub packages.`);
3873
+ console.error(
3874
+ ` "${specifier}" is hosted on GitHub, not the PSPM registry.`
3875
+ );
3876
+ console.error(
3877
+ ` Visibility can only be changed for packages published to the registry.`
3878
+ );
3879
+ } else {
3880
+ console.error(`Error: Invalid GitHub specifier "${specifier}".`);
3881
+ console.error(` Use format: github:{owner}/{repo}[/{path}][@{ref}]`);
3882
+ }
3883
+ process.exit(1);
3884
+ }
3885
+ if (isLocalSpecifier2(specifier)) {
3886
+ console.error(`Error: Cannot change visibility of local packages.`);
3887
+ console.error(
3888
+ ` "${specifier}" is a local directory, not a registry package.`
3889
+ );
3890
+ console.error(
3891
+ ` Visibility can only be changed for packages published to the registry.`
3892
+ );
3893
+ process.exit(1);
3894
+ }
3895
+ const parsed = parseRegistrySpecifier2(specifier);
3896
+ if (!parsed) {
3897
+ console.error(`Error: Invalid package specifier "${specifier}".`);
3898
+ console.error(
3899
+ ` Use format: @user/{username}/{name} or @org/{orgname}/{name}`
3900
+ );
3901
+ console.error(``);
3902
+ console.error(` Examples:`);
3903
+ console.error(` pspm access @user/myname/my-skill --public`);
3904
+ console.error(
3905
+ ` pspm access --public (uses current directory's pspm.json)`
3906
+ );
3907
+ process.exit(1);
3908
+ }
3909
+ packageName = parsed.name;
3910
+ packageUsername = parsed.owner;
3911
+ } else {
3912
+ const { readFile: readFile10 } = await import('fs/promises');
3913
+ const { join: join18 } = await import('path');
3914
+ let manifest = null;
3915
+ try {
3916
+ const content = await readFile10(
3917
+ join18(process.cwd(), "pspm.json"),
3918
+ "utf-8"
3919
+ );
3920
+ manifest = JSON.parse(content);
3921
+ } catch {
3922
+ try {
3923
+ const content = await readFile10(
3924
+ join18(process.cwd(), "package.json"),
3925
+ "utf-8"
3926
+ );
3927
+ manifest = JSON.parse(content);
3928
+ } catch {
3929
+ console.error(
3930
+ "Error: No pspm.json or package.json found in current directory"
3931
+ );
3932
+ console.error(
3933
+ "Either run this command in a package directory or specify a package name"
3934
+ );
3935
+ process.exit(1);
3936
+ }
3937
+ }
3938
+ if (!manifest?.name) {
3939
+ console.error("Error: Package manifest is missing 'name' field");
3940
+ process.exit(1);
3941
+ }
3942
+ packageName = manifest.name;
3429
3943
  }
3430
3944
  if (!packageUsername) {
3431
3945
  const config2 = await resolveConfig();
@@ -3497,7 +4011,6 @@ async function audit(options) {
3497
4011
  }
3498
4012
  const issues = [];
3499
4013
  const skillsDir = getSkillsDir();
3500
- const projectRoot = process.cwd();
3501
4014
  if (!options.json) {
3502
4015
  console.log("Auditing installed skills...\n");
3503
4016
  }
@@ -3506,7 +4019,7 @@ async function audit(options) {
3506
4019
  const match = fullName.match(/^@user\/([^/]+)\/([^/]+)$/);
3507
4020
  if (!match) continue;
3508
4021
  const [, username, skillName] = match;
3509
- const destDir = join(projectRoot, skillsDir, username, skillName);
4022
+ const destDir = join(skillsDir, username, skillName);
3510
4023
  const exists = await pathExists(destDir);
3511
4024
  if (!exists) {
3512
4025
  issues.push({
@@ -3542,14 +4055,7 @@ async function audit(options) {
3542
4055
  for (const { specifier } of githubSkills) {
3543
4056
  const parsed = parseGitHubSpecifier2(specifier);
3544
4057
  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);
4058
+ const destDir = parsed.path ? join(skillsDir, "_github", parsed.owner, parsed.repo, parsed.path) : join(skillsDir, "_github", parsed.owner, parsed.repo);
3553
4059
  const exists = await pathExists(destDir);
3554
4060
  if (!exists) {
3555
4061
  issues.push({
@@ -3576,7 +4082,6 @@ async function audit(options) {
3576
4082
  for (const { specifier, entry } of wellKnownSkills) {
3577
4083
  const wkEntry = entry;
3578
4084
  const destDir = join(
3579
- projectRoot,
3580
4085
  skillsDir,
3581
4086
  "_wellknown",
3582
4087
  wkEntry.hostname,
@@ -3637,383 +4142,92 @@ async function audit(options) {
3637
4142
  console.log(` ${issue.message}`);
3638
4143
  }
3639
4144
  console.log();
3640
- }
3641
- if (warnings.length > 0) {
3642
- console.log("Warnings:");
3643
- for (const issue of warnings) {
3644
- console.log(
3645
- ` [${issue.type.toUpperCase()}] ${issue.name} (${issue.source})`
3646
- );
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"}`
4145
+ }
4146
+ if (warnings.length > 0) {
4147
+ console.log("Warnings:");
4148
+ for (const issue of warnings) {
4149
+ console.log(
4150
+ ` [${issue.type.toUpperCase()}] ${issue.name} (${issue.source})`
3788
4151
  );
3789
- process.exit(1);
4152
+ console.log(` ${issue.message}`);
3790
4153
  }
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.");
4154
+ console.log();
4155
+ }
4156
+ const totalPackages = registrySkills.length + githubSkills.length + wellKnownSkills.length;
4157
+ console.log(
4158
+ `Audited ${totalPackages} package(s): ${errorCount} error(s), ${warningCount} warning(s).`
4159
+ );
4160
+ if (errorCount > 0) {
4161
+ process.exit(1);
3798
4162
  }
3799
4163
  } catch (error) {
3800
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
3801
- console.error(`Error: ${errorMessage}`);
4164
+ const message = error instanceof Error ? error.message : "Unknown error";
4165
+ console.error(`Error: ${message}`);
3802
4166
  process.exit(1);
3803
4167
  }
3804
4168
  }
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() {
4169
+ async function pathExists(path) {
3835
4170
  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;
4171
+ await stat(path);
4172
+ return true;
3852
4173
  } catch {
3853
- return null;
4174
+ return false;
3854
4175
  }
3855
4176
  }
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) {
4177
+ async function configInit(options) {
3867
4178
  try {
3868
- const pspmJsonPath = join(process.cwd(), "pspm.json");
3869
- let exists = false;
4179
+ const configPath = join(process.cwd(), ".pspmrc");
3870
4180
  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.");
4181
+ await stat(configPath);
4182
+ console.error("Error: .pspmrc already exists in this directory.");
3878
4183
  process.exit(1);
4184
+ } catch {
3879
4185
  }
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
- };
4186
+ const lines = ["; Project-specific PSPM configuration", ""];
4187
+ if (options.registry) {
4188
+ lines.push(`registry = ${options.registry}`);
3910
4189
  } 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
- }
4190
+ lines.push("; Uncomment to use a custom registry:");
4191
+ lines.push("; registry = https://custom-registry.example.com");
3981
4192
  }
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);
4193
+ lines.push("");
4194
+ await writeFile(configPath, lines.join("\n"));
4195
+ console.log("Created .pspmrc");
3990
4196
  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
- }
4197
+ console.log("Contents:");
4198
+ console.log(lines.join("\n"));
4199
+ console.log("Note: .pspmrc should be committed to version control.");
4200
+ console.log("API keys should NOT be stored here - use pspm login instead.");
4201
+ } catch (error) {
4202
+ const message = error instanceof Error ? error.message : "Unknown error";
4203
+ console.error(`Error: ${message}`);
4204
+ process.exit(1);
4205
+ }
4206
+ }
4207
+
4208
+ // src/commands/config/show.ts
4209
+ init_config();
4210
+ async function configShow() {
4211
+ try {
4212
+ const resolved = await resolveConfig();
4213
+ const projectConfig = await findProjectConfig();
4214
+ const configPath = getConfigPath();
4215
+ console.log("Resolved Configuration:\n");
4216
+ console.log(` Registry URL: ${resolved.registryUrl}`);
4217
+ console.log(` API Key: ${resolved.apiKey ? "***" : "(not set)"}`);
4218
+ console.log(` Username: ${resolved.username || "(not set)"}`);
4219
+ console.log("");
4220
+ console.log("Config Locations:");
4221
+ console.log(` User config: ${configPath}`);
4222
+ console.log(` Project config: ${projectConfig ? ".pspmrc" : "(none)"}`);
4223
+ console.log("");
4224
+ console.log("Environment Variables:");
4225
+ console.log(
4226
+ ` PSPM_REGISTRY_URL: ${process.env.PSPM_REGISTRY_URL || "(not set)"}`
4227
+ );
4228
+ console.log(
4229
+ ` PSPM_API_KEY: ${process.env.PSPM_API_KEY ? "***" : "(not set)"}`
4230
+ );
4017
4231
  } catch (error) {
4018
4232
  const message = error instanceof Error ? error.message : "Unknown error";
4019
4233
  console.error(`Error: ${message}`);
@@ -4021,502 +4235,289 @@ async function init(options) {
4021
4235
  }
4022
4236
  }
4023
4237
 
4024
- // src/commands/install.ts
4025
- init_agents();
4238
+ // src/commands/deprecate.ts
4026
4239
  init_api_client();
4027
4240
  init_config();
4028
- init_errors();
4029
- init_github();
4030
4241
  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) {
4242
+ async function deprecate(specifier, message, options) {
4058
4243
  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;
4244
+ const apiKey = await requireApiKey();
4245
+ const registryUrl = await getRegistryUrl();
4246
+ const parsed = parseRegistrySpecifier2(specifier);
4247
+ if (!parsed) {
4248
+ console.error(
4249
+ `Error: Invalid skill specifier "${specifier}". Use format: @user/{username}/{name}@{version} or @org/{orgname}/{name}@{version}`
4250
+ );
4251
+ process.exit(1);
4075
4252
  }
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
4253
+ const { owner, name, versionRange } = parsed;
4254
+ const fullName = generateRegistryIdentifier2({
4255
+ namespace: parsed.namespace,
4256
+ owner,
4257
+ name
4093
4258
  });
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
- }
4259
+ if (!versionRange) {
4260
+ console.error(
4261
+ "Error: Version is required for deprecation. Use format: @user/{username}/{name}@{version}"
4262
+ );
4263
+ process.exit(1);
4163
4264
  }
4164
- if (missingDeps.length > 0) {
4165
- if (options.frozenLockfile) {
4265
+ configure2({ registryUrl, apiKey });
4266
+ if (options.undo) {
4267
+ console.log(`Removing deprecation from ${fullName}@${versionRange}...`);
4268
+ const response = await undeprecateSkillVersion(owner, name, versionRange);
4269
+ if (response.status !== 200) {
4166
4270
  console.error(
4167
- "Error: Dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
4271
+ `Error: ${response.error || "Failed to remove deprecation"}`
4168
4272
  );
4169
- console.error("Missing dependencies:");
4170
- for (const dep of missingDeps) {
4171
- console.error(` - ${dep.fullName}@${dep.versionRange}`);
4172
- }
4173
4273
  process.exit(1);
4174
4274
  }
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 });
4275
+ console.log(`Removed deprecation from ${fullName}@${versionRange}`);
4276
+ } else {
4277
+ if (!message) {
4278
+ console.error(
4279
+ "Error: Deprecation message is required. Usage: pspm deprecate <specifier> <message>"
4280
+ );
4281
+ process.exit(1);
4251
4282
  }
4252
- }
4253
- if (missingGitHubDeps.length > 0) {
4254
- if (options.frozenLockfile) {
4283
+ console.log(`Deprecating ${fullName}@${versionRange}...`);
4284
+ const response = await deprecateSkillVersion(
4285
+ owner,
4286
+ name,
4287
+ versionRange,
4288
+ message
4289
+ );
4290
+ if (response.status !== 200) {
4255
4291
  console.error(
4256
- "Error: GitHub dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
4292
+ `Error: ${response.error || "Failed to deprecate version"}`
4257
4293
  );
4258
- console.error("Missing GitHub dependencies:");
4259
- for (const dep of missingGitHubDeps) {
4260
- console.error(` - ${dep.specifier}@${dep.ref}`);
4261
- }
4262
4294
  process.exit(1);
4263
4295
  }
4296
+ console.log(`Deprecated ${fullName}@${versionRange}`);
4297
+ console.log(`Message: ${message}`);
4298
+ console.log("");
4264
4299
  console.log(
4265
- `
4266
- Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
4267
- `
4300
+ "Users installing this version will see a deprecation warning."
4268
4301
  );
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
- }
4304
- }
4305
- lockfile = await readLockfile();
4302
+ console.log("The package is still available for download.");
4306
4303
  }
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();
4304
+ } catch (error) {
4305
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
4306
+ console.error(`Error: ${errorMessage}`);
4307
+ process.exit(1);
4308
+ }
4309
+ }
4310
+
4311
+ // src/commands/init.ts
4312
+ init_lib();
4313
+ function prompt(rl, question, defaultValue) {
4314
+ return new Promise((resolve3) => {
4315
+ const displayDefault = defaultValue ? ` (${defaultValue})` : "";
4316
+ rl.question(`${question}${displayDefault} `, (answer) => {
4317
+ resolve3(answer.trim() || defaultValue);
4318
+ });
4319
+ });
4320
+ }
4321
+ async function readExistingPackageJson() {
4322
+ try {
4323
+ const content = await readFile(
4324
+ join(process.cwd(), "package.json"),
4325
+ "utf-8"
4326
+ );
4327
+ const pkg = JSON.parse(content);
4328
+ return {
4329
+ name: pkg.name,
4330
+ version: pkg.version,
4331
+ description: pkg.description,
4332
+ author: typeof pkg.author === "string" ? pkg.author : pkg.author?.name,
4333
+ license: pkg.license
4334
+ };
4335
+ } catch {
4336
+ return null;
4337
+ }
4338
+ }
4339
+ async function getGitAuthor() {
4340
+ try {
4341
+ const { exec: exec2 } = await import('child_process');
4342
+ const { promisify: promisify2 } = await import('util');
4343
+ const execAsync = promisify2(exec2);
4344
+ const [nameResult, emailResult] = await Promise.all([
4345
+ execAsync("git config user.name").catch(() => ({ stdout: "" })),
4346
+ execAsync("git config user.email").catch(() => ({ stdout: "" }))
4347
+ ]);
4348
+ const name = nameResult.stdout.trim();
4349
+ const email = emailResult.stdout.trim();
4350
+ if (name && email) {
4351
+ return `${name} <${email}>`;
4320
4352
  }
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
4353
+ if (name) {
4354
+ return name;
4355
+ }
4356
+ return null;
4357
+ } catch {
4358
+ return null;
4359
+ }
4360
+ }
4361
+ function sanitizeName(name) {
4362
+ const withoutScope = name.replace(/^@[^/]+\//, "");
4363
+ return withoutScope.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
4364
+ }
4365
+ function isValidName(name) {
4366
+ return /^[a-z][a-z0-9_-]*$/.test(name);
4367
+ }
4368
+ function isValidVersion(version3) {
4369
+ return /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?(\+[a-zA-Z0-9.-]+)?$/.test(version3);
4370
+ }
4371
+ async function init(options) {
4372
+ try {
4373
+ const pspmJsonPath = join(process.cwd(), "pspm.json");
4374
+ let exists = false;
4375
+ try {
4376
+ await stat(pspmJsonPath);
4377
+ exists = true;
4378
+ } catch {
4379
+ }
4380
+ if (exists && !options.force) {
4381
+ console.error("Error: pspm.json already exists in this directory.");
4382
+ console.error("Use --force to overwrite.");
4383
+ process.exit(1);
4384
+ }
4385
+ const existingPkg = await readExistingPackageJson();
4386
+ const gitAuthor = await getGitAuthor();
4387
+ const defaultName = sanitizeName(
4388
+ options.name || existingPkg?.name || basename(process.cwd())
4389
+ );
4390
+ const defaultVersion = existingPkg?.version || "0.1.0";
4391
+ const defaultDescription = options.description || existingPkg?.description || "";
4392
+ const defaultAuthor = options.author || existingPkg?.author || gitAuthor || "";
4393
+ const defaultLicense = existingPkg?.license || "MIT";
4394
+ const defaultMain = "SKILL.md";
4395
+ const defaultCapabilities = "";
4396
+ let manifest;
4397
+ if (options.yes) {
4398
+ manifest = {
4399
+ $schema: PSPM_SCHEMA_URL,
4400
+ name: defaultName,
4401
+ version: defaultVersion,
4402
+ description: defaultDescription || void 0,
4403
+ author: defaultAuthor || void 0,
4404
+ license: defaultLicense,
4405
+ type: "skill",
4406
+ capabilities: [],
4407
+ main: defaultMain,
4408
+ requirements: {
4409
+ pspm: ">=0.1.0"
4410
+ },
4411
+ files: [...DEFAULT_SKILL_FILES],
4412
+ dependencies: {},
4413
+ private: false
4414
+ };
4415
+ } else {
4416
+ console.log(
4417
+ "This utility will walk you through creating a pspm.json file."
4418
+ );
4419
+ console.log(
4420
+ "It only covers the most common items, and tries to guess sensible defaults."
4421
+ );
4422
+ console.log("");
4423
+ console.log(
4424
+ "See `pspm init --help` for definitive documentation on these fields"
4425
+ );
4426
+ console.log("and exactly what they do.");
4427
+ console.log("");
4428
+ console.log("Press ^C at any time to quit.");
4429
+ const rl = createInterface({
4430
+ input: process.stdin,
4431
+ output: process.stdout
4432
+ });
4433
+ try {
4434
+ let name = await prompt(rl, "skill name:", defaultName);
4435
+ while (!isValidName(name)) {
4436
+ console.log(
4437
+ " Name must start with a lowercase letter and contain only lowercase letters, numbers, hyphens, and underscores."
4395
4438
  );
4396
- } else {
4397
- destDir = join(skillsDir, pkgOwner, effectiveSkillName);
4439
+ name = await prompt(rl, "skill name:", sanitizeName(name));
4398
4440
  }
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 });
4441
+ let version3 = await prompt(rl, "version:", defaultVersion);
4442
+ while (!isValidVersion(version3)) {
4443
+ console.log(" Version must be valid semver (e.g., 1.0.0)");
4444
+ version3 = await prompt(rl, "version:", "0.1.0");
4412
4445
  }
4413
- console.log(
4414
- ` Installed to ${destDir}${fromCache ? " (from cache)" : ""}`
4446
+ const description = await prompt(
4447
+ rl,
4448
+ "description:",
4449
+ defaultDescription
4415
4450
  );
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)})...`
4451
+ const main = await prompt(rl, "entry point:", defaultMain);
4452
+ const capabilitiesStr = await prompt(
4453
+ rl,
4454
+ "capabilities (comma-separated):",
4455
+ defaultCapabilities
4440
4456
  );
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}`);
4457
+ const author = await prompt(rl, "author:", defaultAuthor);
4458
+ const license = await prompt(rl, "license:", defaultLicense);
4459
+ rl.close();
4460
+ const capabilities = capabilitiesStr ? capabilitiesStr.split(",").map((s) => s.trim()).filter(Boolean) : [];
4461
+ manifest = {
4462
+ $schema: PSPM_SCHEMA_URL,
4463
+ name,
4464
+ version: version3,
4465
+ description: description || void 0,
4466
+ author: author || void 0,
4467
+ license,
4468
+ type: "skill",
4469
+ capabilities,
4470
+ main,
4471
+ requirements: {
4472
+ pspm: ">=0.1.0"
4473
+ },
4474
+ files: [...DEFAULT_SKILL_FILES],
4475
+ dependencies: {},
4476
+ private: false
4477
+ };
4478
+ } catch (error) {
4479
+ rl.close();
4480
+ if (error instanceof Error && error.message.includes("readline was closed")) {
4481
+ console.log("\nAborted.");
4482
+ process.exit(0);
4497
4483
  }
4484
+ throw error;
4498
4485
  }
4499
4486
  }
4500
- if (installedSkills.length > 0 && agents[0] !== "none") {
4501
- const globalMode = isGlobalMode();
4487
+ if (!manifest.description) manifest.description = void 0;
4488
+ if (!manifest.author) manifest.author = void 0;
4489
+ if (manifest.capabilities?.length === 0) manifest.capabilities = void 0;
4490
+ const content = JSON.stringify(manifest, null, 2);
4491
+ console.log("");
4492
+ console.log(`About to write to ${pspmJsonPath}:`);
4493
+ console.log("");
4494
+ console.log(content);
4495
+ console.log("");
4496
+ if (!options.yes) {
4497
+ const rl = createInterface({
4498
+ input: process.stdin,
4499
+ output: process.stdout
4500
+ });
4501
+ const confirm2 = await prompt(rl, "Is this OK?", "yes");
4502
+ rl.close();
4503
+ if (confirm2.toLowerCase() !== "yes" && confirm2.toLowerCase() !== "y") {
4504
+ console.log("Aborted.");
4505
+ process.exit(0);
4506
+ }
4507
+ }
4508
+ await writeFile(pspmJsonPath, `${content}
4509
+ `);
4510
+ try {
4511
+ await stat(join(process.cwd(), "SKILL.md"));
4512
+ } catch {
4502
4513
  console.log(
4503
- `
4504
- Creating symlinks for agent(s): ${agents.join(", ")}${globalMode ? " (global)" : ""}...`
4514
+ "Note: Create a SKILL.md file with your skill's prompt content."
4505
4515
  );
4506
- await createAgentSymlinks(installedSkills, {
4507
- agents,
4508
- projectRoot: globalMode ? homedir() : process.cwd(),
4509
- agentConfigs,
4510
- global: globalMode
4511
- });
4512
- console.log(" Symlinks created.");
4513
4516
  }
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.`);
4517
+ if (existingPkg) {
4518
+ console.log("Note: Values were derived from existing package.json.");
4519
+ console.log(" pspm.json is for publishing to PSPM registry.");
4520
+ console.log(" package.json can still be used for npm dependencies.");
4520
4521
  }
4521
4522
  } catch (error) {
4522
4523
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -4525,6 +4526,9 @@ All ${totalCount} skill(s) installed.`);
4525
4526
  }
4526
4527
  }
4527
4528
 
4529
+ // src/commands/index.ts
4530
+ init_install();
4531
+
4528
4532
  // src/commands/link.ts
4529
4533
  init_agents();
4530
4534
  init_lib();
@@ -4830,8 +4834,8 @@ function startCallbackServer(expectedState) {
4830
4834
  let resolveToken;
4831
4835
  let rejectToken;
4832
4836
  let timeoutId;
4833
- const tokenPromise = new Promise((resolve2, reject) => {
4834
- resolveToken = resolve2;
4837
+ const tokenPromise = new Promise((resolve3, reject) => {
4838
+ resolveToken = resolve3;
4835
4839
  rejectToken = reject;
4836
4840
  });
4837
4841
  const server = http.createServer((req, res) => {
@@ -5072,18 +5076,104 @@ async function migrate(options) {
5072
5076
  console.log("Migration complete!");
5073
5077
  console.log("");
5074
5078
  console.log(
5075
- "You can safely delete these legacy files if they still exist:"
5079
+ "You can safely delete these legacy files if they still exist:"
5080
+ );
5081
+ console.log(" - skill-lock.json (replaced by pspm-lock.json)");
5082
+ console.log(" - .skills/ (replaced by .pspm/skills/)");
5083
+ console.log("");
5084
+ console.log("Update your .gitignore to include:");
5085
+ console.log(" .pspm/cache/");
5086
+ } catch (error) {
5087
+ const message = error instanceof Error ? error.message : "Unknown error";
5088
+ console.error(`Error: ${message}`);
5089
+ process.exit(1);
5090
+ }
5091
+ }
5092
+
5093
+ // src/commands/notebook.ts
5094
+ init_config();
5095
+ async function fetchApi(path, options = {}) {
5096
+ const config2 = await resolveConfig();
5097
+ const apiKey = await requireApiKey();
5098
+ const baseUrl = config2.registryUrl.replace(/\/api\/skills\/?$/, "");
5099
+ const headers = {
5100
+ "Content-Type": "application/json",
5101
+ ...options.headers
5102
+ };
5103
+ if (apiKey) {
5104
+ headers.Authorization = `Bearer ${apiKey}`;
5105
+ }
5106
+ return fetch(`${baseUrl}${path}`, { ...options, headers });
5107
+ }
5108
+ async function notebookUpload(filePath, options) {
5109
+ const absPath = resolve(filePath);
5110
+ const content = readFileSync(absPath, "utf-8");
5111
+ const name = basename(filePath).replace(/\.anyt\.md$|\.anyt$|\.md$/, "");
5112
+ const body = {
5113
+ name,
5114
+ content,
5115
+ description: options.description,
5116
+ visibility: options.visibility || "private"
5117
+ };
5118
+ const path = options.org ? `/api/notebooks/org/${options.org}` : "/api/notebooks";
5119
+ const response = await fetchApi(path, {
5120
+ method: "POST",
5121
+ body: JSON.stringify(body)
5122
+ });
5123
+ if (!response.ok) {
5124
+ const err = await response.json().catch(() => ({}));
5125
+ throw new Error(
5126
+ err.message || `Upload failed (${response.status})`
5127
+ );
5128
+ }
5129
+ const notebook = await response.json();
5130
+ console.log(
5131
+ `Uploaded: ${notebook.name} (${notebook.id})${options.org ? ` to org ${options.org}` : ""}`
5132
+ );
5133
+ }
5134
+ async function notebookList(options) {
5135
+ const path = options.org ? `/api/notebooks/org/${options.org}` : "/api/notebooks/-/mine";
5136
+ const response = await fetchApi(path);
5137
+ if (!response.ok) {
5138
+ throw new Error(`Failed to list notebooks (${response.status})`);
5139
+ }
5140
+ const notebooks = await response.json();
5141
+ if (notebooks.length === 0) {
5142
+ console.log("No notebooks found");
5143
+ return;
5144
+ }
5145
+ console.log(
5146
+ `
5147
+ ${"Name".padEnd(30)} ${"Cells".padEnd(8)} ${"Visibility".padEnd(12)} ${"Updated".padEnd(12)} ID`
5148
+ );
5149
+ console.log("-".repeat(90));
5150
+ for (const nb of notebooks) {
5151
+ const updated = new Date(nb.updatedAt).toLocaleDateString();
5152
+ console.log(
5153
+ `${nb.name.slice(0, 28).padEnd(30)} ${String(nb.cellCount).padEnd(8)} ${nb.visibility.padEnd(12)} ${updated.padEnd(12)} ${nb.id}`
5076
5154
  );
5077
- console.log(" - skill-lock.json (replaced by pspm-lock.json)");
5078
- console.log(" - .skills/ (replaced by .pspm/skills/)");
5079
- console.log("");
5080
- console.log("Update your .gitignore to include:");
5081
- console.log(" .pspm/cache/");
5082
- } catch (error) {
5083
- const message = error instanceof Error ? error.message : "Unknown error";
5084
- console.error(`Error: ${message}`);
5085
- process.exit(1);
5086
5155
  }
5156
+ console.log(`
5157
+ ${notebooks.length} notebook(s)`);
5158
+ }
5159
+ async function notebookDownload(id, output) {
5160
+ const response = await fetchApi(`/api/notebooks/${id}`);
5161
+ if (!response.ok) {
5162
+ throw new Error(`Notebook not found (${response.status})`);
5163
+ }
5164
+ const notebook = await response.json();
5165
+ const outPath = output || `${notebook.slug}.anyt.md`;
5166
+ writeFileSync(outPath, notebook.content, "utf-8");
5167
+ console.log(`Downloaded: ${notebook.name} -> ${outPath}`);
5168
+ }
5169
+ async function notebookDelete(id) {
5170
+ const response = await fetchApi(`/api/notebooks/${id}`, {
5171
+ method: "DELETE"
5172
+ });
5173
+ if (!response.ok) {
5174
+ throw new Error(`Failed to delete notebook (${response.status})`);
5175
+ }
5176
+ console.log(`Notebook ${id} deleted`);
5087
5177
  }
5088
5178
  function resolveVersion3(range, availableVersions) {
5089
5179
  const sorted = availableVersions.filter((v) => semver2.valid(v)).sort((a, b) => semver2.rcompare(a, b));
@@ -5101,7 +5191,7 @@ function getLatestVersion3(versions) {
5101
5191
  return valid5.sort((a, b) => semver2.rcompare(a, b))[0];
5102
5192
  }
5103
5193
 
5104
- // ../../packages/server/skill-registry/src/client/outdated.ts
5194
+ // ../../packages/features/skill-registry/src/client/outdated.ts
5105
5195
  function createOutdatedChecker(config2) {
5106
5196
  const { registryUrl, apiKey, githubToken } = config2;
5107
5197
  async function fetchWithAuth(url, token) {
@@ -5121,14 +5211,26 @@ function createOutdatedChecker(config2) {
5121
5211
  const response = await fetchWithAuth(url, apiKey);
5122
5212
  return await response.json();
5123
5213
  }
5214
+ async function fetchGithubRegistryVersions(owner, repo, skillname) {
5215
+ const url = `${registryUrl}/@github/${owner}/${repo}/${skillname}/versions`;
5216
+ const response = await fetchWithAuth(url, apiKey);
5217
+ return await response.json();
5218
+ }
5124
5219
  async function checkRegistryPackage(specifier, entry, versionRange) {
5125
- const match = specifier.match(/^@user\/([^/]+)\/([^/]+)$/);
5126
- if (!match) {
5220
+ const userMatch = specifier.match(/^@(?:user|org)\/([^/]+)\/([^/]+)$/);
5221
+ const githubMatch = specifier.match(/^@github\/([^/]+)\/([^/]+)\/([^/]+)$/);
5222
+ if (!userMatch && !githubMatch) {
5127
5223
  throw new Error(`Invalid registry specifier: ${specifier}`);
5128
5224
  }
5129
- const [, username, name] = match;
5225
+ const isGithubRegistry = !!githubMatch;
5226
+ const username = userMatch ? userMatch[1] : "";
5227
+ const name = userMatch ? userMatch[2] : "";
5130
5228
  try {
5131
- const versions = await fetchRegistryVersions(username, name);
5229
+ const versions = isGithubRegistry && githubMatch ? await fetchGithubRegistryVersions(
5230
+ githubMatch[1],
5231
+ githubMatch[2],
5232
+ githubMatch[3]
5233
+ ) : await fetchRegistryVersions(username, name);
5132
5234
  const versionStrings = versions.map((v) => v.version);
5133
5235
  const range = versionRange || "*";
5134
5236
  const wanted = resolveVersion3(range, versionStrings);
@@ -5354,7 +5456,7 @@ init_errors();
5354
5456
  init_lib();
5355
5457
  var exec = promisify(exec$1);
5356
5458
  function confirm(question) {
5357
- return new Promise((resolve2) => {
5459
+ return new Promise((resolve3) => {
5358
5460
  const rl = createInterface({
5359
5461
  input: process.stdin,
5360
5462
  output: process.stdout
@@ -5362,7 +5464,7 @@ function confirm(question) {
5362
5464
  rl.question(`${question} (y/N) `, (answer) => {
5363
5465
  rl.close();
5364
5466
  const normalized = answer.trim().toLowerCase();
5365
- resolve2(normalized === "y" || normalized === "yes");
5467
+ resolve3(normalized === "y" || normalized === "yes");
5366
5468
  });
5367
5469
  });
5368
5470
  }
@@ -5472,8 +5574,10 @@ async function publishCommand(options) {
5472
5574
  }
5473
5575
  console.log("");
5474
5576
  }
5577
+ const nameParts = manifest.name.split("/");
5578
+ const bareName = nameParts[nameParts.length - 1];
5475
5579
  const packageJson2 = {
5476
- name: manifest.name,
5580
+ name: bareName,
5477
5581
  version: manifest.version,
5478
5582
  description: manifest.description,
5479
5583
  files: manifest.files,
@@ -5678,8 +5782,8 @@ async function removeRegistry(specifier, agents, agentConfigs) {
5678
5782
  console.error(`Error: Invalid skill specifier: ${specifier}`);
5679
5783
  process.exit(1);
5680
5784
  }
5681
- const { namespace, owner, name } = parsed;
5682
- const fullName = `@${namespace}/${owner}/${name}`;
5785
+ const { namespace, owner, name, subname } = parsed;
5786
+ const fullName = namespace === "github" && subname ? `@github/${owner}/${name}/${subname}` : `@${namespace}/${owner}/${name}`;
5683
5787
  console.log(`Removing ${fullName}...`);
5684
5788
  const removedFromLockfile = await removeFromLockfile(fullName);
5685
5789
  const removedFromManifest = await removeDependency(fullName);
@@ -5687,13 +5791,21 @@ async function removeRegistry(specifier, agents, agentConfigs) {
5687
5791
  console.error(`Error: ${fullName} not found in lockfile or pspm.json`);
5688
5792
  process.exit(1);
5689
5793
  }
5690
- await removeAgentSymlinks(name, {
5794
+ const symlinkName = subname ?? name;
5795
+ await removeAgentSymlinks(symlinkName, {
5691
5796
  agents,
5692
5797
  projectRoot: process.cwd(),
5693
5798
  agentConfigs
5694
5799
  });
5695
5800
  const skillsDir = getSkillsDir();
5696
- const destDir = namespace === "org" ? join(skillsDir, "_org", owner, name) : join(skillsDir, owner, name);
5801
+ let destDir;
5802
+ if (namespace === "github" && subname) {
5803
+ destDir = join(skillsDir, "_github-registry", owner, name, subname);
5804
+ } else if (namespace === "org") {
5805
+ destDir = join(skillsDir, "_org", owner, name);
5806
+ } else {
5807
+ destDir = join(skillsDir, owner, name);
5808
+ }
5697
5809
  try {
5698
5810
  await rm(destDir, { recursive: true, force: true });
5699
5811
  } catch {
@@ -5733,7 +5845,9 @@ async function removeByShortName(shortName, agents, agentConfigs) {
5733
5845
  const registrySkills = await listLockfileSkills();
5734
5846
  const foundRegistry = registrySkills.find((s) => {
5735
5847
  const parsed = parseRegistrySpecifier2(s.name);
5736
- return parsed && parsed.name === shortName;
5848
+ if (!parsed) return false;
5849
+ const effectiveName = parsed.subname ?? parsed.name;
5850
+ return effectiveName === shortName;
5737
5851
  });
5738
5852
  if (foundRegistry) {
5739
5853
  await removeRegistry(foundRegistry.name, agents, agentConfigs);
@@ -5835,6 +5949,396 @@ function formatDownloads(count) {
5835
5949
  return String(count);
5836
5950
  }
5837
5951
 
5952
+ // src/commands/skill-list.ts
5953
+ init_api_client();
5954
+ init_config();
5955
+ function parseListSpecifier(specifier) {
5956
+ const match = specifier.match(/^@(user|org)\/([^/]+)\/([^/]+)$/);
5957
+ if (!match) return null;
5958
+ return {
5959
+ ownerType: match[1],
5960
+ ownerName: match[2],
5961
+ listName: match[3]
5962
+ };
5963
+ }
5964
+ function extractErrorMessage(text) {
5965
+ try {
5966
+ const json = JSON.parse(text);
5967
+ if (typeof json.message === "string") return json.message;
5968
+ if (typeof json.error === "string") return json.error;
5969
+ if (typeof json.error?.message === "string") return json.error.message;
5970
+ } catch {
5971
+ }
5972
+ return text;
5973
+ }
5974
+ async function getBaseUrl() {
5975
+ const config2 = await resolveConfig();
5976
+ return config2.registryUrl.replace(/\/api\/skills\/?$/, "");
5977
+ }
5978
+ async function getAuthHeaders() {
5979
+ const apiKey = await requireApiKey();
5980
+ return {
5981
+ "Content-Type": "application/json",
5982
+ Authorization: `Bearer ${apiKey}`
5983
+ };
5984
+ }
5985
+ async function getOptionalAuthHeaders() {
5986
+ const config2 = await resolveConfig();
5987
+ const apiKey = getTokenForRegistry(config2, config2.registryUrl);
5988
+ const headers = {
5989
+ "Content-Type": "application/json"
5990
+ };
5991
+ if (apiKey) {
5992
+ headers.Authorization = `Bearer ${apiKey}`;
5993
+ }
5994
+ return headers;
5995
+ }
5996
+ async function skillListList(options) {
5997
+ const baseUrl = await getBaseUrl();
5998
+ const config2 = await resolveConfig();
5999
+ let path;
6000
+ if (options.org) {
6001
+ path = `/api/skill-lists/lists/@org/${options.org}`;
6002
+ } else {
6003
+ if (!config2.username) {
6004
+ console.error("Error: Not logged in. Run `pspm login` first.");
6005
+ process.exit(1);
6006
+ }
6007
+ path = `/api/skill-lists/lists/@user/${config2.username}`;
6008
+ }
6009
+ const headers = await getOptionalAuthHeaders();
6010
+ const response = await fetch(`${baseUrl}${path}`, { headers });
6011
+ if (!response.ok) {
6012
+ const errorText = await response.text();
6013
+ console.error(
6014
+ `Error: Failed to list skill lists (${response.status}): ${extractErrorMessage(errorText)}`
6015
+ );
6016
+ process.exit(1);
6017
+ }
6018
+ const lists = await response.json();
6019
+ if (options.json) {
6020
+ console.log(JSON.stringify(lists, null, 2));
6021
+ return;
6022
+ }
6023
+ if (lists.length === 0) {
6024
+ console.log("No skill lists found.");
6025
+ return;
6026
+ }
6027
+ const ownerLabel = options.org ? `@org/${options.org}` : `@user/${config2.username}`;
6028
+ console.log(`
6029
+ Skill lists for ${ownerLabel}:
6030
+ `);
6031
+ console.log(
6032
+ `${"Name".padEnd(35)} ${"Skills".padEnd(8)} ${"Visibility".padEnd(12)} Description`
6033
+ );
6034
+ console.log("-".repeat(90));
6035
+ for (const list2 of lists) {
6036
+ const desc = list2.description ? list2.description.slice(0, 30) : "";
6037
+ console.log(
6038
+ `${list2.name.slice(0, 33).padEnd(35)} ${String(list2.itemCount).padEnd(8)} ${list2.visibility.padEnd(12)} ${desc}`
6039
+ );
6040
+ }
6041
+ console.log(`
6042
+ ${lists.length} list(s)`);
6043
+ }
6044
+ async function skillListCreate(name, options) {
6045
+ const baseUrl = await getBaseUrl();
6046
+ const headers = await getAuthHeaders();
6047
+ const config2 = await resolveConfig();
6048
+ const visibility = options.visibility || "private";
6049
+ if (visibility !== "public" && visibility !== "private") {
6050
+ console.error('Error: --visibility must be "public" or "private"');
6051
+ process.exit(1);
6052
+ }
6053
+ let path;
6054
+ if (options.org) {
6055
+ path = `/api/skill-lists/lists/@org/${options.org}`;
6056
+ } else {
6057
+ if (!config2.username) {
6058
+ console.error("Error: Not logged in. Run `pspm login` first.");
6059
+ process.exit(1);
6060
+ }
6061
+ path = `/api/skill-lists/lists/@user/${config2.username}`;
6062
+ }
6063
+ const body = { name, visibility };
6064
+ if (options.description) {
6065
+ body.description = options.description;
6066
+ }
6067
+ const response = await fetch(`${baseUrl}${path}`, {
6068
+ method: "POST",
6069
+ headers,
6070
+ body: JSON.stringify(body)
6071
+ });
6072
+ if (!response.ok) {
6073
+ const errorText = await response.text();
6074
+ console.error(
6075
+ `Error: Failed to create list (${response.status}): ${extractErrorMessage(errorText)}`
6076
+ );
6077
+ process.exit(1);
6078
+ }
6079
+ const list2 = await response.json();
6080
+ const ownerLabel = options.org ? `@org/${options.org}` : `@user/${config2.username}`;
6081
+ console.log(`Created list: ${ownerLabel}/${list2.name} (${list2.visibility})`);
6082
+ console.log(
6083
+ `
6084
+ Install command: pspm skill-list install ${ownerLabel}/${list2.name}`
6085
+ );
6086
+ }
6087
+ async function skillListShow(specifier, options) {
6088
+ const parsed = parseListSpecifier(specifier);
6089
+ if (!parsed) {
6090
+ console.error(
6091
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6092
+ );
6093
+ process.exit(1);
6094
+ }
6095
+ const config2 = await resolveConfig();
6096
+ configure2({
6097
+ registryUrl: config2.registryUrl,
6098
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
6099
+ });
6100
+ const response = await fetchSkillList(
6101
+ parsed.ownerType,
6102
+ parsed.ownerName,
6103
+ parsed.listName
6104
+ );
6105
+ if (response.status !== 200 || !response.data) {
6106
+ const errorMsg = response.status === 404 ? `List "${specifier}" not found or is private.` : response.error || "Failed to fetch list";
6107
+ console.error(`Error: ${errorMsg}`);
6108
+ process.exit(1);
6109
+ }
6110
+ const list2 = response.data;
6111
+ if (options.json) {
6112
+ console.log(JSON.stringify(list2, null, 2));
6113
+ return;
6114
+ }
6115
+ console.log(`
6116
+ ${list2.name}`);
6117
+ if (list2.description) {
6118
+ console.log(` ${list2.description}`);
6119
+ }
6120
+ console.log(` Visibility: ${list2.visibility}`);
6121
+ console.log(` Owner: @${list2.ownerType}/${list2.ownerName}`);
6122
+ console.log(` Skills: ${list2.items.length}`);
6123
+ if (list2.items.length > 0) {
6124
+ console.log("");
6125
+ for (const item of list2.items) {
6126
+ const ns = item.namespace === "org" ? "org" : "user";
6127
+ const spec = `@${ns}/${item.ownerName}/${item.skillName}`;
6128
+ const ver = item.pinnedVersion ? `@${item.pinnedVersion}` : "";
6129
+ console.log(` - ${spec}${ver}`);
6130
+ }
6131
+ }
6132
+ console.log(`
6133
+ Install all: pspm skill-list install ${specifier}`);
6134
+ }
6135
+ async function skillListDelete(specifier) {
6136
+ const parsed = parseListSpecifier(specifier);
6137
+ if (!parsed) {
6138
+ console.error(
6139
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6140
+ );
6141
+ process.exit(1);
6142
+ }
6143
+ const baseUrl = await getBaseUrl();
6144
+ const headers = await getAuthHeaders();
6145
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}`;
6146
+ const response = await fetch(`${baseUrl}${path}`, {
6147
+ method: "DELETE",
6148
+ headers
6149
+ });
6150
+ if (!response.ok) {
6151
+ const errorText = await response.text();
6152
+ if (response.status === 404) {
6153
+ console.error(`Error: List "${specifier}" not found.`);
6154
+ } else {
6155
+ console.error(
6156
+ `Error: Failed to delete list (${response.status}): ${extractErrorMessage(errorText)}`
6157
+ );
6158
+ }
6159
+ process.exit(1);
6160
+ }
6161
+ console.log(`Deleted list: ${specifier}`);
6162
+ }
6163
+ async function skillListUpdate(specifier, options) {
6164
+ const parsed = parseListSpecifier(specifier);
6165
+ if (!parsed) {
6166
+ console.error(
6167
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6168
+ );
6169
+ process.exit(1);
6170
+ }
6171
+ if (!options.description && !options.visibility) {
6172
+ console.error(
6173
+ "Error: Provide at least one of --description or --visibility"
6174
+ );
6175
+ process.exit(1);
6176
+ }
6177
+ if (options.visibility && options.visibility !== "public" && options.visibility !== "private") {
6178
+ console.error('Error: --visibility must be "public" or "private"');
6179
+ process.exit(1);
6180
+ }
6181
+ const baseUrl = await getBaseUrl();
6182
+ const headers = await getAuthHeaders();
6183
+ const body = {};
6184
+ if (options.description !== void 0) {
6185
+ body.description = options.description;
6186
+ }
6187
+ if (options.visibility) {
6188
+ body.visibility = options.visibility;
6189
+ }
6190
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}`;
6191
+ const response = await fetch(`${baseUrl}${path}`, {
6192
+ method: "PUT",
6193
+ headers,
6194
+ body: JSON.stringify(body)
6195
+ });
6196
+ if (!response.ok) {
6197
+ const errorText = await response.text();
6198
+ console.error(
6199
+ `Error: Failed to update list (${response.status}): ${extractErrorMessage(errorText)}`
6200
+ );
6201
+ process.exit(1);
6202
+ }
6203
+ console.log(`Updated list: ${specifier}`);
6204
+ }
6205
+ async function skillListAddSkill(specifier, skillSpecifiers, options) {
6206
+ const parsed = parseListSpecifier(specifier);
6207
+ if (!parsed) {
6208
+ console.error(
6209
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6210
+ );
6211
+ process.exit(1);
6212
+ }
6213
+ const baseUrl = await getBaseUrl();
6214
+ const headers = await getAuthHeaders();
6215
+ const config2 = await resolveConfig();
6216
+ configure2({
6217
+ registryUrl: config2.registryUrl,
6218
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
6219
+ });
6220
+ for (const skillSpec of skillSpecifiers) {
6221
+ const skillId = await resolveSkillId(baseUrl, headers, skillSpec);
6222
+ if (!skillId) {
6223
+ console.error(`Error: Skill "${skillSpec}" not found.`);
6224
+ continue;
6225
+ }
6226
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}/items`;
6227
+ const addBody = { skillId };
6228
+ if (options.note) {
6229
+ addBody.note = options.note;
6230
+ }
6231
+ const response = await fetch(`${baseUrl}${path}`, {
6232
+ method: "POST",
6233
+ headers,
6234
+ body: JSON.stringify(addBody)
6235
+ });
6236
+ if (!response.ok) {
6237
+ const errorText = await response.text();
6238
+ if (response.status === 409) {
6239
+ console.log(`Already in list: ${skillSpec}`);
6240
+ } else {
6241
+ console.error(
6242
+ `Error: Failed to add "${skillSpec}" (${response.status}): ${extractErrorMessage(errorText)}`
6243
+ );
6244
+ }
6245
+ continue;
6246
+ }
6247
+ console.log(`Added: ${skillSpec}`);
6248
+ }
6249
+ }
6250
+ async function skillListRemoveSkill(specifier, skillSpecifier) {
6251
+ const parsed = parseListSpecifier(specifier);
6252
+ if (!parsed) {
6253
+ console.error(
6254
+ "Error: Invalid list specifier. Use @user/<username>/<list-name> or @org/<orgname>/<list-name>"
6255
+ );
6256
+ process.exit(1);
6257
+ }
6258
+ const config2 = await resolveConfig();
6259
+ configure2({
6260
+ registryUrl: config2.registryUrl,
6261
+ apiKey: getTokenForRegistry(config2, config2.registryUrl)
6262
+ });
6263
+ const listResponse = await fetchSkillList(
6264
+ parsed.ownerType,
6265
+ parsed.ownerName,
6266
+ parsed.listName
6267
+ );
6268
+ if (listResponse.status !== 200 || !listResponse.data) {
6269
+ console.error(`Error: List "${specifier}" not found.`);
6270
+ process.exit(1);
6271
+ }
6272
+ const item = listResponse.data.items.find((i) => {
6273
+ const ns = i.namespace === "org" ? "org" : "user";
6274
+ const fullSpec = `@${ns}/${i.ownerName}/${i.skillName}`;
6275
+ return fullSpec === skillSpecifier || i.skillName === skillSpecifier || `${i.ownerName}/${i.skillName}` === skillSpecifier;
6276
+ });
6277
+ if (!item) {
6278
+ console.error(
6279
+ `Error: Skill "${skillSpecifier}" not found in list "${specifier}".`
6280
+ );
6281
+ process.exit(1);
6282
+ }
6283
+ const baseUrl = await getBaseUrl();
6284
+ const headers = await getAuthHeaders();
6285
+ const path = `/api/skill-lists/lists/@${parsed.ownerType}/${parsed.ownerName}/${parsed.listName}/items/${item.id}`;
6286
+ const response = await fetch(`${baseUrl}${path}`, {
6287
+ method: "DELETE",
6288
+ headers
6289
+ });
6290
+ if (!response.ok) {
6291
+ const errorText = await response.text();
6292
+ console.error(
6293
+ `Error: Failed to remove skill (${response.status}): ${extractErrorMessage(errorText)}`
6294
+ );
6295
+ process.exit(1);
6296
+ }
6297
+ console.log(`Removed: ${skillSpecifier} from ${specifier}`);
6298
+ }
6299
+ async function skillListInstall(specifier, options) {
6300
+ const { install: install2 } = await Promise.resolve().then(() => (init_install(), install_exports));
6301
+ await install2([], {
6302
+ list: specifier,
6303
+ agent: options.agent,
6304
+ yes: options.yes,
6305
+ global: options.global,
6306
+ dir: options.dir
6307
+ });
6308
+ }
6309
+ async function resolveSkillId(baseUrl, headers, specifier) {
6310
+ const match = specifier.match(/^@(user|org|github)\/(.+)$/);
6311
+ let search2;
6312
+ let namespace;
6313
+ if (match) {
6314
+ namespace = match[1];
6315
+ const parts = match[2].split("/");
6316
+ search2 = parts[parts.length - 1];
6317
+ } else {
6318
+ search2 = specifier;
6319
+ }
6320
+ const params = new URLSearchParams({ search: search2, limit: "5" });
6321
+ if (namespace) {
6322
+ params.set("namespace", namespace);
6323
+ }
6324
+ const response = await fetch(`${baseUrl}/api/skills/-/explore?${params}`, {
6325
+ headers
6326
+ });
6327
+ if (!response.ok) return null;
6328
+ const data = await response.json();
6329
+ if (!data.skills || data.skills.length === 0) return null;
6330
+ if (match) {
6331
+ const parts = match[2].split("/");
6332
+ const skillName = parts[parts.length - 1];
6333
+ const ownerName = parts.length >= 2 ? parts[parts.length - 2] : void 0;
6334
+ const exact = data.skills.find(
6335
+ (s) => s.name === skillName && (!ownerName || s.username === ownerName) && (!namespace || s.namespace === namespace)
6336
+ );
6337
+ if (exact) return exact.id;
6338
+ }
6339
+ return data.skills[0].id;
6340
+ }
6341
+
5838
6342
  // src/commands/unpublish.ts
5839
6343
  init_api_client();
5840
6344
  init_config();
@@ -6367,6 +6871,73 @@ program.command("deprecate <specifier> [message]").description(
6367
6871
  ).option("--undo", "Remove deprecation status").action(async (specifier, message, options) => {
6368
6872
  await deprecate(specifier, message, { undo: options.undo });
6369
6873
  });
6874
+ var skillListCmd = program.command("skill-list").description("Manage skill lists (collections of skills)");
6875
+ skillListCmd.command("list").description("List your skill lists").option("--org <orgname>", "List organization skill lists").option("--json", "Output as JSON").action(async (options) => {
6876
+ await skillListList({ org: options.org, json: options.json });
6877
+ });
6878
+ skillListCmd.command("create <name>").description("Create a new skill list").option("-d, --description <description>", "List description").option(
6879
+ "--visibility <visibility>",
6880
+ "Visibility: private or public",
6881
+ "private"
6882
+ ).option("--org <orgname>", "Create under an organization").action(async (name, options) => {
6883
+ await skillListCreate(name, {
6884
+ description: options.description,
6885
+ visibility: options.visibility,
6886
+ org: options.org
6887
+ });
6888
+ });
6889
+ skillListCmd.command("show <specifier>").description("Show skill list details (e.g. @user/alice/my-tools)").option("--json", "Output as JSON").action(async (specifier, options) => {
6890
+ await skillListShow(specifier, { json: options.json });
6891
+ });
6892
+ skillListCmd.command("delete <specifier>").description("Delete a skill list").action(async (specifier) => {
6893
+ await skillListDelete(specifier);
6894
+ });
6895
+ skillListCmd.command("update <specifier>").description("Update skill list metadata").option("-d, --description <description>", "New description").option("--visibility <visibility>", "New visibility: private or public").action(async (specifier, options) => {
6896
+ await skillListUpdate(specifier, {
6897
+ description: options.description,
6898
+ visibility: options.visibility
6899
+ });
6900
+ });
6901
+ skillListCmd.command("add-skill <specifier> <skills...>").description(
6902
+ "Add skills to a list (e.g. pspm skill-list add-skill @user/me/my-list @user/alice/tool)"
6903
+ ).option("--note <note>", "Note for the added skill").action(async (specifier, skills, options) => {
6904
+ await skillListAddSkill(specifier, skills, {
6905
+ note: options.note
6906
+ });
6907
+ });
6908
+ skillListCmd.command("remove-skill <specifier> <skill>").description("Remove a skill from a list").action(async (specifier, skill) => {
6909
+ await skillListRemoveSkill(specifier, skill);
6910
+ });
6911
+ skillListCmd.command("install <specifier>").description(
6912
+ "Install all skills from a list (e.g. pspm skill-list install @user/alice/my-tools)"
6913
+ ).option(
6914
+ "--agent <agents>",
6915
+ 'Comma-separated agents for symlinks (default: all agents, use "none" to skip)'
6916
+ ).option("--dir <path>", "Install skills to a specific directory").option("-g, --global", "Install to user home directory instead of project").option("-y, --yes", "Skip agent selection prompt and use defaults").action(async (specifier, options) => {
6917
+ await skillListInstall(specifier, {
6918
+ agent: options.agent,
6919
+ yes: options.yes,
6920
+ global: options.global,
6921
+ dir: options.dir
6922
+ });
6923
+ });
6924
+ var notebookCmd = program.command("notebook").description("Manage AnyT notebooks");
6925
+ notebookCmd.command("upload <file>").description("Upload a .anyt notebook").option("--org <orgname>", "Upload to an organization").option(
6926
+ "--visibility <visibility>",
6927
+ "Visibility: private, team, or public",
6928
+ "private"
6929
+ ).option("--description <description>", "Notebook description").action(async (file, options) => {
6930
+ await notebookUpload(file, options);
6931
+ });
6932
+ notebookCmd.command("list").description("List notebooks").option("--org <orgname>", "List organization notebooks").action(async (options) => {
6933
+ await notebookList(options);
6934
+ });
6935
+ notebookCmd.command("download <id>").description("Download a notebook by ID").option("-o, --output <path>", "Output file path").action(async (id, options) => {
6936
+ await notebookDownload(id, options.output);
6937
+ });
6938
+ notebookCmd.command("delete <id>").description("Delete a notebook by ID").action(async (id) => {
6939
+ await notebookDelete(id);
6940
+ });
6370
6941
  await program.parseAsync();
6371
6942
  var executedCommand = program.args[0];
6372
6943
  if (executedCommand !== "upgrade") {