@anytio/pspm 0.3.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 +16 -10
- package/CLI_GUIDE.md +24 -6
- package/README.md +20 -26
- package/dist/index.js +634 -238
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -1,21 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { stat, writeFile, readdir, mkdir, rm, rename, access as access$1, readFile, lstat, unlink, cp, readlink, symlink } from 'fs/promises';
|
|
3
|
-
import { homedir } from 'os';
|
|
4
|
-
import { dirname, join, basename, relative } from 'path';
|
|
5
|
-
import * as ini from 'ini';
|
|
6
|
-
import ignore from 'ignore';
|
|
7
|
-
import { createHash, randomBytes } from 'crypto';
|
|
8
|
-
import * as semver from 'semver';
|
|
9
|
-
import { checkbox } from '@inquirer/prompts';
|
|
10
|
-
import { readFileSync } from 'fs';
|
|
11
|
-
import { fileURLToPath, URL as URL$1 } from 'url';
|
|
12
|
-
import { Command } from 'commander';
|
|
13
|
-
import { createInterface } from 'readline';
|
|
14
|
-
import http from 'http';
|
|
15
|
-
import open from 'open';
|
|
16
|
-
import { exec as exec$1 } from 'child_process';
|
|
17
|
-
import { promisify } from 'util';
|
|
18
|
-
|
|
19
2
|
var __defProp = Object.defineProperty;
|
|
20
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
21
4
|
var __esm = (fn, res) => function __init() {
|
|
@@ -26,7 +9,7 @@ var __export = (target, all) => {
|
|
|
26
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
27
10
|
};
|
|
28
11
|
|
|
29
|
-
//
|
|
12
|
+
// src/sdk/fetcher.ts
|
|
30
13
|
function configure(options) {
|
|
31
14
|
config = options;
|
|
32
15
|
}
|
|
@@ -67,15 +50,17 @@ async function customFetch(url, options) {
|
|
|
67
50
|
}
|
|
68
51
|
var config;
|
|
69
52
|
var init_fetcher = __esm({
|
|
70
|
-
"
|
|
53
|
+
"src/sdk/fetcher.ts"() {
|
|
54
|
+
"use strict";
|
|
71
55
|
config = null;
|
|
72
56
|
}
|
|
73
57
|
});
|
|
74
58
|
|
|
75
|
-
//
|
|
59
|
+
// src/sdk/generated/index.ts
|
|
76
60
|
var getMeUrl, me, getListSkillVersionsUrl, listSkillVersions, getGetSkillVersionUrl, getSkillVersion, getPublishSkillUrl, publishSkill, getDeleteSkillUrl, deleteSkill, getDeleteSkillVersionUrl, deleteSkillVersion;
|
|
77
61
|
var init_generated = __esm({
|
|
78
|
-
"
|
|
62
|
+
"src/sdk/generated/index.ts"() {
|
|
63
|
+
"use strict";
|
|
79
64
|
init_fetcher();
|
|
80
65
|
getMeUrl = () => {
|
|
81
66
|
return `/api/skills/me`;
|
|
@@ -156,14 +141,6 @@ var init_generated = __esm({
|
|
|
156
141
|
}
|
|
157
142
|
});
|
|
158
143
|
|
|
159
|
-
// ../../packages/shared/sdk/src/index.ts
|
|
160
|
-
var init_src = __esm({
|
|
161
|
-
"../../packages/shared/sdk/src/index.ts"() {
|
|
162
|
-
init_fetcher();
|
|
163
|
-
init_generated();
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
|
|
167
144
|
// src/api-client.ts
|
|
168
145
|
function registryUrlToBaseUrl(registryUrl) {
|
|
169
146
|
return registryUrl.replace(/\/api\/skills\/?$/, "");
|
|
@@ -286,7 +263,9 @@ async function changeSkillAccess(skillName, input) {
|
|
|
286
263
|
}
|
|
287
264
|
var init_api_client = __esm({
|
|
288
265
|
"src/api-client.ts"() {
|
|
289
|
-
|
|
266
|
+
"use strict";
|
|
267
|
+
init_fetcher();
|
|
268
|
+
init_generated();
|
|
290
269
|
}
|
|
291
270
|
});
|
|
292
271
|
|
|
@@ -328,6 +307,7 @@ ${issueMessages}`;
|
|
|
328
307
|
var ConfigError, NotLoggedInError;
|
|
329
308
|
var init_errors = __esm({
|
|
330
309
|
"src/errors.ts"() {
|
|
310
|
+
"use strict";
|
|
331
311
|
ConfigError = class extends Error {
|
|
332
312
|
constructor(message) {
|
|
333
313
|
super(message);
|
|
@@ -344,6 +324,12 @@ var init_errors = __esm({
|
|
|
344
324
|
};
|
|
345
325
|
}
|
|
346
326
|
});
|
|
327
|
+
|
|
328
|
+
// src/config.ts
|
|
329
|
+
import { mkdir, readFile, stat, unlink, writeFile } from "fs/promises";
|
|
330
|
+
import { homedir } from "os";
|
|
331
|
+
import { dirname, join } from "path";
|
|
332
|
+
import * as ini from "ini";
|
|
347
333
|
function getConfigPath() {
|
|
348
334
|
return join(homedir(), ".pspmrc");
|
|
349
335
|
}
|
|
@@ -616,55 +602,106 @@ async function getRegistryUrl() {
|
|
|
616
602
|
var DEFAULT_REGISTRY_URL;
|
|
617
603
|
var init_config = __esm({
|
|
618
604
|
"src/config.ts"() {
|
|
605
|
+
"use strict";
|
|
619
606
|
init_errors();
|
|
620
607
|
DEFAULT_REGISTRY_URL = "https://pspm.dev";
|
|
621
608
|
}
|
|
622
609
|
});
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
610
|
+
|
|
611
|
+
// src/lib/integrity.ts
|
|
612
|
+
import { createHash } from "crypto";
|
|
613
|
+
function calculateIntegrity(data) {
|
|
614
|
+
const hash = createHash("sha256").update(data).digest("base64");
|
|
615
|
+
return `sha256-${hash}`;
|
|
616
|
+
}
|
|
617
|
+
var init_integrity = __esm({
|
|
618
|
+
"src/lib/integrity.ts"() {
|
|
619
|
+
"use strict";
|
|
620
|
+
}
|
|
621
|
+
});
|
|
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) {
|
|
627
664
|
try {
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
665
|
+
const stats = await stat2(absolutePath);
|
|
666
|
+
if (!stats.isDirectory()) {
|
|
667
|
+
return { valid: false, error: `Not a directory: ${absolutePath}` };
|
|
668
|
+
}
|
|
632
669
|
} catch {
|
|
670
|
+
return { valid: false, error: `Directory not found: ${absolutePath}` };
|
|
633
671
|
}
|
|
634
|
-
const
|
|
672
|
+
const manifestPath = join2(absolutePath, "pspm.json");
|
|
635
673
|
try {
|
|
636
|
-
|
|
637
|
-
const patterns = parseIgnorePatterns(content);
|
|
638
|
-
ig.add(patterns);
|
|
639
|
-
return { ig, source: ".gitignore", patterns };
|
|
674
|
+
await access(manifestPath);
|
|
640
675
|
} catch {
|
|
676
|
+
return {
|
|
677
|
+
valid: false,
|
|
678
|
+
error: `No pspm.json found in ${absolutePath}`
|
|
679
|
+
};
|
|
641
680
|
}
|
|
642
|
-
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
];
|
|
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
|
+
};
|
|
660
698
|
}
|
|
661
|
-
});
|
|
662
|
-
function calculateIntegrity(data) {
|
|
663
|
-
const hash = createHash("sha256").update(data).digest("base64");
|
|
664
|
-
return `sha256-${hash}`;
|
|
665
699
|
}
|
|
666
|
-
var
|
|
667
|
-
|
|
700
|
+
var LOCAL_SPECIFIER_PATTERN;
|
|
701
|
+
var init_local = __esm({
|
|
702
|
+
"src/lib/local.ts"() {
|
|
703
|
+
"use strict";
|
|
704
|
+
LOCAL_SPECIFIER_PATTERN = /^file:(.+)$/;
|
|
668
705
|
}
|
|
669
706
|
});
|
|
670
707
|
|
|
@@ -693,6 +730,7 @@ function validateManifest(manifest) {
|
|
|
693
730
|
var DEFAULT_SKILL_FILES, PSPM_SCHEMA_URL;
|
|
694
731
|
var init_manifest = __esm({
|
|
695
732
|
"src/lib/manifest.ts"() {
|
|
733
|
+
"use strict";
|
|
696
734
|
DEFAULT_SKILL_FILES = [
|
|
697
735
|
"SKILL.md",
|
|
698
736
|
"runtime",
|
|
@@ -702,6 +740,9 @@ var init_manifest = __esm({
|
|
|
702
740
|
PSPM_SCHEMA_URL = "https://pspm.dev/schema/v1/pspm.json";
|
|
703
741
|
}
|
|
704
742
|
});
|
|
743
|
+
|
|
744
|
+
// src/lib/version.ts
|
|
745
|
+
import * as semver from "semver";
|
|
705
746
|
function resolveVersion(range, availableVersions) {
|
|
706
747
|
const sorted = availableVersions.filter((v) => semver.valid(v)).sort((a, b) => semver.rcompare(a, b));
|
|
707
748
|
if (!range || range === "latest" || range === "*") {
|
|
@@ -727,6 +768,7 @@ function findHighestSatisfying(ranges, availableVersions) {
|
|
|
727
768
|
}
|
|
728
769
|
var init_version = __esm({
|
|
729
770
|
"src/lib/version.ts"() {
|
|
771
|
+
"use strict";
|
|
730
772
|
}
|
|
731
773
|
});
|
|
732
774
|
|
|
@@ -1035,6 +1077,7 @@ function printResolutionErrors(errors, conflicts = []) {
|
|
|
1035
1077
|
var MAX_DEPENDENCY_DEPTH;
|
|
1036
1078
|
var init_resolver = __esm({
|
|
1037
1079
|
"src/lib/resolver.ts"() {
|
|
1080
|
+
"use strict";
|
|
1038
1081
|
init_api_client();
|
|
1039
1082
|
init_version();
|
|
1040
1083
|
MAX_DEPENDENCY_DEPTH = 5;
|
|
@@ -1047,14 +1090,9 @@ function parseSkillSpecifier(specifier) {
|
|
|
1047
1090
|
if (!match) {
|
|
1048
1091
|
return null;
|
|
1049
1092
|
}
|
|
1050
|
-
const username = match[1];
|
|
1051
|
-
const name = match[2];
|
|
1052
|
-
if (!username || !name) {
|
|
1053
|
-
return null;
|
|
1054
|
-
}
|
|
1055
1093
|
return {
|
|
1056
|
-
username,
|
|
1057
|
-
name,
|
|
1094
|
+
username: match[1],
|
|
1095
|
+
name: match[2],
|
|
1058
1096
|
versionRange: match[3]
|
|
1059
1097
|
};
|
|
1060
1098
|
}
|
|
@@ -1064,9 +1102,6 @@ function parseGitHubSpecifier(specifier) {
|
|
|
1064
1102
|
return null;
|
|
1065
1103
|
}
|
|
1066
1104
|
const [, owner, repo, pathWithSlash, ref] = match;
|
|
1067
|
-
if (!owner || !repo) {
|
|
1068
|
-
return null;
|
|
1069
|
-
}
|
|
1070
1105
|
return {
|
|
1071
1106
|
owner,
|
|
1072
1107
|
repo,
|
|
@@ -1088,10 +1123,7 @@ function formatGitHubSpecifier(spec) {
|
|
|
1088
1123
|
function getGitHubSkillName(spec) {
|
|
1089
1124
|
if (spec.path) {
|
|
1090
1125
|
const segments = spec.path.split("/").filter(Boolean);
|
|
1091
|
-
|
|
1092
|
-
if (lastSegment) {
|
|
1093
|
-
return lastSegment;
|
|
1094
|
-
}
|
|
1126
|
+
return segments[segments.length - 1];
|
|
1095
1127
|
}
|
|
1096
1128
|
return spec.repo;
|
|
1097
1129
|
}
|
|
@@ -1101,6 +1133,7 @@ function isGitHubSpecifier(specifier) {
|
|
|
1101
1133
|
var SPECIFIER_PATTERN, GITHUB_SPECIFIER_PATTERN;
|
|
1102
1134
|
var init_specifier = __esm({
|
|
1103
1135
|
"src/lib/specifier.ts"() {
|
|
1136
|
+
"use strict";
|
|
1104
1137
|
SPECIFIER_PATTERN = /^@user\/([a-zA-Z0-9_-]+)\/([a-z][a-z0-9_-]*)(?:@(.+))?$/;
|
|
1105
1138
|
GITHUB_SPECIFIER_PATTERN = /^github:([a-zA-Z0-9_-]+)\/([a-zA-Z0-9_.-]+)(\/[^@]+)?(?:@(.+))?$/;
|
|
1106
1139
|
}
|
|
@@ -1109,14 +1142,18 @@ var init_specifier = __esm({
|
|
|
1109
1142
|
// src/lib/index.ts
|
|
1110
1143
|
var init_lib = __esm({
|
|
1111
1144
|
"src/lib/index.ts"() {
|
|
1112
|
-
|
|
1145
|
+
"use strict";
|
|
1113
1146
|
init_integrity();
|
|
1147
|
+
init_local();
|
|
1114
1148
|
init_manifest();
|
|
1115
1149
|
init_resolver();
|
|
1116
1150
|
init_specifier();
|
|
1117
1151
|
init_version();
|
|
1118
1152
|
}
|
|
1119
1153
|
});
|
|
1154
|
+
|
|
1155
|
+
// src/agents.ts
|
|
1156
|
+
import { checkbox } from "@inquirer/prompts";
|
|
1120
1157
|
function resolveAgentConfig(name, overrides) {
|
|
1121
1158
|
if (overrides?.[name]) {
|
|
1122
1159
|
return overrides[name];
|
|
@@ -1159,6 +1196,7 @@ async function promptForAgents() {
|
|
|
1159
1196
|
var AGENT_INFO, DEFAULT_AGENT_CONFIGS, ALL_AGENTS;
|
|
1160
1197
|
var init_agents = __esm({
|
|
1161
1198
|
"src/agents.ts"() {
|
|
1199
|
+
"use strict";
|
|
1162
1200
|
AGENT_INFO = {
|
|
1163
1201
|
"claude-code": {
|
|
1164
1202
|
displayName: "Claude Code",
|
|
@@ -1203,6 +1241,10 @@ var init_agents = __esm({
|
|
|
1203
1241
|
];
|
|
1204
1242
|
}
|
|
1205
1243
|
});
|
|
1244
|
+
|
|
1245
|
+
// src/github.ts
|
|
1246
|
+
import { cp, lstat, mkdir as mkdir2, readdir, rm, writeFile as writeFile2 } from "fs/promises";
|
|
1247
|
+
import { join as join3 } from "path";
|
|
1206
1248
|
function getGitHubHeaders() {
|
|
1207
1249
|
const headers = {
|
|
1208
1250
|
Accept: "application/vnd.github+json",
|
|
@@ -1278,14 +1320,14 @@ async function downloadGitHubPackage(spec) {
|
|
|
1278
1320
|
return { buffer, commit, integrity };
|
|
1279
1321
|
}
|
|
1280
1322
|
async function extractGitHubPackage(spec, buffer, skillsDir) {
|
|
1281
|
-
const destPath = spec.path ?
|
|
1282
|
-
const tempDir =
|
|
1283
|
-
await
|
|
1284
|
-
const tempFile =
|
|
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()}`);
|
|
1325
|
+
await mkdir2(tempDir, { recursive: true });
|
|
1326
|
+
const tempFile = join3(tempDir, "archive.tgz");
|
|
1285
1327
|
try {
|
|
1286
|
-
await
|
|
1287
|
-
const { exec: exec2 } = await import(
|
|
1288
|
-
const { promisify: promisify2 } = await import(
|
|
1328
|
+
await writeFile2(tempFile, buffer);
|
|
1329
|
+
const { exec: exec2 } = await import("child_process");
|
|
1330
|
+
const { promisify: promisify2 } = await import("util");
|
|
1289
1331
|
const execAsync = promisify2(exec2);
|
|
1290
1332
|
await execAsync(`tar -xzf "${tempFile}" -C "${tempDir}"`);
|
|
1291
1333
|
const entries = await readdir(tempDir);
|
|
@@ -1295,16 +1337,16 @@ async function extractGitHubPackage(spec, buffer, skillsDir) {
|
|
|
1295
1337
|
if (!extractedDir) {
|
|
1296
1338
|
throw new Error("Failed to find extracted directory in tarball");
|
|
1297
1339
|
}
|
|
1298
|
-
const sourcePath =
|
|
1299
|
-
const copySource = spec.path ?
|
|
1340
|
+
const sourcePath = join3(tempDir, extractedDir);
|
|
1341
|
+
const copySource = spec.path ? join3(sourcePath, spec.path) : sourcePath;
|
|
1300
1342
|
if (spec.path) {
|
|
1301
1343
|
const pathExists = await lstat(copySource).catch(() => null);
|
|
1302
1344
|
if (!pathExists) {
|
|
1303
1345
|
const rootEntries = await readdir(sourcePath);
|
|
1304
1346
|
const dirs = [];
|
|
1305
1347
|
for (const entry of rootEntries) {
|
|
1306
|
-
const
|
|
1307
|
-
if (
|
|
1348
|
+
const stat8 = await lstat(join3(sourcePath, entry)).catch(() => null);
|
|
1349
|
+
if (stat8?.isDirectory() && !entry.startsWith(".")) {
|
|
1308
1350
|
dirs.push(entry);
|
|
1309
1351
|
}
|
|
1310
1352
|
}
|
|
@@ -1312,7 +1354,7 @@ async function extractGitHubPackage(spec, buffer, skillsDir) {
|
|
|
1312
1354
|
}
|
|
1313
1355
|
}
|
|
1314
1356
|
await rm(destPath, { recursive: true, force: true });
|
|
1315
|
-
await
|
|
1357
|
+
await mkdir2(destPath, { recursive: true });
|
|
1316
1358
|
await cp(copySource, destPath, { recursive: true });
|
|
1317
1359
|
return spec.path ? `.pspm/skills/_github/${spec.owner}/${spec.repo}/${spec.path}` : `.pspm/skills/_github/${spec.owner}/${spec.repo}`;
|
|
1318
1360
|
} finally {
|
|
@@ -1326,13 +1368,15 @@ function getGitHubDisplayName(spec, commit) {
|
|
|
1326
1368
|
}
|
|
1327
1369
|
if (spec.ref || commit) {
|
|
1328
1370
|
const ref = spec.ref || "HEAD";
|
|
1329
|
-
|
|
1371
|
+
const shortCommit = commit ? commit.slice(0, 7) : "";
|
|
1372
|
+
name += ` (${ref}${shortCommit ? `@${shortCommit}` : ""})`;
|
|
1330
1373
|
}
|
|
1331
1374
|
return name;
|
|
1332
1375
|
}
|
|
1333
1376
|
var GitHubRateLimitError, GitHubNotFoundError, GitHubPathNotFoundError;
|
|
1334
1377
|
var init_github = __esm({
|
|
1335
1378
|
"src/github.ts"() {
|
|
1379
|
+
"use strict";
|
|
1336
1380
|
init_lib();
|
|
1337
1381
|
GitHubRateLimitError = class extends Error {
|
|
1338
1382
|
constructor() {
|
|
@@ -1365,9 +1409,13 @@ Available paths in repository root:
|
|
|
1365
1409
|
};
|
|
1366
1410
|
}
|
|
1367
1411
|
});
|
|
1412
|
+
|
|
1413
|
+
// src/lockfile.ts
|
|
1414
|
+
import { mkdir as mkdir3, readFile as readFile2, stat as stat3, writeFile as writeFile3 } from "fs/promises";
|
|
1415
|
+
import { dirname as dirname2 } from "path";
|
|
1368
1416
|
async function hasLegacyLockfile() {
|
|
1369
1417
|
try {
|
|
1370
|
-
await
|
|
1418
|
+
await stat3(getLegacyLockfilePath());
|
|
1371
1419
|
return true;
|
|
1372
1420
|
} catch {
|
|
1373
1421
|
return false;
|
|
@@ -1377,24 +1425,24 @@ async function migrateLockfileIfNeeded() {
|
|
|
1377
1425
|
const legacyPath = getLegacyLockfilePath();
|
|
1378
1426
|
const newPath = getLockfilePath();
|
|
1379
1427
|
try {
|
|
1380
|
-
await
|
|
1428
|
+
await stat3(legacyPath);
|
|
1381
1429
|
} catch {
|
|
1382
1430
|
return false;
|
|
1383
1431
|
}
|
|
1384
1432
|
try {
|
|
1385
|
-
await
|
|
1433
|
+
await stat3(newPath);
|
|
1386
1434
|
return false;
|
|
1387
1435
|
} catch {
|
|
1388
1436
|
}
|
|
1389
1437
|
try {
|
|
1390
|
-
const content = await
|
|
1438
|
+
const content = await readFile2(legacyPath, "utf-8");
|
|
1391
1439
|
const oldLockfile = JSON.parse(content);
|
|
1392
1440
|
const newLockfile = {
|
|
1393
1441
|
lockfileVersion: 2,
|
|
1394
1442
|
registryUrl: oldLockfile.registryUrl,
|
|
1395
1443
|
packages: oldLockfile.skills ?? {}
|
|
1396
1444
|
};
|
|
1397
|
-
await
|
|
1445
|
+
await writeFile3(newPath, `${JSON.stringify(newLockfile, null, 2)}
|
|
1398
1446
|
`);
|
|
1399
1447
|
console.log("Migrated lockfile: skill-lock.json \u2192 pspm-lock.json");
|
|
1400
1448
|
return true;
|
|
@@ -1405,7 +1453,7 @@ async function migrateLockfileIfNeeded() {
|
|
|
1405
1453
|
async function readLockfile() {
|
|
1406
1454
|
const lockfilePath = getLockfilePath();
|
|
1407
1455
|
try {
|
|
1408
|
-
const content = await
|
|
1456
|
+
const content = await readFile2(lockfilePath, "utf-8");
|
|
1409
1457
|
const lockfile = JSON.parse(content);
|
|
1410
1458
|
if (lockfile.lockfileVersion === 1 && lockfile.skills && !lockfile.packages) {
|
|
1411
1459
|
return {
|
|
@@ -1418,7 +1466,7 @@ async function readLockfile() {
|
|
|
1418
1466
|
} catch {
|
|
1419
1467
|
if (await hasLegacyLockfile()) {
|
|
1420
1468
|
try {
|
|
1421
|
-
const content = await
|
|
1469
|
+
const content = await readFile2(getLegacyLockfilePath(), "utf-8");
|
|
1422
1470
|
const legacyLockfile = JSON.parse(content);
|
|
1423
1471
|
return {
|
|
1424
1472
|
lockfileVersion: 2,
|
|
@@ -1434,12 +1482,18 @@ async function readLockfile() {
|
|
|
1434
1482
|
}
|
|
1435
1483
|
async function writeLockfile(lockfile) {
|
|
1436
1484
|
const lockfilePath = getLockfilePath();
|
|
1437
|
-
await
|
|
1485
|
+
await mkdir3(dirname2(lockfilePath), { recursive: true });
|
|
1438
1486
|
const packages = lockfile.packages ?? lockfile.skills ?? {};
|
|
1439
1487
|
const hasDependencies = Object.values(packages).some(
|
|
1440
1488
|
(pkg) => pkg.dependencies && Object.keys(pkg.dependencies).length > 0
|
|
1441
1489
|
);
|
|
1442
|
-
const
|
|
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
|
+
}
|
|
1443
1497
|
const normalized = {
|
|
1444
1498
|
lockfileVersion: version2,
|
|
1445
1499
|
registryUrl: lockfile.registryUrl,
|
|
@@ -1448,7 +1502,10 @@ async function writeLockfile(lockfile) {
|
|
|
1448
1502
|
if (lockfile.githubPackages && Object.keys(lockfile.githubPackages).length > 0) {
|
|
1449
1503
|
normalized.githubPackages = lockfile.githubPackages;
|
|
1450
1504
|
}
|
|
1451
|
-
|
|
1505
|
+
if (hasLocalPackages) {
|
|
1506
|
+
normalized.localPackages = lockfile.localPackages;
|
|
1507
|
+
}
|
|
1508
|
+
await writeFile3(lockfilePath, `${JSON.stringify(normalized, null, 2)}
|
|
1452
1509
|
`);
|
|
1453
1510
|
}
|
|
1454
1511
|
async function createEmptyLockfile() {
|
|
@@ -1541,17 +1598,52 @@ async function listLockfileGitHubPackages() {
|
|
|
1541
1598
|
entry
|
|
1542
1599
|
}));
|
|
1543
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
|
+
}
|
|
1544
1631
|
var init_lockfile = __esm({
|
|
1545
1632
|
"src/lockfile.ts"() {
|
|
1633
|
+
"use strict";
|
|
1546
1634
|
init_config();
|
|
1547
1635
|
}
|
|
1548
1636
|
});
|
|
1637
|
+
|
|
1638
|
+
// src/manifest.ts
|
|
1639
|
+
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
1640
|
+
import { join as join4 } from "path";
|
|
1549
1641
|
function getManifestPath() {
|
|
1550
|
-
return
|
|
1642
|
+
return join4(process.cwd(), "pspm.json");
|
|
1551
1643
|
}
|
|
1552
1644
|
async function readManifest() {
|
|
1553
1645
|
try {
|
|
1554
|
-
const content = await
|
|
1646
|
+
const content = await readFile3(getManifestPath(), "utf-8");
|
|
1555
1647
|
return JSON.parse(content);
|
|
1556
1648
|
} catch {
|
|
1557
1649
|
return null;
|
|
@@ -1559,7 +1651,7 @@ async function readManifest() {
|
|
|
1559
1651
|
}
|
|
1560
1652
|
async function writeManifest(manifest) {
|
|
1561
1653
|
const content = JSON.stringify(manifest, null, 2);
|
|
1562
|
-
await
|
|
1654
|
+
await writeFile4(getManifestPath(), `${content}
|
|
1563
1655
|
`);
|
|
1564
1656
|
}
|
|
1565
1657
|
async function createMinimalManifest() {
|
|
@@ -1617,10 +1709,36 @@ async function removeGitHubDependency(specifier) {
|
|
|
1617
1709
|
await writeManifest(manifest);
|
|
1618
1710
|
return true;
|
|
1619
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
|
+
}
|
|
1620
1733
|
var init_manifest2 = __esm({
|
|
1621
1734
|
"src/manifest.ts"() {
|
|
1735
|
+
"use strict";
|
|
1622
1736
|
}
|
|
1623
1737
|
});
|
|
1738
|
+
|
|
1739
|
+
// src/symlinks.ts
|
|
1740
|
+
import { lstat as lstat2, mkdir as mkdir4, readlink, rm as rm2, symlink } from "fs/promises";
|
|
1741
|
+
import { dirname as dirname3, join as join5, relative } from "path";
|
|
1624
1742
|
async function createAgentSymlinks(skills, options) {
|
|
1625
1743
|
const { agents, projectRoot, agentConfigs } = options;
|
|
1626
1744
|
if (agents.length === 1 && agents[0] === "none") {
|
|
@@ -1632,26 +1750,26 @@ async function createAgentSymlinks(skills, options) {
|
|
|
1632
1750
|
console.warn(`Warning: Unknown agent "${agentName}", skipping symlinks`);
|
|
1633
1751
|
continue;
|
|
1634
1752
|
}
|
|
1635
|
-
const agentSkillsDir =
|
|
1636
|
-
await
|
|
1753
|
+
const agentSkillsDir = join5(projectRoot, config2.skillsDir);
|
|
1754
|
+
await mkdir4(agentSkillsDir, { recursive: true });
|
|
1637
1755
|
for (const skill of skills) {
|
|
1638
|
-
const symlinkPath =
|
|
1639
|
-
const targetPath =
|
|
1640
|
-
const relativeTarget = relative(
|
|
1756
|
+
const symlinkPath = join5(agentSkillsDir, skill.name);
|
|
1757
|
+
const targetPath = join5(projectRoot, skill.sourcePath);
|
|
1758
|
+
const relativeTarget = relative(dirname3(symlinkPath), targetPath);
|
|
1641
1759
|
await createSymlink(symlinkPath, relativeTarget, skill.name);
|
|
1642
1760
|
}
|
|
1643
1761
|
}
|
|
1644
1762
|
}
|
|
1645
1763
|
async function createSymlink(symlinkPath, target, skillName) {
|
|
1646
1764
|
try {
|
|
1647
|
-
const stats = await
|
|
1765
|
+
const stats = await lstat2(symlinkPath).catch(() => null);
|
|
1648
1766
|
if (stats) {
|
|
1649
1767
|
if (stats.isSymbolicLink()) {
|
|
1650
1768
|
const existingTarget = await readlink(symlinkPath);
|
|
1651
1769
|
if (existingTarget === target) {
|
|
1652
1770
|
return;
|
|
1653
1771
|
}
|
|
1654
|
-
await
|
|
1772
|
+
await rm2(symlinkPath);
|
|
1655
1773
|
} else {
|
|
1656
1774
|
console.warn(
|
|
1657
1775
|
`Warning: File exists at symlink path for "${skillName}", skipping: ${symlinkPath}`
|
|
@@ -1677,11 +1795,11 @@ async function removeAgentSymlinks(skillName, options) {
|
|
|
1677
1795
|
if (!config2) {
|
|
1678
1796
|
continue;
|
|
1679
1797
|
}
|
|
1680
|
-
const symlinkPath =
|
|
1798
|
+
const symlinkPath = join5(projectRoot, config2.skillsDir, skillName);
|
|
1681
1799
|
try {
|
|
1682
|
-
const stats = await
|
|
1800
|
+
const stats = await lstat2(symlinkPath).catch(() => null);
|
|
1683
1801
|
if (stats?.isSymbolicLink()) {
|
|
1684
|
-
await
|
|
1802
|
+
await rm2(symlinkPath);
|
|
1685
1803
|
}
|
|
1686
1804
|
} catch {
|
|
1687
1805
|
}
|
|
@@ -1696,14 +1814,17 @@ function getGitHubSkillPath(owner, repo, path) {
|
|
|
1696
1814
|
}
|
|
1697
1815
|
return `.pspm/skills/_github/${owner}/${repo}`;
|
|
1698
1816
|
}
|
|
1817
|
+
function getLocalSkillPath(skillName) {
|
|
1818
|
+
return `.pspm/skills/_local/${skillName}`;
|
|
1819
|
+
}
|
|
1699
1820
|
async function getLinkedAgents(skillName, agents, projectRoot, agentConfigs) {
|
|
1700
1821
|
const linkedAgents = [];
|
|
1701
1822
|
for (const agentName of agents) {
|
|
1702
1823
|
const config2 = resolveAgentConfig(agentName, agentConfigs);
|
|
1703
1824
|
if (!config2) continue;
|
|
1704
|
-
const symlinkPath =
|
|
1825
|
+
const symlinkPath = join5(projectRoot, config2.skillsDir, skillName);
|
|
1705
1826
|
try {
|
|
1706
|
-
const stats = await
|
|
1827
|
+
const stats = await lstat2(symlinkPath);
|
|
1707
1828
|
if (stats.isSymbolicLink()) {
|
|
1708
1829
|
linkedAgents.push(agentName);
|
|
1709
1830
|
}
|
|
@@ -1714,6 +1835,7 @@ async function getLinkedAgents(skillName, agents, projectRoot, agentConfigs) {
|
|
|
1714
1835
|
}
|
|
1715
1836
|
var init_symlinks = __esm({
|
|
1716
1837
|
"src/symlinks.ts"() {
|
|
1838
|
+
"use strict";
|
|
1717
1839
|
init_agents();
|
|
1718
1840
|
}
|
|
1719
1841
|
});
|
|
@@ -1723,13 +1845,21 @@ var add_exports = {};
|
|
|
1723
1845
|
__export(add_exports, {
|
|
1724
1846
|
add: () => add
|
|
1725
1847
|
});
|
|
1848
|
+
import { mkdir as mkdir5, rm as rm3, symlink as symlink2 } from "fs/promises";
|
|
1849
|
+
import { dirname as dirname4, join as join6 } from "path";
|
|
1726
1850
|
async function add(specifiers, options) {
|
|
1727
1851
|
console.log("Resolving packages...\n");
|
|
1728
1852
|
const resolvedPackages = [];
|
|
1729
1853
|
const validationErrors = [];
|
|
1730
|
-
for (
|
|
1854
|
+
for (let specifier of specifiers) {
|
|
1731
1855
|
try {
|
|
1732
|
-
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)) {
|
|
1733
1863
|
const resolved = await validateGitHubPackage(specifier);
|
|
1734
1864
|
resolvedPackages.push(resolved);
|
|
1735
1865
|
} else {
|
|
@@ -1761,6 +1891,9 @@ async function add(specifiers, options) {
|
|
|
1761
1891
|
const githubPackages = resolvedPackages.filter(
|
|
1762
1892
|
(p) => p.type === "github"
|
|
1763
1893
|
);
|
|
1894
|
+
const localPackages = resolvedPackages.filter(
|
|
1895
|
+
(p) => p.type === "local"
|
|
1896
|
+
);
|
|
1764
1897
|
let resolutionResult = null;
|
|
1765
1898
|
if (registryPackages.length > 0) {
|
|
1766
1899
|
const rootDeps = {};
|
|
@@ -1845,6 +1978,24 @@ async function add(specifiers, options) {
|
|
|
1845
1978
|
error: message
|
|
1846
1979
|
});
|
|
1847
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
|
+
});
|
|
1990
|
+
results.push({ specifier: resolved.specifier, success: true });
|
|
1991
|
+
} catch (error) {
|
|
1992
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1993
|
+
results.push({
|
|
1994
|
+
specifier: resolved.specifier,
|
|
1995
|
+
success: false,
|
|
1996
|
+
error: message
|
|
1997
|
+
});
|
|
1998
|
+
console.error(`Failed to install ${resolved.specifier}: ${message}
|
|
1848
1999
|
`);
|
|
1849
2000
|
}
|
|
1850
2001
|
}
|
|
@@ -1885,23 +2036,23 @@ async function installFromNode(node, options) {
|
|
|
1885
2036
|
throw new Error("Checksum verification failed");
|
|
1886
2037
|
}
|
|
1887
2038
|
const skillsDir = getSkillsDir();
|
|
1888
|
-
const destDir =
|
|
1889
|
-
await
|
|
1890
|
-
const { writeFile: writeFile8 } = await import(
|
|
1891
|
-
const tempFile =
|
|
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");
|
|
1892
2043
|
await writeFile8(tempFile, tarballBuffer);
|
|
1893
|
-
const { exec: exec2 } = await import(
|
|
1894
|
-
const { promisify: promisify2 } = await import(
|
|
2044
|
+
const { exec: exec2 } = await import("child_process");
|
|
2045
|
+
const { promisify: promisify2 } = await import("util");
|
|
1895
2046
|
const execAsync = promisify2(exec2);
|
|
1896
2047
|
try {
|
|
1897
|
-
await
|
|
1898
|
-
await
|
|
2048
|
+
await rm3(destDir, { recursive: true, force: true });
|
|
2049
|
+
await mkdir5(destDir, { recursive: true });
|
|
1899
2050
|
await writeFile8(tempFile, tarballBuffer);
|
|
1900
2051
|
await execAsync(
|
|
1901
2052
|
`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
|
|
1902
2053
|
);
|
|
1903
2054
|
} finally {
|
|
1904
|
-
await
|
|
2055
|
+
await rm3(tempFile, { force: true });
|
|
1905
2056
|
}
|
|
1906
2057
|
const resolvedDeps = {};
|
|
1907
2058
|
for (const [depName, _range] of Object.entries(node.dependencies)) {
|
|
@@ -2069,8 +2220,70 @@ async function installGitHubPackage(resolved, options) {
|
|
|
2069
2220
|
);
|
|
2070
2221
|
console.log(`Location: ${destPath}`);
|
|
2071
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
|
+
}
|
|
2072
2284
|
var init_add = __esm({
|
|
2073
2285
|
"src/commands/add.ts"() {
|
|
2286
|
+
"use strict";
|
|
2074
2287
|
init_agents();
|
|
2075
2288
|
init_api_client();
|
|
2076
2289
|
init_config();
|
|
@@ -2083,11 +2296,17 @@ var init_add = __esm({
|
|
|
2083
2296
|
}
|
|
2084
2297
|
});
|
|
2085
2298
|
|
|
2299
|
+
// src/index.ts
|
|
2300
|
+
import { readFileSync } from "fs";
|
|
2301
|
+
import { dirname as dirname6, join as join13 } from "path";
|
|
2302
|
+
import { fileURLToPath } from "url";
|
|
2303
|
+
import { Command } from "commander";
|
|
2304
|
+
|
|
2086
2305
|
// src/commands/access.ts
|
|
2087
2306
|
init_api_client();
|
|
2088
2307
|
init_config();
|
|
2089
2308
|
init_lib();
|
|
2090
|
-
async function
|
|
2309
|
+
async function access2(specifier, options) {
|
|
2091
2310
|
try {
|
|
2092
2311
|
const apiKey = await requireApiKey();
|
|
2093
2312
|
const registryUrl = await getRegistryUrl();
|
|
@@ -2111,18 +2330,18 @@ async function access(specifier, options) {
|
|
|
2111
2330
|
}
|
|
2112
2331
|
packageName = parsed.name;
|
|
2113
2332
|
} else {
|
|
2114
|
-
const { readFile:
|
|
2115
|
-
const { join: join14 } = await import(
|
|
2333
|
+
const { readFile: readFile7 } = await import("fs/promises");
|
|
2334
|
+
const { join: join14 } = await import("path");
|
|
2116
2335
|
let manifest = null;
|
|
2117
2336
|
try {
|
|
2118
|
-
const content = await
|
|
2337
|
+
const content = await readFile7(
|
|
2119
2338
|
join14(process.cwd(), "pspm.json"),
|
|
2120
2339
|
"utf-8"
|
|
2121
2340
|
);
|
|
2122
2341
|
manifest = JSON.parse(content);
|
|
2123
2342
|
} catch {
|
|
2124
2343
|
try {
|
|
2125
|
-
const content = await
|
|
2344
|
+
const content = await readFile7(
|
|
2126
2345
|
join14(process.cwd(), "package.json"),
|
|
2127
2346
|
"utf-8"
|
|
2128
2347
|
);
|
|
@@ -2170,11 +2389,15 @@ async function access(specifier, options) {
|
|
|
2170
2389
|
|
|
2171
2390
|
// src/commands/index.ts
|
|
2172
2391
|
init_add();
|
|
2392
|
+
|
|
2393
|
+
// src/commands/config/init.ts
|
|
2394
|
+
import { stat as stat4, writeFile as writeFile5 } from "fs/promises";
|
|
2395
|
+
import { join as join7 } from "path";
|
|
2173
2396
|
async function configInit(options) {
|
|
2174
2397
|
try {
|
|
2175
|
-
const configPath =
|
|
2398
|
+
const configPath = join7(process.cwd(), ".pspmrc");
|
|
2176
2399
|
try {
|
|
2177
|
-
await
|
|
2400
|
+
await stat4(configPath);
|
|
2178
2401
|
console.error("Error: .pspmrc already exists in this directory.");
|
|
2179
2402
|
process.exit(1);
|
|
2180
2403
|
} catch {
|
|
@@ -2187,7 +2410,7 @@ async function configInit(options) {
|
|
|
2187
2410
|
lines.push("; registry = https://custom-registry.example.com");
|
|
2188
2411
|
}
|
|
2189
2412
|
lines.push("");
|
|
2190
|
-
await
|
|
2413
|
+
await writeFile5(configPath, lines.join("\n"));
|
|
2191
2414
|
console.log("Created .pspmrc");
|
|
2192
2415
|
console.log("");
|
|
2193
2416
|
console.log("Contents:");
|
|
@@ -2300,18 +2523,21 @@ async function deprecate(specifier, message, options) {
|
|
|
2300
2523
|
|
|
2301
2524
|
// src/commands/init.ts
|
|
2302
2525
|
init_lib();
|
|
2526
|
+
import { readFile as readFile4, stat as stat5, writeFile as writeFile6 } from "fs/promises";
|
|
2527
|
+
import { basename, join as join8 } from "path";
|
|
2528
|
+
import { createInterface } from "readline";
|
|
2303
2529
|
function prompt(rl, question, defaultValue) {
|
|
2304
|
-
return new Promise((
|
|
2530
|
+
return new Promise((resolve2) => {
|
|
2305
2531
|
const displayDefault = defaultValue ? ` (${defaultValue})` : "";
|
|
2306
2532
|
rl.question(`${question}${displayDefault} `, (answer) => {
|
|
2307
|
-
|
|
2533
|
+
resolve2(answer.trim() || defaultValue);
|
|
2308
2534
|
});
|
|
2309
2535
|
});
|
|
2310
2536
|
}
|
|
2311
2537
|
async function readExistingPackageJson() {
|
|
2312
2538
|
try {
|
|
2313
|
-
const content = await
|
|
2314
|
-
|
|
2539
|
+
const content = await readFile4(
|
|
2540
|
+
join8(process.cwd(), "package.json"),
|
|
2315
2541
|
"utf-8"
|
|
2316
2542
|
);
|
|
2317
2543
|
const pkg = JSON.parse(content);
|
|
@@ -2328,8 +2554,8 @@ async function readExistingPackageJson() {
|
|
|
2328
2554
|
}
|
|
2329
2555
|
async function getGitAuthor() {
|
|
2330
2556
|
try {
|
|
2331
|
-
const { exec: exec2 } = await import(
|
|
2332
|
-
const { promisify: promisify2 } = await import(
|
|
2557
|
+
const { exec: exec2 } = await import("child_process");
|
|
2558
|
+
const { promisify: promisify2 } = await import("util");
|
|
2333
2559
|
const execAsync = promisify2(exec2);
|
|
2334
2560
|
const [nameResult, emailResult] = await Promise.all([
|
|
2335
2561
|
execAsync("git config user.name").catch(() => ({ stdout: "" })),
|
|
@@ -2360,10 +2586,10 @@ function isValidVersion(version2) {
|
|
|
2360
2586
|
}
|
|
2361
2587
|
async function init(options) {
|
|
2362
2588
|
try {
|
|
2363
|
-
const pspmJsonPath =
|
|
2589
|
+
const pspmJsonPath = join8(process.cwd(), "pspm.json");
|
|
2364
2590
|
let exists = false;
|
|
2365
2591
|
try {
|
|
2366
|
-
await
|
|
2592
|
+
await stat5(pspmJsonPath);
|
|
2367
2593
|
exists = true;
|
|
2368
2594
|
} catch {
|
|
2369
2595
|
}
|
|
@@ -2495,10 +2721,10 @@ async function init(options) {
|
|
|
2495
2721
|
process.exit(0);
|
|
2496
2722
|
}
|
|
2497
2723
|
}
|
|
2498
|
-
await
|
|
2724
|
+
await writeFile6(pspmJsonPath, `${content}
|
|
2499
2725
|
`);
|
|
2500
2726
|
try {
|
|
2501
|
-
await
|
|
2727
|
+
await stat5(join8(process.cwd(), "SKILL.md"));
|
|
2502
2728
|
} catch {
|
|
2503
2729
|
console.log(
|
|
2504
2730
|
"Note: Create a SKILL.md file with your skill's prompt content."
|
|
@@ -2526,6 +2752,16 @@ init_lib();
|
|
|
2526
2752
|
init_lockfile();
|
|
2527
2753
|
init_manifest2();
|
|
2528
2754
|
init_symlinks();
|
|
2755
|
+
import { createHash as createHash2 } from "crypto";
|
|
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";
|
|
2529
2765
|
function getCacheFilePath(cacheDir, integrity) {
|
|
2530
2766
|
const match = integrity.match(/^sha256-(.+)$/);
|
|
2531
2767
|
if (!match) {
|
|
@@ -2533,15 +2769,15 @@ function getCacheFilePath(cacheDir, integrity) {
|
|
|
2533
2769
|
}
|
|
2534
2770
|
const base64Hash = match[1];
|
|
2535
2771
|
const hexHash = Buffer.from(base64Hash, "base64").toString("hex");
|
|
2536
|
-
return
|
|
2772
|
+
return join9(cacheDir, `sha256-${hexHash}.tgz`);
|
|
2537
2773
|
}
|
|
2538
2774
|
async function readFromCache(cacheDir, integrity) {
|
|
2539
2775
|
try {
|
|
2540
2776
|
const cachePath = getCacheFilePath(cacheDir, integrity);
|
|
2541
|
-
const data = await
|
|
2542
|
-
const actualIntegrity = `sha256-${
|
|
2777
|
+
const data = await readFile5(cachePath);
|
|
2778
|
+
const actualIntegrity = `sha256-${createHash2("sha256").update(data).digest("base64")}`;
|
|
2543
2779
|
if (actualIntegrity !== integrity) {
|
|
2544
|
-
await
|
|
2780
|
+
await rm4(cachePath, { force: true });
|
|
2545
2781
|
return null;
|
|
2546
2782
|
}
|
|
2547
2783
|
return data;
|
|
@@ -2551,9 +2787,9 @@ async function readFromCache(cacheDir, integrity) {
|
|
|
2551
2787
|
}
|
|
2552
2788
|
async function writeToCache(cacheDir, integrity, data) {
|
|
2553
2789
|
try {
|
|
2554
|
-
await
|
|
2790
|
+
await mkdir6(cacheDir, { recursive: true });
|
|
2555
2791
|
const cachePath = getCacheFilePath(cacheDir, integrity);
|
|
2556
|
-
await
|
|
2792
|
+
await writeFile7(cachePath, data);
|
|
2557
2793
|
} catch {
|
|
2558
2794
|
}
|
|
2559
2795
|
}
|
|
@@ -2580,8 +2816,10 @@ async function installFromLockfile(options) {
|
|
|
2580
2816
|
let lockfile = await readLockfile();
|
|
2581
2817
|
const manifestDeps = await getDependencies();
|
|
2582
2818
|
const manifestGitHubDeps = await getGitHubDependencies();
|
|
2819
|
+
const manifestLocalDeps = await getLocalDependencies();
|
|
2583
2820
|
const lockfilePackages = lockfile?.packages ?? lockfile?.skills ?? {};
|
|
2584
2821
|
const lockfileGitHubPackages = lockfile?.githubPackages ?? {};
|
|
2822
|
+
const lockfileLocalPackages = lockfile?.localPackages ?? {};
|
|
2585
2823
|
const installedSkills = [];
|
|
2586
2824
|
const missingDeps = [];
|
|
2587
2825
|
for (const [fullName, versionRange] of Object.entries(manifestDeps)) {
|
|
@@ -2732,6 +2970,53 @@ Resolving ${missingGitHubDeps.length} GitHub dependency(ies)...
|
|
|
2732
2970
|
}
|
|
2733
2971
|
lockfile = await readLockfile();
|
|
2734
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
|
+
}
|
|
2735
3020
|
const manifest = await readManifest();
|
|
2736
3021
|
const agentConfigs = manifest?.agents;
|
|
2737
3022
|
let agents;
|
|
@@ -2797,7 +3082,7 @@ Installing ${packageCount} registry skill(s)...
|
|
|
2797
3082
|
continue;
|
|
2798
3083
|
}
|
|
2799
3084
|
tarballBuffer = Buffer.from(await response.arrayBuffer());
|
|
2800
|
-
const actualIntegrity = `sha256-${
|
|
3085
|
+
const actualIntegrity = `sha256-${createHash2("sha256").update(tarballBuffer).digest("base64")}`;
|
|
2801
3086
|
if (actualIntegrity !== entry.integrity) {
|
|
2802
3087
|
console.error(
|
|
2803
3088
|
` Error: Checksum verification failed for ${fullName}`
|
|
@@ -2809,20 +3094,20 @@ Installing ${packageCount} registry skill(s)...
|
|
|
2809
3094
|
}
|
|
2810
3095
|
await writeToCache(cacheDir, entry.integrity, tarballBuffer);
|
|
2811
3096
|
}
|
|
2812
|
-
const destDir =
|
|
2813
|
-
await
|
|
2814
|
-
await
|
|
2815
|
-
const tempFile =
|
|
2816
|
-
await
|
|
2817
|
-
const { exec: exec2 } = await import(
|
|
2818
|
-
const { promisify: promisify2 } = await import(
|
|
3097
|
+
const destDir = join9(skillsDir, username, name);
|
|
3098
|
+
await rm4(destDir, { recursive: true, force: true });
|
|
3099
|
+
await mkdir6(destDir, { recursive: true });
|
|
3100
|
+
const tempFile = join9(destDir, ".temp.tgz");
|
|
3101
|
+
await writeFile7(tempFile, tarballBuffer);
|
|
3102
|
+
const { exec: exec2 } = await import("child_process");
|
|
3103
|
+
const { promisify: promisify2 } = await import("util");
|
|
2819
3104
|
const execAsync = promisify2(exec2);
|
|
2820
3105
|
try {
|
|
2821
3106
|
await execAsync(
|
|
2822
3107
|
`tar -xzf "${tempFile}" -C "${destDir}" --strip-components=1`
|
|
2823
3108
|
);
|
|
2824
3109
|
} finally {
|
|
2825
|
-
await
|
|
3110
|
+
await rm4(tempFile, { force: true });
|
|
2826
3111
|
}
|
|
2827
3112
|
console.log(
|
|
2828
3113
|
` Installed to ${destDir}${fromCache ? " (from cache)" : ""}`
|
|
@@ -2910,6 +3195,52 @@ Installing ${githubCount} GitHub skill(s)...
|
|
|
2910
3195
|
}
|
|
2911
3196
|
}
|
|
2912
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
|
+
}
|
|
2913
3244
|
if (installedSkills.length > 0 && agents[0] !== "none") {
|
|
2914
3245
|
console.log(`
|
|
2915
3246
|
Creating symlinks for agent(s): ${agents.join(", ")}...`);
|
|
@@ -2920,7 +3251,7 @@ Creating symlinks for agent(s): ${agents.join(", ")}...`);
|
|
|
2920
3251
|
});
|
|
2921
3252
|
console.log(" Symlinks created.");
|
|
2922
3253
|
}
|
|
2923
|
-
const totalCount = packageCount + githubCount;
|
|
3254
|
+
const totalCount = packageCount + githubCount + localCount;
|
|
2924
3255
|
if (totalCount === 0) {
|
|
2925
3256
|
console.log("No skills to install.");
|
|
2926
3257
|
} else {
|
|
@@ -3017,6 +3348,8 @@ init_lib();
|
|
|
3017
3348
|
init_lockfile();
|
|
3018
3349
|
init_manifest2();
|
|
3019
3350
|
init_symlinks();
|
|
3351
|
+
import { access as access3 } from "fs/promises";
|
|
3352
|
+
import { join as join10 } from "path";
|
|
3020
3353
|
async function list(options) {
|
|
3021
3354
|
try {
|
|
3022
3355
|
const registrySkills = await listLockfileSkills();
|
|
@@ -3031,10 +3364,10 @@ async function list(options) {
|
|
|
3031
3364
|
if (!match) continue;
|
|
3032
3365
|
const [, username, skillName] = match;
|
|
3033
3366
|
const sourcePath = getRegistrySkillPath(username, skillName);
|
|
3034
|
-
const absolutePath =
|
|
3367
|
+
const absolutePath = join10(projectRoot, sourcePath);
|
|
3035
3368
|
let status = "installed";
|
|
3036
3369
|
try {
|
|
3037
|
-
await
|
|
3370
|
+
await access3(absolutePath);
|
|
3038
3371
|
} catch {
|
|
3039
3372
|
status = "missing";
|
|
3040
3373
|
}
|
|
@@ -3064,10 +3397,10 @@ async function list(options) {
|
|
|
3064
3397
|
parsed.repo,
|
|
3065
3398
|
parsed.path
|
|
3066
3399
|
);
|
|
3067
|
-
const absolutePath =
|
|
3400
|
+
const absolutePath = join10(projectRoot, sourcePath);
|
|
3068
3401
|
let status = "installed";
|
|
3069
3402
|
try {
|
|
3070
|
-
await
|
|
3403
|
+
await access3(absolutePath);
|
|
3071
3404
|
} catch {
|
|
3072
3405
|
status = "missing";
|
|
3073
3406
|
}
|
|
@@ -3089,6 +3422,35 @@ async function list(options) {
|
|
|
3089
3422
|
gitCommit: ghEntry.gitCommit
|
|
3090
3423
|
});
|
|
3091
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
|
+
}
|
|
3092
3454
|
if (skills.length === 0) {
|
|
3093
3455
|
console.log("No skills installed.");
|
|
3094
3456
|
return;
|
|
@@ -3101,9 +3463,14 @@ async function list(options) {
|
|
|
3101
3463
|
for (const skill of skills) {
|
|
3102
3464
|
if (skill.source === "registry") {
|
|
3103
3465
|
console.log(` ${skill.fullName}@${skill.version} (registry)`);
|
|
3104
|
-
} else {
|
|
3466
|
+
} else if (skill.source === "github") {
|
|
3105
3467
|
const refInfo = skill.gitRef ? `${skill.gitRef}@${skill.gitCommit?.slice(0, 7)}` : skill.version;
|
|
3106
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
|
+
}
|
|
3107
3474
|
}
|
|
3108
3475
|
if (skill.status === "missing") {
|
|
3109
3476
|
console.log(` Status: MISSING (run 'pspm install' to restore)`);
|
|
@@ -3119,9 +3486,11 @@ async function list(options) {
|
|
|
3119
3486
|
}
|
|
3120
3487
|
const registryCount = skills.filter((s) => s.source === "registry").length;
|
|
3121
3488
|
const githubCount = skills.filter((s) => s.source === "github").length;
|
|
3489
|
+
const localCount = skills.filter((s) => s.source === "local").length;
|
|
3122
3490
|
const parts = [];
|
|
3123
3491
|
if (registryCount > 0) parts.push(`${registryCount} registry`);
|
|
3124
3492
|
if (githubCount > 0) parts.push(`${githubCount} github`);
|
|
3493
|
+
if (localCount > 0) parts.push(`${localCount} local`);
|
|
3125
3494
|
console.log(`
|
|
3126
3495
|
Total: ${skills.length} skill(s) (${parts.join(", ")})`);
|
|
3127
3496
|
} catch (error) {
|
|
@@ -3134,13 +3503,17 @@ Total: ${skills.length} skill(s) (${parts.join(", ")})`);
|
|
|
3134
3503
|
// src/commands/login.ts
|
|
3135
3504
|
init_api_client();
|
|
3136
3505
|
init_config();
|
|
3506
|
+
import { randomBytes } from "crypto";
|
|
3507
|
+
import http from "http";
|
|
3508
|
+
import { URL as URL2 } from "url";
|
|
3509
|
+
import open from "open";
|
|
3137
3510
|
var DEFAULT_WEB_APP_URL = "https://pspm.dev";
|
|
3138
3511
|
function getWebAppUrl(registryUrl) {
|
|
3139
3512
|
if (process.env.PSPM_WEB_URL) {
|
|
3140
3513
|
return process.env.PSPM_WEB_URL.replace(/\/$/, "");
|
|
3141
3514
|
}
|
|
3142
3515
|
try {
|
|
3143
|
-
const url = new
|
|
3516
|
+
const url = new URL2(registryUrl);
|
|
3144
3517
|
return `${url.protocol}//${url.host}`;
|
|
3145
3518
|
} catch {
|
|
3146
3519
|
return DEFAULT_WEB_APP_URL;
|
|
@@ -3148,7 +3521,7 @@ function getWebAppUrl(registryUrl) {
|
|
|
3148
3521
|
}
|
|
3149
3522
|
function getServerUrl(registryUrl) {
|
|
3150
3523
|
try {
|
|
3151
|
-
const url = new
|
|
3524
|
+
const url = new URL2(registryUrl);
|
|
3152
3525
|
return `${url.protocol}//${url.host}`;
|
|
3153
3526
|
} catch {
|
|
3154
3527
|
return DEFAULT_WEB_APP_URL;
|
|
@@ -3175,12 +3548,12 @@ function startCallbackServer(expectedState) {
|
|
|
3175
3548
|
let resolveToken;
|
|
3176
3549
|
let rejectToken;
|
|
3177
3550
|
let timeoutId;
|
|
3178
|
-
const tokenPromise = new Promise((
|
|
3179
|
-
resolveToken =
|
|
3551
|
+
const tokenPromise = new Promise((resolve2, reject) => {
|
|
3552
|
+
resolveToken = resolve2;
|
|
3180
3553
|
rejectToken = reject;
|
|
3181
3554
|
});
|
|
3182
3555
|
const server = http.createServer((req, res) => {
|
|
3183
|
-
const url = new
|
|
3556
|
+
const url = new URL2(req.url || "/", "http://localhost");
|
|
3184
3557
|
if (url.pathname === "/callback") {
|
|
3185
3558
|
const token = url.searchParams.get("token");
|
|
3186
3559
|
const state = url.searchParams.get("state");
|
|
@@ -3331,6 +3704,7 @@ async function logout() {
|
|
|
3331
3704
|
// src/commands/migrate.ts
|
|
3332
3705
|
init_config();
|
|
3333
3706
|
init_lockfile();
|
|
3707
|
+
import { mkdir as mkdir7, readdir as readdir2, rename, rm as rm5, stat as stat6 } from "fs/promises";
|
|
3334
3708
|
async function migrate(options) {
|
|
3335
3709
|
try {
|
|
3336
3710
|
const legacySkillsDir = getLegacySkillsDir();
|
|
@@ -3341,9 +3715,9 @@ async function migrate(options) {
|
|
|
3341
3715
|
let migrationNeeded = false;
|
|
3342
3716
|
const actions = [];
|
|
3343
3717
|
try {
|
|
3344
|
-
const legacyStats = await
|
|
3718
|
+
const legacyStats = await stat6(legacySkillsDir);
|
|
3345
3719
|
if (legacyStats.isDirectory()) {
|
|
3346
|
-
const contents = await
|
|
3720
|
+
const contents = await readdir2(legacySkillsDir);
|
|
3347
3721
|
if (contents.length > 0) {
|
|
3348
3722
|
migrationNeeded = true;
|
|
3349
3723
|
actions.push("Move .skills/ \u2192 .pspm/skills/");
|
|
@@ -3352,9 +3726,9 @@ async function migrate(options) {
|
|
|
3352
3726
|
} catch {
|
|
3353
3727
|
}
|
|
3354
3728
|
try {
|
|
3355
|
-
await
|
|
3729
|
+
await stat6(legacyLockfilePath);
|
|
3356
3730
|
try {
|
|
3357
|
-
await
|
|
3731
|
+
await stat6(newLockfilePath);
|
|
3358
3732
|
actions.push(
|
|
3359
3733
|
"Note: Both skill-lock.json and pspm-lock.json exist. Manual merge may be needed."
|
|
3360
3734
|
);
|
|
@@ -3386,21 +3760,21 @@ async function migrate(options) {
|
|
|
3386
3760
|
console.log(" \u2713 Migrated skill-lock.json \u2192 pspm-lock.json");
|
|
3387
3761
|
}
|
|
3388
3762
|
try {
|
|
3389
|
-
const legacyStats = await
|
|
3763
|
+
const legacyStats = await stat6(legacySkillsDir);
|
|
3390
3764
|
if (legacyStats.isDirectory()) {
|
|
3391
|
-
const contents = await
|
|
3765
|
+
const contents = await readdir2(legacySkillsDir);
|
|
3392
3766
|
if (contents.length > 0) {
|
|
3393
|
-
await
|
|
3767
|
+
await mkdir7(pspmDir, { recursive: true });
|
|
3394
3768
|
try {
|
|
3395
|
-
const newStats = await
|
|
3769
|
+
const newStats = await stat6(newSkillsDir);
|
|
3396
3770
|
if (newStats.isDirectory()) {
|
|
3397
|
-
const newContents = await
|
|
3771
|
+
const newContents = await readdir2(newSkillsDir);
|
|
3398
3772
|
if (newContents.length > 0) {
|
|
3399
3773
|
console.log(
|
|
3400
3774
|
" ! Both .skills/ and .pspm/skills/ have content. Manual merge required."
|
|
3401
3775
|
);
|
|
3402
3776
|
} else {
|
|
3403
|
-
await
|
|
3777
|
+
await rm5(newSkillsDir, { recursive: true, force: true });
|
|
3404
3778
|
await rename(legacySkillsDir, newSkillsDir);
|
|
3405
3779
|
console.log(" \u2713 Moved .skills/ \u2192 .pspm/skills/");
|
|
3406
3780
|
}
|
|
@@ -3436,19 +3810,24 @@ init_api_client();
|
|
|
3436
3810
|
init_config();
|
|
3437
3811
|
init_errors();
|
|
3438
3812
|
init_lib();
|
|
3439
|
-
|
|
3813
|
+
import { exec as execCb } from "child_process";
|
|
3814
|
+
import { createHash as createHash3 } from "crypto";
|
|
3815
|
+
import { readdir as readdir3, readFile as readFile6, stat as stat7 } from "fs/promises";
|
|
3816
|
+
import { join as join11, relative as relative3 } from "path";
|
|
3817
|
+
import { promisify } from "util";
|
|
3818
|
+
var exec = promisify(execCb);
|
|
3440
3819
|
async function detectManifest() {
|
|
3441
3820
|
const cwd = process.cwd();
|
|
3442
|
-
const pspmJsonPath =
|
|
3821
|
+
const pspmJsonPath = join11(cwd, "pspm.json");
|
|
3443
3822
|
try {
|
|
3444
|
-
const content = await
|
|
3823
|
+
const content = await readFile6(pspmJsonPath, "utf-8");
|
|
3445
3824
|
const manifest = JSON.parse(content);
|
|
3446
3825
|
return { type: "pspm.json", manifest, path: pspmJsonPath };
|
|
3447
3826
|
} catch {
|
|
3448
3827
|
}
|
|
3449
|
-
const packageJsonPath =
|
|
3828
|
+
const packageJsonPath = join11(cwd, "package.json");
|
|
3450
3829
|
try {
|
|
3451
|
-
const content = await
|
|
3830
|
+
const content = await readFile6(packageJsonPath, "utf-8");
|
|
3452
3831
|
const packageJson2 = JSON.parse(content);
|
|
3453
3832
|
const manifest = {
|
|
3454
3833
|
name: packageJson2.name,
|
|
@@ -3468,31 +3847,21 @@ function formatBytes(bytes) {
|
|
|
3468
3847
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}kB`;
|
|
3469
3848
|
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
3470
3849
|
}
|
|
3471
|
-
async function getFilesWithSizes(dir, baseDir
|
|
3850
|
+
async function getFilesWithSizes(dir, baseDir) {
|
|
3472
3851
|
const results = [];
|
|
3473
3852
|
try {
|
|
3474
|
-
const entries = await
|
|
3853
|
+
const entries = await readdir3(dir, { withFileTypes: true });
|
|
3475
3854
|
for (const entry of entries) {
|
|
3476
|
-
const fullPath =
|
|
3477
|
-
const relativePath =
|
|
3478
|
-
if (
|
|
3855
|
+
const fullPath = join11(dir, entry.name);
|
|
3856
|
+
const relativePath = relative3(baseDir, fullPath);
|
|
3857
|
+
if (entry.name === "node_modules" || entry.name === ".git") {
|
|
3479
3858
|
continue;
|
|
3480
3859
|
}
|
|
3481
|
-
if (ignoreResult?.ig) {
|
|
3482
|
-
const pathToCheck = entry.isDirectory() ? `${relativePath}/` : relativePath;
|
|
3483
|
-
if (ignoreResult.ig.ignores(pathToCheck)) {
|
|
3484
|
-
continue;
|
|
3485
|
-
}
|
|
3486
|
-
}
|
|
3487
3860
|
if (entry.isDirectory()) {
|
|
3488
|
-
const subFiles = await getFilesWithSizes(
|
|
3489
|
-
fullPath,
|
|
3490
|
-
baseDir,
|
|
3491
|
-
ignoreResult
|
|
3492
|
-
);
|
|
3861
|
+
const subFiles = await getFilesWithSizes(fullPath, baseDir);
|
|
3493
3862
|
results.push(...subFiles);
|
|
3494
3863
|
} else {
|
|
3495
|
-
const fileStat = await
|
|
3864
|
+
const fileStat = await stat7(fullPath);
|
|
3496
3865
|
results.push({ path: relativePath, size: fileStat.size });
|
|
3497
3866
|
}
|
|
3498
3867
|
}
|
|
@@ -3525,7 +3894,7 @@ async function publishCommand(options) {
|
|
|
3525
3894
|
files: manifest.files
|
|
3526
3895
|
};
|
|
3527
3896
|
if (options.bump) {
|
|
3528
|
-
const semver2 = await import(
|
|
3897
|
+
const semver2 = await import("semver");
|
|
3529
3898
|
const newVersion = semver2.default.inc(packageJson2.version, options.bump);
|
|
3530
3899
|
if (!newVersion) {
|
|
3531
3900
|
console.error(
|
|
@@ -3536,16 +3905,9 @@ async function publishCommand(options) {
|
|
|
3536
3905
|
packageJson2.version = newVersion;
|
|
3537
3906
|
console.log(`Bumped version to ${newVersion}`);
|
|
3538
3907
|
}
|
|
3539
|
-
const ignoreResult = await loadIgnorePatterns();
|
|
3540
|
-
if (ignoreResult.source) {
|
|
3541
|
-
console.log(
|
|
3542
|
-
`pspm notice Using ${ignoreResult.source} for ignore patterns`
|
|
3543
|
-
);
|
|
3544
|
-
}
|
|
3545
|
-
const excludeArgs = getExcludeArgsForRsync(ignoreResult.patterns);
|
|
3546
3908
|
const safeName = packageJson2.name.replace(/[@/]/g, "-").replace(/^-+/, "");
|
|
3547
3909
|
const tarballName = `${safeName}-${packageJson2.version}.tgz`;
|
|
3548
|
-
const tempDir =
|
|
3910
|
+
const tempDir = join11(process.cwd(), ".pspm-publish");
|
|
3549
3911
|
try {
|
|
3550
3912
|
await exec(`rm -rf "${tempDir}" && mkdir -p "${tempDir}"`);
|
|
3551
3913
|
const files = packageJson2.files || [...DEFAULT_SKILL_FILES];
|
|
@@ -3553,7 +3915,7 @@ async function publishCommand(options) {
|
|
|
3553
3915
|
for (const file of files) {
|
|
3554
3916
|
try {
|
|
3555
3917
|
await exec(
|
|
3556
|
-
`rsync -a
|
|
3918
|
+
`rsync -a --exclude='node_modules' --exclude='.git' "${file}" "${tempDir}/package/" 2>/dev/null || true`
|
|
3557
3919
|
);
|
|
3558
3920
|
} catch {
|
|
3559
3921
|
}
|
|
@@ -3561,7 +3923,7 @@ async function publishCommand(options) {
|
|
|
3561
3923
|
if (detection.type === "pspm.json") {
|
|
3562
3924
|
await exec(`cp pspm.json "${tempDir}/package/"`);
|
|
3563
3925
|
try {
|
|
3564
|
-
await
|
|
3926
|
+
await stat7(join11(process.cwd(), "package.json"));
|
|
3565
3927
|
await exec(
|
|
3566
3928
|
`cp package.json "${tempDir}/package/" 2>/dev/null || true`
|
|
3567
3929
|
);
|
|
@@ -3570,22 +3932,18 @@ async function publishCommand(options) {
|
|
|
3570
3932
|
} else {
|
|
3571
3933
|
await exec(`cp package.json "${tempDir}/package/"`);
|
|
3572
3934
|
}
|
|
3573
|
-
const packageDir =
|
|
3574
|
-
const tarballContents = await getFilesWithSizes(
|
|
3575
|
-
packageDir,
|
|
3576
|
-
packageDir,
|
|
3577
|
-
ignoreResult
|
|
3578
|
-
);
|
|
3935
|
+
const packageDir = join11(tempDir, "package");
|
|
3936
|
+
const tarballContents = await getFilesWithSizes(packageDir, packageDir);
|
|
3579
3937
|
const unpackedSize = tarballContents.reduce((acc, f) => acc + f.size, 0);
|
|
3580
|
-
const tarballPath =
|
|
3938
|
+
const tarballPath = join11(tempDir, tarballName);
|
|
3581
3939
|
await exec(
|
|
3582
|
-
`tar -czf "${tarballPath}" -C "${tempDir}"
|
|
3940
|
+
`tar -czf "${tarballPath}" -C "${tempDir}" --exclude='node_modules' --exclude='.git' package`
|
|
3583
3941
|
);
|
|
3584
|
-
const tarballBuffer = await
|
|
3942
|
+
const tarballBuffer = await readFile6(tarballPath);
|
|
3585
3943
|
const tarballBase64 = tarballBuffer.toString("base64");
|
|
3586
3944
|
const tarballSize = tarballBuffer.length;
|
|
3587
|
-
const shasum =
|
|
3588
|
-
const integrityHash =
|
|
3945
|
+
const shasum = createHash3("sha1").update(tarballBuffer).digest("hex");
|
|
3946
|
+
const integrityHash = createHash3("sha512").update(tarballBuffer).digest("base64");
|
|
3589
3947
|
const integrity = `sha512-${integrityHash}`;
|
|
3590
3948
|
console.log("");
|
|
3591
3949
|
console.log("pspm notice");
|
|
@@ -3670,12 +4028,16 @@ init_lib();
|
|
|
3670
4028
|
init_lockfile();
|
|
3671
4029
|
init_manifest2();
|
|
3672
4030
|
init_symlinks();
|
|
4031
|
+
import { rm as rm6 } from "fs/promises";
|
|
4032
|
+
import { join as join12 } from "path";
|
|
3673
4033
|
async function remove(nameOrSpecifier) {
|
|
3674
4034
|
try {
|
|
3675
4035
|
const manifest = await readManifest();
|
|
3676
4036
|
const agentConfigs = manifest?.agents;
|
|
3677
4037
|
const agents = getAvailableAgents(agentConfigs);
|
|
3678
|
-
if (
|
|
4038
|
+
if (isLocalSpecifier(nameOrSpecifier)) {
|
|
4039
|
+
await removeLocal(nameOrSpecifier, agents, agentConfigs);
|
|
4040
|
+
} else if (isGitHubSpecifier(nameOrSpecifier)) {
|
|
3679
4041
|
await removeGitHub(nameOrSpecifier, agents, agentConfigs);
|
|
3680
4042
|
} else if (nameOrSpecifier.startsWith("@user/")) {
|
|
3681
4043
|
await removeRegistry(nameOrSpecifier, agents, agentConfigs);
|
|
@@ -3710,9 +4072,9 @@ async function removeRegistry(specifier, agents, agentConfigs) {
|
|
|
3710
4072
|
agentConfigs
|
|
3711
4073
|
});
|
|
3712
4074
|
const skillsDir = getSkillsDir();
|
|
3713
|
-
const destDir =
|
|
4075
|
+
const destDir = join12(skillsDir, username, name);
|
|
3714
4076
|
try {
|
|
3715
|
-
await
|
|
4077
|
+
await rm6(destDir, { recursive: true, force: true });
|
|
3716
4078
|
} catch {
|
|
3717
4079
|
}
|
|
3718
4080
|
console.log(`Removed ${fullName}`);
|
|
@@ -3739,13 +4101,42 @@ async function removeGitHub(specifier, agents, agentConfigs) {
|
|
|
3739
4101
|
});
|
|
3740
4102
|
const skillsDir = getSkillsDir();
|
|
3741
4103
|
const destPath = getGitHubSkillPath(parsed.owner, parsed.repo, parsed.path);
|
|
3742
|
-
const destDir =
|
|
4104
|
+
const destDir = join12(skillsDir, "..", destPath);
|
|
3743
4105
|
try {
|
|
3744
|
-
await
|
|
4106
|
+
await rm6(destDir, { recursive: true, force: true });
|
|
3745
4107
|
} catch {
|
|
3746
4108
|
}
|
|
3747
4109
|
console.log(`Removed ${lockfileKey}`);
|
|
3748
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
|
+
}
|
|
3749
4140
|
async function removeByShortName(shortName, agents, agentConfigs) {
|
|
3750
4141
|
const registrySkills = await listLockfileSkills();
|
|
3751
4142
|
const foundRegistry = registrySkills.find((s) => {
|
|
@@ -3766,6 +4157,12 @@ async function removeByShortName(shortName, agents, agentConfigs) {
|
|
|
3766
4157
|
await removeGitHub(foundGitHub.specifier, agents, agentConfigs);
|
|
3767
4158
|
return;
|
|
3768
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
|
+
}
|
|
3769
4166
|
console.error(`Error: Skill "${shortName}" not found in lockfile`);
|
|
3770
4167
|
process.exit(1);
|
|
3771
4168
|
}
|
|
@@ -3938,9 +4335,9 @@ async function whoami() {
|
|
|
3938
4335
|
}
|
|
3939
4336
|
|
|
3940
4337
|
// src/index.ts
|
|
3941
|
-
var __dirname
|
|
4338
|
+
var __dirname = dirname6(fileURLToPath(import.meta.url));
|
|
3942
4339
|
var packageJson = JSON.parse(
|
|
3943
|
-
readFileSync(
|
|
4340
|
+
readFileSync(join13(__dirname, "..", "package.json"), "utf-8")
|
|
3944
4341
|
);
|
|
3945
4342
|
var version = packageJson.version;
|
|
3946
4343
|
var program = new Command();
|
|
@@ -4033,7 +4430,7 @@ program.command("unpublish <specifier>").description(
|
|
|
4033
4430
|
await unpublish(specifier, { force: options.force });
|
|
4034
4431
|
});
|
|
4035
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) => {
|
|
4036
|
-
await
|
|
4433
|
+
await access2(specifier, {
|
|
4037
4434
|
public: options.public,
|
|
4038
4435
|
private: options.private
|
|
4039
4436
|
});
|
|
@@ -4044,5 +4441,4 @@ program.command("deprecate <specifier> [message]").description(
|
|
|
4044
4441
|
await deprecate(specifier, message, { undo: options.undo });
|
|
4045
4442
|
});
|
|
4046
4443
|
program.parse();
|
|
4047
|
-
//# sourceMappingURL=index.js.map
|
|
4048
4444
|
//# sourceMappingURL=index.js.map
|