@promptscript/cli 1.13.1 → 1.13.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/bin/prs.js +3 -1
- package/index.js +486 -145
- package/package.json +2 -2
- package/src/commands/compile.d.ts.map +1 -1
- package/src/commands/hooks.d.ts +6 -0
- package/src/commands/hooks.d.ts.map +1 -1
- package/src/commands/registry/index.d.ts.map +1 -1
- package/src/commands/update.d.ts.map +1 -1
- package/src/types.d.ts +2 -0
- package/src/types.d.ts.map +1 -1
- package/src/utils/registry-scaffolder.d.ts +2 -0
- package/src/utils/registry-scaffolder.d.ts.map +1 -1
package/index.js
CHANGED
|
@@ -1415,6 +1415,12 @@ var init_mapper = __esm({
|
|
|
1415
1415
|
});
|
|
1416
1416
|
|
|
1417
1417
|
// packages/importer/src/emitter.ts
|
|
1418
|
+
function resolveQuoteDelimiter(content) {
|
|
1419
|
+
const matches = content.match(/"+/g);
|
|
1420
|
+
if (!matches) return '"""';
|
|
1421
|
+
const maxRun = matches.reduce((max, m) => Math.max(max, m.length), 0);
|
|
1422
|
+
return '"'.repeat(Math.max(3, maxRun + 1));
|
|
1423
|
+
}
|
|
1418
1424
|
function emitPrs(sections, options) {
|
|
1419
1425
|
const lines = [];
|
|
1420
1426
|
lines.push("@meta {");
|
|
@@ -1440,11 +1446,12 @@ function emitPrs(sections, options) {
|
|
|
1440
1446
|
lines.push(` ${entryName}: {`);
|
|
1441
1447
|
lines.push(` applyTo: [${applyToStr}]`);
|
|
1442
1448
|
lines.push(` description: "${description}"`);
|
|
1443
|
-
|
|
1449
|
+
const contentDelimiter2 = resolveQuoteDelimiter(section.content);
|
|
1450
|
+
lines.push(` content: ${contentDelimiter2}`);
|
|
1444
1451
|
for (const contentLine of section.content.split("\n")) {
|
|
1445
1452
|
lines.push(` ${contentLine}`);
|
|
1446
1453
|
}
|
|
1447
|
-
lines.push(`
|
|
1454
|
+
lines.push(` ${contentDelimiter2}`);
|
|
1448
1455
|
lines.push(` }`);
|
|
1449
1456
|
}
|
|
1450
1457
|
lines.push(`}`);
|
|
@@ -1453,11 +1460,12 @@ function emitPrs(sections, options) {
|
|
|
1453
1460
|
}
|
|
1454
1461
|
lines.push(`@${block} {`);
|
|
1455
1462
|
const mergedContent = blockSections.map((s) => s.content).join("\n\n");
|
|
1456
|
-
|
|
1463
|
+
const contentDelimiter = resolveQuoteDelimiter(mergedContent);
|
|
1464
|
+
lines.push(` ${contentDelimiter}`);
|
|
1457
1465
|
for (const contentLine of mergedContent.split("\n")) {
|
|
1458
1466
|
lines.push(` ${contentLine}`);
|
|
1459
1467
|
}
|
|
1460
|
-
lines.push(`
|
|
1468
|
+
lines.push(` ${contentDelimiter}`);
|
|
1461
1469
|
lines.push("}");
|
|
1462
1470
|
}
|
|
1463
1471
|
lines.push("");
|
|
@@ -1497,11 +1505,12 @@ function emitModularFiles(blocks, options) {
|
|
|
1497
1505
|
lines.push("# REVIEW: low confidence -- verify this mapping");
|
|
1498
1506
|
}
|
|
1499
1507
|
lines.push(`@${block.targetBlock} {`);
|
|
1500
|
-
|
|
1508
|
+
const blockDelimiter = resolveQuoteDelimiter(block.content);
|
|
1509
|
+
lines.push(` ${blockDelimiter}`);
|
|
1501
1510
|
for (const contentLine of block.content.split("\n")) {
|
|
1502
1511
|
lines.push(` ${contentLine}`);
|
|
1503
1512
|
}
|
|
1504
|
-
lines.push(
|
|
1513
|
+
lines.push(` ${blockDelimiter}`);
|
|
1505
1514
|
lines.push("}");
|
|
1506
1515
|
lines.push("");
|
|
1507
1516
|
}
|
|
@@ -1525,11 +1534,12 @@ function emitModularFiles(blocks, options) {
|
|
|
1525
1534
|
projectLines.push(comment);
|
|
1526
1535
|
}
|
|
1527
1536
|
projectLines.push("@identity {");
|
|
1528
|
-
|
|
1537
|
+
const identityDelimiter = resolveQuoteDelimiter(identity.content);
|
|
1538
|
+
projectLines.push(` ${identityDelimiter}`);
|
|
1529
1539
|
for (const line of identity.content.split("\n")) {
|
|
1530
1540
|
projectLines.push(` ${line}`);
|
|
1531
1541
|
}
|
|
1532
|
-
projectLines.push(
|
|
1542
|
+
projectLines.push(` ${identityDelimiter}`);
|
|
1533
1543
|
projectLines.push("}");
|
|
1534
1544
|
}
|
|
1535
1545
|
projectLines.push("");
|
|
@@ -8922,6 +8932,7 @@ var init_parser2 = __esm({
|
|
|
8922
8932
|
{ ALT: () => this.SUBRULE(this.block) }
|
|
8923
8933
|
])
|
|
8924
8934
|
);
|
|
8935
|
+
this.CONSUME(EOF);
|
|
8925
8936
|
});
|
|
8926
8937
|
/**
|
|
8927
8938
|
* metaBlock
|
|
@@ -10093,6 +10104,12 @@ ${s.content}`);
|
|
|
10093
10104
|
reviewComments: []
|
|
10094
10105
|
};
|
|
10095
10106
|
}
|
|
10107
|
+
function isStructuralMarker(line) {
|
|
10108
|
+
const trimmed = line.trim();
|
|
10109
|
+
if (/^```[^\s`]*$/.test(trimmed)) return true;
|
|
10110
|
+
if (/^(---|\*\*\*|___)$/.test(trimmed)) return true;
|
|
10111
|
+
return false;
|
|
10112
|
+
}
|
|
10096
10113
|
function mergeUnion(sections) {
|
|
10097
10114
|
const seenLines = /* @__PURE__ */ new Set();
|
|
10098
10115
|
const uniqueLines = [];
|
|
@@ -10102,6 +10119,10 @@ function mergeUnion(sections) {
|
|
|
10102
10119
|
for (const line of lines) {
|
|
10103
10120
|
const normalized = line.trim().replace(/\s+/g, " ");
|
|
10104
10121
|
if (normalized.length === 0) continue;
|
|
10122
|
+
if (isStructuralMarker(line)) {
|
|
10123
|
+
uniqueLines.push(line);
|
|
10124
|
+
continue;
|
|
10125
|
+
}
|
|
10105
10126
|
if (seenLines.has(normalized)) {
|
|
10106
10127
|
deduped++;
|
|
10107
10128
|
continue;
|
|
@@ -11783,11 +11804,18 @@ var init_base_formatter = __esm({
|
|
|
11783
11804
|
referenceProvenance(sourcePath) {
|
|
11784
11805
|
return `<!-- from: ${sourcePath} -->`;
|
|
11785
11806
|
}
|
|
11807
|
+
/**
|
|
11808
|
+
* Check if a name is safe for use in file paths.
|
|
11809
|
+
* Rejects path traversal sequences and path separators.
|
|
11810
|
+
*/
|
|
11811
|
+
isSafeName(name) {
|
|
11812
|
+
return !name.includes("..") && !name.includes("/") && !name.includes("\\");
|
|
11813
|
+
}
|
|
11786
11814
|
/**
|
|
11787
11815
|
* Check if a skill name is safe for use in file paths.
|
|
11788
11816
|
*/
|
|
11789
11817
|
isSafeSkillName(name) {
|
|
11790
|
-
return
|
|
11818
|
+
return this.isSafeName(name);
|
|
11791
11819
|
}
|
|
11792
11820
|
/**
|
|
11793
11821
|
* Render content inside a code fence, using a longer fence if the content
|
|
@@ -11961,9 +11989,11 @@ var init_markdown_instruction_formatter = __esm({
|
|
|
11961
11989
|
if ("type" in value && value["type"] === "TextContent") {
|
|
11962
11990
|
const content = this.valueToString(value);
|
|
11963
11991
|
if (content.includes("\n")) {
|
|
11992
|
+
const cmdName = name.replace(/^\/+/, "");
|
|
11993
|
+
if (!this.isSafeName(cmdName)) continue;
|
|
11964
11994
|
commands.push({
|
|
11965
|
-
name:
|
|
11966
|
-
description:
|
|
11995
|
+
name: cmdName,
|
|
11996
|
+
description: cmdName,
|
|
11967
11997
|
content
|
|
11968
11998
|
});
|
|
11969
11999
|
}
|
|
@@ -11971,8 +12001,10 @@ var init_markdown_instruction_formatter = __esm({
|
|
|
11971
12001
|
}
|
|
11972
12002
|
const obj = value;
|
|
11973
12003
|
if (obj["prompt"] === true || obj["content"]) {
|
|
12004
|
+
const cmdName = name.replace(/^\/+/, "");
|
|
12005
|
+
if (!this.isSafeName(cmdName)) continue;
|
|
11974
12006
|
commands.push({
|
|
11975
|
-
name:
|
|
12007
|
+
name: cmdName,
|
|
11976
12008
|
description: obj["description"] ? this.valueToString(obj["description"]) : name,
|
|
11977
12009
|
argumentHint: obj["argumentHint"] ? this.valueToString(obj["argumentHint"]) : void 0,
|
|
11978
12010
|
content: obj["content"] ? this.valueToString(obj["content"]) : ""
|
|
@@ -18401,11 +18433,21 @@ function detectTools() {
|
|
|
18401
18433
|
);
|
|
18402
18434
|
}
|
|
18403
18435
|
async function readSettingsFile(path) {
|
|
18436
|
+
let content;
|
|
18437
|
+
try {
|
|
18438
|
+
content = await readFile4(path, "utf-8");
|
|
18439
|
+
} catch (err) {
|
|
18440
|
+
const nodeErr = err;
|
|
18441
|
+
if (nodeErr.code === "ENOENT") {
|
|
18442
|
+
return {};
|
|
18443
|
+
}
|
|
18444
|
+
throw err;
|
|
18445
|
+
}
|
|
18404
18446
|
try {
|
|
18405
|
-
const content = await readFile4(path, "utf-8");
|
|
18406
18447
|
return JSON.parse(content);
|
|
18407
|
-
} catch {
|
|
18408
|
-
|
|
18448
|
+
} catch (err) {
|
|
18449
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
18450
|
+
throw new Error(`Failed to parse settings file ${path}: ${message}`, { cause: err });
|
|
18409
18451
|
}
|
|
18410
18452
|
}
|
|
18411
18453
|
async function writeSettingsFile(path, data) {
|
|
@@ -20882,9 +20924,9 @@ var require_rate_limit = __commonJS({
|
|
|
20882
20924
|
let ttl = 0;
|
|
20883
20925
|
let ttlInSeconds = 0;
|
|
20884
20926
|
try {
|
|
20885
|
-
const res = await new Promise((
|
|
20927
|
+
const res = await new Promise((resolve24, reject) => {
|
|
20886
20928
|
store.incr(key, (err, res2) => {
|
|
20887
|
-
err ? reject(err) :
|
|
20929
|
+
err ? reject(err) : resolve24(res2);
|
|
20888
20930
|
}, timeWindow, max);
|
|
20889
20931
|
});
|
|
20890
20932
|
current = res.current;
|
|
@@ -21053,17 +21095,43 @@ var init_config2 = __esm({
|
|
|
21053
21095
|
});
|
|
21054
21096
|
|
|
21055
21097
|
// packages/server/src/path-guard.ts
|
|
21056
|
-
import { resolve as
|
|
21057
|
-
|
|
21098
|
+
import { resolve as resolve23, relative as relative6, isAbsolute as isAbsolute6, dirname as dirname15, basename as basename16 } from "path";
|
|
21099
|
+
import { realpath as realpath2 } from "fs/promises";
|
|
21100
|
+
async function resolveSafePath(workspace, requestedPath) {
|
|
21058
21101
|
const decoded = decodeURIComponent(requestedPath);
|
|
21059
21102
|
if (isAbsolute6(decoded)) {
|
|
21060
21103
|
throw new PathTraversalError(requestedPath);
|
|
21061
21104
|
}
|
|
21062
|
-
const resolved =
|
|
21063
|
-
const rel =
|
|
21105
|
+
const resolved = resolve23(workspace, decoded);
|
|
21106
|
+
const rel = relative6(workspace, resolved);
|
|
21064
21107
|
if (rel.startsWith("..") || isAbsolute6(rel)) {
|
|
21065
21108
|
throw new PathTraversalError(requestedPath);
|
|
21066
21109
|
}
|
|
21110
|
+
let realWorkspace;
|
|
21111
|
+
try {
|
|
21112
|
+
realWorkspace = await realpath2(workspace);
|
|
21113
|
+
} catch {
|
|
21114
|
+
realWorkspace = workspace;
|
|
21115
|
+
}
|
|
21116
|
+
let real;
|
|
21117
|
+
try {
|
|
21118
|
+
real = await realpath2(resolved);
|
|
21119
|
+
} catch (err) {
|
|
21120
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
21121
|
+
try {
|
|
21122
|
+
const realParent = await realpath2(dirname15(resolved));
|
|
21123
|
+
real = resolve23(realParent, basename16(resolved));
|
|
21124
|
+
} catch {
|
|
21125
|
+
return resolved;
|
|
21126
|
+
}
|
|
21127
|
+
} else {
|
|
21128
|
+
throw err;
|
|
21129
|
+
}
|
|
21130
|
+
}
|
|
21131
|
+
const realRel = relative6(realWorkspace, real);
|
|
21132
|
+
if (realRel.startsWith("..") || isAbsolute6(realRel)) {
|
|
21133
|
+
throw new PathTraversalError(requestedPath);
|
|
21134
|
+
}
|
|
21067
21135
|
return resolved;
|
|
21068
21136
|
}
|
|
21069
21137
|
var PathTraversalError;
|
|
@@ -21081,7 +21149,7 @@ var init_path_guard = __esm({
|
|
|
21081
21149
|
|
|
21082
21150
|
// packages/server/src/routes/files.ts
|
|
21083
21151
|
import { readFile as readFile25, writeFile as writeFile13, unlink as unlink2, stat as stat3, mkdir as mkdir10 } from "fs/promises";
|
|
21084
|
-
import { dirname as
|
|
21152
|
+
import { dirname as dirname16 } from "path";
|
|
21085
21153
|
import fg from "fast-glob";
|
|
21086
21154
|
function registerRoutes(app, workspace, readOnly) {
|
|
21087
21155
|
app.get("/api/files", async () => {
|
|
@@ -21100,7 +21168,7 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
21100
21168
|
app.get("/api/files/*", async (request, reply) => {
|
|
21101
21169
|
const filePath = request.params["*"];
|
|
21102
21170
|
try {
|
|
21103
|
-
const resolved = resolveSafePath(workspace, filePath);
|
|
21171
|
+
const resolved = await resolveSafePath(workspace, filePath);
|
|
21104
21172
|
const content = await readFile25(resolved, "utf-8");
|
|
21105
21173
|
return { path: filePath, content };
|
|
21106
21174
|
} catch (err) {
|
|
@@ -21117,7 +21185,7 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
21117
21185
|
if (readOnly) return reply.status(403).send({ error: "Read-only mode" });
|
|
21118
21186
|
const filePath = request.params["*"];
|
|
21119
21187
|
try {
|
|
21120
|
-
const resolved = resolveSafePath(workspace, filePath);
|
|
21188
|
+
const resolved = await resolveSafePath(workspace, filePath);
|
|
21121
21189
|
await writeFile13(resolved, request.body.content, "utf-8");
|
|
21122
21190
|
return { path: filePath, status: "updated" };
|
|
21123
21191
|
} catch (err) {
|
|
@@ -21135,13 +21203,13 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
21135
21203
|
if (readOnly) return reply.status(403).send({ error: "Read-only mode" });
|
|
21136
21204
|
const filePath = request.params["*"];
|
|
21137
21205
|
try {
|
|
21138
|
-
const resolved = resolveSafePath(workspace, filePath);
|
|
21206
|
+
const resolved = await resolveSafePath(workspace, filePath);
|
|
21139
21207
|
try {
|
|
21140
21208
|
await stat3(resolved);
|
|
21141
21209
|
return reply.status(409).send({ error: "File already exists" });
|
|
21142
21210
|
} catch {
|
|
21143
21211
|
}
|
|
21144
|
-
await mkdir10(
|
|
21212
|
+
await mkdir10(dirname16(resolved), { recursive: true });
|
|
21145
21213
|
await writeFile13(resolved, request.body.content, "utf-8");
|
|
21146
21214
|
return reply.status(201).send({ path: filePath, status: "created" });
|
|
21147
21215
|
} catch (err) {
|
|
@@ -21156,7 +21224,7 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
21156
21224
|
if (readOnly) return reply.status(403).send({ error: "Read-only mode" });
|
|
21157
21225
|
const filePath = request.params["*"];
|
|
21158
21226
|
try {
|
|
21159
|
-
const resolved = resolveSafePath(workspace, filePath);
|
|
21227
|
+
const resolved = await resolveSafePath(workspace, filePath);
|
|
21160
21228
|
await unlink2(resolved);
|
|
21161
21229
|
return { path: filePath, status: "deleted" };
|
|
21162
21230
|
} catch (err) {
|
|
@@ -21179,7 +21247,7 @@ var init_files = __esm({
|
|
|
21179
21247
|
|
|
21180
21248
|
// packages/server/src/watcher.ts
|
|
21181
21249
|
import { watch } from "chokidar";
|
|
21182
|
-
import { relative as
|
|
21250
|
+
import { relative as relative7 } from "path";
|
|
21183
21251
|
function isWatchedFile(filePath) {
|
|
21184
21252
|
const base = filePath.split("/").pop() ?? "";
|
|
21185
21253
|
if (CONFIG_FILENAMES2.has(base)) return true;
|
|
@@ -21195,15 +21263,15 @@ function createFileWatcher(workspace, watchPaths, onEvent) {
|
|
|
21195
21263
|
});
|
|
21196
21264
|
watcher.on("add", (filePath) => {
|
|
21197
21265
|
if (!ready || !isWatchedFile(filePath)) return;
|
|
21198
|
-
onEvent({ type: "file:created", path:
|
|
21266
|
+
onEvent({ type: "file:created", path: relative7(workspace, filePath) });
|
|
21199
21267
|
});
|
|
21200
21268
|
watcher.on("change", (filePath) => {
|
|
21201
21269
|
if (!ready || !isWatchedFile(filePath)) return;
|
|
21202
|
-
onEvent({ type: "file:changed", path:
|
|
21270
|
+
onEvent({ type: "file:changed", path: relative7(workspace, filePath) });
|
|
21203
21271
|
});
|
|
21204
21272
|
watcher.on("unlink", (filePath) => {
|
|
21205
21273
|
if (!ready || !isWatchedFile(filePath)) return;
|
|
21206
|
-
onEvent({ type: "file:deleted", path:
|
|
21274
|
+
onEvent({ type: "file:deleted", path: relative7(workspace, filePath) });
|
|
21207
21275
|
});
|
|
21208
21276
|
return watcher;
|
|
21209
21277
|
}
|
|
@@ -21225,7 +21293,20 @@ async function createServer(options) {
|
|
|
21225
21293
|
origin: options.corsOrigin
|
|
21226
21294
|
});
|
|
21227
21295
|
await app.register(import_rate_limit.default, { max: 1e3, timeWindow: "1 minute" });
|
|
21228
|
-
await app.register(fastifyWebsocket
|
|
21296
|
+
await app.register(fastifyWebsocket, {
|
|
21297
|
+
options: {
|
|
21298
|
+
// Validate Origin header to prevent cross-origin WebSocket connections.
|
|
21299
|
+
// Browser WebSockets don't enforce CORS, so we must check server-side.
|
|
21300
|
+
verifyClient: (info, callback) => {
|
|
21301
|
+
const origin = info.req.headers.origin;
|
|
21302
|
+
if (origin && origin !== options.corsOrigin && options.corsOrigin !== "*") {
|
|
21303
|
+
callback(false, 403, "Origin not allowed");
|
|
21304
|
+
return;
|
|
21305
|
+
}
|
|
21306
|
+
callback(true);
|
|
21307
|
+
}
|
|
21308
|
+
}
|
|
21309
|
+
});
|
|
21229
21310
|
registerHealthRoute(app);
|
|
21230
21311
|
registerConfigRoute(
|
|
21231
21312
|
app,
|
|
@@ -21366,12 +21447,12 @@ var init_migrate = __esm({
|
|
|
21366
21447
|
init_src();
|
|
21367
21448
|
init_init();
|
|
21368
21449
|
import { Command } from "commander";
|
|
21369
|
-
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
21370
|
-
import { dirname as
|
|
21450
|
+
import { fileURLToPath as fileURLToPath4, pathToFileURL } from "url";
|
|
21451
|
+
import { dirname as dirname17 } from "path";
|
|
21371
21452
|
|
|
21372
21453
|
// packages/cli/src/commands/compile.ts
|
|
21373
21454
|
init_src();
|
|
21374
|
-
import { resolve as
|
|
21455
|
+
import { resolve as resolve10, dirname as dirname9, isAbsolute as isAbsolute5 } from "path";
|
|
21375
21456
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
21376
21457
|
import { writeFile as writeFile4, mkdir as mkdir4, readFile as readFile12 } from "fs/promises";
|
|
21377
21458
|
import { existsSync as existsSync12 } from "fs";
|
|
@@ -21383,6 +21464,68 @@ init_env_config();
|
|
|
21383
21464
|
import { readFile as readFile6 } from "fs/promises";
|
|
21384
21465
|
import { existsSync as existsSync6 } from "fs";
|
|
21385
21466
|
import { parse as parseYaml4 } from "yaml";
|
|
21467
|
+
|
|
21468
|
+
// packages/cli/src/config/merge-config.ts
|
|
21469
|
+
function deepMergeRegistry(target, source) {
|
|
21470
|
+
if (!source) return target;
|
|
21471
|
+
if (!target) return source;
|
|
21472
|
+
const merged = { ...target };
|
|
21473
|
+
if (source.path !== void 0) merged.path = source.path;
|
|
21474
|
+
if (source.url !== void 0) merged.url = source.url;
|
|
21475
|
+
if (source.git) {
|
|
21476
|
+
const mergedAuth = source.git.auth ?? merged.git?.auth;
|
|
21477
|
+
merged.git = {
|
|
21478
|
+
...merged.git,
|
|
21479
|
+
...source.git,
|
|
21480
|
+
...mergedAuth ? { auth: mergedAuth } : {}
|
|
21481
|
+
};
|
|
21482
|
+
}
|
|
21483
|
+
if (source.cache) {
|
|
21484
|
+
merged.cache = { ...merged.cache, ...source.cache };
|
|
21485
|
+
}
|
|
21486
|
+
if (source.auth) {
|
|
21487
|
+
merged.auth = { ...merged.auth, ...source.auth };
|
|
21488
|
+
}
|
|
21489
|
+
return merged;
|
|
21490
|
+
}
|
|
21491
|
+
function userConfigToProjectRegistry(userRegistry) {
|
|
21492
|
+
const registry2 = {};
|
|
21493
|
+
if (userRegistry.git) {
|
|
21494
|
+
registry2.git = {
|
|
21495
|
+
url: userRegistry.git.url,
|
|
21496
|
+
...userRegistry.git.fallbackUrl ? { fallbackUrl: userRegistry.git.fallbackUrl } : {},
|
|
21497
|
+
...userRegistry.git.ref ? { ref: userRegistry.git.ref } : {},
|
|
21498
|
+
...userRegistry.git.path ? { path: userRegistry.git.path } : {},
|
|
21499
|
+
...userRegistry.git.auth ? { auth: userRegistry.git.auth } : {}
|
|
21500
|
+
};
|
|
21501
|
+
}
|
|
21502
|
+
if (userRegistry.url) {
|
|
21503
|
+
registry2.url = userRegistry.url;
|
|
21504
|
+
}
|
|
21505
|
+
if (userRegistry.cache) {
|
|
21506
|
+
registry2.cache = { ...userRegistry.cache };
|
|
21507
|
+
}
|
|
21508
|
+
return registry2;
|
|
21509
|
+
}
|
|
21510
|
+
function mergeConfigs(userConfig, projectConfig, envOverrides, cliFlags) {
|
|
21511
|
+
const merged = { ...projectConfig };
|
|
21512
|
+
if (userConfig.registry) {
|
|
21513
|
+
const userRegistry = userConfigToProjectRegistry(userConfig.registry);
|
|
21514
|
+
merged.registry = deepMergeRegistry(userRegistry, merged.registry);
|
|
21515
|
+
}
|
|
21516
|
+
if (envOverrides.registry) {
|
|
21517
|
+
merged.registry = deepMergeRegistry(merged.registry, envOverrides.registry);
|
|
21518
|
+
}
|
|
21519
|
+
if (cliFlags?.registry) {
|
|
21520
|
+
merged.registry = deepMergeRegistry(merged.registry, cliFlags.registry);
|
|
21521
|
+
}
|
|
21522
|
+
if (cliFlags?.targets) {
|
|
21523
|
+
merged.targets = cliFlags.targets;
|
|
21524
|
+
}
|
|
21525
|
+
return merged;
|
|
21526
|
+
}
|
|
21527
|
+
|
|
21528
|
+
// packages/cli/src/config/loader.ts
|
|
21386
21529
|
var CONFIG_FILES = [
|
|
21387
21530
|
"promptscript.yaml",
|
|
21388
21531
|
"promptscript.yml",
|
|
@@ -21435,6 +21578,12 @@ async function loadConfig(customPath) {
|
|
|
21435
21578
|
throw new Error(`Failed to parse ${configFile}: ${message}`, { cause: error });
|
|
21436
21579
|
}
|
|
21437
21580
|
}
|
|
21581
|
+
async function loadEffectiveConfig(customPath, cliFlags) {
|
|
21582
|
+
const userConfig = await loadUserConfig();
|
|
21583
|
+
const projectConfig = await loadConfig(customPath);
|
|
21584
|
+
const envOverrides = loadEnvOverrides();
|
|
21585
|
+
return mergeConfigs(userConfig, projectConfig, envOverrides, cliFlags);
|
|
21586
|
+
}
|
|
21438
21587
|
|
|
21439
21588
|
// packages/cli/src/commands/compile.ts
|
|
21440
21589
|
init_loader();
|
|
@@ -21498,8 +21647,8 @@ init_src4();
|
|
|
21498
21647
|
init_src2();
|
|
21499
21648
|
init_src();
|
|
21500
21649
|
import { existsSync as existsSync9 } from "fs";
|
|
21501
|
-
import { lstat as
|
|
21502
|
-
import { basename as basename11, dirname as dirname7, join as join8 } from "path";
|
|
21650
|
+
import { lstat as lstat4, readdir as readdir4, readFile as readFile11 } from "fs/promises";
|
|
21651
|
+
import { basename as basename11, dirname as dirname7, join as join8, relative as relative4, resolve as resolve9 } from "path";
|
|
21503
21652
|
|
|
21504
21653
|
// packages/resolver/src/loader.ts
|
|
21505
21654
|
init_src();
|
|
@@ -23042,10 +23191,10 @@ function parseSkillIgnore(content) {
|
|
|
23042
23191
|
return { patterns };
|
|
23043
23192
|
}
|
|
23044
23193
|
function isIgnoredByRules(relPath, rules) {
|
|
23045
|
-
const
|
|
23194
|
+
const basename17 = relPath.split("/").pop() ?? relPath;
|
|
23046
23195
|
let ignored = false;
|
|
23047
23196
|
for (const { regex, matchPath, negated } of rules.patterns) {
|
|
23048
|
-
const target = matchPath ? relPath :
|
|
23197
|
+
const target = matchPath ? relPath : basename17;
|
|
23049
23198
|
if (regex.test(target)) {
|
|
23050
23199
|
ignored = !negated;
|
|
23051
23200
|
}
|
|
@@ -24605,6 +24754,27 @@ var GitRegistry = class {
|
|
|
24605
24754
|
await this.cacheManager.remove(this.url, targetRef);
|
|
24606
24755
|
await this.ensureCloned(targetRef);
|
|
24607
24756
|
}
|
|
24757
|
+
/**
|
|
24758
|
+
* Checkout a specific commit in an already-cloned repository.
|
|
24759
|
+
* Used to enforce lockfile pinning: after cloning at a tag/branch,
|
|
24760
|
+
* checkout the exact commit recorded in the lockfile.
|
|
24761
|
+
*
|
|
24762
|
+
* @param targetDir - Path to the cloned repository
|
|
24763
|
+
* @param commit - Commit hash to checkout
|
|
24764
|
+
*/
|
|
24765
|
+
async checkoutCommit(targetDir, commit) {
|
|
24766
|
+
const git = this.createGit(targetDir);
|
|
24767
|
+
try {
|
|
24768
|
+
await git.fetch(["origin", commit, "--depth=1"]);
|
|
24769
|
+
} catch {
|
|
24770
|
+
try {
|
|
24771
|
+
await git.fetch(["--unshallow"]);
|
|
24772
|
+
} catch {
|
|
24773
|
+
await git.fetch(["origin"]);
|
|
24774
|
+
}
|
|
24775
|
+
}
|
|
24776
|
+
await git.checkout(commit);
|
|
24777
|
+
}
|
|
24608
24778
|
/**
|
|
24609
24779
|
* Get the current commit hash for a ref.
|
|
24610
24780
|
*
|
|
@@ -25134,8 +25304,16 @@ async function validateRemoteAccess(repoUrl) {
|
|
|
25134
25304
|
}
|
|
25135
25305
|
|
|
25136
25306
|
// packages/resolver/src/registry-cache.ts
|
|
25137
|
-
import { mkdir as mkdir3, stat, writeFile as writeFile3, readFile as readFile9 } from "fs/promises";
|
|
25138
|
-
import { join as join7 } from "path";
|
|
25307
|
+
import { mkdir as mkdir3, stat, writeFile as writeFile3, readFile as readFile9, lstat as lstat2 } from "fs/promises";
|
|
25308
|
+
import { join as join7, resolve as resolve7, relative as relative3 } from "path";
|
|
25309
|
+
function validatePathComponent(component, label) {
|
|
25310
|
+
if (component.includes("..")) {
|
|
25311
|
+
throw new Error(`Invalid ${label}: contains parent directory traversal (..)`);
|
|
25312
|
+
}
|
|
25313
|
+
if (component.startsWith("/")) {
|
|
25314
|
+
throw new Error(`Invalid ${label}: absolute paths are not allowed`);
|
|
25315
|
+
}
|
|
25316
|
+
}
|
|
25139
25317
|
var RegistryCache = class {
|
|
25140
25318
|
constructor(baseDir) {
|
|
25141
25319
|
this.baseDir = baseDir;
|
|
@@ -25143,7 +25321,13 @@ var RegistryCache = class {
|
|
|
25143
25321
|
baseDir;
|
|
25144
25322
|
getCachePath(repoUrl, version) {
|
|
25145
25323
|
const normalized = repoUrl.replace(/^(https?:\/\/|git@|git:\/\/)/, "").replace(":", "/");
|
|
25146
|
-
|
|
25324
|
+
validatePathComponent(version, "version");
|
|
25325
|
+
const result = join7(this.baseDir, "registries", normalized, version);
|
|
25326
|
+
const rel = relative3(resolve7(this.baseDir), resolve7(result));
|
|
25327
|
+
if (rel.startsWith("..")) {
|
|
25328
|
+
throw new Error(`Path traversal detected: version "${version}" escapes cache root`);
|
|
25329
|
+
}
|
|
25330
|
+
return result;
|
|
25147
25331
|
}
|
|
25148
25332
|
async has(repoUrl, version) {
|
|
25149
25333
|
try {
|
|
@@ -25157,8 +25341,19 @@ var RegistryCache = class {
|
|
|
25157
25341
|
async set(repoUrl, version, commit) {
|
|
25158
25342
|
const cachePath = this.getCachePath(repoUrl, version);
|
|
25159
25343
|
await mkdir3(cachePath, { recursive: true });
|
|
25344
|
+
const metaPath = join7(cachePath, ".prs-registry-meta.json");
|
|
25345
|
+
try {
|
|
25346
|
+
const statResult = await lstat2(metaPath);
|
|
25347
|
+
if (statResult.isSymbolicLink()) {
|
|
25348
|
+
throw new Error(
|
|
25349
|
+
`Refusing to write metadata: ${metaPath} is a symlink (possible supply-chain attack)`
|
|
25350
|
+
);
|
|
25351
|
+
}
|
|
25352
|
+
} catch (err) {
|
|
25353
|
+
if (err.code !== "ENOENT") throw err;
|
|
25354
|
+
}
|
|
25160
25355
|
const meta = { commit, cachedAt: Date.now() };
|
|
25161
|
-
await writeFile3(
|
|
25356
|
+
await writeFile3(metaPath, JSON.stringify(meta, null, 2));
|
|
25162
25357
|
return cachePath;
|
|
25163
25358
|
}
|
|
25164
25359
|
async isStale(repoUrl, version, ttlMs) {
|
|
@@ -25207,9 +25402,16 @@ var RegistryCache = class {
|
|
|
25207
25402
|
}
|
|
25208
25403
|
};
|
|
25209
25404
|
|
|
25405
|
+
// packages/resolver/src/reference-hasher.ts
|
|
25406
|
+
import { createHash as createHash2 } from "crypto";
|
|
25407
|
+
function hashContent(content) {
|
|
25408
|
+
const hex = createHash2("sha256").update(content).digest("hex");
|
|
25409
|
+
return `sha256-${hex}`;
|
|
25410
|
+
}
|
|
25411
|
+
|
|
25210
25412
|
// packages/resolver/src/auto-discovery.ts
|
|
25211
|
-
import { readFile as readFile10, readdir as readdir3, access as access2, lstat as
|
|
25212
|
-
import { resolve as
|
|
25413
|
+
import { readFile as readFile10, readdir as readdir3, access as access2, lstat as lstat3 } from "fs/promises";
|
|
25414
|
+
import { resolve as resolve8, basename as basename10 } from "path";
|
|
25213
25415
|
var CONTEXT_FILES = ["CLAUDE.md", ".clinerules", ".cursorrules"];
|
|
25214
25416
|
async function fileExists2(path) {
|
|
25215
25417
|
try {
|
|
@@ -25245,9 +25447,9 @@ async function discoverSkills(dir) {
|
|
|
25245
25447
|
const properties = {};
|
|
25246
25448
|
for (const entry of entries) {
|
|
25247
25449
|
if (!entry.isDirectory()) continue;
|
|
25248
|
-
let skillMdPath =
|
|
25450
|
+
let skillMdPath = resolve8(dir, entry.name, "SKILL.md");
|
|
25249
25451
|
if (!await fileExists2(skillMdPath)) {
|
|
25250
|
-
const dirnameMdPath =
|
|
25452
|
+
const dirnameMdPath = resolve8(dir, entry.name, `${entry.name}.md`);
|
|
25251
25453
|
if (await fileExists2(dirnameMdPath)) {
|
|
25252
25454
|
skillMdPath = dirnameMdPath;
|
|
25253
25455
|
} else {
|
|
@@ -25274,7 +25476,7 @@ async function discoverSkills(dir) {
|
|
|
25274
25476
|
return Object.keys(properties).length > 0 ? properties : null;
|
|
25275
25477
|
}
|
|
25276
25478
|
async function discoverRootSkill(dir) {
|
|
25277
|
-
const skillMdPath =
|
|
25479
|
+
const skillMdPath = resolve8(dir, "SKILL.md");
|
|
25278
25480
|
if (!await fileExists2(skillMdPath)) return null;
|
|
25279
25481
|
try {
|
|
25280
25482
|
const raw = await readFile10(skillMdPath, "utf-8");
|
|
@@ -25305,9 +25507,9 @@ async function discoverAgents(dir) {
|
|
|
25305
25507
|
const properties = {};
|
|
25306
25508
|
for (const entry of entries) {
|
|
25307
25509
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
25308
|
-
const fullPath =
|
|
25510
|
+
const fullPath = resolve8(dir, entry.name);
|
|
25309
25511
|
try {
|
|
25310
|
-
const stat4 = await
|
|
25512
|
+
const stat4 = await lstat3(fullPath);
|
|
25311
25513
|
if (stat4.isSymbolicLink()) continue;
|
|
25312
25514
|
const raw = await readFile10(fullPath, "utf-8");
|
|
25313
25515
|
const fm = parseFrontmatter(raw);
|
|
@@ -25341,9 +25543,9 @@ async function discoverCommands(dir) {
|
|
|
25341
25543
|
for (const entry of entries) {
|
|
25342
25544
|
if (!entry.isFile() || !entry.name.endsWith(".md")) continue;
|
|
25343
25545
|
if (entry.name === "SKILL.md") continue;
|
|
25344
|
-
const fullPath =
|
|
25546
|
+
const fullPath = resolve8(dir, entry.name);
|
|
25345
25547
|
try {
|
|
25346
|
-
const stat4 = await
|
|
25548
|
+
const stat4 = await lstat3(fullPath);
|
|
25347
25549
|
if (stat4.isSymbolicLink()) continue;
|
|
25348
25550
|
const raw = await readFile10(fullPath, "utf-8");
|
|
25349
25551
|
const fm = parseFrontmatter(raw);
|
|
@@ -25358,7 +25560,7 @@ async function discoverCommands(dir) {
|
|
|
25358
25560
|
}
|
|
25359
25561
|
async function discoverContext(dir) {
|
|
25360
25562
|
for (const fileName of CONTEXT_FILES) {
|
|
25361
|
-
const fullPath =
|
|
25563
|
+
const fullPath = resolve8(dir, fileName);
|
|
25362
25564
|
if (!await fileExists2(fullPath)) continue;
|
|
25363
25565
|
try {
|
|
25364
25566
|
const content = await readFile10(fullPath, "utf-8");
|
|
@@ -25375,7 +25577,7 @@ var AGENT_WRAPPER_DIRS = ["agents"];
|
|
|
25375
25577
|
var COMMAND_WRAPPER_DIRS = ["commands"];
|
|
25376
25578
|
async function safeIsDirectory(dir) {
|
|
25377
25579
|
try {
|
|
25378
|
-
const stat4 = await
|
|
25580
|
+
const stat4 = await lstat3(dir);
|
|
25379
25581
|
return stat4.isDirectory();
|
|
25380
25582
|
} catch {
|
|
25381
25583
|
return false;
|
|
@@ -25388,7 +25590,7 @@ async function discoverNativeContent(dir) {
|
|
|
25388
25590
|
const subSkills = await discoverSkills(dir);
|
|
25389
25591
|
const wrappedSkillResults = await Promise.all(
|
|
25390
25592
|
SKILL_WRAPPER_DIRS.map(async (name) => {
|
|
25391
|
-
const candidate =
|
|
25593
|
+
const candidate = resolve8(dir, name);
|
|
25392
25594
|
if (!await safeIsDirectory(candidate)) return null;
|
|
25393
25595
|
return discoverSkills(candidate);
|
|
25394
25596
|
})
|
|
@@ -25409,7 +25611,7 @@ async function discoverNativeContent(dir) {
|
|
|
25409
25611
|
const rootAgents = await discoverAgents(dir) ?? {};
|
|
25410
25612
|
const wrappedAgents = {};
|
|
25411
25613
|
for (const name of AGENT_WRAPPER_DIRS) {
|
|
25412
|
-
const candidate =
|
|
25614
|
+
const candidate = resolve8(dir, name);
|
|
25413
25615
|
if (await safeIsDirectory(candidate)) {
|
|
25414
25616
|
const found = await discoverAgents(candidate);
|
|
25415
25617
|
if (found) Object.assign(wrappedAgents, found);
|
|
@@ -25422,7 +25624,7 @@ async function discoverNativeContent(dir) {
|
|
|
25422
25624
|
const rootCommands = await discoverCommands(dir) ?? {};
|
|
25423
25625
|
const wrappedCommands = {};
|
|
25424
25626
|
for (const name of COMMAND_WRAPPER_DIRS) {
|
|
25425
|
-
const candidate =
|
|
25627
|
+
const candidate = resolve8(dir, name);
|
|
25426
25628
|
if (await safeIsDirectory(candidate)) {
|
|
25427
25629
|
const found = await discoverCommands(candidate);
|
|
25428
25630
|
if (found) Object.assign(wrappedCommands, found);
|
|
@@ -25889,23 +26091,56 @@ var Resolver = class {
|
|
|
25889
26091
|
const lockEntry = this.options.lockfile?.dependencies[repoUrl];
|
|
25890
26092
|
const pinnedTag = lockEntry?.version ?? (version || void 0);
|
|
25891
26093
|
const tag = pinnedTag ?? "main";
|
|
26094
|
+
const lockedCommit = lockEntry?.commit && /^[0-9a-f]{40}$/.test(lockEntry.commit) && !/^0{40}$/.test(lockEntry.commit) ? lockEntry.commit : null;
|
|
25892
26095
|
const hasCached = await this.registryCache.has(repoUrl, effectiveVersion);
|
|
25893
26096
|
let cachePath;
|
|
25894
26097
|
if (hasCached) {
|
|
25895
26098
|
this.logger.debug(`Registry cache hit: ${repoUrl}@${effectiveVersion}`);
|
|
25896
26099
|
cachePath = this.registryCache.getCachePath(repoUrl, effectiveVersion);
|
|
26100
|
+
if (lockedCommit) {
|
|
26101
|
+
try {
|
|
26102
|
+
const cachedMeta = await this.registryCache.getMeta(repoUrl, effectiveVersion);
|
|
26103
|
+
if (cachedMeta && cachedMeta.commit !== lockedCommit) {
|
|
26104
|
+
this.logger.verbose(
|
|
26105
|
+
`Lockfile commit mismatch for ${repoUrl}: cached=${cachedMeta.commit}, locked=${lockedCommit}. Re-cloning.`
|
|
26106
|
+
);
|
|
26107
|
+
const fallbackRepoUrl = this.options.registries ? findFallbackUrl(repoUrl, this.options.registries) : void 0;
|
|
26108
|
+
await this.gitRegistry.cloneAtTag(repoUrl, tag, cachePath, fallbackRepoUrl);
|
|
26109
|
+
await this.gitRegistry.checkoutCommit(cachePath, lockedCommit);
|
|
26110
|
+
await this.registryCache.set(repoUrl, effectiveVersion, lockedCommit);
|
|
26111
|
+
}
|
|
26112
|
+
} catch (err) {
|
|
26113
|
+
this.logger.debug(`Lockfile commit verification failed: ${err}`);
|
|
26114
|
+
}
|
|
26115
|
+
}
|
|
25897
26116
|
} else {
|
|
25898
26117
|
this.logger.verbose(`Registry cache miss, cloning: ${repoUrl}@${tag}`);
|
|
25899
26118
|
cachePath = this.registryCache.getCachePath(repoUrl, effectiveVersion);
|
|
25900
26119
|
const fallbackRepoUrl = this.options.registries ? findFallbackUrl(repoUrl, this.options.registries) : void 0;
|
|
25901
26120
|
await this.gitRegistry.cloneAtTag(repoUrl, tag, cachePath, fallbackRepoUrl);
|
|
25902
|
-
|
|
26121
|
+
if (lockedCommit) {
|
|
26122
|
+
this.logger.verbose(`Checking out locked commit: ${lockedCommit}`);
|
|
26123
|
+
await this.gitRegistry.checkoutCommit(cachePath, lockedCommit);
|
|
26124
|
+
}
|
|
26125
|
+
const commitHash = lockedCommit ?? lockEntry?.commit ?? "unknown";
|
|
25903
26126
|
await this.registryCache.set(repoUrl, effectiveVersion, commitHash);
|
|
25904
26127
|
}
|
|
25905
26128
|
const isRoot = subPath === "";
|
|
25906
26129
|
const isMdPath = !isRoot && subPath.endsWith(".md");
|
|
25907
26130
|
const resolvedFileName = isRoot ? "" : isMdPath ? subPath : subPath.endsWith(".prs") ? subPath : `${subPath}.prs`;
|
|
25908
26131
|
const resolvedFullPath = isRoot ? "" : join8(cachePath, resolvedFileName);
|
|
26132
|
+
if (!isRoot) {
|
|
26133
|
+
const rel = relative4(resolve9(cachePath), resolve9(resolvedFullPath));
|
|
26134
|
+
if (rel.startsWith("..") || resolve9(resolvedFullPath) === resolve9(cachePath)) {
|
|
26135
|
+
errors.push(
|
|
26136
|
+
new ResolveError(
|
|
26137
|
+
`Path traversal detected: subpath '${subPath}' escapes repository cache boundary.`
|
|
26138
|
+
)
|
|
26139
|
+
);
|
|
26140
|
+
this.resolving.delete(marker);
|
|
26141
|
+
return { ast: null, sources: [marker], errors };
|
|
26142
|
+
}
|
|
26143
|
+
}
|
|
25909
26144
|
let resolvedAST = null;
|
|
25910
26145
|
if (!isRoot && existsSync9(resolvedFullPath) && isMdPath) {
|
|
25911
26146
|
this.logger.debug(`Found .md file: ${resolvedFullPath}`);
|
|
@@ -25931,6 +26166,18 @@ var Resolver = class {
|
|
|
25931
26166
|
);
|
|
25932
26167
|
} else {
|
|
25933
26168
|
const discoverDir = isRoot ? cachePath : join8(cachePath, subPath);
|
|
26169
|
+
if (!isRoot) {
|
|
26170
|
+
const dirRel = relative4(resolve9(cachePath), resolve9(discoverDir));
|
|
26171
|
+
if (dirRel.startsWith("..")) {
|
|
26172
|
+
errors.push(
|
|
26173
|
+
new ResolveError(
|
|
26174
|
+
`Path traversal detected: subpath '${subPath}' escapes repository cache boundary.`
|
|
26175
|
+
)
|
|
26176
|
+
);
|
|
26177
|
+
this.resolving.delete(marker);
|
|
26178
|
+
return { ast: null, sources: [marker], errors };
|
|
26179
|
+
}
|
|
26180
|
+
}
|
|
25934
26181
|
if (!isRoot && !existsSync9(discoverDir)) {
|
|
25935
26182
|
errors.push(
|
|
25936
26183
|
new ResolveError(
|
|
@@ -25985,7 +26232,7 @@ var Resolver = class {
|
|
|
25985
26232
|
async tryDirectoryScan(dirPath, sources, errors) {
|
|
25986
26233
|
let stat4;
|
|
25987
26234
|
try {
|
|
25988
|
-
stat4 = await
|
|
26235
|
+
stat4 = await lstat4(dirPath);
|
|
25989
26236
|
} catch {
|
|
25990
26237
|
return null;
|
|
25991
26238
|
}
|
|
@@ -26038,7 +26285,7 @@ var Resolver = class {
|
|
|
26038
26285
|
const subDir = join8(currentDir, entry.name);
|
|
26039
26286
|
let subStat;
|
|
26040
26287
|
try {
|
|
26041
|
-
subStat = await
|
|
26288
|
+
subStat = await lstat4(subDir);
|
|
26042
26289
|
} catch {
|
|
26043
26290
|
continue;
|
|
26044
26291
|
}
|
|
@@ -26093,6 +26340,41 @@ var Resolver = class {
|
|
|
26093
26340
|
clearCache() {
|
|
26094
26341
|
this.cache.clear();
|
|
26095
26342
|
}
|
|
26343
|
+
/**
|
|
26344
|
+
* Verify integrity hashes for registry reference files.
|
|
26345
|
+
* Reads each referenced file from the registry cache and compares its
|
|
26346
|
+
* hash against the lockfile entry.
|
|
26347
|
+
*
|
|
26348
|
+
* @param lockfile - Lockfile with reference hashes
|
|
26349
|
+
* @returns Array of errors for mismatched references
|
|
26350
|
+
*/
|
|
26351
|
+
async verifyReferenceHashes(lockfile) {
|
|
26352
|
+
if (!lockfile.references) return [];
|
|
26353
|
+
const errors = [];
|
|
26354
|
+
for (const [key, entry] of Object.entries(lockfile.references)) {
|
|
26355
|
+
const parts = key.split("\0");
|
|
26356
|
+
if (parts.length !== 3) continue;
|
|
26357
|
+
const [repoUrl, relativePath, version] = parts;
|
|
26358
|
+
try {
|
|
26359
|
+
const cachePath = this.registryCache.getCachePath(repoUrl, version);
|
|
26360
|
+
const fullPath = join8(cachePath, relativePath);
|
|
26361
|
+
if (!existsSync9(fullPath)) continue;
|
|
26362
|
+
const content = await readFile11(fullPath);
|
|
26363
|
+
const actualHash = hashContent(content);
|
|
26364
|
+
if (actualHash !== entry.hash) {
|
|
26365
|
+
errors.push(
|
|
26366
|
+
new ResolveError(
|
|
26367
|
+
`Reference file hash mismatch: ${relativePath} has changed since last lock. Run \`prs lock --update\` to accept changes.`,
|
|
26368
|
+
void 0,
|
|
26369
|
+
"PS2023" /* LOCKFILE_INTEGRITY */
|
|
26370
|
+
)
|
|
26371
|
+
);
|
|
26372
|
+
}
|
|
26373
|
+
} catch {
|
|
26374
|
+
}
|
|
26375
|
+
}
|
|
26376
|
+
return errors;
|
|
26377
|
+
}
|
|
26096
26378
|
/**
|
|
26097
26379
|
* Get the file loader.
|
|
26098
26380
|
*/
|
|
@@ -26186,7 +26468,7 @@ var HttpRegistry = class {
|
|
|
26186
26468
|
* Sleep for specified milliseconds.
|
|
26187
26469
|
*/
|
|
26188
26470
|
sleep(ms) {
|
|
26189
|
-
return new Promise((
|
|
26471
|
+
return new Promise((resolve24) => setTimeout(resolve24, ms));
|
|
26190
26472
|
}
|
|
26191
26473
|
/**
|
|
26192
26474
|
* Fetch with timeout.
|
|
@@ -26568,13 +26850,6 @@ function formatSkillValidationIssues(issues) {
|
|
|
26568
26850
|
// packages/resolver/src/index.ts
|
|
26569
26851
|
init_src();
|
|
26570
26852
|
|
|
26571
|
-
// packages/resolver/src/reference-hasher.ts
|
|
26572
|
-
import { createHash as createHash2 } from "crypto";
|
|
26573
|
-
function hashContent(content) {
|
|
26574
|
-
const hex = createHash2("sha256").update(content).digest("hex");
|
|
26575
|
-
return `sha256-${hex}`;
|
|
26576
|
-
}
|
|
26577
|
-
|
|
26578
26853
|
// packages/validator/src/validator.ts
|
|
26579
26854
|
init_src();
|
|
26580
26855
|
|
|
@@ -26791,10 +27066,10 @@ function walkValue(value, loc, callback, exclude) {
|
|
|
26791
27066
|
const typed = value;
|
|
26792
27067
|
if (typed.type === "TextContent" && typeof typed.value === "string") {
|
|
26793
27068
|
callback(typed.value, typed.loc ?? loc);
|
|
27069
|
+
return;
|
|
26794
27070
|
}
|
|
26795
|
-
} else {
|
|
26796
|
-
walkObjectProperties(value, loc, callback, exclude);
|
|
26797
27071
|
}
|
|
27072
|
+
walkObjectProperties(value, loc, callback, exclude);
|
|
26798
27073
|
}
|
|
26799
27074
|
}
|
|
26800
27075
|
function offsetLocation(baseLoc, text, charIndex) {
|
|
@@ -27883,7 +28158,10 @@ function decodeRawHex(hex) {
|
|
|
27883
28158
|
if (isNaN(byte)) return null;
|
|
27884
28159
|
bytes.push(byte);
|
|
27885
28160
|
}
|
|
27886
|
-
|
|
28161
|
+
let decoded = "";
|
|
28162
|
+
for (const byte of bytes) {
|
|
28163
|
+
decoded += String.fromCharCode(byte);
|
|
28164
|
+
}
|
|
27887
28165
|
if (/[\x00-\x08\x0E-\x1F]/.test(decoded)) {
|
|
27888
28166
|
return null;
|
|
27889
28167
|
}
|
|
@@ -29381,10 +29659,14 @@ function getComposedFrom(skill) {
|
|
|
29381
29659
|
return raw.filter(isComposedFromEntry);
|
|
29382
29660
|
}
|
|
29383
29661
|
function extractRegistry(source) {
|
|
29384
|
-
if (
|
|
29385
|
-
|
|
29386
|
-
|
|
29387
|
-
|
|
29662
|
+
if (source.startsWith("@")) {
|
|
29663
|
+
const slash = source.indexOf("/");
|
|
29664
|
+
if (slash === -1) return source;
|
|
29665
|
+
return source.slice(0, slash);
|
|
29666
|
+
}
|
|
29667
|
+
const match = /\/(@[a-zA-Z_][a-zA-Z0-9_-]*)\//.exec(source);
|
|
29668
|
+
if (match) return match[1] ?? null;
|
|
29669
|
+
return null;
|
|
29388
29670
|
}
|
|
29389
29671
|
function matchesTargetPattern(composedFrom, targetPattern) {
|
|
29390
29672
|
const registry2 = extractRegistry(targetPattern);
|
|
@@ -29646,6 +29928,17 @@ var Validator = class {
|
|
|
29646
29928
|
}
|
|
29647
29929
|
this.logger.debug(`Validator initialized with ${this.rules.length} rules`);
|
|
29648
29930
|
}
|
|
29931
|
+
/**
|
|
29932
|
+
* Update validator configuration after construction.
|
|
29933
|
+
* Allows the compiler to inject runtime data (registry references, lockfile, etc.)
|
|
29934
|
+
* that becomes available only after resolution.
|
|
29935
|
+
*/
|
|
29936
|
+
updateConfig(partial) {
|
|
29937
|
+
this.config = { ...this.config, ...partial };
|
|
29938
|
+
if (partial.disableRules) {
|
|
29939
|
+
this.disabledRules = new Set(partial.disableRules);
|
|
29940
|
+
}
|
|
29941
|
+
}
|
|
29649
29942
|
/**
|
|
29650
29943
|
* Validate an AST.
|
|
29651
29944
|
*
|
|
@@ -29776,7 +30069,7 @@ var SECURITY_STRICT_MULTILINGUAL = {
|
|
|
29776
30069
|
};
|
|
29777
30070
|
|
|
29778
30071
|
// packages/compiler/src/compiler.ts
|
|
29779
|
-
import { relative as
|
|
30072
|
+
import { relative as relative5, isAbsolute as isAbsolute4 } from "path";
|
|
29780
30073
|
function generateHtmlMarker(source, target) {
|
|
29781
30074
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
29782
30075
|
return `<!-- PromptScript ${timestamp} | source: ${source} | target: ${target} - do not edit -->`;
|
|
@@ -29852,7 +30145,7 @@ var Compiler = class _Compiler {
|
|
|
29852
30145
|
formatTime: 0,
|
|
29853
30146
|
totalTime: 0
|
|
29854
30147
|
};
|
|
29855
|
-
const sourceLabel = isAbsolute4(entryPath) ?
|
|
30148
|
+
const sourceLabel = isAbsolute4(entryPath) ? relative5(process.cwd(), entryPath) : entryPath;
|
|
29856
30149
|
this.logger.verbose("=== Stage 1: Resolve ===");
|
|
29857
30150
|
const startResolve = Date.now();
|
|
29858
30151
|
let resolved;
|
|
@@ -29886,6 +30179,7 @@ var Compiler = class _Compiler {
|
|
|
29886
30179
|
stats
|
|
29887
30180
|
};
|
|
29888
30181
|
}
|
|
30182
|
+
const compileErrors = [];
|
|
29889
30183
|
if (!this.options.ignoreHashes && this.options.resolver.lockfile?.references) {
|
|
29890
30184
|
this.logger.verbose("=== Stage 1.5: Reference Integrity ===");
|
|
29891
30185
|
const registryReferences = /* @__PURE__ */ new Set();
|
|
@@ -29902,17 +30196,29 @@ var Compiler = class _Compiler {
|
|
|
29902
30196
|
}
|
|
29903
30197
|
}
|
|
29904
30198
|
}
|
|
29905
|
-
|
|
29906
|
-
|
|
29907
|
-
|
|
29908
|
-
this.options.validator.ignoreHashes = this.options.ignoreHashes;
|
|
30199
|
+
const hashErrors = await this.resolver.verifyReferenceHashes(this.options.resolver.lockfile);
|
|
30200
|
+
for (const err of hashErrors) {
|
|
30201
|
+
compileErrors.push(this.toCompileError(err));
|
|
29909
30202
|
}
|
|
30203
|
+
this.validator.updateConfig({
|
|
30204
|
+
registryReferences,
|
|
30205
|
+
lockfile: this.options.resolver.lockfile,
|
|
30206
|
+
ignoreHashes: this.options.ignoreHashes
|
|
30207
|
+
});
|
|
29910
30208
|
} else if (this.options.ignoreHashes) {
|
|
29911
30209
|
this.logger.verbose("--ignore-hashes is set: reference integrity verification is disabled");
|
|
29912
30210
|
console.error("\u26A0 --ignore-hashes is set: reference integrity verification is disabled");
|
|
29913
|
-
|
|
29914
|
-
|
|
29915
|
-
|
|
30211
|
+
this.validator.updateConfig({ ignoreHashes: true });
|
|
30212
|
+
}
|
|
30213
|
+
if (compileErrors.length > 0) {
|
|
30214
|
+
stats.totalTime = Date.now() - startTotal;
|
|
30215
|
+
return {
|
|
30216
|
+
success: false,
|
|
30217
|
+
outputs: /* @__PURE__ */ new Map(),
|
|
30218
|
+
errors: compileErrors,
|
|
30219
|
+
warnings: [],
|
|
30220
|
+
stats
|
|
30221
|
+
};
|
|
29916
30222
|
}
|
|
29917
30223
|
this.logger.verbose("=== Stage 2: Validate ===");
|
|
29918
30224
|
const startValidate = Date.now();
|
|
@@ -30109,12 +30415,12 @@ var Compiler = class _Compiler {
|
|
|
30109
30415
|
*/
|
|
30110
30416
|
async watch(entryPath, options = {}) {
|
|
30111
30417
|
const { default: chokidar2 } = await import("chokidar");
|
|
30112
|
-
const { dirname:
|
|
30113
|
-
const baseDir =
|
|
30418
|
+
const { dirname: dirname18, resolve: resolve24 } = await import("path");
|
|
30419
|
+
const baseDir = dirname18(resolve24(entryPath));
|
|
30114
30420
|
const includePatterns = options.include ?? ["**/*.prs"];
|
|
30115
30421
|
const excludePatterns = options.exclude ?? ["**/node_modules/**"];
|
|
30116
30422
|
const debounceMs = options.debounce ?? 300;
|
|
30117
|
-
const watchPatterns = includePatterns.map((p) =>
|
|
30423
|
+
const watchPatterns = includePatterns.map((p) => resolve24(baseDir, p));
|
|
30118
30424
|
let debounceTimer = null;
|
|
30119
30425
|
let pendingChanges = [];
|
|
30120
30426
|
const handleChange = async (changedFiles) => {
|
|
@@ -30289,7 +30595,7 @@ var Pager = class {
|
|
|
30289
30595
|
}
|
|
30290
30596
|
return;
|
|
30291
30597
|
}
|
|
30292
|
-
return new Promise((
|
|
30598
|
+
return new Promise((resolve24) => {
|
|
30293
30599
|
const pagerCmd = getPagerCommand();
|
|
30294
30600
|
const [cmd, ...args] = pagerCmd.split(" ");
|
|
30295
30601
|
if (cmd === "less" && !args.includes("-R")) {
|
|
@@ -30297,7 +30603,7 @@ var Pager = class {
|
|
|
30297
30603
|
}
|
|
30298
30604
|
if (!cmd) {
|
|
30299
30605
|
console.log(content);
|
|
30300
|
-
|
|
30606
|
+
resolve24();
|
|
30301
30607
|
return;
|
|
30302
30608
|
}
|
|
30303
30609
|
const pager = spawn(cmd, args, {
|
|
@@ -30310,17 +30616,17 @@ var Pager = class {
|
|
|
30310
30616
|
});
|
|
30311
30617
|
pager.on("error", (_err) => {
|
|
30312
30618
|
console.log(content);
|
|
30313
|
-
|
|
30619
|
+
resolve24();
|
|
30314
30620
|
});
|
|
30315
30621
|
pager.on("close", () => {
|
|
30316
|
-
|
|
30622
|
+
resolve24();
|
|
30317
30623
|
});
|
|
30318
30624
|
if (pager.stdin) {
|
|
30319
30625
|
pager.stdin.write(content);
|
|
30320
30626
|
pager.stdin.end();
|
|
30321
30627
|
} else {
|
|
30322
30628
|
console.log(content);
|
|
30323
|
-
|
|
30629
|
+
resolve24();
|
|
30324
30630
|
}
|
|
30325
30631
|
});
|
|
30326
30632
|
}
|
|
@@ -30430,9 +30736,9 @@ var __dirname2 = dirname9(__filename2);
|
|
|
30430
30736
|
async function loadBundledSkillContent(logger) {
|
|
30431
30737
|
const skillRelPath = "skills/promptscript/SKILL.md";
|
|
30432
30738
|
const candidates = [
|
|
30433
|
-
|
|
30739
|
+
resolve10(__dirname2, skillRelPath),
|
|
30434
30740
|
// bundled: dist/packages/cli/skills/...
|
|
30435
|
-
|
|
30741
|
+
resolve10(__dirname2, "..", "..", skillRelPath)
|
|
30436
30742
|
// dev: packages/cli/skills/...
|
|
30437
30743
|
];
|
|
30438
30744
|
for (const candidate of candidates) {
|
|
@@ -30453,7 +30759,7 @@ async function loadBundledSkillContent(logger) {
|
|
|
30453
30759
|
}
|
|
30454
30760
|
function findConfigInDir(dir) {
|
|
30455
30761
|
for (const file of CONFIG_FILES) {
|
|
30456
|
-
const fullPath =
|
|
30762
|
+
const fullPath = resolve10(dir, file);
|
|
30457
30763
|
if (existsSync12(fullPath)) {
|
|
30458
30764
|
return fullPath;
|
|
30459
30765
|
}
|
|
@@ -30510,7 +30816,7 @@ function getBuildProfile(config, buildName) {
|
|
|
30510
30816
|
}
|
|
30511
30817
|
function resolveOutputBase(projectRoot, output) {
|
|
30512
30818
|
if (!output) return projectRoot;
|
|
30513
|
-
return isAbsolute5(output) ? output :
|
|
30819
|
+
return isAbsolute5(output) ? output : resolve10(projectRoot, output);
|
|
30514
30820
|
}
|
|
30515
30821
|
async function isPromptScriptGenerated2(filePath) {
|
|
30516
30822
|
try {
|
|
@@ -30576,7 +30882,7 @@ async function writeOutputs(outputs, options, _config, services) {
|
|
|
30576
30882
|
}
|
|
30577
30883
|
}
|
|
30578
30884
|
for (const output of outputs.values()) {
|
|
30579
|
-
const outputPath = options.output ?
|
|
30885
|
+
const outputPath = options.output ? resolve10(options.output, output.path) : resolve10(output.path);
|
|
30580
30886
|
const fileExists3 = existsSync12(outputPath);
|
|
30581
30887
|
if (options.dryRun) {
|
|
30582
30888
|
if (fileExists3) {
|
|
@@ -30710,14 +31016,21 @@ async function compileCommand(options, services = createDefaultServices()) {
|
|
|
30710
31016
|
const spinner = createSpinner("Loading configuration...").start();
|
|
30711
31017
|
const logger = createCliLogger();
|
|
30712
31018
|
try {
|
|
30713
|
-
const projectRoot = options.cwd ?
|
|
31019
|
+
const projectRoot = options.cwd ? resolve10(options.cwd) : process.cwd();
|
|
30714
31020
|
logger.verbose("Loading configuration...");
|
|
30715
31021
|
const configPath = options.config ? options.config : options.cwd ? findConfigInDir(projectRoot) : void 0;
|
|
30716
|
-
const config = await
|
|
31022
|
+
const config = await loadEffectiveConfig(configPath);
|
|
30717
31023
|
const buildProfile = getBuildProfile(config, options.build);
|
|
30718
31024
|
const selectedTarget = options.target ?? options.format;
|
|
30719
31025
|
const targetEntries = buildProfile?.targets ?? config.targets;
|
|
30720
|
-
const
|
|
31026
|
+
const parsedTargets = parseTargets(targetEntries);
|
|
31027
|
+
let targets;
|
|
31028
|
+
if (selectedTarget) {
|
|
31029
|
+
const matched = parsedTargets.find((t) => t.name === selectedTarget);
|
|
31030
|
+
targets = matched ? [matched] : [{ name: selectedTarget }];
|
|
31031
|
+
} else {
|
|
31032
|
+
targets = parsedTargets;
|
|
31033
|
+
}
|
|
30721
31034
|
const conflicts = detectOutputConflicts(targets);
|
|
30722
31035
|
if (conflicts.size > 0) {
|
|
30723
31036
|
const lines = [...conflicts.entries()].map(
|
|
@@ -30750,7 +31063,7 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
30750
31063
|
}
|
|
30751
31064
|
}
|
|
30752
31065
|
let lockfile;
|
|
30753
|
-
const lockfilePath =
|
|
31066
|
+
const lockfilePath = resolve10(projectRoot, "promptscript.lock");
|
|
30754
31067
|
try {
|
|
30755
31068
|
const lockfileContent = await readFile12(lockfilePath, "utf-8");
|
|
30756
31069
|
const parsed = parseYaml5(lockfileContent, { maxAliasCount: 100 });
|
|
@@ -30770,7 +31083,7 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
30770
31083
|
if (config.includePromptScriptSkill !== false) {
|
|
30771
31084
|
skillContent = await loadBundledSkillContent(logger);
|
|
30772
31085
|
}
|
|
30773
|
-
const localPath =
|
|
31086
|
+
const localPath = resolve10(projectRoot, ".promptscript");
|
|
30774
31087
|
const compiler = new Compiler({
|
|
30775
31088
|
resolver: {
|
|
30776
31089
|
registryPath,
|
|
@@ -30790,7 +31103,7 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
30790
31103
|
ignoreHashes: options.ignoreHashes
|
|
30791
31104
|
});
|
|
30792
31105
|
const entry = buildProfile?.entry ?? config.input?.entry;
|
|
30793
|
-
const entryPath = entry ?
|
|
31106
|
+
const entryPath = entry ? resolve10(projectRoot, entry) : resolve10(localPath, "project.prs");
|
|
30794
31107
|
if (!existsSync12(entryPath)) {
|
|
30795
31108
|
spinner.fail("Entry file not found");
|
|
30796
31109
|
ConsoleOutput.error(`File not found: ${entryPath}`);
|
|
@@ -30830,7 +31143,7 @@ Disable one of the conflicting targets or assign a custom output path.`
|
|
|
30830
31143
|
ConsoleOutput.newline();
|
|
30831
31144
|
ConsoleOutput.info("Watching for changes... (Ctrl+C to stop)");
|
|
30832
31145
|
watchForChanges(
|
|
30833
|
-
|
|
31146
|
+
resolve10(projectRoot, ".promptscript"),
|
|
30834
31147
|
() => compileCommand({ ...options, watch: false }, services)
|
|
30835
31148
|
);
|
|
30836
31149
|
}
|
|
@@ -30881,7 +31194,7 @@ function watchForChanges(dir, callback) {
|
|
|
30881
31194
|
}
|
|
30882
31195
|
|
|
30883
31196
|
// packages/cli/src/commands/validate.ts
|
|
30884
|
-
import { resolve as
|
|
31197
|
+
import { resolve as resolve11, join as join12 } from "path";
|
|
30885
31198
|
import { existsSync as existsSync13, readFileSync as readFileSync4, writeFileSync, readdirSync } from "fs";
|
|
30886
31199
|
init_console();
|
|
30887
31200
|
init_src();
|
|
@@ -31031,7 +31344,7 @@ async function validateCommand(options) {
|
|
|
31031
31344
|
formatters: []
|
|
31032
31345
|
// No formatters needed for validation only
|
|
31033
31346
|
});
|
|
31034
|
-
const entryPath =
|
|
31347
|
+
const entryPath = resolve11("./.promptscript/project.prs");
|
|
31035
31348
|
if (!existsSync13(entryPath)) {
|
|
31036
31349
|
handleEntryNotFound(entryPath, isJsonFormat, spinner);
|
|
31037
31350
|
return;
|
|
@@ -31115,7 +31428,7 @@ ${fixedCount} file(s) fixed.`);
|
|
|
31115
31428
|
// packages/cli/src/commands/pull.ts
|
|
31116
31429
|
import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
|
|
31117
31430
|
import { existsSync as existsSync14 } from "fs";
|
|
31118
|
-
import { resolve as
|
|
31431
|
+
import { resolve as resolve12, dirname as dirname10 } from "path";
|
|
31119
31432
|
init_console();
|
|
31120
31433
|
async function pullCommand(options) {
|
|
31121
31434
|
const spinner = createSpinner("Loading configuration...").start();
|
|
@@ -31140,7 +31453,7 @@ async function pullCommand(options) {
|
|
|
31140
31453
|
}
|
|
31141
31454
|
spinner.text = `Fetching ${inheritPath}...`;
|
|
31142
31455
|
const content = await registry2.fetch(inheritPath);
|
|
31143
|
-
const destPath =
|
|
31456
|
+
const destPath = resolve12("./.promptscript/.inherited", inheritPath);
|
|
31144
31457
|
if (existsSync14(destPath) && !options.force && !options.dryRun) {
|
|
31145
31458
|
spinner.warn("File already exists (use --force to overwrite)");
|
|
31146
31459
|
ConsoleOutput.muted(destPath);
|
|
@@ -31202,7 +31515,7 @@ async function createRegistry(config, options, spinner) {
|
|
|
31202
31515
|
if (!existsSync14(registryPath)) {
|
|
31203
31516
|
throw new Error(`Local registry path not found: ${registryPath}`);
|
|
31204
31517
|
}
|
|
31205
|
-
return createFileSystemRegistry(
|
|
31518
|
+
return createFileSystemRegistry(resolve12(registryPath));
|
|
31206
31519
|
}
|
|
31207
31520
|
function parseInheritPath(inheritPath) {
|
|
31208
31521
|
const versionIndex = inheritPath.lastIndexOf("@");
|
|
@@ -31249,7 +31562,7 @@ function handlePullError(error, spinner) {
|
|
|
31249
31562
|
}
|
|
31250
31563
|
|
|
31251
31564
|
// packages/cli/src/commands/diff.ts
|
|
31252
|
-
import { resolve as
|
|
31565
|
+
import { resolve as resolve13 } from "path";
|
|
31253
31566
|
import { readFile as readFile13 } from "fs/promises";
|
|
31254
31567
|
import { existsSync as existsSync15 } from "fs";
|
|
31255
31568
|
init_console();
|
|
@@ -31286,7 +31599,7 @@ function printNewFilePreview(content, showFull, pager) {
|
|
|
31286
31599
|
}
|
|
31287
31600
|
}
|
|
31288
31601
|
async function compareOutput(_name, output, _config, showFull, pager) {
|
|
31289
|
-
const outputPath =
|
|
31602
|
+
const outputPath = resolve13(output.path);
|
|
31290
31603
|
const newContent = output.content;
|
|
31291
31604
|
if (!existsSync15(outputPath)) {
|
|
31292
31605
|
pager.write(chalk2.green(`+ ${outputPath} (new file)`));
|
|
@@ -31328,7 +31641,7 @@ async function diffCommand(options) {
|
|
|
31328
31641
|
formatters: targets,
|
|
31329
31642
|
customConventions: config.customConventions
|
|
31330
31643
|
});
|
|
31331
|
-
const entryPath =
|
|
31644
|
+
const entryPath = resolve13("./.promptscript/project.prs");
|
|
31332
31645
|
if (!existsSync15(entryPath)) {
|
|
31333
31646
|
spinner.fail("Entry file not found");
|
|
31334
31647
|
ConsoleOutput.error(`File not found: ${entryPath}`);
|
|
@@ -31398,7 +31711,7 @@ function printSimpleDiff(existingLines, newLines, showFull, pager) {
|
|
|
31398
31711
|
|
|
31399
31712
|
// packages/cli/src/commands/check.ts
|
|
31400
31713
|
import { existsSync as existsSync16 } from "fs";
|
|
31401
|
-
import { resolve as
|
|
31714
|
+
import { resolve as resolve14 } from "path";
|
|
31402
31715
|
init_console();
|
|
31403
31716
|
async function checkCommand(_options) {
|
|
31404
31717
|
const spinner = createSpinner("Checking project health...").start();
|
|
@@ -31476,7 +31789,7 @@ async function checkCommand(_options) {
|
|
|
31476
31789
|
hasErrors = true;
|
|
31477
31790
|
}
|
|
31478
31791
|
if (config.registry?.path) {
|
|
31479
|
-
const registryPath =
|
|
31792
|
+
const registryPath = resolve14(config.registry.path);
|
|
31480
31793
|
if (existsSync16(registryPath)) {
|
|
31481
31794
|
results.push({
|
|
31482
31795
|
name: "Registry path",
|
|
@@ -31840,8 +32153,32 @@ var SEED_SECURITY = `@meta {
|
|
|
31840
32153
|
`;
|
|
31841
32154
|
async function scaffoldRegistry(options, services) {
|
|
31842
32155
|
const { fs: fs4 } = services;
|
|
31843
|
-
const { directory, name, description, namespaces, seed } = options;
|
|
32156
|
+
const { directory, name, description, namespaces, seed, force } = options;
|
|
31844
32157
|
const createdFiles = [];
|
|
32158
|
+
const existingManifestPath = join14(directory, "registry-manifest.yaml");
|
|
32159
|
+
if (fs4.existsSync(existingManifestPath) && !force) {
|
|
32160
|
+
throw new Error(
|
|
32161
|
+
`Directory already contains a registry-manifest.yaml. Use --force to overwrite.`
|
|
32162
|
+
);
|
|
32163
|
+
}
|
|
32164
|
+
if (!force) {
|
|
32165
|
+
let entries;
|
|
32166
|
+
try {
|
|
32167
|
+
entries = await fs4.readdir(directory);
|
|
32168
|
+
} catch (err) {
|
|
32169
|
+
const nodeErr = err;
|
|
32170
|
+
if (nodeErr.code !== "ENOENT") {
|
|
32171
|
+
throw err;
|
|
32172
|
+
}
|
|
32173
|
+
entries = [];
|
|
32174
|
+
}
|
|
32175
|
+
const visibleEntries = entries.filter((e) => !e.startsWith("."));
|
|
32176
|
+
if (visibleEntries.length > 0) {
|
|
32177
|
+
throw new Error(
|
|
32178
|
+
`Directory "${directory}" is not empty. Use --force to overwrite existing files.`
|
|
32179
|
+
);
|
|
32180
|
+
}
|
|
32181
|
+
}
|
|
31845
32182
|
await fs4.mkdir(directory, { recursive: true });
|
|
31846
32183
|
const manifest = generateManifest(name, description, namespaces, seed);
|
|
31847
32184
|
const manifestPath = join14(directory, "registry-manifest.yaml");
|
|
@@ -31981,7 +32318,7 @@ Then reference configs with \`@inherit\` or \`@use\`:
|
|
|
31981
32318
|
}
|
|
31982
32319
|
|
|
31983
32320
|
// packages/cli/src/utils/resolve-target-directory.ts
|
|
31984
|
-
import { join as join15, resolve as
|
|
32321
|
+
import { join as join15, resolve as resolve15 } from "path";
|
|
31985
32322
|
|
|
31986
32323
|
// packages/cli/src/utils/slugify.ts
|
|
31987
32324
|
function slugify(name) {
|
|
@@ -31992,7 +32329,7 @@ function slugify(name) {
|
|
|
31992
32329
|
async function resolveTargetDirectory(options, services) {
|
|
31993
32330
|
const { cwd, directoryArg, registryName, nonInteractive } = options;
|
|
31994
32331
|
if (directoryArg) {
|
|
31995
|
-
return
|
|
32332
|
+
return resolve15(directoryArg);
|
|
31996
32333
|
}
|
|
31997
32334
|
const empty = await isDirectoryEmpty(cwd, services.fs);
|
|
31998
32335
|
if (empty && nonInteractive) {
|
|
@@ -32068,7 +32405,7 @@ async function registryInitCommand(directory, options, services = createDefaultS
|
|
|
32068
32405
|
);
|
|
32069
32406
|
const spinner = createSpinner("Creating registry...").start();
|
|
32070
32407
|
const createdFiles = await scaffoldRegistry(
|
|
32071
|
-
{ directory: targetDir, name, description, namespaces, seed },
|
|
32408
|
+
{ directory: targetDir, name, description, namespaces, seed, force: options.force },
|
|
32072
32409
|
services
|
|
32073
32410
|
);
|
|
32074
32411
|
spinner.succeed("Registry created");
|
|
@@ -32099,7 +32436,7 @@ async function registryInitCommand(directory, options, services = createDefaultS
|
|
|
32099
32436
|
// packages/cli/src/commands/registry/validate.ts
|
|
32100
32437
|
init_services();
|
|
32101
32438
|
init_console();
|
|
32102
|
-
import { resolve as
|
|
32439
|
+
import { resolve as resolve16 } from "path";
|
|
32103
32440
|
|
|
32104
32441
|
// packages/cli/src/utils/registry-validator.ts
|
|
32105
32442
|
import { join as join16 } from "path";
|
|
@@ -32299,7 +32636,7 @@ function detectCircularDependencies(catalog) {
|
|
|
32299
32636
|
|
|
32300
32637
|
// packages/cli/src/commands/registry/validate.ts
|
|
32301
32638
|
async function registryValidateCommand(path, options, services = createDefaultServices()) {
|
|
32302
|
-
const registryPath =
|
|
32639
|
+
const registryPath = resolve16(path ?? ".");
|
|
32303
32640
|
const spinner = createSpinner("Validating registry...").start();
|
|
32304
32641
|
try {
|
|
32305
32642
|
const result = await validateRegistry(registryPath, services);
|
|
@@ -32351,10 +32688,10 @@ async function registryValidateCommand(path, options, services = createDefaultSe
|
|
|
32351
32688
|
// packages/cli/src/commands/registry/publish.ts
|
|
32352
32689
|
init_services();
|
|
32353
32690
|
init_console();
|
|
32354
|
-
import { resolve as
|
|
32691
|
+
import { resolve as resolve17, join as join17 } from "path";
|
|
32355
32692
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
32356
32693
|
async function registryPublishCommand(path, options, services = createDefaultServices()) {
|
|
32357
|
-
const registryPath =
|
|
32694
|
+
const registryPath = resolve17(path ?? ".");
|
|
32358
32695
|
try {
|
|
32359
32696
|
if (!options.force) {
|
|
32360
32697
|
const spinner2 = createSpinner("Validating registry...").start();
|
|
@@ -32579,7 +32916,7 @@ async function addToProjectConfig(alias, url) {
|
|
|
32579
32916
|
|
|
32580
32917
|
// packages/cli/src/commands/registry/index.ts
|
|
32581
32918
|
function registerRegistryCommands(registry2) {
|
|
32582
|
-
registry2.command("init [directory]").description("Create a new PromptScript registry").option("-n, --name <name>", "Registry name").option("-d, --description <desc>", "Registry description").option("--namespaces <ns...>", "Namespace names (e.g., @core @stacks)").option("-y, --yes", "Non-interactive mode with defaults").option("-o, --output <dir>", "Output directory").option("--no-seed", "Skip seed configurations").action((directory, opts) => registryInitCommand(directory, opts));
|
|
32919
|
+
registry2.command("init [directory]").description("Create a new PromptScript registry").option("-n, --name <name>", "Registry name").option("-d, --description <desc>", "Registry description").option("--namespaces <ns...>", "Namespace names (e.g., @core @stacks)").option("-y, --yes", "Non-interactive mode with defaults").option("-o, --output <dir>", "Output directory").option("--no-seed", "Skip seed configurations").option("-f, --force", "Force overwrite existing files").action((directory, opts) => registryInitCommand(directory, opts));
|
|
32583
32920
|
registry2.command("validate [path]").description("Validate registry structure and manifest").option("--strict", "Treat warnings as errors").option("--format <format>", "Output format (text, json)", "text").action((path, opts) => registryValidateCommand(path, opts));
|
|
32584
32921
|
registry2.command("publish [path]").description("Publish registry to remote").option("--dry-run", "Preview what would be published").option("-f, --force", "Skip validation").option("-m, --message <msg>", "Git commit message").option("--tag <tag>", "Git tag for release").action((path, opts) => registryPublishCommand(path, opts));
|
|
32585
32922
|
registry2.command("list").description("Show merged registry alias mappings from all config levels").action(() => registryListCommand());
|
|
@@ -32589,7 +32926,7 @@ function registerRegistryCommands(registry2) {
|
|
|
32589
32926
|
// packages/cli/src/commands/lock.ts
|
|
32590
32927
|
import { writeFile as writeFile8, readFile as readFile18 } from "fs/promises";
|
|
32591
32928
|
import { existsSync as existsSync19 } from "fs";
|
|
32592
|
-
import { resolve as
|
|
32929
|
+
import { resolve as resolve18 } from "path";
|
|
32593
32930
|
import { parse as parseYaml9, stringify as stringifyYaml2 } from "yaml";
|
|
32594
32931
|
init_console();
|
|
32595
32932
|
init_src();
|
|
@@ -32662,8 +32999,8 @@ async function lockCommand(options) {
|
|
|
32662
32999
|
const aliases = config.registries ?? {};
|
|
32663
33000
|
const aliasEntries = Object.entries(aliases);
|
|
32664
33001
|
const projectRoot = process.cwd();
|
|
32665
|
-
const localPath =
|
|
32666
|
-
const entryPath = config.input?.entry ?
|
|
33002
|
+
const localPath = resolve18(projectRoot, ".promptscript");
|
|
33003
|
+
const entryPath = config.input?.entry ? resolve18(projectRoot, config.input.entry) : resolve18(localPath, "project.prs");
|
|
32667
33004
|
const scannedImports = await collectRemoteImports(entryPath, {
|
|
32668
33005
|
localPath,
|
|
32669
33006
|
registries: config.registries
|
|
@@ -32790,10 +33127,12 @@ async function updateCommand(packageArg, options) {
|
|
|
32790
33127
|
const dependencies = { ...existing.dependencies };
|
|
32791
33128
|
for (const repoUrl of toUpdate) {
|
|
32792
33129
|
const before = existing.dependencies[repoUrl]?.version ?? "(none)";
|
|
33130
|
+
const existingDep = existing.dependencies[repoUrl];
|
|
33131
|
+
const validation = await validateRemoteAccess(repoUrl);
|
|
32793
33132
|
const updated = {
|
|
32794
33133
|
version: "latest",
|
|
32795
|
-
commit: "0000000000000000000000000000000000000000",
|
|
32796
|
-
integrity: "sha256-pending"
|
|
33134
|
+
commit: validation.headCommit ?? existingDep?.commit ?? "0000000000000000000000000000000000000000",
|
|
33135
|
+
integrity: existingDep?.integrity ?? "sha256-pending"
|
|
32797
33136
|
};
|
|
32798
33137
|
dependencies[repoUrl] = updated;
|
|
32799
33138
|
updates.push({ repoUrl, before, after: updated.version });
|
|
@@ -32831,7 +33170,7 @@ init_console();
|
|
|
32831
33170
|
init_src();
|
|
32832
33171
|
import { readFile as readFile20, writeFile as writeFile10, mkdir as mkdir8, readdir as readdir5 } from "fs/promises";
|
|
32833
33172
|
import { existsSync as existsSync21 } from "fs";
|
|
32834
|
-
import { join as join18, resolve as
|
|
33173
|
+
import { join as join18, resolve as resolve19 } from "path";
|
|
32835
33174
|
import { homedir as homedir4 } from "os";
|
|
32836
33175
|
import { parse as parseYaml11 } from "yaml";
|
|
32837
33176
|
var VENDOR_DIR = ".promptscript/vendor";
|
|
@@ -32859,7 +33198,7 @@ async function vendorSyncCommand(options) {
|
|
|
32859
33198
|
continue;
|
|
32860
33199
|
}
|
|
32861
33200
|
if (!options.dryRun) {
|
|
32862
|
-
const vendorTarget =
|
|
33201
|
+
const vendorTarget = resolve19(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
|
|
32863
33202
|
await mkdir8(vendorTarget, { recursive: true });
|
|
32864
33203
|
await copyDirectory(cacheDir, vendorTarget);
|
|
32865
33204
|
}
|
|
@@ -32913,7 +33252,7 @@ async function vendorCheckCommand(_options) {
|
|
|
32913
33252
|
}
|
|
32914
33253
|
const issues = [];
|
|
32915
33254
|
for (const [repoUrl] of deps) {
|
|
32916
|
-
const vendorTarget =
|
|
33255
|
+
const vendorTarget = resolve19(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
|
|
32917
33256
|
if (!existsSync21(vendorTarget)) {
|
|
32918
33257
|
issues.push({ repoUrl, issue: "missing from vendor directory" });
|
|
32919
33258
|
}
|
|
@@ -33050,7 +33389,7 @@ async function resolveCommand(importPath, _options) {
|
|
|
33050
33389
|
|
|
33051
33390
|
// packages/cli/src/commands/inspect.ts
|
|
33052
33391
|
import { existsSync as existsSync23 } from "fs";
|
|
33053
|
-
import { resolve as
|
|
33392
|
+
import { resolve as resolve20, basename as basename13 } from "path";
|
|
33054
33393
|
init_console();
|
|
33055
33394
|
async function inspectCommand(skillName, options) {
|
|
33056
33395
|
const isJson = options.format === "json";
|
|
@@ -33063,7 +33402,7 @@ async function inspectCommand(skillName, options) {
|
|
|
33063
33402
|
localPath: "./.promptscript",
|
|
33064
33403
|
registries: config.registries
|
|
33065
33404
|
});
|
|
33066
|
-
const entryPath =
|
|
33405
|
+
const entryPath = resolve20("./.promptscript/project.prs");
|
|
33067
33406
|
if (!existsSync23(entryPath)) {
|
|
33068
33407
|
spinner.stop();
|
|
33069
33408
|
ConsoleOutput.error(`Entry file not found: ${entryPath}`);
|
|
@@ -33244,7 +33583,7 @@ init_console();
|
|
|
33244
33583
|
// packages/cli/src/commands/import.ts
|
|
33245
33584
|
init_src3();
|
|
33246
33585
|
init_console();
|
|
33247
|
-
import { resolve as
|
|
33586
|
+
import { resolve as resolve21, join as join20, basename as basename14 } from "path";
|
|
33248
33587
|
import { writeFileSync as writeFileSync2, mkdirSync, existsSync as existsSync24 } from "fs";
|
|
33249
33588
|
import { stat as stat2, readdir as readdir6, readFile as readFile21, writeFile as writeFile11, mkdir as mkdir9 } from "fs/promises";
|
|
33250
33589
|
function parseInstructionFrontmatter(content) {
|
|
@@ -33299,7 +33638,7 @@ async function importDirectory(dirPath) {
|
|
|
33299
33638
|
return lines.join("\n");
|
|
33300
33639
|
}
|
|
33301
33640
|
async function importCommand(file, options) {
|
|
33302
|
-
const filepath =
|
|
33641
|
+
const filepath = resolve21(file);
|
|
33303
33642
|
const inputStat = await stat2(filepath).catch(() => null);
|
|
33304
33643
|
if (inputStat?.isDirectory()) {
|
|
33305
33644
|
const prsContent = await importDirectory(filepath);
|
|
@@ -33331,11 +33670,11 @@ async function importCommand(file, options) {
|
|
|
33331
33670
|
console.log(result.prsContent);
|
|
33332
33671
|
return;
|
|
33333
33672
|
}
|
|
33334
|
-
const outputDir =
|
|
33673
|
+
const outputDir = resolve21(options.output ?? ".promptscript");
|
|
33335
33674
|
if (!existsSync24(outputDir)) {
|
|
33336
33675
|
mkdirSync(outputDir, { recursive: true });
|
|
33337
33676
|
}
|
|
33338
|
-
const outputPath =
|
|
33677
|
+
const outputPath = resolve21(outputDir, "imported.prs");
|
|
33339
33678
|
writeFileSync2(outputPath, result.prsContent, "utf-8");
|
|
33340
33679
|
ConsoleOutput.success(`Written to ${outputPath}`);
|
|
33341
33680
|
if (options.validate) {
|
|
@@ -33570,7 +33909,7 @@ init_hooks();
|
|
|
33570
33909
|
|
|
33571
33910
|
// packages/cli/src/commands/skills.ts
|
|
33572
33911
|
import { writeFile as writeFile12, readFile as readFile23, readdir as readdir7, mkdtemp, rm } from "fs/promises";
|
|
33573
|
-
import { resolve as
|
|
33912
|
+
import { resolve as resolve22, join as join22, basename as basename15, dirname as dirname13 } from "path";
|
|
33574
33913
|
import { existsSync as existsSync26 } from "fs";
|
|
33575
33914
|
import { tmpdir as tmpdir2 } from "os";
|
|
33576
33915
|
import { parse as parseYaml12, stringify as stringifyYaml4 } from "yaml";
|
|
@@ -33690,7 +34029,7 @@ function validateRemoteSource(source) {
|
|
|
33690
34029
|
}
|
|
33691
34030
|
async function resolveEntryFile(fileFlag) {
|
|
33692
34031
|
if (fileFlag) {
|
|
33693
|
-
const resolved =
|
|
34032
|
+
const resolved = resolve22(fileFlag);
|
|
33694
34033
|
if (!existsSync26(resolved)) {
|
|
33695
34034
|
throw new Error(`File not found: ${resolved}`);
|
|
33696
34035
|
}
|
|
@@ -33701,7 +34040,7 @@ async function resolveEntryFile(fileFlag) {
|
|
|
33701
34040
|
try {
|
|
33702
34041
|
const config = await loadConfig();
|
|
33703
34042
|
if (config.input?.entry) {
|
|
33704
|
-
const entryPath =
|
|
34043
|
+
const entryPath = resolve22(config.input.entry);
|
|
33705
34044
|
if (existsSync26(entryPath)) {
|
|
33706
34045
|
return entryPath;
|
|
33707
34046
|
}
|
|
@@ -33709,16 +34048,16 @@ async function resolveEntryFile(fileFlag) {
|
|
|
33709
34048
|
} catch {
|
|
33710
34049
|
}
|
|
33711
34050
|
}
|
|
33712
|
-
const promptscriptDir =
|
|
34051
|
+
const promptscriptDir = resolve22(".promptscript");
|
|
33713
34052
|
if (existsSync26(promptscriptDir)) {
|
|
33714
34053
|
const entries = await readdir7(promptscriptDir);
|
|
33715
34054
|
const prsFiles = entries.filter((f) => f.endsWith(".prs"));
|
|
33716
34055
|
if (prsFiles.length === 1) {
|
|
33717
|
-
return
|
|
34056
|
+
return resolve22(promptscriptDir, prsFiles[0]);
|
|
33718
34057
|
}
|
|
33719
34058
|
if (prsFiles.length > 1) {
|
|
33720
34059
|
if (prsFiles.includes("project.prs")) {
|
|
33721
|
-
return
|
|
34060
|
+
return resolve22(promptscriptDir, "project.prs");
|
|
33722
34061
|
}
|
|
33723
34062
|
throw new Error(
|
|
33724
34063
|
`Multiple .prs files found in .promptscript/. Use --file to specify which one.`
|
|
@@ -34069,7 +34408,7 @@ async function skillsUpdateCommand(name, options) {
|
|
|
34069
34408
|
|
|
34070
34409
|
// packages/cli/src/cli.ts
|
|
34071
34410
|
var __filename4 = fileURLToPath4(import.meta.url);
|
|
34072
|
-
var __dirname4 =
|
|
34411
|
+
var __dirname4 = dirname17(__filename4);
|
|
34073
34412
|
var program = new Command();
|
|
34074
34413
|
program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(getPackageVersion(__dirname3, "./package.json")).option("--verbose", "Enable verbose output").option("--debug", "Enable debug output (includes verbose)").option("--quiet", "Suppress non-error output").hook("preAction", async (thisCommand, actionCommand) => {
|
|
34075
34414
|
const opts = thisCommand.opts();
|
|
@@ -34137,7 +34476,9 @@ registerRegistryCommands(registry);
|
|
|
34137
34476
|
function run(args = process.argv) {
|
|
34138
34477
|
program.parse(args);
|
|
34139
34478
|
}
|
|
34140
|
-
|
|
34479
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
34480
|
+
run();
|
|
34481
|
+
}
|
|
34141
34482
|
|
|
34142
34483
|
// packages/cli/src/index.ts
|
|
34143
34484
|
init_init();
|