@anytio/pspm 0.7.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 +8 -0
- package/README.md +1 -0
- package/dist/index.js +275 -11
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,14 @@ 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
|
+
|
|
8
16
|
## [0.7.0] - 2026-02-25
|
|
9
17
|
|
|
10
18
|
### 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):**
|
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:
|
|
1989
|
+
const { writeFile: writeFile9 } = await import('fs/promises');
|
|
1990
1990
|
const tempFile = join(destDir, ".temp.tgz");
|
|
1991
|
-
await
|
|
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
|
|
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:
|
|
2335
|
-
const { join:
|
|
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
|
|
2339
|
-
|
|
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
|
|
2346
|
-
|
|
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
|
|
3817
|
-
const newVersion =
|
|
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)) {
|