@anytio/pspm 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,20 @@ All notable changes to the PSPM CLI will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.7.1] - 2026-03-02
9
+
10
+ ### Added
11
+
12
+ - **`outdated` command**: Check for outdated packages in your project
13
+ - Shows installed vs latest versions for registry, GitHub, and local dependencies
14
+ - Helps identify which packages need updating
15
+
16
+ ## [0.7.0] - 2026-02-25
17
+
18
+ ### Changed
19
+
20
+ - **BREAKING: `--access` is now required for `pspm publish`** — You must explicitly specify `--access public` or `--access private` when publishing. Previously, omitting `--access` would default to public (free users) or private (pro users). This prevents accidental public publishes.
21
+
8
22
  ## [0.6.0] - 2026-02-17
9
23
 
10
24
  ### Changed
package/README.md CHANGED
@@ -80,6 +80,7 @@ pspm list # List installed skills (alias: ls)
80
80
  pspm install [specifiers...] # Install from lockfile, or add specific packages (alias: i)
81
81
  pspm link # Recreate agent symlinks without reinstalling
82
82
  pspm update # Update skills to latest compatible versions
83
+ pspm outdated # Check for outdated packages
83
84
  ```
84
85
 
85
86
  **Multiple package support (like npm):**
@@ -125,13 +126,15 @@ pspm version patch --dry-run # Preview changes without writing
125
126
  ### Publishing
126
127
 
127
128
  ```bash
128
- pspm publish # Publish current directory as a skill
129
- pspm publish --bump patch # Auto-bump version (major, minor, patch)
130
- pspm publish --access public # Publish and make public in one step
131
- pspm unpublish <spec> --force # Remove a published skill version
132
- pspm deprecate <spec> [msg] # Mark a version as deprecated
129
+ pspm publish --access private # Publish as private (requires Pro)
130
+ pspm publish --access public # Publish as public (irreversible)
131
+ pspm publish --access private --bump patch # Bump version and publish
132
+ pspm unpublish <spec> --force # Remove a published skill version
133
+ pspm deprecate <spec> [msg] # Mark a version as deprecated
133
134
  ```
134
135
 
136
+ **`--access` is required.** You must specify `--access public` or `--access private` every time you publish. Private skills require a Pro subscription.
137
+
135
138
  **Publish preview:** Before uploading, `pspm publish` shows a preview of included files, package size, and requires confirmation. Max package size is **10MB**.
136
139
 
137
140
 
@@ -310,3 +313,5 @@ pspm install --frozen-lockfile
310
313
  ## License
311
314
 
312
315
  This project is licensed under [The Artistic License 2.0](LICENSE), the same license used by npm.
316
+
317
+ <!-- @doc-sync: 17484e41aef17ad72e470bb5511876ce0bdfcbb4 | 2026-02-20 15:00 -->
package/dist/index.js CHANGED
@@ -1986,16 +1986,16 @@ async function installFromNode(node, options) {
1986
1986
  const skillsDir = getSkillsDir();
1987
1987
  const destDir = join(skillsDir, username, name);
1988
1988
  await mkdir(destDir, { recursive: true });
1989
- const { writeFile: writeFile8 } = await import('fs/promises');
1989
+ const { writeFile: writeFile9 } = await import('fs/promises');
1990
1990
  const tempFile = join(destDir, ".temp.tgz");
1991
- await writeFile8(tempFile, tarballBuffer);
1991
+ await writeFile9(tempFile, tarballBuffer);
1992
1992
  const { exec: exec2 } = await import('child_process');
1993
1993
  const { promisify: promisify2 } = await import('util');
1994
1994
  const execAsync = promisify2(exec2);
1995
1995
  try {
1996
1996
  await rm(destDir, { recursive: true, force: true });
1997
1997
  await mkdir(destDir, { recursive: true });
1998
- await writeFile8(tempFile, tarballBuffer);
1998
+ await writeFile9(tempFile, tarballBuffer);
1999
1999
  await execAsync(
2000
2000
  `tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
2001
2001
  );
@@ -2331,19 +2331,19 @@ async function access(specifier, options) {
2331
2331
  packageName = parsed.name;
2332
2332
  packageUsername = parsed.username;
2333
2333
  } else {
2334
- const { readFile: readFile8 } = await import('fs/promises');
2335
- const { join: join14 } = await import('path');
2334
+ const { readFile: readFile9 } = await import('fs/promises');
2335
+ const { join: join15 } = await import('path');
2336
2336
  let manifest = null;
2337
2337
  try {
2338
- const content = await readFile8(
2339
- join14(process.cwd(), "pspm.json"),
2338
+ const content = await readFile9(
2339
+ join15(process.cwd(), "pspm.json"),
2340
2340
  "utf-8"
2341
2341
  );
2342
2342
  manifest = JSON.parse(content);
2343
2343
  } catch {
2344
2344
  try {
2345
- const content = await readFile8(
2346
- join14(process.cwd(), "package.json"),
2345
+ const content = await readFile9(
2346
+ join15(process.cwd(), "package.json"),
2347
2347
  "utf-8"
2348
2348
  );
2349
2349
  manifest = JSON.parse(content);
@@ -3679,6 +3679,267 @@ async function migrate(options) {
3679
3679
  process.exit(1);
3680
3680
  }
3681
3681
  }
3682
+ function resolveVersion2(range, availableVersions) {
3683
+ const sorted = availableVersions.filter((v) => semver.valid(v)).sort((a, b) => semver.rcompare(a, b));
3684
+ if (range === "latest") {
3685
+ return sorted[0] ?? null;
3686
+ }
3687
+ return semver.maxSatisfying(sorted, range);
3688
+ }
3689
+ function compareVersions2(a, b) {
3690
+ return semver.compare(a, b);
3691
+ }
3692
+ function getLatestVersion2(versions) {
3693
+ const valid3 = versions.filter((v) => semver.valid(v));
3694
+ if (valid3.length === 0) return null;
3695
+ return valid3.sort((a, b) => semver.rcompare(a, b))[0];
3696
+ }
3697
+
3698
+ // ../../packages/server/skill-registry/src/client/outdated.ts
3699
+ function createOutdatedChecker(config2) {
3700
+ const { registryUrl, apiKey, githubToken } = config2;
3701
+ async function fetchWithAuth(url, token) {
3702
+ const headers = {};
3703
+ if (token) {
3704
+ headers.Authorization = `Bearer ${token}`;
3705
+ }
3706
+ const response = await fetch(url, { headers });
3707
+ if (!response.ok) {
3708
+ const text = await response.text();
3709
+ throw new Error(`Request failed (${response.status}): ${text}`);
3710
+ }
3711
+ return response;
3712
+ }
3713
+ async function fetchRegistryVersions(username, name) {
3714
+ const url = `${registryUrl}/@user/${username}/${name}/versions`;
3715
+ const response = await fetchWithAuth(url, apiKey);
3716
+ return await response.json();
3717
+ }
3718
+ async function checkRegistryPackage(specifier, entry, versionRange) {
3719
+ const match = specifier.match(/^@user\/([^/]+)\/([^/]+)$/);
3720
+ if (!match) {
3721
+ throw new Error(`Invalid registry specifier: ${specifier}`);
3722
+ }
3723
+ const [, username, name] = match;
3724
+ try {
3725
+ const versions = await fetchRegistryVersions(username, name);
3726
+ const versionStrings = versions.map((v) => v.version);
3727
+ const range = versionRange || "*";
3728
+ const wanted = resolveVersion2(range, versionStrings);
3729
+ const latest = getLatestVersion2(versionStrings);
3730
+ const currentVersionInfo = versions.find(
3731
+ (v) => v.version === entry.version
3732
+ );
3733
+ const deprecated = currentVersionInfo?.deprecationMessage ?? void 0;
3734
+ const isOutdated = wanted !== null && compareVersions2(entry.version, wanted) < 0 || latest !== null && compareVersions2(entry.version, latest) < 0;
3735
+ const wantedBehindLatest = wanted !== null && latest !== null && compareVersions2(wanted, latest) < 0;
3736
+ return {
3737
+ name: specifier,
3738
+ current: entry.version,
3739
+ wanted,
3740
+ latest,
3741
+ type: "registry",
3742
+ isOutdated,
3743
+ wantedBehindLatest,
3744
+ versionRange: range,
3745
+ deprecated
3746
+ };
3747
+ } catch {
3748
+ return {
3749
+ name: specifier,
3750
+ current: entry.version,
3751
+ wanted: null,
3752
+ latest: null,
3753
+ type: "registry",
3754
+ isOutdated: false,
3755
+ wantedBehindLatest: false,
3756
+ versionRange
3757
+ };
3758
+ }
3759
+ }
3760
+ async function fetchGitHubLatestCommit(owner, repo, ref) {
3761
+ try {
3762
+ const url = `https://api.github.com/repos/${owner}/${repo}/commits/${ref}`;
3763
+ const response = await fetchWithAuth(url, githubToken);
3764
+ const data = await response.json();
3765
+ return data.sha;
3766
+ } catch {
3767
+ return null;
3768
+ }
3769
+ }
3770
+ async function checkGitHubPackage(specifier, entry) {
3771
+ const match = specifier.match(
3772
+ /^github:([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+)/
3773
+ );
3774
+ if (!match) {
3775
+ throw new Error(`Invalid GitHub specifier: ${specifier}`);
3776
+ }
3777
+ const [, owner, repo] = match;
3778
+ const ref = entry.gitRef || "HEAD";
3779
+ const latestCommit = await fetchGitHubLatestCommit(owner, repo, ref);
3780
+ const currentShort = entry.gitCommit.slice(0, 7);
3781
+ const latestShort = latestCommit?.slice(0, 7) ?? null;
3782
+ const isOutdated = latestCommit !== null && entry.gitCommit !== latestCommit;
3783
+ return {
3784
+ name: specifier,
3785
+ current: currentShort,
3786
+ wanted: latestShort,
3787
+ latest: latestShort,
3788
+ type: "github",
3789
+ isOutdated,
3790
+ wantedBehindLatest: false,
3791
+ versionRange: ref
3792
+ };
3793
+ }
3794
+ function checkLocalPackage(specifier, _entry) {
3795
+ return {
3796
+ name: specifier,
3797
+ current: "local",
3798
+ wanted: null,
3799
+ latest: null,
3800
+ type: "local",
3801
+ isOutdated: false,
3802
+ wantedBehindLatest: false
3803
+ };
3804
+ }
3805
+ async function checkOutdated2(options) {
3806
+ const {
3807
+ lockfile,
3808
+ manifest,
3809
+ includeUpToDate = false,
3810
+ includeLocal = false,
3811
+ packages: filterPackages
3812
+ } = options;
3813
+ const results = [];
3814
+ const registryPackages = lockfile.packages || lockfile.skills || {};
3815
+ const registryDeps = manifest?.dependencies || {};
3816
+ const registryEntries = Object.entries(registryPackages).filter(
3817
+ ([specifier]) => !filterPackages || filterPackages.includes(specifier)
3818
+ );
3819
+ const registryResults = await Promise.all(
3820
+ registryEntries.map(
3821
+ ([specifier, entry]) => checkRegistryPackage(
3822
+ specifier,
3823
+ entry,
3824
+ registryDeps[specifier]
3825
+ )
3826
+ )
3827
+ );
3828
+ results.push(...registryResults);
3829
+ const githubPackages = lockfile.githubPackages || {};
3830
+ const githubEntries = Object.entries(githubPackages).filter(
3831
+ ([specifier]) => !filterPackages || filterPackages.includes(specifier)
3832
+ );
3833
+ const githubResults = await Promise.all(
3834
+ githubEntries.map(
3835
+ ([specifier, entry]) => checkGitHubPackage(specifier, entry)
3836
+ )
3837
+ );
3838
+ results.push(...githubResults);
3839
+ if (includeLocal) {
3840
+ const localPackages = lockfile.localPackages || {};
3841
+ const localEntries = Object.entries(localPackages).filter(
3842
+ ([specifier]) => !filterPackages || filterPackages.includes(specifier)
3843
+ );
3844
+ for (const [specifier, entry] of localEntries) {
3845
+ results.push(checkLocalPackage(specifier));
3846
+ }
3847
+ }
3848
+ if (!includeUpToDate) {
3849
+ return results.filter((r) => r.isOutdated);
3850
+ }
3851
+ return results;
3852
+ }
3853
+ return {
3854
+ checkOutdated: checkOutdated2,
3855
+ checkRegistryPackage,
3856
+ checkGitHubPackage,
3857
+ checkLocalPackage,
3858
+ fetchRegistryVersions
3859
+ };
3860
+ }
3861
+ async function checkOutdated(config2, options) {
3862
+ const checker = createOutdatedChecker(config2);
3863
+ return checker.checkOutdated(options);
3864
+ }
3865
+
3866
+ // src/commands/outdated.ts
3867
+ init_config();
3868
+ init_lockfile2();
3869
+ init_manifest2();
3870
+ async function outdated(packages, options) {
3871
+ try {
3872
+ const lockfile = await readLockfile();
3873
+ if (!lockfile) {
3874
+ console.log("No skills installed.");
3875
+ return;
3876
+ }
3877
+ const hasPackages = Object.keys(lockfile.packages ?? lockfile.skills ?? {}).length > 0 || Object.keys(lockfile.githubPackages ?? {}).length > 0 || Object.keys(lockfile.localPackages ?? {}).length > 0;
3878
+ if (!hasPackages) {
3879
+ console.log("No skills installed.");
3880
+ return;
3881
+ }
3882
+ const config2 = await resolveConfig();
3883
+ const registryUrl = config2.registryUrl;
3884
+ const apiKey = getTokenForRegistry(config2, registryUrl);
3885
+ const githubToken = process.env.GITHUB_TOKEN;
3886
+ const manifest = await readManifest();
3887
+ console.log("Checking for outdated packages...\n");
3888
+ const results = await checkOutdated(
3889
+ { registryUrl, apiKey, githubToken },
3890
+ {
3891
+ lockfile,
3892
+ manifest: manifest ?? void 0,
3893
+ includeUpToDate: options.all,
3894
+ packages: packages.length > 0 ? packages : void 0
3895
+ }
3896
+ );
3897
+ if (results.length === 0) {
3898
+ console.log("All skills are up to date.");
3899
+ return;
3900
+ }
3901
+ if (options.json) {
3902
+ console.log(JSON.stringify(results, null, 2));
3903
+ } else {
3904
+ printTable(results);
3905
+ }
3906
+ const deprecated = results.filter((r) => r.deprecated);
3907
+ if (deprecated.length > 0) {
3908
+ console.log("");
3909
+ for (const r of deprecated) {
3910
+ console.log(`\x1B[33m\u26A0 ${r.name}: ${r.deprecated}\x1B[0m`);
3911
+ }
3912
+ }
3913
+ const hasOutdated = results.some((r) => r.isOutdated);
3914
+ if (hasOutdated) {
3915
+ process.exitCode = 1;
3916
+ }
3917
+ } catch (error) {
3918
+ const message = error instanceof Error ? error.message : "Unknown error";
3919
+ console.error(`Error: ${message}`);
3920
+ process.exit(1);
3921
+ }
3922
+ }
3923
+ function printTable(results) {
3924
+ const headers = ["Package", "Current", "Wanted", "Latest", "Type"];
3925
+ const rows = results.map((r) => [
3926
+ r.name,
3927
+ r.current,
3928
+ r.wanted ?? "\u2014",
3929
+ r.latest ?? "\u2014",
3930
+ r.type
3931
+ ]);
3932
+ const widths = headers.map(
3933
+ (h, i) => Math.max(h.length, ...rows.map((row) => row[i].length))
3934
+ );
3935
+ const headerLine = headers.map((h, i) => h.padEnd(widths[i])).join(" ");
3936
+ console.log(headerLine);
3937
+ console.log(widths.map((w) => "\u2500".repeat(w)).join("\u2500\u2500"));
3938
+ for (const row of rows) {
3939
+ const line = row.map((cell, i) => cell.padEnd(widths[i])).join(" ");
3940
+ console.log(line);
3941
+ }
3942
+ }
3682
3943
 
3683
3944
  // src/commands/publish.ts
3684
3945
  init_api_client();
@@ -3813,8 +4074,8 @@ async function publishCommand(options) {
3813
4074
  dependencies: manifest.dependencies
3814
4075
  };
3815
4076
  if (options.bump) {
3816
- const semver3 = await import('semver');
3817
- const newVersion = semver3.default.inc(packageJson2.version, options.bump);
4077
+ const semver4 = await import('semver');
4078
+ const newVersion = semver4.default.inc(packageJson2.version, options.bump);
3818
4079
  if (!newVersion) {
3819
4080
  console.error(
3820
4081
  `Error: Failed to bump version from ${packageJson2.version}`
@@ -4370,6 +4631,9 @@ program.command("link").description("Recreate agent symlinks without reinstallin
4370
4631
  program.command("update").description("Update all skills to latest compatible versions").option("--dry-run", "Show what would be updated without making changes").action(async (options) => {
4371
4632
  await update({ dryRun: options.dryRun });
4372
4633
  });
4634
+ program.command("outdated [packages...]").description("Check for outdated skills").option("--json", "Output as JSON").option("--all", "Include up-to-date packages").action(async (packages, options) => {
4635
+ await outdated(packages, { json: options.json, all: options.all });
4636
+ });
4373
4637
  program.command("version <bump>").description("Bump package version (major, minor, patch)").option("--dry-run", "Show what would be changed without writing").action(async (bump, options) => {
4374
4638
  const validBumps = ["major", "minor", "patch"];
4375
4639
  if (!validBumps.includes(bump)) {
@@ -4381,11 +4645,19 @@ program.command("version <bump>").description("Bump package version (major, mino
4381
4645
  dryRun: options.dryRun
4382
4646
  });
4383
4647
  });
4384
- program.command("publish").description("Publish current directory as a skill").option("--bump <level>", "Bump version (major, minor, patch)").option("--tag <tag>", "Tag for the release").option("--access <level>", "Set package visibility (public or private)").action(async (options) => {
4648
+ program.command("publish").description("Publish current directory as a skill").option("--bump <level>", "Bump version (major, minor, patch)").option("--tag <tag>", "Tag for the release").requiredOption(
4649
+ "--access <level>",
4650
+ "Set package visibility (public or private)"
4651
+ ).action(async (options) => {
4652
+ const access3 = options.access;
4653
+ if (access3 !== "public" && access3 !== "private") {
4654
+ console.error('Error: --access must be "public" or "private"');
4655
+ process.exit(1);
4656
+ }
4385
4657
  await publishCommand({
4386
4658
  bump: options.bump,
4387
4659
  tag: options.tag,
4388
- access: options.access
4660
+ access: access3
4389
4661
  });
4390
4662
  });
4391
4663
  program.command("unpublish <specifier>").description(