@anytio/pspm 0.2.0 → 0.3.2
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 +21 -0
- package/CLI_GUIDE.md +24 -6
- package/README.md +21 -6
- package/dist/index.js +1031 -190
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/dist/index.js
CHANGED
|
@@ -63,13 +63,16 @@ var init_generated = __esm({
|
|
|
63
63
|
"use strict";
|
|
64
64
|
init_fetcher();
|
|
65
65
|
getMeUrl = () => {
|
|
66
|
-
return
|
|
66
|
+
return `/api/skills/me`;
|
|
67
67
|
};
|
|
68
68
|
me = async (options) => {
|
|
69
|
-
return customFetch(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
return customFetch(
|
|
70
|
+
getMeUrl(),
|
|
71
|
+
{
|
|
72
|
+
...options,
|
|
73
|
+
method: "GET"
|
|
74
|
+
}
|
|
75
|
+
);
|
|
73
76
|
};
|
|
74
77
|
getListSkillVersionsUrl = (username, name) => {
|
|
75
78
|
return `/api/skills/@user/${username}/${name}/versions`;
|
|
@@ -96,24 +99,32 @@ var init_generated = __esm({
|
|
|
96
99
|
);
|
|
97
100
|
};
|
|
98
101
|
getPublishSkillUrl = () => {
|
|
99
|
-
return
|
|
102
|
+
return `/api/skills/publish`;
|
|
100
103
|
};
|
|
101
104
|
publishSkill = async (publishSkillInput, options) => {
|
|
102
|
-
return customFetch(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
return customFetch(
|
|
106
|
+
getPublishSkillUrl(),
|
|
107
|
+
{
|
|
108
|
+
...options,
|
|
109
|
+
method: "POST",
|
|
110
|
+
headers: { "Content-Type": "application/json", ...options?.headers },
|
|
111
|
+
body: JSON.stringify(
|
|
112
|
+
publishSkillInput
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
);
|
|
108
116
|
};
|
|
109
117
|
getDeleteSkillUrl = (name) => {
|
|
110
118
|
return `/api/skills/${name}`;
|
|
111
119
|
};
|
|
112
120
|
deleteSkill = async (name, options) => {
|
|
113
|
-
return customFetch(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
121
|
+
return customFetch(
|
|
122
|
+
getDeleteSkillUrl(name),
|
|
123
|
+
{
|
|
124
|
+
...options,
|
|
125
|
+
method: "DELETE"
|
|
126
|
+
}
|
|
127
|
+
);
|
|
117
128
|
};
|
|
118
129
|
getDeleteSkillVersionUrl = (name, version2) => {
|
|
119
130
|
return `/api/skills/${name}/${version2}`;
|
|
@@ -609,6 +620,91 @@ var init_integrity = __esm({
|
|
|
609
620
|
}
|
|
610
621
|
});
|
|
611
622
|
|
|
623
|
+
// src/lib/local.ts
|
|
624
|
+
import { access, stat as stat2 } from "fs/promises";
|
|
625
|
+
import { isAbsolute, join as join2, resolve } from "path";
|
|
626
|
+
function isLocalSpecifier(specifier) {
|
|
627
|
+
return specifier.startsWith("file:");
|
|
628
|
+
}
|
|
629
|
+
function isBareLocalPath(specifier) {
|
|
630
|
+
return specifier.startsWith("./") || specifier.startsWith("../");
|
|
631
|
+
}
|
|
632
|
+
function parseLocalSpecifier(specifier) {
|
|
633
|
+
const match = specifier.match(LOCAL_SPECIFIER_PATTERN);
|
|
634
|
+
if (!match) {
|
|
635
|
+
return null;
|
|
636
|
+
}
|
|
637
|
+
const path = match[1];
|
|
638
|
+
if (!path || path.trim() === "") {
|
|
639
|
+
return null;
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
path,
|
|
643
|
+
isAbsolute: isAbsolute(path)
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function getLocalSkillName(spec) {
|
|
647
|
+
const normalizedPath = spec.path.replace(/\/+$/, "");
|
|
648
|
+
const segments = normalizedPath.split("/").filter(Boolean);
|
|
649
|
+
return segments[segments.length - 1] || spec.path;
|
|
650
|
+
}
|
|
651
|
+
function normalizeToFileSpecifier(path) {
|
|
652
|
+
if (isLocalSpecifier(path)) {
|
|
653
|
+
return path;
|
|
654
|
+
}
|
|
655
|
+
return `file:${path}`;
|
|
656
|
+
}
|
|
657
|
+
function resolveLocalPath(spec, basePath = process.cwd()) {
|
|
658
|
+
if (spec.isAbsolute) {
|
|
659
|
+
return resolve(spec.path);
|
|
660
|
+
}
|
|
661
|
+
return resolve(basePath, spec.path);
|
|
662
|
+
}
|
|
663
|
+
async function validateLocalSkill(absolutePath) {
|
|
664
|
+
try {
|
|
665
|
+
const stats = await stat2(absolutePath);
|
|
666
|
+
if (!stats.isDirectory()) {
|
|
667
|
+
return { valid: false, error: `Not a directory: ${absolutePath}` };
|
|
668
|
+
}
|
|
669
|
+
} catch {
|
|
670
|
+
return { valid: false, error: `Directory not found: ${absolutePath}` };
|
|
671
|
+
}
|
|
672
|
+
const manifestPath = join2(absolutePath, "pspm.json");
|
|
673
|
+
try {
|
|
674
|
+
await access(manifestPath);
|
|
675
|
+
} catch {
|
|
676
|
+
return {
|
|
677
|
+
valid: false,
|
|
678
|
+
error: `No pspm.json found in ${absolutePath}`
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
try {
|
|
682
|
+
const { readFile: readFile7 } = await import("fs/promises");
|
|
683
|
+
const content = await readFile7(manifestPath, "utf-8");
|
|
684
|
+
const manifest = JSON.parse(content);
|
|
685
|
+
if (!manifest.name) {
|
|
686
|
+
return {
|
|
687
|
+
valid: false,
|
|
688
|
+
error: `Manifest in ${absolutePath} is missing 'name' field`
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
return { valid: true, manifest };
|
|
692
|
+
} catch (error) {
|
|
693
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
694
|
+
return {
|
|
695
|
+
valid: false,
|
|
696
|
+
error: `Failed to read manifest in ${absolutePath}: ${message}`
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
var LOCAL_SPECIFIER_PATTERN;
|
|
701
|
+
var init_local = __esm({
|
|
702
|
+
"src/lib/local.ts"() {
|
|
703
|
+
"use strict";
|
|
704
|
+
LOCAL_SPECIFIER_PATTERN = /^file:(.+)$/;
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
|
|
612
708
|
// src/lib/manifest.ts
|
|
613
709
|
function validateManifest(manifest) {
|
|
614
710
|
if (!manifest.name) {
|
|
@@ -645,6 +741,349 @@ var init_manifest = __esm({
|
|
|
645
741
|
}
|
|
646
742
|
});
|
|
647
743
|
|
|
744
|
+
// src/lib/version.ts
|
|
745
|
+
import * as semver from "semver";
|
|
746
|
+
function resolveVersion(range, availableVersions) {
|
|
747
|
+
const sorted = availableVersions.filter((v) => semver.valid(v)).sort((a, b) => semver.rcompare(a, b));
|
|
748
|
+
if (!range || range === "latest" || range === "*") {
|
|
749
|
+
return sorted[0] ?? null;
|
|
750
|
+
}
|
|
751
|
+
return semver.maxSatisfying(sorted, range);
|
|
752
|
+
}
|
|
753
|
+
function findHighestSatisfying(ranges, availableVersions) {
|
|
754
|
+
const sorted = availableVersions.filter((v) => semver.valid(v)).sort((a, b) => semver.rcompare(a, b));
|
|
755
|
+
if (sorted.length === 0) return null;
|
|
756
|
+
const normalizedRanges = ranges.map(
|
|
757
|
+
(r) => !r || r === "latest" || r === "*" ? "*" : r
|
|
758
|
+
);
|
|
759
|
+
for (const version2 of sorted) {
|
|
760
|
+
const satisfiesAll = normalizedRanges.every(
|
|
761
|
+
(range) => semver.satisfies(version2, range)
|
|
762
|
+
);
|
|
763
|
+
if (satisfiesAll) {
|
|
764
|
+
return version2;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return null;
|
|
768
|
+
}
|
|
769
|
+
var init_version = __esm({
|
|
770
|
+
"src/lib/version.ts"() {
|
|
771
|
+
"use strict";
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
// src/lib/resolver.ts
|
|
776
|
+
async function resolveRecursive(rootDeps, config2) {
|
|
777
|
+
const graph = {
|
|
778
|
+
nodes: /* @__PURE__ */ new Map(),
|
|
779
|
+
roots: Object.keys(rootDeps),
|
|
780
|
+
errors: [],
|
|
781
|
+
conflicts: []
|
|
782
|
+
};
|
|
783
|
+
configure2({
|
|
784
|
+
registryUrl: config2.registryUrl,
|
|
785
|
+
apiKey: config2.apiKey ?? ""
|
|
786
|
+
});
|
|
787
|
+
const rangesByPackage = /* @__PURE__ */ new Map();
|
|
788
|
+
const queue = [];
|
|
789
|
+
for (const [name, range] of Object.entries(rootDeps)) {
|
|
790
|
+
queue.push({
|
|
791
|
+
name,
|
|
792
|
+
versionRange: range,
|
|
793
|
+
depth: 0,
|
|
794
|
+
dependent: "root",
|
|
795
|
+
path: []
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
const processing = /* @__PURE__ */ new Set();
|
|
799
|
+
while (queue.length > 0) {
|
|
800
|
+
const item = queue.shift();
|
|
801
|
+
if (!item) continue;
|
|
802
|
+
const { name, versionRange, depth, dependent, path } = item;
|
|
803
|
+
if (depth > config2.maxDepth) {
|
|
804
|
+
graph.errors.push({
|
|
805
|
+
type: "max_depth_exceeded",
|
|
806
|
+
package: name,
|
|
807
|
+
message: `Maximum dependency depth (${config2.maxDepth}) exceeded at: ${[...path, name].join(" -> ")}`,
|
|
808
|
+
path: [...path, name]
|
|
809
|
+
});
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
if (path.includes(name)) {
|
|
813
|
+
graph.errors.push({
|
|
814
|
+
type: "circular_dependency",
|
|
815
|
+
package: name,
|
|
816
|
+
message: `Circular dependency detected: ${[...path, name].join(" -> ")}`,
|
|
817
|
+
path: [...path, name]
|
|
818
|
+
});
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (!rangesByPackage.has(name)) {
|
|
822
|
+
rangesByPackage.set(name, []);
|
|
823
|
+
}
|
|
824
|
+
rangesByPackage.get(name)?.push({
|
|
825
|
+
range: versionRange,
|
|
826
|
+
dependent,
|
|
827
|
+
depth
|
|
828
|
+
});
|
|
829
|
+
if (processing.has(name)) {
|
|
830
|
+
continue;
|
|
831
|
+
}
|
|
832
|
+
processing.add(name);
|
|
833
|
+
const match = name.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
834
|
+
if (!match) {
|
|
835
|
+
graph.errors.push({
|
|
836
|
+
type: "package_not_found",
|
|
837
|
+
package: name,
|
|
838
|
+
message: `Invalid package name format: ${name}`
|
|
839
|
+
});
|
|
840
|
+
continue;
|
|
841
|
+
}
|
|
842
|
+
const [, username, skillName] = match;
|
|
843
|
+
try {
|
|
844
|
+
const versionsResponse = await listSkillVersions(username, skillName);
|
|
845
|
+
if (versionsResponse.status !== 200) {
|
|
846
|
+
graph.errors.push({
|
|
847
|
+
type: "package_not_found",
|
|
848
|
+
package: name,
|
|
849
|
+
message: `Package ${name} not found in registry`
|
|
850
|
+
});
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
const versions = versionsResponse.data;
|
|
854
|
+
if (versions.length === 0) {
|
|
855
|
+
graph.errors.push({
|
|
856
|
+
type: "package_not_found",
|
|
857
|
+
package: name,
|
|
858
|
+
message: `Package ${name} has no versions`
|
|
859
|
+
});
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
const availableVersions = versions.map((v) => v.version);
|
|
863
|
+
const resolvedVersion = findHighestSatisfying(
|
|
864
|
+
[versionRange],
|
|
865
|
+
availableVersions
|
|
866
|
+
);
|
|
867
|
+
if (!resolvedVersion) {
|
|
868
|
+
graph.errors.push({
|
|
869
|
+
type: "no_satisfying_version",
|
|
870
|
+
package: name,
|
|
871
|
+
message: `No version of ${name} satisfies: ${versionRange}`
|
|
872
|
+
});
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
const versionResponse = await getSkillVersion(
|
|
876
|
+
username,
|
|
877
|
+
skillName,
|
|
878
|
+
resolvedVersion
|
|
879
|
+
);
|
|
880
|
+
if (versionResponse.status !== 200 || !versionResponse.data) {
|
|
881
|
+
graph.errors.push({
|
|
882
|
+
type: "fetch_error",
|
|
883
|
+
package: name,
|
|
884
|
+
message: `Failed to fetch ${name}@${resolvedVersion}`
|
|
885
|
+
});
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
const versionInfo = versionResponse.data;
|
|
889
|
+
const manifest = versionInfo.manifest;
|
|
890
|
+
const dependencies = manifest?.dependencies ?? {};
|
|
891
|
+
const node = {
|
|
892
|
+
name,
|
|
893
|
+
version: resolvedVersion,
|
|
894
|
+
versionRange,
|
|
895
|
+
downloadUrl: versionInfo.downloadUrl,
|
|
896
|
+
integrity: `sha256-${Buffer.from(versionInfo.checksum, "hex").toString("base64")}`,
|
|
897
|
+
depth,
|
|
898
|
+
dependencies,
|
|
899
|
+
dependents: [dependent],
|
|
900
|
+
isDirect: depth === 0,
|
|
901
|
+
deprecated: versionInfo.deprecationMessage ?? void 0
|
|
902
|
+
};
|
|
903
|
+
graph.nodes.set(name, node);
|
|
904
|
+
for (const [depName, depRange] of Object.entries(dependencies)) {
|
|
905
|
+
queue.push({
|
|
906
|
+
name: depName,
|
|
907
|
+
versionRange: depRange,
|
|
908
|
+
depth: depth + 1,
|
|
909
|
+
dependent: name,
|
|
910
|
+
path: [...path, name]
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
} catch (error) {
|
|
914
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
915
|
+
graph.errors.push({
|
|
916
|
+
type: "fetch_error",
|
|
917
|
+
package: name,
|
|
918
|
+
message: `Error fetching ${name}: ${message}`
|
|
919
|
+
});
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
for (const [name, ranges] of rangesByPackage.entries()) {
|
|
923
|
+
const node = graph.nodes.get(name);
|
|
924
|
+
if (!node) continue;
|
|
925
|
+
const uniqueDependents = [...new Set(ranges.map((r) => r.dependent))];
|
|
926
|
+
node.dependents = uniqueDependents;
|
|
927
|
+
const allRanges = ranges.map((r) => r.range);
|
|
928
|
+
const match = name.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
929
|
+
if (!match) continue;
|
|
930
|
+
const [, username, skillName] = match;
|
|
931
|
+
try {
|
|
932
|
+
const versionsResponse = await listSkillVersions(username, skillName);
|
|
933
|
+
if (versionsResponse.status !== 200) continue;
|
|
934
|
+
const versions = versionsResponse.data;
|
|
935
|
+
const availableVersions = versions.map((v) => v.version);
|
|
936
|
+
const finalVersion = findHighestSatisfying(allRanges, availableVersions);
|
|
937
|
+
if (!finalVersion) {
|
|
938
|
+
graph.conflicts.push({
|
|
939
|
+
package: name,
|
|
940
|
+
ranges: ranges.map((r) => ({
|
|
941
|
+
dependent: r.dependent,
|
|
942
|
+
range: r.range
|
|
943
|
+
})),
|
|
944
|
+
availableVersions
|
|
945
|
+
});
|
|
946
|
+
graph.errors.push({
|
|
947
|
+
type: "no_satisfying_version",
|
|
948
|
+
package: name,
|
|
949
|
+
message: `No version of ${name} satisfies all requirements: ${allRanges.join(", ")}`
|
|
950
|
+
});
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
if (finalVersion !== node.version) {
|
|
954
|
+
const versionResponse = await getSkillVersion(
|
|
955
|
+
username,
|
|
956
|
+
skillName,
|
|
957
|
+
finalVersion
|
|
958
|
+
);
|
|
959
|
+
if (versionResponse.status === 200 && versionResponse.data) {
|
|
960
|
+
const versionInfo = versionResponse.data;
|
|
961
|
+
node.version = finalVersion;
|
|
962
|
+
node.downloadUrl = versionInfo.downloadUrl;
|
|
963
|
+
node.integrity = `sha256-${Buffer.from(versionInfo.checksum, "hex").toString("base64")}`;
|
|
964
|
+
node.deprecated = versionInfo.deprecationMessage ?? void 0;
|
|
965
|
+
const manifest = versionInfo.manifest;
|
|
966
|
+
node.dependencies = manifest?.dependencies ?? {};
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
} catch {
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
const installOrder = topologicalSort(graph);
|
|
973
|
+
const success = graph.errors.length === 0 && graph.conflicts.length === 0;
|
|
974
|
+
return {
|
|
975
|
+
success,
|
|
976
|
+
graph,
|
|
977
|
+
installOrder
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
function topologicalSort(graph) {
|
|
981
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
982
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
983
|
+
for (const name of graph.nodes.keys()) {
|
|
984
|
+
inDegree.set(name, 0);
|
|
985
|
+
dependents.set(name, []);
|
|
986
|
+
}
|
|
987
|
+
for (const [name, node] of graph.nodes.entries()) {
|
|
988
|
+
for (const depName of Object.keys(node.dependencies)) {
|
|
989
|
+
if (graph.nodes.has(depName)) {
|
|
990
|
+
inDegree.set(name, (inDegree.get(name) ?? 0) + 1);
|
|
991
|
+
if (!dependents.has(depName)) {
|
|
992
|
+
dependents.set(depName, []);
|
|
993
|
+
}
|
|
994
|
+
dependents.get(depName)?.push(name);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
const queue = [];
|
|
999
|
+
for (const [name, degree] of inDegree.entries()) {
|
|
1000
|
+
if (degree === 0) {
|
|
1001
|
+
queue.push(name);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
const sorted = [];
|
|
1005
|
+
while (queue.length > 0) {
|
|
1006
|
+
const current = queue.shift();
|
|
1007
|
+
if (!current) continue;
|
|
1008
|
+
sorted.push(current);
|
|
1009
|
+
const deps = dependents.get(current) ?? [];
|
|
1010
|
+
for (const dependent of deps) {
|
|
1011
|
+
const newDegree = (inDegree.get(dependent) ?? 1) - 1;
|
|
1012
|
+
inDegree.set(dependent, newDegree);
|
|
1013
|
+
if (newDegree === 0 && !sorted.includes(dependent)) {
|
|
1014
|
+
queue.push(dependent);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
return sorted;
|
|
1019
|
+
}
|
|
1020
|
+
function computeInstallOrder(packages) {
|
|
1021
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1022
|
+
const order = [];
|
|
1023
|
+
function visit(name) {
|
|
1024
|
+
if (visited.has(name)) return;
|
|
1025
|
+
visited.add(name);
|
|
1026
|
+
const entry = packages[name];
|
|
1027
|
+
if (entry?.dependencies) {
|
|
1028
|
+
for (const dep of Object.keys(entry.dependencies)) {
|
|
1029
|
+
visit(dep);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
order.push(name);
|
|
1033
|
+
}
|
|
1034
|
+
for (const name of Object.keys(packages)) {
|
|
1035
|
+
visit(name);
|
|
1036
|
+
}
|
|
1037
|
+
return order;
|
|
1038
|
+
}
|
|
1039
|
+
function formatResolutionErrors(errors) {
|
|
1040
|
+
return errors.map((error) => {
|
|
1041
|
+
switch (error.type) {
|
|
1042
|
+
case "circular_dependency":
|
|
1043
|
+
return `Circular dependency: ${error.path?.join(" -> ") ?? error.package}`;
|
|
1044
|
+
case "max_depth_exceeded":
|
|
1045
|
+
return `Max depth exceeded at: ${error.path?.join(" -> ") ?? error.package}`;
|
|
1046
|
+
case "no_satisfying_version":
|
|
1047
|
+
return error.message;
|
|
1048
|
+
case "package_not_found":
|
|
1049
|
+
return `Package not found: ${error.package}`;
|
|
1050
|
+
case "fetch_error":
|
|
1051
|
+
return error.message;
|
|
1052
|
+
default:
|
|
1053
|
+
return error.message;
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
function formatVersionConflicts(conflicts) {
|
|
1058
|
+
return conflicts.map((conflict) => {
|
|
1059
|
+
const requirements = conflict.ranges.map((r) => `${r.dependent} needs ${r.range}`).join(", ");
|
|
1060
|
+
return `No version of ${conflict.package} satisfies: ${requirements}`;
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
function printResolutionErrors(errors, conflicts = []) {
|
|
1064
|
+
if (errors.length > 0) {
|
|
1065
|
+
console.error("\nResolution errors:");
|
|
1066
|
+
for (const msg of formatResolutionErrors(errors)) {
|
|
1067
|
+
console.error(` - ${msg}`);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
if (conflicts.length > 0) {
|
|
1071
|
+
console.error("\nVersion conflicts:");
|
|
1072
|
+
for (const msg of formatVersionConflicts(conflicts)) {
|
|
1073
|
+
console.error(` - ${msg}`);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
var MAX_DEPENDENCY_DEPTH;
|
|
1078
|
+
var init_resolver = __esm({
|
|
1079
|
+
"src/lib/resolver.ts"() {
|
|
1080
|
+
"use strict";
|
|
1081
|
+
init_api_client();
|
|
1082
|
+
init_version();
|
|
1083
|
+
MAX_DEPENDENCY_DEPTH = 5;
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
|
|
648
1087
|
// src/lib/specifier.ts
|
|
649
1088
|
function parseSkillSpecifier(specifier) {
|
|
650
1089
|
const match = specifier.match(SPECIFIER_PATTERN);
|
|
@@ -700,27 +1139,14 @@ var init_specifier = __esm({
|
|
|
700
1139
|
}
|
|
701
1140
|
});
|
|
702
1141
|
|
|
703
|
-
// src/lib/version.ts
|
|
704
|
-
import * as semver from "semver";
|
|
705
|
-
function resolveVersion(range, availableVersions) {
|
|
706
|
-
const sorted = availableVersions.filter((v) => semver.valid(v)).sort((a, b) => semver.rcompare(a, b));
|
|
707
|
-
if (!range || range === "latest" || range === "*") {
|
|
708
|
-
return sorted[0] ?? null;
|
|
709
|
-
}
|
|
710
|
-
return semver.maxSatisfying(sorted, range);
|
|
711
|
-
}
|
|
712
|
-
var init_version = __esm({
|
|
713
|
-
"src/lib/version.ts"() {
|
|
714
|
-
"use strict";
|
|
715
|
-
}
|
|
716
|
-
});
|
|
717
|
-
|
|
718
1142
|
// src/lib/index.ts
|
|
719
1143
|
var init_lib = __esm({
|
|
720
1144
|
"src/lib/index.ts"() {
|
|
721
1145
|
"use strict";
|
|
722
1146
|
init_integrity();
|
|
1147
|
+
init_local();
|
|
723
1148
|
init_manifest();
|
|
1149
|
+
init_resolver();
|
|
724
1150
|
init_specifier();
|
|
725
1151
|
init_version();
|
|
726
1152
|
}
|
|
@@ -818,7 +1244,7 @@ var init_agents = __esm({
|
|
|
818
1244
|
|
|
819
1245
|
// src/github.ts
|
|
820
1246
|
import { cp, lstat, mkdir as mkdir2, readdir, rm, writeFile as writeFile2 } from "fs/promises";
|
|
821
|
-
import { join as
|
|
1247
|
+
import { join as join3 } from "path";
|
|
822
1248
|
function getGitHubHeaders() {
|
|
823
1249
|
const headers = {
|
|
824
1250
|
Accept: "application/vnd.github+json",
|
|
@@ -894,10 +1320,10 @@ async function downloadGitHubPackage(spec) {
|
|
|
894
1320
|
return { buffer, commit, integrity };
|
|
895
1321
|
}
|
|
896
1322
|
async function extractGitHubPackage(spec, buffer, skillsDir) {
|
|
897
|
-
const destPath = spec.path ?
|
|
898
|
-
const tempDir =
|
|
1323
|
+
const destPath = spec.path ? join3(skillsDir, "_github", spec.owner, spec.repo, spec.path) : join3(skillsDir, "_github", spec.owner, spec.repo);
|
|
1324
|
+
const tempDir = join3(skillsDir, "_github", ".temp", `${Date.now()}`);
|
|
899
1325
|
await mkdir2(tempDir, { recursive: true });
|
|
900
|
-
const tempFile =
|
|
1326
|
+
const tempFile = join3(tempDir, "archive.tgz");
|
|
901
1327
|
try {
|
|
902
1328
|
await writeFile2(tempFile, buffer);
|
|
903
1329
|
const { exec: exec2 } = await import("child_process");
|
|
@@ -911,16 +1337,16 @@ async function extractGitHubPackage(spec, buffer, skillsDir) {
|
|
|
911
1337
|
if (!extractedDir) {
|
|
912
1338
|
throw new Error("Failed to find extracted directory in tarball");
|
|
913
1339
|
}
|
|
914
|
-
const sourcePath =
|
|
915
|
-
const copySource = spec.path ?
|
|
1340
|
+
const sourcePath = join3(tempDir, extractedDir);
|
|
1341
|
+
const copySource = spec.path ? join3(sourcePath, spec.path) : sourcePath;
|
|
916
1342
|
if (spec.path) {
|
|
917
1343
|
const pathExists = await lstat(copySource).catch(() => null);
|
|
918
1344
|
if (!pathExists) {
|
|
919
1345
|
const rootEntries = await readdir(sourcePath);
|
|
920
1346
|
const dirs = [];
|
|
921
1347
|
for (const entry of rootEntries) {
|
|
922
|
-
const
|
|
923
|
-
if (
|
|
1348
|
+
const stat8 = await lstat(join3(sourcePath, entry)).catch(() => null);
|
|
1349
|
+
if (stat8?.isDirectory() && !entry.startsWith(".")) {
|
|
924
1350
|
dirs.push(entry);
|
|
925
1351
|
}
|
|
926
1352
|
}
|
|
@@ -985,11 +1411,11 @@ Available paths in repository root:
|
|
|
985
1411
|
});
|
|
986
1412
|
|
|
987
1413
|
// src/lockfile.ts
|
|
988
|
-
import { mkdir as mkdir3, readFile as readFile2, stat as
|
|
1414
|
+
import { mkdir as mkdir3, readFile as readFile2, stat as stat3, writeFile as writeFile3 } from "fs/promises";
|
|
989
1415
|
import { dirname as dirname2 } from "path";
|
|
990
1416
|
async function hasLegacyLockfile() {
|
|
991
1417
|
try {
|
|
992
|
-
await
|
|
1418
|
+
await stat3(getLegacyLockfilePath());
|
|
993
1419
|
return true;
|
|
994
1420
|
} catch {
|
|
995
1421
|
return false;
|
|
@@ -999,12 +1425,12 @@ async function migrateLockfileIfNeeded() {
|
|
|
999
1425
|
const legacyPath = getLegacyLockfilePath();
|
|
1000
1426
|
const newPath = getLockfilePath();
|
|
1001
1427
|
try {
|
|
1002
|
-
await
|
|
1428
|
+
await stat3(legacyPath);
|
|
1003
1429
|
} catch {
|
|
1004
1430
|
return false;
|
|
1005
1431
|
}
|
|
1006
1432
|
try {
|
|
1007
|
-
await
|
|
1433
|
+
await stat3(newPath);
|
|
1008
1434
|
return false;
|
|
1009
1435
|
} catch {
|
|
1010
1436
|
}
|
|
@@ -1057,21 +1483,35 @@ async function readLockfile() {
|
|
|
1057
1483
|
async function writeLockfile(lockfile) {
|
|
1058
1484
|
const lockfilePath = getLockfilePath();
|
|
1059
1485
|
await mkdir3(dirname2(lockfilePath), { recursive: true });
|
|
1486
|
+
const packages = lockfile.packages ?? lockfile.skills ?? {};
|
|
1487
|
+
const hasDependencies = Object.values(packages).some(
|
|
1488
|
+
(pkg) => pkg.dependencies && Object.keys(pkg.dependencies).length > 0
|
|
1489
|
+
);
|
|
1490
|
+
const hasLocalPackages = lockfile.localPackages && Object.keys(lockfile.localPackages).length > 0;
|
|
1491
|
+
let version2 = 3;
|
|
1492
|
+
if (hasLocalPackages) {
|
|
1493
|
+
version2 = 5;
|
|
1494
|
+
} else if (hasDependencies) {
|
|
1495
|
+
version2 = 4;
|
|
1496
|
+
}
|
|
1060
1497
|
const normalized = {
|
|
1061
|
-
lockfileVersion:
|
|
1498
|
+
lockfileVersion: version2,
|
|
1062
1499
|
registryUrl: lockfile.registryUrl,
|
|
1063
|
-
packages
|
|
1500
|
+
packages
|
|
1064
1501
|
};
|
|
1065
1502
|
if (lockfile.githubPackages && Object.keys(lockfile.githubPackages).length > 0) {
|
|
1066
1503
|
normalized.githubPackages = lockfile.githubPackages;
|
|
1067
1504
|
}
|
|
1505
|
+
if (hasLocalPackages) {
|
|
1506
|
+
normalized.localPackages = lockfile.localPackages;
|
|
1507
|
+
}
|
|
1068
1508
|
await writeFile3(lockfilePath, `${JSON.stringify(normalized, null, 2)}
|
|
1069
1509
|
`);
|
|
1070
1510
|
}
|
|
1071
1511
|
async function createEmptyLockfile() {
|
|
1072
1512
|
const registryUrl = await getRegistryUrl();
|
|
1073
1513
|
return {
|
|
1074
|
-
lockfileVersion:
|
|
1514
|
+
lockfileVersion: 4,
|
|
1075
1515
|
registryUrl,
|
|
1076
1516
|
packages: {}
|
|
1077
1517
|
};
|
|
@@ -1089,6 +1529,20 @@ async function addToLockfile(fullName, entry) {
|
|
|
1089
1529
|
lockfile.packages = packages;
|
|
1090
1530
|
await writeLockfile(lockfile);
|
|
1091
1531
|
}
|
|
1532
|
+
async function addToLockfileWithDeps(fullName, entry, dependencies) {
|
|
1533
|
+
let lockfile = await readLockfile();
|
|
1534
|
+
if (!lockfile) {
|
|
1535
|
+
lockfile = await createEmptyLockfile();
|
|
1536
|
+
}
|
|
1537
|
+
const packages = getPackages(lockfile);
|
|
1538
|
+
const entryWithDeps = { ...entry };
|
|
1539
|
+
if (dependencies && Object.keys(dependencies).length > 0) {
|
|
1540
|
+
entryWithDeps.dependencies = dependencies;
|
|
1541
|
+
}
|
|
1542
|
+
packages[fullName] = entryWithDeps;
|
|
1543
|
+
lockfile.packages = packages;
|
|
1544
|
+
await writeLockfile(lockfile);
|
|
1545
|
+
}
|
|
1092
1546
|
async function removeFromLockfile(fullName) {
|
|
1093
1547
|
const lockfile = await readLockfile();
|
|
1094
1548
|
if (!lockfile) {
|
|
@@ -1144,6 +1598,36 @@ async function listLockfileGitHubPackages() {
|
|
|
1144
1598
|
entry
|
|
1145
1599
|
}));
|
|
1146
1600
|
}
|
|
1601
|
+
async function addLocalToLockfile(specifier, entry) {
|
|
1602
|
+
let lockfile = await readLockfile();
|
|
1603
|
+
if (!lockfile) {
|
|
1604
|
+
lockfile = await createEmptyLockfile();
|
|
1605
|
+
}
|
|
1606
|
+
if (!lockfile.localPackages) {
|
|
1607
|
+
lockfile.localPackages = {};
|
|
1608
|
+
}
|
|
1609
|
+
lockfile.localPackages[specifier] = entry;
|
|
1610
|
+
await writeLockfile(lockfile);
|
|
1611
|
+
}
|
|
1612
|
+
async function removeLocalFromLockfile(specifier) {
|
|
1613
|
+
const lockfile = await readLockfile();
|
|
1614
|
+
if (!lockfile?.localPackages?.[specifier]) {
|
|
1615
|
+
return false;
|
|
1616
|
+
}
|
|
1617
|
+
delete lockfile.localPackages[specifier];
|
|
1618
|
+
await writeLockfile(lockfile);
|
|
1619
|
+
return true;
|
|
1620
|
+
}
|
|
1621
|
+
async function listLockfileLocalPackages() {
|
|
1622
|
+
const lockfile = await readLockfile();
|
|
1623
|
+
if (!lockfile?.localPackages) {
|
|
1624
|
+
return [];
|
|
1625
|
+
}
|
|
1626
|
+
return Object.entries(lockfile.localPackages).map(([specifier, entry]) => ({
|
|
1627
|
+
specifier,
|
|
1628
|
+
entry
|
|
1629
|
+
}));
|
|
1630
|
+
}
|
|
1147
1631
|
var init_lockfile = __esm({
|
|
1148
1632
|
"src/lockfile.ts"() {
|
|
1149
1633
|
"use strict";
|
|
@@ -1153,9 +1637,9 @@ var init_lockfile = __esm({
|
|
|
1153
1637
|
|
|
1154
1638
|
// src/manifest.ts
|
|
1155
1639
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
1156
|
-
import { join as
|
|
1640
|
+
import { join as join4 } from "path";
|
|
1157
1641
|
function getManifestPath() {
|
|
1158
|
-
return
|
|
1642
|
+
return join4(process.cwd(), "pspm.json");
|
|
1159
1643
|
}
|
|
1160
1644
|
async function readManifest() {
|
|
1161
1645
|
try {
|
|
@@ -1225,6 +1709,27 @@ async function removeGitHubDependency(specifier) {
|
|
|
1225
1709
|
await writeManifest(manifest);
|
|
1226
1710
|
return true;
|
|
1227
1711
|
}
|
|
1712
|
+
async function getLocalDependencies() {
|
|
1713
|
+
const manifest = await readManifest();
|
|
1714
|
+
return manifest?.localDependencies ?? {};
|
|
1715
|
+
}
|
|
1716
|
+
async function addLocalDependency(specifier, version2 = "*") {
|
|
1717
|
+
const manifest = await ensureManifest();
|
|
1718
|
+
if (!manifest.localDependencies) {
|
|
1719
|
+
manifest.localDependencies = {};
|
|
1720
|
+
}
|
|
1721
|
+
manifest.localDependencies[specifier] = version2;
|
|
1722
|
+
await writeManifest(manifest);
|
|
1723
|
+
}
|
|
1724
|
+
async function removeLocalDependency(specifier) {
|
|
1725
|
+
const manifest = await readManifest();
|
|
1726
|
+
if (!manifest?.localDependencies?.[specifier]) {
|
|
1727
|
+
return false;
|
|
1728
|
+
}
|
|
1729
|
+
delete manifest.localDependencies[specifier];
|
|
1730
|
+
await writeManifest(manifest);
|
|
1731
|
+
return true;
|
|
1732
|
+
}
|
|
1228
1733
|
var init_manifest2 = __esm({
|
|
1229
1734
|
"src/manifest.ts"() {
|
|
1230
1735
|
"use strict";
|
|
@@ -1233,7 +1738,7 @@ var init_manifest2 = __esm({
|
|
|
1233
1738
|
|
|
1234
1739
|
// src/symlinks.ts
|
|
1235
1740
|
import { lstat as lstat2, mkdir as mkdir4, readlink, rm as rm2, symlink } from "fs/promises";
|
|
1236
|
-
import { dirname as dirname3, join as
|
|
1741
|
+
import { dirname as dirname3, join as join5, relative } from "path";
|
|
1237
1742
|
async function createAgentSymlinks(skills, options) {
|
|
1238
1743
|
const { agents, projectRoot, agentConfigs } = options;
|
|
1239
1744
|
if (agents.length === 1 && agents[0] === "none") {
|
|
@@ -1245,11 +1750,11 @@ async function createAgentSymlinks(skills, options) {
|
|
|
1245
1750
|
console.warn(`Warning: Unknown agent "${agentName}", skipping symlinks`);
|
|
1246
1751
|
continue;
|
|
1247
1752
|
}
|
|
1248
|
-
const agentSkillsDir =
|
|
1753
|
+
const agentSkillsDir = join5(projectRoot, config2.skillsDir);
|
|
1249
1754
|
await mkdir4(agentSkillsDir, { recursive: true });
|
|
1250
1755
|
for (const skill of skills) {
|
|
1251
|
-
const symlinkPath =
|
|
1252
|
-
const targetPath =
|
|
1756
|
+
const symlinkPath = join5(agentSkillsDir, skill.name);
|
|
1757
|
+
const targetPath = join5(projectRoot, skill.sourcePath);
|
|
1253
1758
|
const relativeTarget = relative(dirname3(symlinkPath), targetPath);
|
|
1254
1759
|
await createSymlink(symlinkPath, relativeTarget, skill.name);
|
|
1255
1760
|
}
|
|
@@ -1290,7 +1795,7 @@ async function removeAgentSymlinks(skillName, options) {
|
|
|
1290
1795
|
if (!config2) {
|
|
1291
1796
|
continue;
|
|
1292
1797
|
}
|
|
1293
|
-
const symlinkPath =
|
|
1798
|
+
const symlinkPath = join5(projectRoot, config2.skillsDir, skillName);
|
|
1294
1799
|
try {
|
|
1295
1800
|
const stats = await lstat2(symlinkPath).catch(() => null);
|
|
1296
1801
|
if (stats?.isSymbolicLink()) {
|
|
@@ -1309,12 +1814,15 @@ function getGitHubSkillPath(owner, repo, path) {
|
|
|
1309
1814
|
}
|
|
1310
1815
|
return `.pspm/skills/_github/${owner}/${repo}`;
|
|
1311
1816
|
}
|
|
1817
|
+
function getLocalSkillPath(skillName) {
|
|
1818
|
+
return `.pspm/skills/_local/${skillName}`;
|
|
1819
|
+
}
|
|
1312
1820
|
async function getLinkedAgents(skillName, agents, projectRoot, agentConfigs) {
|
|
1313
1821
|
const linkedAgents = [];
|
|
1314
1822
|
for (const agentName of agents) {
|
|
1315
1823
|
const config2 = resolveAgentConfig(agentName, agentConfigs);
|
|
1316
1824
|
if (!config2) continue;
|
|
1317
|
-
const symlinkPath =
|
|
1825
|
+
const symlinkPath = join5(projectRoot, config2.skillsDir, skillName);
|
|
1318
1826
|
try {
|
|
1319
1827
|
const stats = await lstat2(symlinkPath);
|
|
1320
1828
|
if (stats.isSymbolicLink()) {
|
|
@@ -1337,15 +1845,21 @@ var add_exports = {};
|
|
|
1337
1845
|
__export(add_exports, {
|
|
1338
1846
|
add: () => add
|
|
1339
1847
|
});
|
|
1340
|
-
import { mkdir as mkdir5, rm as rm3 } from "fs/promises";
|
|
1341
|
-
import { join as
|
|
1848
|
+
import { mkdir as mkdir5, rm as rm3, symlink as symlink2 } from "fs/promises";
|
|
1849
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
1342
1850
|
async function add(specifiers, options) {
|
|
1343
1851
|
console.log("Resolving packages...\n");
|
|
1344
1852
|
const resolvedPackages = [];
|
|
1345
1853
|
const validationErrors = [];
|
|
1346
|
-
for (
|
|
1854
|
+
for (let specifier of specifiers) {
|
|
1347
1855
|
try {
|
|
1348
|
-
if (
|
|
1856
|
+
if (isBareLocalPath(specifier)) {
|
|
1857
|
+
specifier = normalizeToFileSpecifier(specifier);
|
|
1858
|
+
}
|
|
1859
|
+
if (isLocalSpecifier(specifier)) {
|
|
1860
|
+
const resolved = await validateLocalPackage(specifier);
|
|
1861
|
+
resolvedPackages.push(resolved);
|
|
1862
|
+
} else if (isGitHubSpecifier(specifier)) {
|
|
1349
1863
|
const resolved = await validateGitHubPackage(specifier);
|
|
1350
1864
|
resolvedPackages.push(resolved);
|
|
1351
1865
|
} else {
|
|
@@ -1369,6 +1883,49 @@ async function add(specifiers, options) {
|
|
|
1369
1883
|
`
|
|
1370
1884
|
);
|
|
1371
1885
|
}
|
|
1886
|
+
const config2 = await resolveConfig();
|
|
1887
|
+
const apiKey = getTokenForRegistry(config2, config2.registryUrl);
|
|
1888
|
+
const registryPackages = resolvedPackages.filter(
|
|
1889
|
+
(p) => p.type === "registry"
|
|
1890
|
+
);
|
|
1891
|
+
const githubPackages = resolvedPackages.filter(
|
|
1892
|
+
(p) => p.type === "github"
|
|
1893
|
+
);
|
|
1894
|
+
const localPackages = resolvedPackages.filter(
|
|
1895
|
+
(p) => p.type === "local"
|
|
1896
|
+
);
|
|
1897
|
+
let resolutionResult = null;
|
|
1898
|
+
if (registryPackages.length > 0) {
|
|
1899
|
+
const rootDeps = {};
|
|
1900
|
+
for (const pkg of registryPackages) {
|
|
1901
|
+
const fullName = `@user/${pkg.username}/${pkg.name}`;
|
|
1902
|
+
rootDeps[fullName] = pkg.versionRange || `^${pkg.resolvedVersion}`;
|
|
1903
|
+
}
|
|
1904
|
+
console.log("Resolving dependencies...");
|
|
1905
|
+
resolutionResult = await resolveRecursive(rootDeps, {
|
|
1906
|
+
maxDepth: MAX_DEPENDENCY_DEPTH,
|
|
1907
|
+
registryUrl: config2.registryUrl,
|
|
1908
|
+
apiKey
|
|
1909
|
+
});
|
|
1910
|
+
if (!resolutionResult.success) {
|
|
1911
|
+
printResolutionErrors(
|
|
1912
|
+
resolutionResult.graph.errors,
|
|
1913
|
+
resolutionResult.graph.conflicts
|
|
1914
|
+
);
|
|
1915
|
+
process.exit(1);
|
|
1916
|
+
}
|
|
1917
|
+
const transitiveDeps = resolutionResult.installOrder.filter(
|
|
1918
|
+
(name) => !rootDeps[name]
|
|
1919
|
+
);
|
|
1920
|
+
if (transitiveDeps.length > 0) {
|
|
1921
|
+
console.log(
|
|
1922
|
+
`Resolved ${transitiveDeps.length} transitive dependencies.
|
|
1923
|
+
`
|
|
1924
|
+
);
|
|
1925
|
+
} else {
|
|
1926
|
+
console.log();
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1372
1929
|
let agents;
|
|
1373
1930
|
const manifest = await readManifest();
|
|
1374
1931
|
if (options.agent) {
|
|
@@ -1383,19 +1940,53 @@ async function add(specifiers, options) {
|
|
|
1383
1940
|
console.log();
|
|
1384
1941
|
}
|
|
1385
1942
|
const results = [];
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1943
|
+
if (resolutionResult) {
|
|
1944
|
+
for (const name of resolutionResult.installOrder) {
|
|
1945
|
+
const node = resolutionResult.graph.nodes.get(name);
|
|
1946
|
+
if (!node) continue;
|
|
1947
|
+
try {
|
|
1948
|
+
await installFromNode(node, {
|
|
1390
1949
|
...options,
|
|
1391
|
-
resolvedAgents: agents
|
|
1950
|
+
resolvedAgents: agents,
|
|
1951
|
+
isDirect: node.isDirect
|
|
1392
1952
|
});
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1953
|
+
results.push({ specifier: name, success: true });
|
|
1954
|
+
} catch (error) {
|
|
1955
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1956
|
+
results.push({
|
|
1957
|
+
specifier: name,
|
|
1958
|
+
success: false,
|
|
1959
|
+
error: message
|
|
1397
1960
|
});
|
|
1961
|
+
console.error(`Failed to install ${name}: ${message}
|
|
1962
|
+
`);
|
|
1398
1963
|
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
for (const resolved of githubPackages) {
|
|
1967
|
+
try {
|
|
1968
|
+
await installGitHubPackage(resolved, {
|
|
1969
|
+
...options,
|
|
1970
|
+
resolvedAgents: agents
|
|
1971
|
+
});
|
|
1972
|
+
results.push({ specifier: resolved.specifier, success: true });
|
|
1973
|
+
} catch (error) {
|
|
1974
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1975
|
+
results.push({
|
|
1976
|
+
specifier: resolved.specifier,
|
|
1977
|
+
success: false,
|
|
1978
|
+
error: message
|
|
1979
|
+
});
|
|
1980
|
+
console.error(`Failed to install ${resolved.specifier}: ${message}
|
|
1981
|
+
`);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
for (const resolved of localPackages) {
|
|
1985
|
+
try {
|
|
1986
|
+
await installLocalPackage(resolved, {
|
|
1987
|
+
...options,
|
|
1988
|
+
resolvedAgents: agents
|
|
1989
|
+
});
|
|
1399
1990
|
results.push({ specifier: resolved.specifier, success: true });
|
|
1400
1991
|
} catch (error) {
|
|
1401
1992
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -1418,6 +2009,85 @@ Summary: ${succeeded} added, ${failed} failed`);
|
|
|
1418
2009
|
}
|
|
1419
2010
|
}
|
|
1420
2011
|
}
|
|
2012
|
+
async function installFromNode(node, options) {
|
|
2013
|
+
const match = node.name.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
2014
|
+
if (!match) {
|
|
2015
|
+
throw new Error(`Invalid package name: ${node.name}`);
|
|
2016
|
+
}
|
|
2017
|
+
const [, username, name] = match;
|
|
2018
|
+
console.log(`Installing ${node.name}@${node.version}...`);
|
|
2019
|
+
const config2 = await resolveConfig();
|
|
2020
|
+
const apiKey = getTokenForRegistry(config2, config2.registryUrl);
|
|
2021
|
+
const isPresignedUrl = node.downloadUrl.includes(".r2.cloudflarestorage.com") || node.downloadUrl.includes("X-Amz-Signature");
|
|
2022
|
+
const downloadHeaders = {};
|
|
2023
|
+
if (!isPresignedUrl && apiKey) {
|
|
2024
|
+
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
2025
|
+
}
|
|
2026
|
+
const tarballResponse = await fetch(node.downloadUrl, {
|
|
2027
|
+
headers: downloadHeaders,
|
|
2028
|
+
redirect: "follow"
|
|
2029
|
+
});
|
|
2030
|
+
if (!tarballResponse.ok) {
|
|
2031
|
+
throw new Error(`Failed to download tarball (${tarballResponse.status})`);
|
|
2032
|
+
}
|
|
2033
|
+
const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
|
|
2034
|
+
const integrity = calculateIntegrity(tarballBuffer);
|
|
2035
|
+
if (integrity !== node.integrity) {
|
|
2036
|
+
throw new Error("Checksum verification failed");
|
|
2037
|
+
}
|
|
2038
|
+
const skillsDir = getSkillsDir();
|
|
2039
|
+
const destDir = join6(skillsDir, username, name);
|
|
2040
|
+
await mkdir5(destDir, { recursive: true });
|
|
2041
|
+
const { writeFile: writeFile8 } = await import("fs/promises");
|
|
2042
|
+
const tempFile = join6(destDir, ".temp.tgz");
|
|
2043
|
+
await writeFile8(tempFile, tarballBuffer);
|
|
2044
|
+
const { exec: exec2 } = await import("child_process");
|
|
2045
|
+
const { promisify: promisify2 } = await import("util");
|
|
2046
|
+
const execAsync = promisify2(exec2);
|
|
2047
|
+
try {
|
|
2048
|
+
await rm3(destDir, { recursive: true, force: true });
|
|
2049
|
+
await mkdir5(destDir, { recursive: true });
|
|
2050
|
+
await writeFile8(tempFile, tarballBuffer);
|
|
2051
|
+
await execAsync(
|
|
2052
|
+
`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
|
|
2053
|
+
);
|
|
2054
|
+
} finally {
|
|
2055
|
+
await rm3(tempFile, { force: true });
|
|
2056
|
+
}
|
|
2057
|
+
const resolvedDeps = {};
|
|
2058
|
+
for (const [depName, _range] of Object.entries(node.dependencies)) {
|
|
2059
|
+
resolvedDeps[depName] = _range;
|
|
2060
|
+
}
|
|
2061
|
+
await addToLockfileWithDeps(
|
|
2062
|
+
node.name,
|
|
2063
|
+
{
|
|
2064
|
+
version: node.version,
|
|
2065
|
+
resolved: node.downloadUrl,
|
|
2066
|
+
integrity,
|
|
2067
|
+
deprecated: node.deprecated
|
|
2068
|
+
},
|
|
2069
|
+
Object.keys(resolvedDeps).length > 0 ? resolvedDeps : void 0
|
|
2070
|
+
);
|
|
2071
|
+
if (options.isDirect) {
|
|
2072
|
+
const dependencyRange = node.versionRange || `^${node.version}`;
|
|
2073
|
+
await addDependency(node.name, dependencyRange);
|
|
2074
|
+
}
|
|
2075
|
+
const agents = options.resolvedAgents;
|
|
2076
|
+
if (agents[0] !== "none") {
|
|
2077
|
+
const skillManifest = await readManifest();
|
|
2078
|
+
const skillInfo = {
|
|
2079
|
+
name,
|
|
2080
|
+
sourcePath: getRegistrySkillPath(username, name)
|
|
2081
|
+
};
|
|
2082
|
+
await createAgentSymlinks([skillInfo], {
|
|
2083
|
+
agents,
|
|
2084
|
+
projectRoot: process.cwd(),
|
|
2085
|
+
agentConfigs: skillManifest?.agents
|
|
2086
|
+
});
|
|
2087
|
+
}
|
|
2088
|
+
console.log(`Installed ${node.name}@${node.version}`);
|
|
2089
|
+
console.log(`Location: ${destDir}`);
|
|
2090
|
+
}
|
|
1421
2091
|
async function validateRegistryPackage(specifier) {
|
|
1422
2092
|
const config2 = await resolveConfig();
|
|
1423
2093
|
const registryUrl = config2.registryUrl;
|
|
@@ -1505,72 +2175,6 @@ async function validateGitHubPackage(specifier) {
|
|
|
1505
2175
|
downloadResult: result
|
|
1506
2176
|
};
|
|
1507
2177
|
}
|
|
1508
|
-
async function installRegistryPackage(resolved, options) {
|
|
1509
|
-
const { username, name, versionRange, resolvedVersion, versionInfo } = resolved;
|
|
1510
|
-
console.log(`Installing @user/${username}/${name}@${resolvedVersion}...`);
|
|
1511
|
-
const config2 = await resolveConfig();
|
|
1512
|
-
const apiKey = getTokenForRegistry(config2, config2.registryUrl);
|
|
1513
|
-
const isPresignedUrl = versionInfo.downloadUrl.includes(".r2.cloudflarestorage.com") || versionInfo.downloadUrl.includes("X-Amz-Signature");
|
|
1514
|
-
const downloadHeaders = {};
|
|
1515
|
-
if (!isPresignedUrl && apiKey) {
|
|
1516
|
-
downloadHeaders.Authorization = `Bearer ${apiKey}`;
|
|
1517
|
-
}
|
|
1518
|
-
const tarballResponse = await fetch(versionInfo.downloadUrl, {
|
|
1519
|
-
headers: downloadHeaders,
|
|
1520
|
-
redirect: "follow"
|
|
1521
|
-
});
|
|
1522
|
-
if (!tarballResponse.ok) {
|
|
1523
|
-
throw new Error(`Failed to download tarball (${tarballResponse.status})`);
|
|
1524
|
-
}
|
|
1525
|
-
const tarballBuffer = Buffer.from(await tarballResponse.arrayBuffer());
|
|
1526
|
-
const integrity = calculateIntegrity(tarballBuffer);
|
|
1527
|
-
const expectedIntegrity = `sha256-${Buffer.from(versionInfo.checksum, "hex").toString("base64")}`;
|
|
1528
|
-
if (integrity !== expectedIntegrity) {
|
|
1529
|
-
throw new Error("Checksum verification failed");
|
|
1530
|
-
}
|
|
1531
|
-
const skillsDir = getSkillsDir();
|
|
1532
|
-
const destDir = join5(skillsDir, username, name);
|
|
1533
|
-
await mkdir5(destDir, { recursive: true });
|
|
1534
|
-
const { writeFile: writeFile8 } = await import("fs/promises");
|
|
1535
|
-
const tempFile = join5(destDir, ".temp.tgz");
|
|
1536
|
-
await writeFile8(tempFile, tarballBuffer);
|
|
1537
|
-
const { exec: exec2 } = await import("child_process");
|
|
1538
|
-
const { promisify: promisify2 } = await import("util");
|
|
1539
|
-
const execAsync = promisify2(exec2);
|
|
1540
|
-
try {
|
|
1541
|
-
await rm3(destDir, { recursive: true, force: true });
|
|
1542
|
-
await mkdir5(destDir, { recursive: true });
|
|
1543
|
-
await writeFile8(tempFile, tarballBuffer);
|
|
1544
|
-
await execAsync(
|
|
1545
|
-
`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
|
|
1546
|
-
);
|
|
1547
|
-
} finally {
|
|
1548
|
-
await rm3(tempFile, { force: true });
|
|
1549
|
-
}
|
|
1550
|
-
const fullName = `@user/${username}/${name}`;
|
|
1551
|
-
await addToLockfile(fullName, {
|
|
1552
|
-
version: resolvedVersion,
|
|
1553
|
-
resolved: versionInfo.downloadUrl,
|
|
1554
|
-
integrity
|
|
1555
|
-
});
|
|
1556
|
-
const dependencyRange = versionRange || `^${resolvedVersion}`;
|
|
1557
|
-
await addDependency(fullName, dependencyRange);
|
|
1558
|
-
const agents = options.resolvedAgents;
|
|
1559
|
-
if (agents[0] !== "none") {
|
|
1560
|
-
const skillManifest = await readManifest();
|
|
1561
|
-
const skillInfo = {
|
|
1562
|
-
name,
|
|
1563
|
-
sourcePath: getRegistrySkillPath(username, name)
|
|
1564
|
-
};
|
|
1565
|
-
await createAgentSymlinks([skillInfo], {
|
|
1566
|
-
agents,
|
|
1567
|
-
projectRoot: process.cwd(),
|
|
1568
|
-
agentConfigs: skillManifest?.agents
|
|
1569
|
-
});
|
|
1570
|
-
}
|
|
1571
|
-
console.log(`Installed @user/${username}/${name}@${resolvedVersion}`);
|
|
1572
|
-
console.log(`Location: ${destDir}`);
|
|
1573
|
-
}
|
|
1574
2178
|
async function installGitHubPackage(resolved, options) {
|
|
1575
2179
|
const { specifier, parsed, ref, downloadResult } = resolved;
|
|
1576
2180
|
console.log(
|
|
@@ -1616,6 +2220,67 @@ async function installGitHubPackage(resolved, options) {
|
|
|
1616
2220
|
);
|
|
1617
2221
|
console.log(`Location: ${destPath}`);
|
|
1618
2222
|
}
|
|
2223
|
+
async function validateLocalPackage(specifier) {
|
|
2224
|
+
const parsed = parseLocalSpecifier(specifier);
|
|
2225
|
+
if (!parsed) {
|
|
2226
|
+
throw new Error(
|
|
2227
|
+
`Invalid local specifier "${specifier}". Use format: file:../path or file:/absolute/path`
|
|
2228
|
+
);
|
|
2229
|
+
}
|
|
2230
|
+
console.log(`Resolving ${specifier}...`);
|
|
2231
|
+
const resolvedPath = resolveLocalPath(parsed);
|
|
2232
|
+
const validation = await validateLocalSkill(resolvedPath);
|
|
2233
|
+
if (!validation.valid) {
|
|
2234
|
+
throw new Error(validation.error);
|
|
2235
|
+
}
|
|
2236
|
+
const name = validation.manifest?.name || getLocalSkillName(parsed);
|
|
2237
|
+
console.log(`Resolved ${specifier} -> ${name} (local)`);
|
|
2238
|
+
return {
|
|
2239
|
+
type: "local",
|
|
2240
|
+
specifier,
|
|
2241
|
+
parsed,
|
|
2242
|
+
name,
|
|
2243
|
+
resolvedPath
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
async function installLocalPackage(resolved, options) {
|
|
2247
|
+
const { specifier, name, resolvedPath, parsed } = resolved;
|
|
2248
|
+
console.log(`Installing ${specifier} (local symlink)...`);
|
|
2249
|
+
const skillsDir = getSkillsDir();
|
|
2250
|
+
const localDir = join6(skillsDir, "_local");
|
|
2251
|
+
await mkdir5(localDir, { recursive: true });
|
|
2252
|
+
const symlinkPath = join6(localDir, name);
|
|
2253
|
+
try {
|
|
2254
|
+
await rm3(symlinkPath, { force: true });
|
|
2255
|
+
} catch {
|
|
2256
|
+
}
|
|
2257
|
+
const { relative: relativePath } = await import("path");
|
|
2258
|
+
const relativeTarget = relativePath(dirname4(symlinkPath), resolvedPath);
|
|
2259
|
+
await symlink2(relativeTarget, symlinkPath);
|
|
2260
|
+
const entry = {
|
|
2261
|
+
version: "local",
|
|
2262
|
+
path: parsed.path,
|
|
2263
|
+
resolvedPath,
|
|
2264
|
+
name
|
|
2265
|
+
};
|
|
2266
|
+
await addLocalToLockfile(specifier, entry);
|
|
2267
|
+
await addLocalDependency(specifier, "*");
|
|
2268
|
+
const agents = options.resolvedAgents;
|
|
2269
|
+
if (agents[0] !== "none") {
|
|
2270
|
+
const manifest = await readManifest();
|
|
2271
|
+
const skillInfo = {
|
|
2272
|
+
name,
|
|
2273
|
+
sourcePath: getLocalSkillPath(name)
|
|
2274
|
+
};
|
|
2275
|
+
await createAgentSymlinks([skillInfo], {
|
|
2276
|
+
agents,
|
|
2277
|
+
projectRoot: process.cwd(),
|
|
2278
|
+
agentConfigs: manifest?.agents
|
|
2279
|
+
});
|
|
2280
|
+
}
|
|
2281
|
+
console.log(`Installed ${specifier} (local)`);
|
|
2282
|
+
console.log(`Location: ${symlinkPath} -> ${resolvedPath}`);
|
|
2283
|
+
}
|
|
1619
2284
|
var init_add = __esm({
|
|
1620
2285
|
"src/commands/add.ts"() {
|
|
1621
2286
|
"use strict";
|
|
@@ -1633,7 +2298,7 @@ var init_add = __esm({
|
|
|
1633
2298
|
|
|
1634
2299
|
// src/index.ts
|
|
1635
2300
|
import { readFileSync } from "fs";
|
|
1636
|
-
import { dirname as
|
|
2301
|
+
import { dirname as dirname6, join as join13 } from "path";
|
|
1637
2302
|
import { fileURLToPath } from "url";
|
|
1638
2303
|
import { Command } from "commander";
|
|
1639
2304
|
|
|
@@ -1641,7 +2306,7 @@ import { Command } from "commander";
|
|
|
1641
2306
|
init_api_client();
|
|
1642
2307
|
init_config();
|
|
1643
2308
|
init_lib();
|
|
1644
|
-
async function
|
|
2309
|
+
async function access2(specifier, options) {
|
|
1645
2310
|
try {
|
|
1646
2311
|
const apiKey = await requireApiKey();
|
|
1647
2312
|
const registryUrl = await getRegistryUrl();
|
|
@@ -1666,18 +2331,18 @@ async function access(specifier, options) {
|
|
|
1666
2331
|
packageName = parsed.name;
|
|
1667
2332
|
} else {
|
|
1668
2333
|
const { readFile: readFile7 } = await import("fs/promises");
|
|
1669
|
-
const { join:
|
|
2334
|
+
const { join: join14 } = await import("path");
|
|
1670
2335
|
let manifest = null;
|
|
1671
2336
|
try {
|
|
1672
2337
|
const content = await readFile7(
|
|
1673
|
-
|
|
2338
|
+
join14(process.cwd(), "pspm.json"),
|
|
1674
2339
|
"utf-8"
|
|
1675
2340
|
);
|
|
1676
2341
|
manifest = JSON.parse(content);
|
|
1677
2342
|
} catch {
|
|
1678
2343
|
try {
|
|
1679
2344
|
const content = await readFile7(
|
|
1680
|
-
|
|
2345
|
+
join14(process.cwd(), "package.json"),
|
|
1681
2346
|
"utf-8"
|
|
1682
2347
|
);
|
|
1683
2348
|
manifest = JSON.parse(content);
|
|
@@ -1726,13 +2391,13 @@ async function access(specifier, options) {
|
|
|
1726
2391
|
init_add();
|
|
1727
2392
|
|
|
1728
2393
|
// src/commands/config/init.ts
|
|
1729
|
-
import { stat as
|
|
1730
|
-
import { join as
|
|
2394
|
+
import { stat as stat4, writeFile as writeFile5 } from "fs/promises";
|
|
2395
|
+
import { join as join7 } from "path";
|
|
1731
2396
|
async function configInit(options) {
|
|
1732
2397
|
try {
|
|
1733
|
-
const configPath =
|
|
2398
|
+
const configPath = join7(process.cwd(), ".pspmrc");
|
|
1734
2399
|
try {
|
|
1735
|
-
await
|
|
2400
|
+
await stat4(configPath);
|
|
1736
2401
|
console.error("Error: .pspmrc already exists in this directory.");
|
|
1737
2402
|
process.exit(1);
|
|
1738
2403
|
} catch {
|
|
@@ -1858,21 +2523,21 @@ async function deprecate(specifier, message, options) {
|
|
|
1858
2523
|
|
|
1859
2524
|
// src/commands/init.ts
|
|
1860
2525
|
init_lib();
|
|
1861
|
-
import { readFile as readFile4, stat as
|
|
1862
|
-
import { basename, join as
|
|
2526
|
+
import { readFile as readFile4, stat as stat5, writeFile as writeFile6 } from "fs/promises";
|
|
2527
|
+
import { basename, join as join8 } from "path";
|
|
1863
2528
|
import { createInterface } from "readline";
|
|
1864
2529
|
function prompt(rl, question, defaultValue) {
|
|
1865
|
-
return new Promise((
|
|
2530
|
+
return new Promise((resolve2) => {
|
|
1866
2531
|
const displayDefault = defaultValue ? ` (${defaultValue})` : "";
|
|
1867
2532
|
rl.question(`${question}${displayDefault} `, (answer) => {
|
|
1868
|
-
|
|
2533
|
+
resolve2(answer.trim() || defaultValue);
|
|
1869
2534
|
});
|
|
1870
2535
|
});
|
|
1871
2536
|
}
|
|
1872
2537
|
async function readExistingPackageJson() {
|
|
1873
2538
|
try {
|
|
1874
2539
|
const content = await readFile4(
|
|
1875
|
-
|
|
2540
|
+
join8(process.cwd(), "package.json"),
|
|
1876
2541
|
"utf-8"
|
|
1877
2542
|
);
|
|
1878
2543
|
const pkg = JSON.parse(content);
|
|
@@ -1921,10 +2586,10 @@ function isValidVersion(version2) {
|
|
|
1921
2586
|
}
|
|
1922
2587
|
async function init(options) {
|
|
1923
2588
|
try {
|
|
1924
|
-
const pspmJsonPath =
|
|
2589
|
+
const pspmJsonPath = join8(process.cwd(), "pspm.json");
|
|
1925
2590
|
let exists = false;
|
|
1926
2591
|
try {
|
|
1927
|
-
await
|
|
2592
|
+
await stat5(pspmJsonPath);
|
|
1928
2593
|
exists = true;
|
|
1929
2594
|
} catch {
|
|
1930
2595
|
}
|
|
@@ -2059,7 +2724,7 @@ async function init(options) {
|
|
|
2059
2724
|
await writeFile6(pspmJsonPath, `${content}
|
|
2060
2725
|
`);
|
|
2061
2726
|
try {
|
|
2062
|
-
await
|
|
2727
|
+
await stat5(join8(process.cwd(), "SKILL.md"));
|
|
2063
2728
|
} catch {
|
|
2064
2729
|
console.log(
|
|
2065
2730
|
"Note: Create a SKILL.md file with your skill's prompt content."
|
|
@@ -2088,8 +2753,15 @@ init_lockfile();
|
|
|
2088
2753
|
init_manifest2();
|
|
2089
2754
|
init_symlinks();
|
|
2090
2755
|
import { createHash as createHash2 } from "crypto";
|
|
2091
|
-
import {
|
|
2092
|
-
|
|
2756
|
+
import {
|
|
2757
|
+
lstat as lstat3,
|
|
2758
|
+
mkdir as mkdir6,
|
|
2759
|
+
readFile as readFile5,
|
|
2760
|
+
rm as rm4,
|
|
2761
|
+
symlink as symlink3,
|
|
2762
|
+
writeFile as writeFile7
|
|
2763
|
+
} from "fs/promises";
|
|
2764
|
+
import { dirname as dirname5, join as join9, relative as relative2 } from "path";
|
|
2093
2765
|
function getCacheFilePath(cacheDir, integrity) {
|
|
2094
2766
|
const match = integrity.match(/^sha256-(.+)$/);
|
|
2095
2767
|
if (!match) {
|
|
@@ -2097,7 +2769,7 @@ function getCacheFilePath(cacheDir, integrity) {
|
|
|
2097
2769
|
}
|
|
2098
2770
|
const base64Hash = match[1];
|
|
2099
2771
|
const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
|
|
2100
|
-
return
|
|
2772
|
+
return join9(cacheDir, `sha256-${hexHash}.tgz`);
|
|
2101
2773
|
}
|
|
2102
2774
|
async function readFromCache(cacheDir, integrity) {
|
|
2103
2775
|
try {
|
|
@@ -2144,8 +2816,10 @@ async function installFromLockfile(options) {
|
|
|
2144
2816
|
let lockfile = await readLockfile();
|
|
2145
2817
|
const manifestDeps = await getDependencies();
|
|
2146
2818
|
const manifestGitHubDeps = await getGitHubDependencies();
|
|
2819
|
+
const manifestLocalDeps = await getLocalDependencies();
|
|
2147
2820
|
const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
|
|
2148
2821
|
const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
|
|
2822
|
+
const lockfileLocalPackages = lockfile?.localPackages ?? {};
|
|
2149
2823
|
const installedSkills = [];
|
|
2150
2824
|
const missingDeps = [];
|
|
2151
2825
|
for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
|
|
@@ -2296,6 +2970,53 @@ Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
|
|
|
2296
2970
|
}
|
|
2297
2971
|
lockfile = await readLockfile();
|
|
2298
2972
|
}
|
|
2973
|
+
const missingLocalDeps = [];
|
|
2974
|
+
for (const [specifier] of Object.entries(manifestLocalDeps)) {
|
|
2975
|
+
if (!lockfileLocalPackages[specifier]) {
|
|
2976
|
+
missingLocalDeps.push({ specifier });
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
if (missingLocalDeps.length > 0) {
|
|
2980
|
+
if (options.frozenLockfile) {
|
|
2981
|
+
console.error(
|
|
2982
|
+
"Error: Local dependencies in pspm.json are not in lockfile. Cannot install with --frozen-lockfile"
|
|
2983
|
+
);
|
|
2984
|
+
console.error("Missing local dependencies:");
|
|
2985
|
+
for (const dep of missingLocalDeps) {
|
|
2986
|
+
console.error(` - ${dep.specifier}`);
|
|
2987
|
+
}
|
|
2988
|
+
process.exit(1);
|
|
2989
|
+
}
|
|
2990
|
+
console.log(
|
|
2991
|
+
`
|
|
2992
|
+
Resolving ${missingLocalDeps.length} local dependency(ies)...
|
|
2993
|
+
`
|
|
2994
|
+
);
|
|
2995
|
+
for (const { specifier } of missingLocalDeps) {
|
|
2996
|
+
const parsed = parseLocalSpecifier(specifier);
|
|
2997
|
+
if (!parsed) {
|
|
2998
|
+
console.error(`Error: Invalid local specifier: ${specifier}`);
|
|
2999
|
+
continue;
|
|
3000
|
+
}
|
|
3001
|
+
console.log(`Resolving ${specifier}...`);
|
|
3002
|
+
const resolvedPath = resolveLocalPath(parsed);
|
|
3003
|
+
const validation = await validateLocalSkill(resolvedPath);
|
|
3004
|
+
if (!validation.valid) {
|
|
3005
|
+
console.error(`Error: ${validation.error}`);
|
|
3006
|
+
continue;
|
|
3007
|
+
}
|
|
3008
|
+
const name = validation.manifest?.name || parsed.path.split("/").pop() || "unknown";
|
|
3009
|
+
const entry = {
|
|
3010
|
+
version: "local",
|
|
3011
|
+
path: parsed.path,
|
|
3012
|
+
resolvedPath,
|
|
3013
|
+
name
|
|
3014
|
+
};
|
|
3015
|
+
await addLocalToLockfile(specifier, entry);
|
|
3016
|
+
console.log(` Resolved ${specifier} -> ${name} (local)`);
|
|
3017
|
+
}
|
|
3018
|
+
lockfile = await readLockfile();
|
|
3019
|
+
}
|
|
2299
3020
|
const manifest = await readManifest();
|
|
2300
3021
|
const agentConfigs = manifest?.agents;
|
|
2301
3022
|
let agents;
|
|
@@ -2316,7 +3037,8 @@ Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
|
|
|
2316
3037
|
console.log(`
|
|
2317
3038
|
Installing ${packageCount} registry skill(s)...
|
|
2318
3039
|
`);
|
|
2319
|
-
const
|
|
3040
|
+
const installOrder = computeInstallOrder(packages);
|
|
3041
|
+
const entries = installOrder.filter((name) => packages[name]).map((name) => [name, packages[name]]);
|
|
2320
3042
|
for (const [fullName, entry] of entries) {
|
|
2321
3043
|
const match = fullName.match(/^@user\/([^/]+)\/([^/]+)$/);
|
|
2322
3044
|
if (!match) {
|
|
@@ -2372,10 +3094,10 @@ Installing ${packageCount} registry skill(s)...
|
|
|
2372
3094
|
}
|
|
2373
3095
|
await writeToCache(cacheDir, entry.integrity, tarballBuffer);
|
|
2374
3096
|
}
|
|
2375
|
-
const destDir =
|
|
3097
|
+
const destDir = join9(skillsDir, username, name);
|
|
2376
3098
|
await rm4(destDir, { recursive: true, force: true });
|
|
2377
3099
|
await mkdir6(destDir, { recursive: true });
|
|
2378
|
-
const tempFile =
|
|
3100
|
+
const tempFile = join9(destDir, ".temp.tgz");
|
|
2379
3101
|
await writeFile7(tempFile, tarballBuffer);
|
|
2380
3102
|
const { exec: exec2 } = await import("child_process");
|
|
2381
3103
|
const { promisify: promisify2 } = await import("util");
|
|
@@ -2473,6 +3195,52 @@ Installing ${githubCount} GitHub skill(s)...
|
|
|
2473
3195
|
}
|
|
2474
3196
|
}
|
|
2475
3197
|
}
|
|
3198
|
+
const localPackages = lockfile?.localPackages ?? {};
|
|
3199
|
+
const localCount = Object.keys(localPackages).length;
|
|
3200
|
+
if (localCount > 0) {
|
|
3201
|
+
console.log(`
|
|
3202
|
+
Installing ${localCount} local skill(s)...
|
|
3203
|
+
`);
|
|
3204
|
+
for (const [specifier, entry] of Object.entries(localPackages)) {
|
|
3205
|
+
const localEntry = entry;
|
|
3206
|
+
console.log(`Installing ${specifier} (local symlink)...`);
|
|
3207
|
+
const validation = await validateLocalSkill(localEntry.resolvedPath);
|
|
3208
|
+
if (!validation.valid) {
|
|
3209
|
+
console.error(` Error: ${validation.error}`);
|
|
3210
|
+
console.error(
|
|
3211
|
+
` Hint: The local skill at ${localEntry.resolvedPath} may have been moved or deleted.`
|
|
3212
|
+
);
|
|
3213
|
+
continue;
|
|
3214
|
+
}
|
|
3215
|
+
const localDir = join9(skillsDir, "_local");
|
|
3216
|
+
await mkdir6(localDir, { recursive: true });
|
|
3217
|
+
const symlinkPath = join9(localDir, localEntry.name);
|
|
3218
|
+
try {
|
|
3219
|
+
const stats = await lstat3(symlinkPath);
|
|
3220
|
+
if (stats.isSymbolicLink()) {
|
|
3221
|
+
await rm4(symlinkPath);
|
|
3222
|
+
}
|
|
3223
|
+
} catch {
|
|
3224
|
+
}
|
|
3225
|
+
const relativeTarget = relative2(
|
|
3226
|
+
dirname5(symlinkPath),
|
|
3227
|
+
localEntry.resolvedPath
|
|
3228
|
+
);
|
|
3229
|
+
try {
|
|
3230
|
+
await symlink3(relativeTarget, symlinkPath);
|
|
3231
|
+
console.log(
|
|
3232
|
+
` Installed to ${symlinkPath} -> ${localEntry.resolvedPath}`
|
|
3233
|
+
);
|
|
3234
|
+
installedSkills.push({
|
|
3235
|
+
name: localEntry.name,
|
|
3236
|
+
sourcePath: getLocalSkillPath(localEntry.name)
|
|
3237
|
+
});
|
|
3238
|
+
} catch (error) {
|
|
3239
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3240
|
+
console.error(` Error creating symlink: ${message}`);
|
|
3241
|
+
}
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
2476
3244
|
if (installedSkills.length > 0 && agents[0] !== "none") {
|
|
2477
3245
|
console.log(`
|
|
2478
3246
|
Creating symlinks for agent(s): ${agents.join(", ")}...`);
|
|
@@ -2483,7 +3251,7 @@ Creating symlinks for agent(s): ${agents.join(", ")}...`);
|
|
|
2483
3251
|
});
|
|
2484
3252
|
console.log(" Symlinks created.");
|
|
2485
3253
|
}
|
|
2486
|
-
const totalCount = packageCount + githubCount;
|
|
3254
|
+
const totalCount = packageCount + githubCount + localCount;
|
|
2487
3255
|
if (totalCount === 0) {
|
|
2488
3256
|
console.log("No skills to install.");
|
|
2489
3257
|
} else {
|
|
@@ -2580,8 +3348,8 @@ init_lib();
|
|
|
2580
3348
|
init_lockfile();
|
|
2581
3349
|
init_manifest2();
|
|
2582
3350
|
init_symlinks();
|
|
2583
|
-
import { access as
|
|
2584
|
-
import { join as
|
|
3351
|
+
import { access as access3 } from "fs/promises";
|
|
3352
|
+
import { join as join10 } from "path";
|
|
2585
3353
|
async function list(options) {
|
|
2586
3354
|
try {
|
|
2587
3355
|
const registrySkills = await listLockfileSkills();
|
|
@@ -2596,10 +3364,10 @@ async function list(options) {
|
|
|
2596
3364
|
if (!match) continue;
|
|
2597
3365
|
const [, username, skillName] = match;
|
|
2598
3366
|
const sourcePath = getRegistrySkillPath(username, skillName);
|
|
2599
|
-
const absolutePath =
|
|
3367
|
+
const absolutePath = join10(projectRoot, sourcePath);
|
|
2600
3368
|
let status = "installed";
|
|
2601
3369
|
try {
|
|
2602
|
-
await
|
|
3370
|
+
await access3(absolutePath);
|
|
2603
3371
|
} catch {
|
|
2604
3372
|
status = "missing";
|
|
2605
3373
|
}
|
|
@@ -2629,10 +3397,10 @@ async function list(options) {
|
|
|
2629
3397
|
parsed.repo,
|
|
2630
3398
|
parsed.path
|
|
2631
3399
|
);
|
|
2632
|
-
const absolutePath =
|
|
3400
|
+
const absolutePath = join10(projectRoot, sourcePath);
|
|
2633
3401
|
let status = "installed";
|
|
2634
3402
|
try {
|
|
2635
|
-
await
|
|
3403
|
+
await access3(absolutePath);
|
|
2636
3404
|
} catch {
|
|
2637
3405
|
status = "missing";
|
|
2638
3406
|
}
|
|
@@ -2654,6 +3422,35 @@ async function list(options) {
|
|
|
2654
3422
|
gitCommit: ghEntry.gitCommit
|
|
2655
3423
|
});
|
|
2656
3424
|
}
|
|
3425
|
+
const localSkills = await listLockfileLocalPackages();
|
|
3426
|
+
for (const { specifier, entry } of localSkills) {
|
|
3427
|
+
const localEntry = entry;
|
|
3428
|
+
const skillName = localEntry.name;
|
|
3429
|
+
const sourcePath = getLocalSkillPath(skillName);
|
|
3430
|
+
const absolutePath = join10(projectRoot, sourcePath);
|
|
3431
|
+
let status = "installed";
|
|
3432
|
+
try {
|
|
3433
|
+
await access3(absolutePath);
|
|
3434
|
+
} catch {
|
|
3435
|
+
status = "missing";
|
|
3436
|
+
}
|
|
3437
|
+
const linkedAgents = await getLinkedAgents(
|
|
3438
|
+
skillName,
|
|
3439
|
+
availableAgents,
|
|
3440
|
+
projectRoot,
|
|
3441
|
+
agentConfigs
|
|
3442
|
+
);
|
|
3443
|
+
skills.push({
|
|
3444
|
+
name: skillName,
|
|
3445
|
+
fullName: specifier,
|
|
3446
|
+
version: "local",
|
|
3447
|
+
source: "local",
|
|
3448
|
+
sourcePath,
|
|
3449
|
+
status,
|
|
3450
|
+
linkedAgents,
|
|
3451
|
+
localPath: localEntry.resolvedPath
|
|
3452
|
+
});
|
|
3453
|
+
}
|
|
2657
3454
|
if (skills.length === 0) {
|
|
2658
3455
|
console.log("No skills installed.");
|
|
2659
3456
|
return;
|
|
@@ -2666,9 +3463,14 @@ async function list(options) {
|
|
|
2666
3463
|
for (const skill of skills) {
|
|
2667
3464
|
if (skill.source === "registry") {
|
|
2668
3465
|
console.log(` ${skill.fullName}@${skill.version} (registry)`);
|
|
2669
|
-
} else {
|
|
3466
|
+
} else if (skill.source === "github") {
|
|
2670
3467
|
const refInfo = skill.gitRef ? `${skill.gitRef}@${skill.gitCommit?.slice(0, 7)}` : skill.version;
|
|
2671
3468
|
console.log(` ${skill.fullName} (${refInfo})`);
|
|
3469
|
+
} else {
|
|
3470
|
+
console.log(` ${skill.fullName} [local]`);
|
|
3471
|
+
if (skill.localPath) {
|
|
3472
|
+
console.log(` Path: ${skill.localPath}`);
|
|
3473
|
+
}
|
|
2672
3474
|
}
|
|
2673
3475
|
if (skill.status === "missing") {
|
|
2674
3476
|
console.log(` Status: MISSING (run 'pspm install' to restore)`);
|
|
@@ -2684,9 +3486,11 @@ async function list(options) {
|
|
|
2684
3486
|
}
|
|
2685
3487
|
const registryCount = skills.filter((s) => s.source === "registry").length;
|
|
2686
3488
|
const githubCount = skills.filter((s) => s.source === "github").length;
|
|
3489
|
+
const localCount = skills.filter((s) => s.source === "local").length;
|
|
2687
3490
|
const parts = [];
|
|
2688
3491
|
if (registryCount > 0) parts.push(`${registryCount} registry`);
|
|
2689
3492
|
if (githubCount > 0) parts.push(`${githubCount} github`);
|
|
3493
|
+
if (localCount > 0) parts.push(`${localCount} local`);
|
|
2690
3494
|
console.log(`
|
|
2691
3495
|
Total: ${skills.length} skill(s) (${parts.join(", ")})`);
|
|
2692
3496
|
} catch (error) {
|
|
@@ -2744,8 +3548,8 @@ function startCallbackServer(expectedState) {
|
|
|
2744
3548
|
let resolveToken;
|
|
2745
3549
|
let rejectToken;
|
|
2746
3550
|
let timeoutId;
|
|
2747
|
-
const tokenPromise = new Promise((
|
|
2748
|
-
resolveToken =
|
|
3551
|
+
const tokenPromise = new Promise((resolve2, reject) => {
|
|
3552
|
+
resolveToken = resolve2;
|
|
2749
3553
|
rejectToken = reject;
|
|
2750
3554
|
});
|
|
2751
3555
|
const server = http.createServer((req, res) => {
|
|
@@ -2900,7 +3704,7 @@ async function logout() {
|
|
|
2900
3704
|
// src/commands/migrate.ts
|
|
2901
3705
|
init_config();
|
|
2902
3706
|
init_lockfile();
|
|
2903
|
-
import { mkdir as mkdir7, readdir as readdir2, rename, rm as rm5, stat as
|
|
3707
|
+
import { mkdir as mkdir7, readdir as readdir2, rename, rm as rm5, stat as stat6 } from "fs/promises";
|
|
2904
3708
|
async function migrate(options) {
|
|
2905
3709
|
try {
|
|
2906
3710
|
const legacySkillsDir = getLegacySkillsDir();
|
|
@@ -2911,7 +3715,7 @@ async function migrate(options) {
|
|
|
2911
3715
|
let migrationNeeded = false;
|
|
2912
3716
|
const actions = [];
|
|
2913
3717
|
try {
|
|
2914
|
-
const legacyStats = await
|
|
3718
|
+
const legacyStats = await stat6(legacySkillsDir);
|
|
2915
3719
|
if (legacyStats.isDirectory()) {
|
|
2916
3720
|
const contents = await readdir2(legacySkillsDir);
|
|
2917
3721
|
if (contents.length > 0) {
|
|
@@ -2922,9 +3726,9 @@ async function migrate(options) {
|
|
|
2922
3726
|
} catch {
|
|
2923
3727
|
}
|
|
2924
3728
|
try {
|
|
2925
|
-
await
|
|
3729
|
+
await stat6(legacyLockfilePath);
|
|
2926
3730
|
try {
|
|
2927
|
-
await
|
|
3731
|
+
await stat6(newLockfilePath);
|
|
2928
3732
|
actions.push(
|
|
2929
3733
|
"Note: Both skill-lock.json and pspm-lock.json exist. Manual merge may be needed."
|
|
2930
3734
|
);
|
|
@@ -2956,13 +3760,13 @@ async function migrate(options) {
|
|
|
2956
3760
|
console.log(" \u2713 Migrated skill-lock.json \u2192 pspm-lock.json");
|
|
2957
3761
|
}
|
|
2958
3762
|
try {
|
|
2959
|
-
const legacyStats = await
|
|
3763
|
+
const legacyStats = await stat6(legacySkillsDir);
|
|
2960
3764
|
if (legacyStats.isDirectory()) {
|
|
2961
3765
|
const contents = await readdir2(legacySkillsDir);
|
|
2962
3766
|
if (contents.length > 0) {
|
|
2963
3767
|
await mkdir7(pspmDir, { recursive: true });
|
|
2964
3768
|
try {
|
|
2965
|
-
const newStats = await
|
|
3769
|
+
const newStats = await stat6(newSkillsDir);
|
|
2966
3770
|
if (newStats.isDirectory()) {
|
|
2967
3771
|
const newContents = await readdir2(newSkillsDir);
|
|
2968
3772
|
if (newContents.length > 0) {
|
|
@@ -3008,20 +3812,20 @@ init_errors();
|
|
|
3008
3812
|
init_lib();
|
|
3009
3813
|
import { exec as execCb } from "child_process";
|
|
3010
3814
|
import { createHash as createHash3 } from "crypto";
|
|
3011
|
-
import { readdir as readdir3, readFile as readFile6, stat as
|
|
3012
|
-
import { join as
|
|
3815
|
+
import { readdir as readdir3, readFile as readFile6, stat as stat7 } from "fs/promises";
|
|
3816
|
+
import { join as join11, relative as relative3 } from "path";
|
|
3013
3817
|
import { promisify } from "util";
|
|
3014
3818
|
var exec = promisify(execCb);
|
|
3015
3819
|
async function detectManifest() {
|
|
3016
3820
|
const cwd = process.cwd();
|
|
3017
|
-
const pspmJsonPath =
|
|
3821
|
+
const pspmJsonPath = join11(cwd, "pspm.json");
|
|
3018
3822
|
try {
|
|
3019
3823
|
const content = await readFile6(pspmJsonPath, "utf-8");
|
|
3020
3824
|
const manifest = JSON.parse(content);
|
|
3021
3825
|
return { type: "pspm.json", manifest, path: pspmJsonPath };
|
|
3022
3826
|
} catch {
|
|
3023
3827
|
}
|
|
3024
|
-
const packageJsonPath =
|
|
3828
|
+
const packageJsonPath = join11(cwd, "package.json");
|
|
3025
3829
|
try {
|
|
3026
3830
|
const content = await readFile6(packageJsonPath, "utf-8");
|
|
3027
3831
|
const packageJson2 = JSON.parse(content);
|
|
@@ -3048,8 +3852,8 @@ async function getFilesWithSizes(dir, baseDir) {
|
|
|
3048
3852
|
try {
|
|
3049
3853
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
3050
3854
|
for (const entry of entries) {
|
|
3051
|
-
const fullPath =
|
|
3052
|
-
const relativePath =
|
|
3855
|
+
const fullPath = join11(dir, entry.name);
|
|
3856
|
+
const relativePath = relative3(baseDir, fullPath);
|
|
3053
3857
|
if (entry.name === "node_modules" || entry.name === ".git") {
|
|
3054
3858
|
continue;
|
|
3055
3859
|
}
|
|
@@ -3057,7 +3861,7 @@ async function getFilesWithSizes(dir, baseDir) {
|
|
|
3057
3861
|
const subFiles = await getFilesWithSizes(fullPath, baseDir);
|
|
3058
3862
|
results.push(...subFiles);
|
|
3059
3863
|
} else {
|
|
3060
|
-
const fileStat = await
|
|
3864
|
+
const fileStat = await stat7(fullPath);
|
|
3061
3865
|
results.push({ path: relativePath, size: fileStat.size });
|
|
3062
3866
|
}
|
|
3063
3867
|
}
|
|
@@ -3103,7 +3907,7 @@ async function publishCommand(options) {
|
|
|
3103
3907
|
}
|
|
3104
3908
|
const safeName = packageJson2.name.replace(/[@/]/g, "-").replace(/^-+/, "");
|
|
3105
3909
|
const tarballName = `${safeName}-${packageJson2.version}.tgz`;
|
|
3106
|
-
const tempDir =
|
|
3910
|
+
const tempDir = join11(process.cwd(), ".pspm-publish");
|
|
3107
3911
|
try {
|
|
3108
3912
|
await exec(`rm -rf "${tempDir}" && mkdir -p "${tempDir}"`);
|
|
3109
3913
|
const files = packageJson2.files || [...DEFAULT_SKILL_FILES];
|
|
@@ -3119,7 +3923,7 @@ async function publishCommand(options) {
|
|
|
3119
3923
|
if (detection.type === "pspm.json") {
|
|
3120
3924
|
await exec(`cp pspm.json "${tempDir}/package/"`);
|
|
3121
3925
|
try {
|
|
3122
|
-
await
|
|
3926
|
+
await stat7(join11(process.cwd(), "package.json"));
|
|
3123
3927
|
await exec(
|
|
3124
3928
|
`cp package.json "${tempDir}/package/" 2>/dev/null || true`
|
|
3125
3929
|
);
|
|
@@ -3128,10 +3932,10 @@ async function publishCommand(options) {
|
|
|
3128
3932
|
} else {
|
|
3129
3933
|
await exec(`cp package.json "${tempDir}/package/"`);
|
|
3130
3934
|
}
|
|
3131
|
-
const packageDir =
|
|
3935
|
+
const packageDir = join11(tempDir, "package");
|
|
3132
3936
|
const tarballContents = await getFilesWithSizes(packageDir, packageDir);
|
|
3133
3937
|
const unpackedSize = tarballContents.reduce((acc, f) => acc + f.size, 0);
|
|
3134
|
-
const tarballPath =
|
|
3938
|
+
const tarballPath = join11(tempDir, tarballName);
|
|
3135
3939
|
await exec(
|
|
3136
3940
|
`tar -czf "${tarballPath}" -C "${tempDir}" --exclude='node_modules' --exclude='.git' package`
|
|
3137
3941
|
);
|
|
@@ -3225,13 +4029,15 @@ init_lockfile();
|
|
|
3225
4029
|
init_manifest2();
|
|
3226
4030
|
init_symlinks();
|
|
3227
4031
|
import { rm as rm6 } from "fs/promises";
|
|
3228
|
-
import { join as
|
|
4032
|
+
import { join as join12 } from "path";
|
|
3229
4033
|
async function remove(nameOrSpecifier) {
|
|
3230
4034
|
try {
|
|
3231
4035
|
const manifest = await readManifest();
|
|
3232
4036
|
const agentConfigs = manifest?.agents;
|
|
3233
4037
|
const agents = getAvailableAgents(agentConfigs);
|
|
3234
|
-
if (
|
|
4038
|
+
if (isLocalSpecifier(nameOrSpecifier)) {
|
|
4039
|
+
await removeLocal(nameOrSpecifier, agents, agentConfigs);
|
|
4040
|
+
} else if (isGitHubSpecifier(nameOrSpecifier)) {
|
|
3235
4041
|
await removeGitHub(nameOrSpecifier, agents, agentConfigs);
|
|
3236
4042
|
} else if (nameOrSpecifier.startsWith("@user/")) {
|
|
3237
4043
|
await removeRegistry(nameOrSpecifier, agents, agentConfigs);
|
|
@@ -3266,7 +4072,7 @@ async function removeRegistry(specifier, agents, agentConfigs) {
|
|
|
3266
4072
|
agentConfigs
|
|
3267
4073
|
});
|
|
3268
4074
|
const skillsDir = getSkillsDir();
|
|
3269
|
-
const destDir =
|
|
4075
|
+
const destDir = join12(skillsDir, username, name);
|
|
3270
4076
|
try {
|
|
3271
4077
|
await rm6(destDir, { recursive: true, force: true });
|
|
3272
4078
|
} catch {
|
|
@@ -3295,13 +4101,42 @@ async function removeGitHub(specifier, agents, agentConfigs) {
|
|
|
3295
4101
|
});
|
|
3296
4102
|
const skillsDir = getSkillsDir();
|
|
3297
4103
|
const destPath = getGitHubSkillPath(parsed.owner, parsed.repo, parsed.path);
|
|
3298
|
-
const destDir =
|
|
4104
|
+
const destDir = join12(skillsDir, "..", destPath);
|
|
3299
4105
|
try {
|
|
3300
4106
|
await rm6(destDir, { recursive: true, force: true });
|
|
3301
4107
|
} catch {
|
|
3302
4108
|
}
|
|
3303
4109
|
console.log(`Removed ${lockfileKey}`);
|
|
3304
4110
|
}
|
|
4111
|
+
async function removeLocal(specifier, agents, agentConfigs) {
|
|
4112
|
+
const parsed = parseLocalSpecifier(specifier);
|
|
4113
|
+
if (!parsed) {
|
|
4114
|
+
console.error(`Error: Invalid local specifier: ${specifier}`);
|
|
4115
|
+
process.exit(1);
|
|
4116
|
+
}
|
|
4117
|
+
console.log(`Removing ${specifier}...`);
|
|
4118
|
+
const removedFromLockfile = await removeLocalFromLockfile(specifier);
|
|
4119
|
+
const removedFromManifest = await removeLocalDependency(specifier);
|
|
4120
|
+
if (!removedFromLockfile && !removedFromManifest) {
|
|
4121
|
+
console.error(`Error: ${specifier} not found in lockfile or pspm.json`);
|
|
4122
|
+
process.exit(1);
|
|
4123
|
+
}
|
|
4124
|
+
const localSkills = await listLockfileLocalPackages();
|
|
4125
|
+
const foundLocal = localSkills.find((s) => s.specifier === specifier);
|
|
4126
|
+
const skillName = foundLocal?.entry.name || parsed.path.split("/").filter(Boolean).pop() || "unknown";
|
|
4127
|
+
await removeAgentSymlinks(skillName, {
|
|
4128
|
+
agents,
|
|
4129
|
+
projectRoot: process.cwd(),
|
|
4130
|
+
agentConfigs
|
|
4131
|
+
});
|
|
4132
|
+
const skillsDir = getSkillsDir();
|
|
4133
|
+
const symlinkPath = join12(skillsDir, "_local", skillName);
|
|
4134
|
+
try {
|
|
4135
|
+
await rm6(symlinkPath, { force: true });
|
|
4136
|
+
} catch {
|
|
4137
|
+
}
|
|
4138
|
+
console.log(`Removed ${specifier}`);
|
|
4139
|
+
}
|
|
3305
4140
|
async function removeByShortName(shortName, agents, agentConfigs) {
|
|
3306
4141
|
const registrySkills = await listLockfileSkills();
|
|
3307
4142
|
const foundRegistry = registrySkills.find((s) => {
|
|
@@ -3322,6 +4157,12 @@ async function removeByShortName(shortName, agents, agentConfigs) {
|
|
|
3322
4157
|
await removeGitHub(foundGitHub.specifier, agents, agentConfigs);
|
|
3323
4158
|
return;
|
|
3324
4159
|
}
|
|
4160
|
+
const localSkills = await listLockfileLocalPackages();
|
|
4161
|
+
const foundLocal = localSkills.find((s) => s.entry.name === shortName);
|
|
4162
|
+
if (foundLocal) {
|
|
4163
|
+
await removeLocal(foundLocal.specifier, agents, agentConfigs);
|
|
4164
|
+
return;
|
|
4165
|
+
}
|
|
3325
4166
|
console.error(`Error: Skill "${shortName}" not found in lockfile`);
|
|
3326
4167
|
process.exit(1);
|
|
3327
4168
|
}
|
|
@@ -3494,9 +4335,9 @@ async function whoami() {
|
|
|
3494
4335
|
}
|
|
3495
4336
|
|
|
3496
4337
|
// src/index.ts
|
|
3497
|
-
var __dirname =
|
|
4338
|
+
var __dirname = dirname6(fileURLToPath(import.meta.url));
|
|
3498
4339
|
var packageJson = JSON.parse(
|
|
3499
|
-
readFileSync(
|
|
4340
|
+
readFileSync(join13(__dirname, "..", "package.json"), "utf-8")
|
|
3500
4341
|
);
|
|
3501
4342
|
var version = packageJson.version;
|
|
3502
4343
|
var program = new Command();
|
|
@@ -3589,7 +4430,7 @@ program.command("unpublish <specifier>").description(
|
|
|
3589
4430
|
await unpublish(specifier, { force: options.force });
|
|
3590
4431
|
});
|
|
3591
4432
|
program.command("access [specifier]").description("Change package visibility (public/private)").option("--public", "Make the package public (irreversible)").option("--private", "Make the package private (only for private packages)").action(async (specifier, options) => {
|
|
3592
|
-
await
|
|
4433
|
+
await access2(specifier, {
|
|
3593
4434
|
public: options.public,
|
|
3594
4435
|
private: options.private
|
|
3595
4436
|
});
|