@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/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
- lines.push(` content: """`);
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
- lines.push(` """`);
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
- lines.push(' """');
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
- projectLines.push(' """');
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 !name.includes("..") && !name.includes("/") && !name.includes("\\");
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: name.replace(/^\/+/, ""),
11966
- description: name.replace(/^\/+/, ""),
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: name.replace(/^\/+/, ""),
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
- return {};
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((resolve22, reject) => {
20927
+ const res = await new Promise((resolve24, reject) => {
20886
20928
  store.incr(key, (err, res2) => {
20887
- err ? reject(err) : resolve22(res2);
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 resolve21, relative as relative4, isAbsolute as isAbsolute6 } from "path";
21057
- function resolveSafePath(workspace, requestedPath) {
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 = resolve21(workspace, decoded);
21063
- const rel = relative4(workspace, resolved);
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 dirname15 } from "path";
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(dirname15(resolved), { recursive: true });
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 relative5 } from "path";
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: relative5(workspace, filePath) });
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: relative5(workspace, filePath) });
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: relative5(workspace, filePath) });
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 dirname16 } from "path";
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 resolve8, dirname as dirname9, isAbsolute as isAbsolute5 } from "path";
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 lstat3, readdir as readdir4, readFile as readFile11 } from "fs/promises";
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 basename16 = relPath.split("/").pop() ?? relPath;
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 : basename16;
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
- return join7(this.baseDir, "registries", normalized, version);
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(join7(cachePath, ".prs-registry-meta.json"), JSON.stringify(meta, null, 2));
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 lstat2 } from "fs/promises";
25212
- import { resolve as resolve7, basename as basename10 } from "path";
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 = resolve7(dir, entry.name, "SKILL.md");
25450
+ let skillMdPath = resolve8(dir, entry.name, "SKILL.md");
25249
25451
  if (!await fileExists2(skillMdPath)) {
25250
- const dirnameMdPath = resolve7(dir, entry.name, `${entry.name}.md`);
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 = resolve7(dir, "SKILL.md");
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 = resolve7(dir, entry.name);
25510
+ const fullPath = resolve8(dir, entry.name);
25309
25511
  try {
25310
- const stat4 = await lstat2(fullPath);
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 = resolve7(dir, entry.name);
25546
+ const fullPath = resolve8(dir, entry.name);
25345
25547
  try {
25346
- const stat4 = await lstat2(fullPath);
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 = resolve7(dir, fileName);
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 lstat2(dir);
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 = resolve7(dir, name);
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 = resolve7(dir, name);
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 = resolve7(dir, name);
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
- const commitHash = lockEntry?.commit ?? "unknown";
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 lstat3(dirPath);
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 lstat3(subDir);
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((resolve22) => setTimeout(resolve22, ms));
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
- const decoded = String.fromCharCode(...bytes);
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 (!source.startsWith("@")) return null;
29385
- const slash = source.indexOf("/");
29386
- if (slash === -1) return source;
29387
- return source.slice(0, slash);
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 relative3, isAbsolute as isAbsolute4 } from "path";
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) ? relative3(process.cwd(), entryPath) : 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
- if (this.options.validator) {
29906
- this.options.validator.registryReferences = registryReferences;
29907
- this.options.validator.lockfile = this.options.resolver.lockfile;
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
- if (this.options.validator) {
29914
- this.options.validator.ignoreHashes = true;
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: dirname17, resolve: resolve22 } = await import("path");
30113
- const baseDir = dirname17(resolve22(entryPath));
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) => resolve22(baseDir, 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((resolve22) => {
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
- resolve22();
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
- resolve22();
30619
+ resolve24();
30314
30620
  });
30315
30621
  pager.on("close", () => {
30316
- resolve22();
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
- resolve22();
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
- resolve8(__dirname2, skillRelPath),
30739
+ resolve10(__dirname2, skillRelPath),
30434
30740
  // bundled: dist/packages/cli/skills/...
30435
- resolve8(__dirname2, "..", "..", skillRelPath)
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 = resolve8(dir, file);
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 : resolve8(projectRoot, 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 ? resolve8(options.output, output.path) : resolve8(output.path);
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 ? resolve8(options.cwd) : process.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 loadConfig(configPath);
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 targets = selectedTarget ? [{ name: selectedTarget }] : parseTargets(targetEntries);
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 = resolve8(projectRoot, "promptscript.lock");
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 = resolve8(projectRoot, ".promptscript");
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 ? resolve8(projectRoot, entry) : resolve8(localPath, "project.prs");
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
- resolve8(projectRoot, ".promptscript"),
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 resolve9, join as join12 } from "path";
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 = resolve9("./.promptscript/project.prs");
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 resolve10, dirname as dirname10 } from "path";
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 = resolve10("./.promptscript/.inherited", inheritPath);
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(resolve10(registryPath));
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 resolve11 } from "path";
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 = resolve11(output.path);
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 = resolve11("./.promptscript/project.prs");
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 resolve12 } from "path";
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 = resolve12(config.registry.path);
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 resolve13 } from "path";
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 resolve13(directoryArg);
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 resolve14 } from "path";
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 = resolve14(path ?? ".");
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 resolve15, join as join17 } from "path";
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 = resolve15(path ?? ".");
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 resolve16 } from "path";
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 = resolve16(projectRoot, ".promptscript");
32666
- const entryPath = config.input?.entry ? resolve16(projectRoot, config.input.entry) : resolve16(localPath, "project.prs");
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 resolve17 } from "path";
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 = resolve17(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
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 = resolve17(VENDOR_DIR, repoUrl.replace(/[/:]/g, "_"));
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 resolve18, basename as basename13 } from "path";
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 = resolve18("./.promptscript/project.prs");
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 resolve19, join as join20, basename as basename14 } from "path";
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 = resolve19(file);
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 = resolve19(options.output ?? ".promptscript");
33673
+ const outputDir = resolve21(options.output ?? ".promptscript");
33335
33674
  if (!existsSync24(outputDir)) {
33336
33675
  mkdirSync(outputDir, { recursive: true });
33337
33676
  }
33338
- const outputPath = resolve19(outputDir, "imported.prs");
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 resolve20, join as join22, basename as basename15, dirname as dirname13 } from "path";
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 = resolve20(fileFlag);
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 = resolve20(config.input.entry);
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 = resolve20(".promptscript");
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 resolve20(promptscriptDir, prsFiles[0]);
34056
+ return resolve22(promptscriptDir, prsFiles[0]);
33718
34057
  }
33719
34058
  if (prsFiles.length > 1) {
33720
34059
  if (prsFiles.includes("project.prs")) {
33721
- return resolve20(promptscriptDir, "project.prs");
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 = dirname16(__filename4);
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
- run();
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();