@promptscript/cli 1.16.0 → 1.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -4437,20 +4437,20 @@ var init_services = __esm({
4437
4437
  existsSync,
4438
4438
  readFileSync: readFileSync2,
4439
4439
  rename: async (...args) => {
4440
- const { rename: rename2 } = await import("node:fs/promises");
4441
- return rename2(...args);
4440
+ const { rename: rename3 } = await import("node:fs/promises");
4441
+ return rename3(...args);
4442
4442
  },
4443
4443
  rm: async (...args) => {
4444
4444
  const { rm: rm3 } = await import("node:fs/promises");
4445
4445
  return rm3(...args);
4446
4446
  },
4447
4447
  lstat: async (path2) => {
4448
- const { lstat: lstat9 } = await import("node:fs/promises");
4449
- return lstat9(path2);
4448
+ const { lstat: lstat10 } = await import("node:fs/promises");
4449
+ return lstat10(path2);
4450
4450
  },
4451
4451
  realpath: async (path2) => {
4452
- const { realpath: realpath6 } = await import("node:fs/promises");
4453
- return realpath6(path2);
4452
+ const { realpath: realpath7 } = await import("node:fs/promises");
4453
+ return realpath7(path2);
4454
4454
  }
4455
4455
  };
4456
4456
  defaultPrompts = {
@@ -18437,8 +18437,8 @@ ${f.content}`
18437
18437
  if (project) sections.push(project);
18438
18438
  const techStack = this.techStack(ast, renderer);
18439
18439
  if (techStack) sections.push(techStack);
18440
- const architecture = this.architecture(ast, renderer);
18441
- if (architecture) sections.push(architecture);
18440
+ const architecture2 = this.architecture(ast, renderer);
18441
+ if (architecture2) sections.push(architecture2);
18442
18442
  const contextSection = this.contextSection(ast, renderer);
18443
18443
  if (contextSection) sections.push(contextSection);
18444
18444
  const codeStandards = this.codeStandards(ast, renderer);
@@ -20437,8 +20437,8 @@ ${content}`);
20437
20437
  if (intro) sections.push(intro);
20438
20438
  const techStack = this.techStack(ast);
20439
20439
  if (techStack) sections.push(techStack);
20440
- const architecture = this.architecture(ast);
20441
- if (architecture) sections.push(architecture);
20440
+ const architecture2 = this.architecture(ast);
20441
+ if (architecture2) sections.push(architecture2);
20442
20442
  const context = this.contextSection(ast);
20443
20443
  if (context) sections.push(context);
20444
20444
  const codeStyle = this.codeStyle(ast);
@@ -21021,8 +21021,8 @@ ${content}`);
21021
21021
  if (identity) sections.push(identity);
21022
21022
  const techStack = this.techStack(ast, renderer);
21023
21023
  if (techStack) sections.push(techStack);
21024
- const architecture = this.architecture(ast, renderer);
21025
- if (architecture) sections.push(architecture);
21024
+ const architecture2 = this.architecture(ast, renderer);
21025
+ if (architecture2) sections.push(architecture2);
21026
21026
  const contextSection = this.contextSection(ast, renderer);
21027
21027
  if (contextSection) sections.push(contextSection);
21028
21028
  const codeStandards = this.codeStandards(ast, renderer);
@@ -26597,8 +26597,20 @@ function loadEnvOverrides() {
26597
26597
  const registryUrl = process.env["PROMPTSCRIPT_REGISTRY_URL"];
26598
26598
  const cacheTtl = process.env["PROMPTSCRIPT_CACHE_TTL"];
26599
26599
  const cacheEnabled = process.env["PROMPTSCRIPT_CACHE_ENABLED"];
26600
+ const telemetry = process.env["PROMPTSCRIPT_TELEMETRY"];
26600
26601
  const hasCache = cacheTtl || cacheEnabled;
26601
26602
  const hasRegistry = gitUrl || gitRef || registryUrl || hasCache;
26603
+ if (!hasRegistry && telemetry === void 0) {
26604
+ return overrides;
26605
+ }
26606
+ if (telemetry !== void 0) {
26607
+ const normalized = telemetry.trim().toLowerCase();
26608
+ if (["1", "true", "yes", "on"].includes(normalized)) {
26609
+ overrides.telemetry = true;
26610
+ } else if (["0", "false", "no", "off"].includes(normalized)) {
26611
+ overrides.telemetry = false;
26612
+ }
26613
+ }
26602
26614
  if (!hasRegistry) {
26603
26615
  return overrides;
26604
26616
  }
@@ -26677,6 +26689,22 @@ function userConfigToProjectRegistry(userRegistry) {
26677
26689
  }
26678
26690
  function mergeConfigs(userConfig, projectConfig, envOverrides, cliFlags) {
26679
26691
  const merged = { ...projectConfig };
26692
+ const telemetryValues = [
26693
+ userConfig.telemetry,
26694
+ projectConfig.telemetry,
26695
+ envOverrides.telemetry,
26696
+ cliFlags?.telemetry
26697
+ ];
26698
+ if (telemetryValues.includes(false)) {
26699
+ merged.telemetry = false;
26700
+ } else {
26701
+ const explicitTelemetry = telemetryValues.find(
26702
+ (value) => value !== void 0
26703
+ );
26704
+ if (explicitTelemetry !== void 0) {
26705
+ merged.telemetry = explicitTelemetry;
26706
+ }
26707
+ }
26680
26708
  if (userConfig.registry) {
26681
26709
  const userRegistry = userConfigToProjectRegistry(userConfig.registry);
26682
26710
  merged.registry = deepMergeRegistry(userRegistry, merged.registry);
@@ -27416,13 +27444,7 @@ function getSkillWrites(targets) {
27416
27444
  const skillSource = resolve7(BUNDLED_SKILLS_DIR, skillName, "SKILL.md");
27417
27445
  try {
27418
27446
  const rawSkillContent = readFileSync3(skillSource, "utf-8");
27419
- let skillContent = rawSkillContent;
27420
- const hasMarker = rawSkillContent.includes("<!-- PromptScript") || rawSkillContent.includes("# promptscript-generated:");
27421
- if (!hasMarker && rawSkillContent.startsWith("---")) {
27422
- const yamlMarker = "# promptscript-generated: true";
27423
- skillContent = `---
27424
- ${yamlMarker}${rawSkillContent.slice(3)}`;
27425
- }
27447
+ const skillContent = addPromptScriptMarker(rawSkillContent);
27426
27448
  writes.push({
27427
27449
  path: `.promptscript/skills/${skillName}/SKILL.md`,
27428
27450
  content: skillContent
@@ -27430,7 +27452,14 @@ ${yamlMarker}${rawSkillContent.slice(3)}`;
27430
27452
  for (const target of targets) {
27431
27453
  const targetSkillDir = getTargetSkillDir(target, skillName);
27432
27454
  if (targetSkillDir && !writes.some((write) => write.path === targetSkillDir.path)) {
27433
- writes.push({ path: targetSkillDir.path, content: skillContent });
27455
+ const formatter = FormatterRegistry.get(target);
27456
+ let targetContent = skillContent;
27457
+ if (formatter?.transformInjectedSkillContent) {
27458
+ targetContent = addPromptScriptMarker(
27459
+ formatter.transformInjectedSkillContent(rawSkillContent)
27460
+ );
27461
+ }
27462
+ writes.push({ path: targetSkillDir.path, content: targetContent });
27434
27463
  }
27435
27464
  }
27436
27465
  } catch {
@@ -27438,6 +27467,11 @@ ${yamlMarker}${rawSkillContent.slice(3)}`;
27438
27467
  }
27439
27468
  return writes;
27440
27469
  }
27470
+ function addPromptScriptMarker(content) {
27471
+ const hasMarker = content.includes("<!-- PromptScript") || content.includes("# promptscript-generated:");
27472
+ return !hasMarker && content.startsWith("---") ? `---
27473
+ # promptscript-generated: true${content.slice(3)}` : content;
27474
+ }
27441
27475
  async function resolveConfig(options, projectInfo, aiToolsDetection, prettierConfigPath, manifest, services) {
27442
27476
  if (options.yes) {
27443
27477
  const userConfig = await loadUserConfig();
@@ -28009,13 +28043,13 @@ var require_ipv4 = __commonJS({
28009
28043
  Object.defineProperty(exports, "__esModule", { value: true });
28010
28044
  exports.Address4 = void 0;
28011
28045
  var common = __importStar(require_common());
28012
- var constants3 = __importStar(require_constants());
28046
+ var constants4 = __importStar(require_constants());
28013
28047
  var address_error_1 = require_address_error();
28014
- var isCorrect4 = common.isCorrect(constants3.BITS);
28048
+ var isCorrect4 = common.isCorrect(constants4.BITS);
28015
28049
  var Address4 = class _Address4 {
28016
28050
  constructor(address) {
28017
28051
  this.addressMinusSuffix = "";
28018
- this.groups = constants3.GROUPS;
28052
+ this.groups = constants4.GROUPS;
28019
28053
  this.parsedAddress = [];
28020
28054
  this.parsedSubnet = "";
28021
28055
  this.subnet = "/32";
@@ -28025,15 +28059,15 @@ var require_ipv4 = __commonJS({
28025
28059
  this.isInSubnet = common.isInSubnet;
28026
28060
  this.isHostInSubnet = common.isHostInSubnet;
28027
28061
  this.address = address;
28028
- const subnet = constants3.RE_SUBNET_STRING.exec(address);
28062
+ const subnet = constants4.RE_SUBNET_STRING.exec(address);
28029
28063
  if (subnet) {
28030
28064
  this.parsedSubnet = subnet[0].replace("/", "");
28031
28065
  this.subnetMask = parseInt(this.parsedSubnet, 10);
28032
28066
  this.subnet = `/${this.subnetMask}`;
28033
- if (this.subnetMask < 0 || this.subnetMask > constants3.BITS) {
28067
+ if (this.subnetMask < 0 || this.subnetMask > constants4.BITS) {
28034
28068
  throw new address_error_1.AddressError("Invalid subnet mask.");
28035
28069
  }
28036
- address = address.replace(constants3.RE_SUBNET_STRING, "");
28070
+ address = address.replace(constants4.RE_SUBNET_STRING, "");
28037
28071
  }
28038
28072
  this.addressMinusSuffix = address;
28039
28073
  this.parsedAddress = this.parse(address);
@@ -28064,7 +28098,7 @@ var require_ipv4 = __commonJS({
28064
28098
  if (groups.some((group) => /^0\d/.test(group))) {
28065
28099
  throw new address_error_1.AddressError("IPv4 addresses can't have leading zeroes.");
28066
28100
  }
28067
- if (!address.match(constants3.RE_ADDRESS)) {
28101
+ if (!address.match(constants4.RE_ADDRESS)) {
28068
28102
  throw new address_error_1.AddressError("Invalid IPv4 address.");
28069
28103
  }
28070
28104
  return groups;
@@ -28087,7 +28121,7 @@ var require_ipv4 = __commonJS({
28087
28121
  * address.subnetMask; // 24
28088
28122
  */
28089
28123
  static fromAddressAndMask(address, mask) {
28090
- const bits = common.prefixLengthFromMask(new _Address4(mask).bigInt(), constants3.BITS);
28124
+ const bits = common.prefixLengthFromMask(new _Address4(mask).bigInt(), constants4.BITS);
28091
28125
  return new _Address4(`${address}/${bits}`);
28092
28126
  }
28093
28127
  /**
@@ -28101,9 +28135,9 @@ var require_ipv4 = __commonJS({
28101
28135
  */
28102
28136
  static fromAddressAndWildcardMask(address, wildcardMask) {
28103
28137
  const wildcard = new _Address4(wildcardMask).bigInt();
28104
- const allOnes = (BigInt(1) << BigInt(constants3.BITS)) - BigInt(1);
28138
+ const allOnes = (BigInt(1) << BigInt(constants4.BITS)) - BigInt(1);
28105
28139
  const mask = wildcard ^ allOnes;
28106
- const bits = common.prefixLengthFromMask(mask, constants3.BITS);
28140
+ const bits = common.prefixLengthFromMask(mask, constants4.BITS);
28107
28141
  return new _Address4(`${address}/${bits}`);
28108
28142
  }
28109
28143
  /**
@@ -28121,7 +28155,7 @@ var require_ipv4 = __commonJS({
28121
28155
  */
28122
28156
  static fromWildcard(input2) {
28123
28157
  const groups = input2.split(".");
28124
- if (groups.length !== constants3.GROUPS) {
28158
+ if (groups.length !== constants4.GROUPS) {
28125
28159
  throw new address_error_1.AddressError("Wildcard pattern must have 4 octets");
28126
28160
  }
28127
28161
  let firstWildcard = -1;
@@ -28136,7 +28170,7 @@ var require_ipv4 = __commonJS({
28136
28170
  }
28137
28171
  const trailing = firstWildcard === -1 ? 0 : groups.length - firstWildcard;
28138
28172
  const replaced = groups.map((g) => g === "*" ? "0" : g);
28139
- const subnetBits = constants3.BITS - trailing * 8;
28173
+ const subnetBits = constants4.BITS - trailing * 8;
28140
28174
  return new _Address4(`${replaced.join(".")}/${subnetBits}`);
28141
28175
  }
28142
28176
  /**
@@ -28206,7 +28240,7 @@ var require_ipv4 = __commonJS({
28206
28240
  toGroup6() {
28207
28241
  const output = [];
28208
28242
  let i;
28209
- for (i = 0; i < constants3.GROUPS; i += 2) {
28243
+ for (i = 0; i < constants4.GROUPS; i += 2) {
28210
28244
  output.push(`${common.stringToPaddedHex(this.parsedAddress[i])}${common.stringToPaddedHex(this.parsedAddress[i + 1])}`);
28211
28245
  }
28212
28246
  return output.join(":");
@@ -28223,7 +28257,7 @@ var require_ipv4 = __commonJS({
28223
28257
  * @returns {bigint}
28224
28258
  */
28225
28259
  _startAddress() {
28226
- return BigInt(`0b${this.mask() + "0".repeat(constants3.BITS - this.subnetMask)}`);
28260
+ return BigInt(`0b${this.mask() + "0".repeat(constants4.BITS - this.subnetMask)}`);
28227
28261
  }
28228
28262
  /**
28229
28263
  * The first address in the range given by this address' subnet.
@@ -28247,7 +28281,7 @@ var require_ipv4 = __commonJS({
28247
28281
  * @returns {bigint}
28248
28282
  */
28249
28283
  _endAddress() {
28250
- return BigInt(`0b${this.mask() + "1".repeat(constants3.BITS - this.subnetMask)}`);
28284
+ return BigInt(`0b${this.mask() + "1".repeat(constants4.BITS - this.subnetMask)}`);
28251
28285
  }
28252
28286
  /**
28253
28287
  * The last address in the range given by this address' subnet
@@ -28272,7 +28306,7 @@ var require_ipv4 = __commonJS({
28272
28306
  * @returns {Address4}
28273
28307
  */
28274
28308
  subnetMaskAddress() {
28275
- return _Address4.fromBigInt(BigInt(`0b${"1".repeat(this.subnetMask)}${"0".repeat(constants3.BITS - this.subnetMask)}`));
28309
+ return _Address4.fromBigInt(BigInt(`0b${"1".repeat(this.subnetMask)}${"0".repeat(constants4.BITS - this.subnetMask)}`));
28276
28310
  }
28277
28311
  /**
28278
28312
  * The Cisco-style wildcard mask, e.g. `0.0.0.255` for a `/24`. This is
@@ -28281,7 +28315,7 @@ var require_ipv4 = __commonJS({
28281
28315
  * @returns {Address4}
28282
28316
  */
28283
28317
  wildcardMask() {
28284
- return _Address4.fromBigInt(BigInt(`0b${"0".repeat(this.subnetMask)}${"1".repeat(constants3.BITS - this.subnetMask)}`));
28318
+ return _Address4.fromBigInt(BigInt(`0b${"0".repeat(this.subnetMask)}${"1".repeat(constants4.BITS - this.subnetMask)}`));
28285
28319
  }
28286
28320
  /**
28287
28321
  * The network address in CIDR string form, e.g. `192.168.1.0/24` for
@@ -28416,7 +28450,7 @@ var require_ipv4 = __commonJS({
28416
28450
  */
28417
28451
  binaryZeroPad() {
28418
28452
  if (this._binaryZeroPad === void 0) {
28419
- this._binaryZeroPad = this.bigInt().toString(2).padStart(constants3.BITS, "0");
28453
+ this._binaryZeroPad = this.bigInt().toString(2).padStart(constants4.BITS, "0");
28420
28454
  }
28421
28455
  return this._binaryZeroPad;
28422
28456
  }
@@ -28426,7 +28460,7 @@ var require_ipv4 = __commonJS({
28426
28460
  */
28427
28461
  groupForV6() {
28428
28462
  const segments = this.parsedAddress;
28429
- return this.correctForm().replace(constants3.RE_ADDRESS, `<span class="hover-group group-v4 group-6">${segments.slice(0, 2).join(".")}</span>.<span class="hover-group group-v4 group-7">${segments.slice(2, 4).join(".")}</span>`);
28463
+ return this.correctForm().replace(constants4.RE_ADDRESS, `<span class="hover-group group-v4 group-6">${segments.slice(0, 2).join(".")}</span>.<span class="hover-group group-v4 group-7">${segments.slice(2, 4).join(".")}</span>`);
28430
28464
  }
28431
28465
  };
28432
28466
  exports.Address4 = Address4;
@@ -31082,9 +31116,9 @@ var require_rate_limit = __commonJS({
31082
31116
  throw new Error("The configured rate-limit store does not implement a `read` method, which is required to use `{ increment: false }`");
31083
31117
  }
31084
31118
  try {
31085
- const res = await new Promise((resolve37, reject) => {
31119
+ const res = await new Promise((resolve38, reject) => {
31086
31120
  store[storeMethod](key, (err, res2) => {
31087
- err ? reject(err) : resolve37(res2);
31121
+ err ? reject(err) : resolve38(res2);
31088
31122
  }, timeWindow, max);
31089
31123
  });
31090
31124
  current = res.current;
@@ -31191,13 +31225,13 @@ var init_health = __esm({
31191
31225
  });
31192
31226
 
31193
31227
  // packages/server/src/source-dirs.ts
31194
- import { readFile as readFile28 } from "fs/promises";
31195
- import { join as join28, dirname as dirname22 } from "path";
31228
+ import { readFile as readFile29 } from "fs/promises";
31229
+ import { join as join31, dirname as dirname23 } from "path";
31196
31230
  import { parse as parseYaml16 } from "yaml";
31197
31231
  async function readProjectConfig(workspace) {
31198
31232
  for (const filename of CONFIG_FILENAMES) {
31199
31233
  try {
31200
- const raw = await readFile28(join28(workspace, filename), "utf-8");
31234
+ const raw = await readFile29(join31(workspace, filename), "utf-8");
31201
31235
  const config = parseYaml16(raw);
31202
31236
  if (config) return config;
31203
31237
  } catch {
@@ -31208,10 +31242,10 @@ async function readProjectConfig(workspace) {
31208
31242
  async function resolveSourceDir(workspace) {
31209
31243
  for (const filename of CONFIG_FILENAMES) {
31210
31244
  try {
31211
- const raw = await readFile28(join28(workspace, filename), "utf-8");
31245
+ const raw = await readFile29(join31(workspace, filename), "utf-8");
31212
31246
  const config = parseYaml16(raw);
31213
31247
  if (config?.input?.entry) {
31214
- return dirname22(config.input.entry);
31248
+ return dirname23(config.input.entry);
31215
31249
  }
31216
31250
  } catch {
31217
31251
  }
@@ -31225,9 +31259,9 @@ async function resolveFileGlobs(workspace) {
31225
31259
  async function resolveWatchPaths(workspace) {
31226
31260
  const sourceDir = await resolveSourceDir(workspace);
31227
31261
  return [
31228
- join28(workspace, sourceDir),
31229
- join28(workspace, "promptscript.yaml"),
31230
- join28(workspace, "promptscript.yml")
31262
+ join31(workspace, sourceDir),
31263
+ join31(workspace, "promptscript.yaml"),
31264
+ join31(workspace, "promptscript.yml")
31231
31265
  ];
31232
31266
  }
31233
31267
  var DEFAULT_SOURCE_DIR, CONFIG_FILENAMES;
@@ -31254,32 +31288,32 @@ var init_config2 = __esm({
31254
31288
  });
31255
31289
 
31256
31290
  // packages/server/src/path-guard.ts
31257
- import { resolve as resolve35, relative as relative16, isAbsolute as isAbsolute15, dirname as dirname23, basename as basename18 } from "path";
31258
- import { realpath as realpath5 } from "fs/promises";
31291
+ import { resolve as resolve36, relative as relative16, isAbsolute as isAbsolute15, dirname as dirname24, basename as basename18 } from "path";
31292
+ import { realpath as realpath6 } from "fs/promises";
31259
31293
  async function resolveSafePath(workspace, requestedPath) {
31260
31294
  const decoded = decodeURIComponent(requestedPath);
31261
31295
  if (isAbsolute15(decoded)) {
31262
31296
  throw new PathTraversalError(requestedPath);
31263
31297
  }
31264
- const resolved = resolve35(workspace, decoded);
31298
+ const resolved = resolve36(workspace, decoded);
31265
31299
  const rel = relative16(workspace, resolved);
31266
31300
  if (rel.startsWith("..") || isAbsolute15(rel)) {
31267
31301
  throw new PathTraversalError(requestedPath);
31268
31302
  }
31269
31303
  let realWorkspace;
31270
31304
  try {
31271
- realWorkspace = await realpath5(workspace);
31305
+ realWorkspace = await realpath6(workspace);
31272
31306
  } catch {
31273
31307
  realWorkspace = workspace;
31274
31308
  }
31275
31309
  let real;
31276
31310
  try {
31277
- real = await realpath5(resolved);
31311
+ real = await realpath6(resolved);
31278
31312
  } catch (err) {
31279
31313
  if (err instanceof Error && "code" in err && err.code === "ENOENT") {
31280
31314
  try {
31281
- const realParent = await realpath5(dirname23(resolved));
31282
- real = resolve35(realParent, basename18(resolved));
31315
+ const realParent = await realpath6(dirname24(resolved));
31316
+ real = resolve36(realParent, basename18(resolved));
31283
31317
  } catch {
31284
31318
  return resolved;
31285
31319
  }
@@ -31307,8 +31341,8 @@ var init_path_guard = __esm({
31307
31341
  });
31308
31342
 
31309
31343
  // packages/server/src/routes/files.ts
31310
- import { readFile as readFile29, writeFile as writeFile12, unlink as unlink3, stat as stat3, mkdir as mkdir11 } from "fs/promises";
31311
- import { dirname as dirname24 } from "path";
31344
+ import { readFile as readFile30, writeFile as writeFile13, unlink as unlink3, stat as stat3, mkdir as mkdir12 } from "fs/promises";
31345
+ import { dirname as dirname25 } from "path";
31312
31346
  import fg from "fast-glob";
31313
31347
  function registerRoutes(app, workspace, readOnly) {
31314
31348
  app.get("/api/files", async () => {
@@ -31328,7 +31362,7 @@ function registerRoutes(app, workspace, readOnly) {
31328
31362
  const filePath = request.params["*"];
31329
31363
  try {
31330
31364
  const resolved = await resolveSafePath(workspace, filePath);
31331
- const content = await readFile29(resolved, "utf-8");
31365
+ const content = await readFile30(resolved, "utf-8");
31332
31366
  return { path: filePath, content };
31333
31367
  } catch (err) {
31334
31368
  if (err instanceof PathTraversalError) {
@@ -31345,7 +31379,7 @@ function registerRoutes(app, workspace, readOnly) {
31345
31379
  const filePath = request.params["*"];
31346
31380
  try {
31347
31381
  const resolved = await resolveSafePath(workspace, filePath);
31348
- await writeFile12(resolved, request.body.content, "utf-8");
31382
+ await writeFile13(resolved, request.body.content, "utf-8");
31349
31383
  return { path: filePath, status: "updated" };
31350
31384
  } catch (err) {
31351
31385
  if (err instanceof PathTraversalError) {
@@ -31368,8 +31402,8 @@ function registerRoutes(app, workspace, readOnly) {
31368
31402
  return reply.status(409).send({ error: "File already exists" });
31369
31403
  } catch {
31370
31404
  }
31371
- await mkdir11(dirname24(resolved), { recursive: true });
31372
- await writeFile12(resolved, request.body.content, "utf-8");
31405
+ await mkdir12(dirname25(resolved), { recursive: true });
31406
+ await writeFile13(resolved, request.body.content, "utf-8");
31373
31407
  return reply.status(201).send({ path: filePath, status: "created" });
31374
31408
  } catch (err) {
31375
31409
  if (err instanceof PathTraversalError) {
@@ -31597,7 +31631,7 @@ var migrate_exports = {};
31597
31631
  __export(migrate_exports, {
31598
31632
  migrateCommand: () => migrateCommand
31599
31633
  });
31600
- import { dirname as dirname25, relative as relative18, resolve as resolve36 } from "path";
31634
+ import { dirname as dirname26, relative as relative18, resolve as resolve37 } from "path";
31601
31635
  import { parse as parseYaml17 } from "yaml";
31602
31636
  async function migrateCommand(options, services = createDefaultServices()) {
31603
31637
  const previousOutputStream = getContext().outputStream;
@@ -31617,7 +31651,7 @@ async function runMigrateCommand(options, services) {
31617
31651
  ConsoleOutput.info("No migration candidates found. No files changed.");
31618
31652
  return;
31619
31653
  }
31620
- const configPath = findProjectConfig(services);
31654
+ const configPath = findProjectConfig2(services);
31621
31655
  if (!configPath) {
31622
31656
  await initCommand(
31623
31657
  {
@@ -31730,7 +31764,7 @@ function validateOptions(options) {
31730
31764
  throw new Error("--static and --llm cannot be used together");
31731
31765
  }
31732
31766
  }
31733
- function findProjectConfig(services) {
31767
+ function findProjectConfig2(services) {
31734
31768
  const envConfig = process.env["PROMPTSCRIPT_CONFIG"];
31735
31769
  if (envConfig && services.fs.existsSync(envConfig)) {
31736
31770
  return envConfig;
@@ -31781,7 +31815,7 @@ function extractTargetNames(targets) {
31781
31815
  }
31782
31816
  async function createStaticMigrationWrites(candidates, config, services) {
31783
31817
  const result = await importMultipleFiles(
31784
- candidates.map((candidate) => resolve36(services.cwd, candidate.path)),
31818
+ candidates.map((candidate) => resolve37(services.cwd, candidate.path)),
31785
31819
  {
31786
31820
  projectName: config.id,
31787
31821
  syntaxVersion: config.syntax || getLatestSyntaxVersion()
@@ -31873,7 +31907,7 @@ ${content}`;
31873
31907
  }
31874
31908
  function toUsePath(entryPath, targetPath) {
31875
31909
  const withoutExtension = targetPath.replace(/\.prs$/, "");
31876
- const path2 = relative18(dirname25(entryPath), withoutExtension).replace(/\\/g, "/");
31910
+ const path2 = relative18(dirname26(entryPath), withoutExtension).replace(/\\/g, "/");
31877
31911
  return path2.startsWith(".") ? path2 : `./${path2}`;
31878
31912
  }
31879
31913
  function validateProjectPath(path2) {
@@ -31933,16 +31967,16 @@ init_src();
31933
31967
  init_init();
31934
31968
  import { Command } from "commander";
31935
31969
  import { fileURLToPath as fileURLToPath4, pathToFileURL } from "url";
31936
- import { dirname as dirname26 } from "path";
31970
+ import { dirname as dirname27 } from "path";
31937
31971
 
31938
31972
  // packages/cli/src/commands/compile.ts
31939
- import { resolve as resolve19, dirname as dirname15, isAbsolute as isAbsolute13, relative as relative13 } from "path";
31973
+ import { resolve as resolve19, dirname as dirname15, isAbsolute as isAbsolute12, relative as relative13 } from "path";
31940
31974
  import { fileURLToPath as fileURLToPath2 } from "url";
31941
31975
  import { chmod, writeFile as writeFile4, mkdir as mkdir5, readFile as readFile15 } from "fs/promises";
31942
31976
  import { existsSync as existsSync13 } from "fs";
31943
31977
  import chokidar from "chokidar";
31944
31978
 
31945
- // node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js
31979
+ // node_modules/.pnpm/balanced-match@4.0.3/node_modules/balanced-match/dist/esm/index.js
31946
31980
  var balanced = (a, b, str) => {
31947
31981
  const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
31948
31982
  const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
@@ -33722,10 +33756,10 @@ var Minimatch = class {
33722
33756
  }
33723
33757
  return filtered.join("/");
33724
33758
  }).join("|");
33725
- const [open3, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
33726
- re = "^" + open3 + re + close + "$";
33759
+ const [open4, close] = set.length > 1 ? ["(?:", ")"] : ["", ""];
33760
+ re = "^" + open4 + re + close + "$";
33727
33761
  if (this.partial) {
33728
- re = "^(?:\\/|" + open3 + re.slice(1, -1) + close + ")$";
33762
+ re = "^(?:\\/|" + open4 + re.slice(1, -1) + close + ")$";
33729
33763
  }
33730
33764
  if (this.negate)
33731
33765
  re = "^(?!" + re + ").+$";
@@ -38017,14 +38051,66 @@ async function discoverNativeContent(dir) {
38017
38051
 
38018
38052
  // packages/resolver/src/vendor-manifest.ts
38019
38053
  import { createHash as createHash3 } from "crypto";
38020
- import { execFile } from "child_process";
38021
- import { lstat as lstat5, readFile as readFile12, readdir as readdir4, realpath as realpath3 } from "fs/promises";
38022
- import { basename as basename10, dirname as dirname9, isAbsolute as isAbsolute8, join as join9, posix as posix2, relative as relative7, sep as sep5 } from "path";
38054
+ import { execFile, spawn } from "child_process";
38055
+ import { constants as constants2 } from "fs";
38056
+ import { lstat as lstat5, open as open2, readFile as readFile12, readdir as readdir4, readlink, realpath as realpath3 } from "fs/promises";
38057
+ import { basename as basename10, dirname as dirname9, join as join9, posix as posix2, relative as relative7, sep as sep5 } from "path";
38023
38058
  import { promisify } from "util";
38024
38059
  var VENDOR_MANIFEST_FILE = ".vendor-manifest.json";
38025
38060
  var VENDOR_GIT_DIR = ".promptscript-git";
38026
38061
  var execFileAsync = promisify(execFile);
38027
38062
  var NULL_DEVICE = process.platform === "win32" ? "NUL" : "/dev/null";
38063
+ var MAX_GIT_STDERR_BYTES = 64 * 1024;
38064
+ var MAX_GIT_TREE_RECORD_BYTES = 1024 * 1024;
38065
+ var MAX_GIT_CONFIG_BYTES = 1024 * 1024;
38066
+ function hashGitBlob(content) {
38067
+ return createHash3("sha1").update(`blob ${content.length}\0`).update(content).digest("hex");
38068
+ }
38069
+ async function readBoundedRegularFile(filePath, maxBytes, description) {
38070
+ const flags = constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0) | (constants2.O_NONBLOCK ?? 0);
38071
+ const handle = await open2(filePath, flags);
38072
+ try {
38073
+ const metadata = await handle.stat();
38074
+ if (!metadata.isFile()) {
38075
+ throw new Error(`${description} is not a regular file: ${filePath}`);
38076
+ }
38077
+ const content = Buffer.alloc(maxBytes + 1);
38078
+ let offset = 0;
38079
+ while (offset < content.length) {
38080
+ const { bytesRead } = await handle.read(content, offset, content.length - offset, null);
38081
+ if (bytesRead === 0) {
38082
+ break;
38083
+ }
38084
+ offset += bytesRead;
38085
+ }
38086
+ if (offset > maxBytes) {
38087
+ throw new Error(`${description} exceeds ${maxBytes} bytes: ${filePath}`);
38088
+ }
38089
+ return content.subarray(0, offset);
38090
+ } finally {
38091
+ await handle.close();
38092
+ }
38093
+ }
38094
+ async function resolveSafeRepositorySymlink(symlinkPath, repositoryRoot, gitMetadataRoot) {
38095
+ let targetPath;
38096
+ try {
38097
+ targetPath = await realpath3(symlinkPath);
38098
+ } catch (error) {
38099
+ throw new Error(`Symbolic links must resolve inside vendor repositories: ${symlinkPath}`, {
38100
+ cause: error
38101
+ });
38102
+ }
38103
+ if (targetPath === repositoryRoot || !isInsideCachePath(targetPath, repositoryRoot) || isInsideCachePath(targetPath, gitMetadataRoot)) {
38104
+ throw new Error(`Symbolic links must resolve inside vendor repositories: ${symlinkPath}`);
38105
+ }
38106
+ const targetMetadata = await lstat5(targetPath);
38107
+ if (!targetMetadata.isFile()) {
38108
+ throw new Error(
38109
+ `Symbolic links must resolve to regular files in vendor repositories: ${symlinkPath}`
38110
+ );
38111
+ }
38112
+ return targetPath;
38113
+ }
38028
38114
  function getVendorRepositoryRelativePath(repoUrl) {
38029
38115
  const sshMatch = /^git@([^:]+):(.+)$/.exec(repoUrl);
38030
38116
  const candidate = sshMatch ? `https://${sshMatch[1]}/${sshMatch[2]}` : /^[a-z][a-z\d+.-]*:\/\//i.test(repoUrl) ? repoUrl : `https://${repoUrl}`;
@@ -38062,6 +38148,8 @@ function isValidVendorManifest(value) {
38062
38148
  }
38063
38149
  async function hashVendorRepository(directory) {
38064
38150
  const hash = createHash3("sha256");
38151
+ const repositoryRoot = await realpath3(directory);
38152
+ const gitMetadataRoot = join9(repositoryRoot, VENDOR_GIT_DIR);
38065
38153
  async function visit(currentDirectory, prefix) {
38066
38154
  const entries = await readdir4(currentDirectory, { withFileTypes: true });
38067
38155
  entries.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
@@ -38077,9 +38165,11 @@ async function hashVendorRepository(directory) {
38077
38165
  const entryPath = join9(currentDirectory, entry.name);
38078
38166
  const relativePath = posix2.join(prefix, entry.name);
38079
38167
  if (entry.isSymbolicLink()) {
38080
- throw new Error(`Symbolic links are not allowed in vendor repositories: ${entryPath}`);
38081
- }
38082
- if (entry.isDirectory()) {
38168
+ await resolveSafeRepositorySymlink(entryPath, repositoryRoot, gitMetadataRoot);
38169
+ hash.update(`symlink:${relativePath}\0`);
38170
+ hash.update(await readlink(entryPath, { encoding: "buffer" }));
38171
+ hash.update("\0");
38172
+ } else if (entry.isDirectory()) {
38083
38173
  hash.update(`directory:${relativePath}\0`);
38084
38174
  await visit(entryPath, relativePath);
38085
38175
  } else if (entry.isFile()) {
@@ -38107,23 +38197,43 @@ async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expected
38107
38197
  }
38108
38198
  }
38109
38199
  }
38200
+ async function rejectMetadataPath(relativePath, message) {
38201
+ try {
38202
+ await lstat5(join9(gitDir, ...relativePath.split("/")));
38203
+ throw new Error(`${message}: ${gitDir}`);
38204
+ } catch (error) {
38205
+ if (!(typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")) {
38206
+ throw error;
38207
+ }
38208
+ }
38209
+ }
38110
38210
  const gitMetadata = await lstat5(gitDir);
38111
38211
  if (!gitMetadata.isDirectory() || gitMetadata.isSymbolicLink()) {
38112
38212
  throw new Error(`Invalid vendor Git metadata directory: ${gitDir}`);
38113
38213
  }
38114
38214
  await rejectMetadataSymlinks(gitDir);
38115
- const localConfig = await readFile12(join9(gitDir, "config"), "utf-8");
38116
- if (/^\s*\[(?:include|includeif)\b/im.test(localConfig) || /\bpromisor\s*=\s*true\b/i.test(localConfig) || /\bpartialclone/i.test(localConfig)) {
38215
+ const [repositoryRoot, gitMetadataRoot] = await Promise.all([
38216
+ realpath3(directory),
38217
+ realpath3(gitDir)
38218
+ ]);
38219
+ const localConfigContent = await readBoundedRegularFile(
38220
+ join9(gitDir, "config"),
38221
+ MAX_GIT_CONFIG_BYTES,
38222
+ "Vendor Git config"
38223
+ );
38224
+ const localConfig = localConfigContent.toString("utf-8");
38225
+ if (/^[ \t]*\[(?:include|includeif)\b/im.test(localConfig) || /\bpromisor\s*=\s*true\b/i.test(localConfig) || /\bpartialclone/i.test(localConfig) || /\bworktreeconfig\s*=\s*true\b/i.test(localConfig)) {
38117
38226
  throw new Error(`External or partial Git object sources are not allowed: ${gitDir}`);
38118
38227
  }
38119
- try {
38120
- await lstat5(join9(gitDir, "objects", "info", "alternates"));
38121
- throw new Error(`Git object alternates are not allowed in vendor metadata: ${gitDir}`);
38122
- } catch (error) {
38123
- if (!(typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")) {
38124
- throw error;
38125
- }
38126
- }
38228
+ await rejectMetadataPath("commondir", "Git common directories are not allowed");
38229
+ await rejectMetadataPath(
38230
+ "objects/info/alternates",
38231
+ "Git object alternates are not allowed in vendor metadata"
38232
+ );
38233
+ await rejectMetadataPath(
38234
+ "objects/info/http-alternates",
38235
+ "Git HTTP alternates are not allowed in vendor metadata"
38236
+ );
38127
38237
  const commonArgs = [`--git-dir=${gitDir}`, `--work-tree=${directory}`];
38128
38238
  const environment = {
38129
38239
  PATH: process.env["PATH"],
@@ -38141,27 +38251,96 @@ async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expected
38141
38251
  if (head.stdout.trim().toLowerCase() !== expectedCommit.toLowerCase()) {
38142
38252
  throw new Error(`Vendored repository commit does not match the lockfile: ${directory}`);
38143
38253
  }
38144
- const tree = await execFileAsync("git", [...commonArgs, "ls-tree", "-r", "-z", expectedCommit], {
38145
- env: environment
38146
- });
38147
38254
  const trackedFiles = /* @__PURE__ */ new Map();
38148
- for (const row of tree.stdout.split("\0").filter(Boolean)) {
38149
- const separator = row.indexOf(" ");
38150
- const metadata = row.slice(0, separator).split(" ");
38151
- const path2 = row.slice(separator + 1);
38152
- if (separator < 0 || metadata.length !== 3 || metadata[1] !== "blob") {
38153
- throw new Error(`Unsupported Git tree entry in vendored repository: ${path2}`);
38154
- }
38155
- const mode = metadata[0];
38156
- if (mode !== "100644" && mode !== "100755") {
38157
- throw new Error(`Unsupported Git tree mode in vendored repository: ${path2}`);
38158
- }
38159
- trackedFiles.set(path2, {
38160
- objectId: metadata[2],
38161
- executable: mode === "100755"
38255
+ const treeProcess = spawn("git", [...commonArgs, "ls-tree", "-r", "-z", expectedCommit], {
38256
+ env: environment,
38257
+ stdio: ["ignore", "pipe", "pipe"]
38258
+ });
38259
+ let processError = null;
38260
+ const processCompletion = new Promise((resolveProcess) => {
38261
+ treeProcess.once("error", (error) => {
38262
+ processError = new Error(`Failed to run git ls-tree: ${error.message}`, { cause: error });
38263
+ resolveProcess({ code: null, signal: null });
38162
38264
  });
38163
- }
38164
- const worktreeFiles = [];
38265
+ treeProcess.once("close", (code2, signal2) => {
38266
+ resolveProcess({ code: code2, signal: signal2 });
38267
+ });
38268
+ });
38269
+ const stderrChunks = [];
38270
+ let stderrBytes = 0;
38271
+ let stderrTruncated = false;
38272
+ const stderrCompletion = (async () => {
38273
+ for await (const chunk of treeProcess.stderr) {
38274
+ const remaining = MAX_GIT_STDERR_BYTES - stderrBytes;
38275
+ if (remaining > 0) {
38276
+ const captured = chunk.subarray(0, remaining);
38277
+ stderrChunks.push(Buffer.from(captured));
38278
+ stderrBytes += captured.length;
38279
+ }
38280
+ if (chunk.length > remaining) {
38281
+ stderrTruncated = true;
38282
+ }
38283
+ }
38284
+ })();
38285
+ let pendingRecord = Buffer.alloc(0);
38286
+ let stdoutError = null;
38287
+ try {
38288
+ for await (const chunk of treeProcess.stdout) {
38289
+ const output = pendingRecord.length > 0 ? Buffer.concat([pendingRecord, chunk]) : chunk;
38290
+ let recordStart = 0;
38291
+ let separator = output.indexOf(0, recordStart);
38292
+ while (separator >= 0) {
38293
+ if (separator > recordStart) {
38294
+ const row = output.toString("utf8", recordStart, separator);
38295
+ const metadataSeparator = row.indexOf(" ");
38296
+ const metadata = row.slice(0, metadataSeparator).split(" ");
38297
+ const path2 = row.slice(metadataSeparator + 1);
38298
+ const [mode, entryType, objectId, ...extraFields] = metadata;
38299
+ if (metadataSeparator < 0 || entryType !== "blob" || extraFields.length > 0 || mode === void 0 || objectId === void 0) {
38300
+ throw new Error(`Unsupported Git tree entry in vendored repository: ${path2}`);
38301
+ }
38302
+ if (mode !== "100644" && mode !== "100755" && mode !== "120000") {
38303
+ throw new Error(`Unsupported Git tree mode in vendored repository: ${path2}`);
38304
+ }
38305
+ trackedFiles.set(path2, {
38306
+ objectId,
38307
+ executable: mode === "100755",
38308
+ symbolicLink: mode === "120000"
38309
+ });
38310
+ }
38311
+ recordStart = separator + 1;
38312
+ separator = output.indexOf(0, recordStart);
38313
+ }
38314
+ pendingRecord = recordStart < output.length ? Buffer.from(output.subarray(recordStart)) : Buffer.alloc(0);
38315
+ if (pendingRecord.length > MAX_GIT_TREE_RECORD_BYTES) {
38316
+ throw new Error(`Git ls-tree record exceeds ${MAX_GIT_TREE_RECORD_BYTES} bytes`);
38317
+ }
38318
+ }
38319
+ } catch (error) {
38320
+ stdoutError = error instanceof Error ? error : new Error(String(error), { cause: error });
38321
+ treeProcess.kill();
38322
+ }
38323
+ const [{ code, signal }] = await Promise.all([processCompletion, stderrCompletion]);
38324
+ if (processError) {
38325
+ throw processError;
38326
+ }
38327
+ if (stdoutError) {
38328
+ throw stdoutError;
38329
+ }
38330
+ if (code !== 0) {
38331
+ const stderrOutput = Buffer.concat(stderrChunks, stderrBytes).toString("utf8");
38332
+ const stderrDetails = stderrTruncated ? `${stderrOutput}
38333
+ [stderr truncated]` : stderrOutput;
38334
+ const details = stderrDetails.trim();
38335
+ const status = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
38336
+ throw new Error(`Git ls-tree failed with ${status}${details ? `: ${details}` : ""}`);
38337
+ }
38338
+ if (pendingRecord.length > 0) {
38339
+ throw new Error("Git ls-tree output ended with an unterminated record");
38340
+ }
38341
+ let worktreeFileCount = 0;
38342
+ const regularFiles = [];
38343
+ const worktreeSymlinks = /* @__PURE__ */ new Map();
38165
38344
  async function collectFiles(currentDirectory, prefix) {
38166
38345
  const entries = await readdir4(currentDirectory, { withFileTypes: true });
38167
38346
  for (const entry of entries) {
@@ -38171,13 +38350,32 @@ async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expected
38171
38350
  const entryPath = join9(currentDirectory, entry.name);
38172
38351
  const relativePath = posix2.join(prefix, entry.name);
38173
38352
  if (entry.isSymbolicLink()) {
38174
- throw new Error(`Symbolic links are not allowed in vendor repositories: ${entryPath}`);
38175
- }
38176
- if (entry.isDirectory()) {
38353
+ const tracked = trackedFiles.get(relativePath);
38354
+ if (!tracked?.symbolicLink) {
38355
+ throw new Error(`Symbolic links are not allowed for regular files: ${entryPath}`);
38356
+ }
38357
+ const targetPath = await resolveSafeRepositorySymlink(
38358
+ entryPath,
38359
+ repositoryRoot,
38360
+ gitMetadataRoot
38361
+ );
38362
+ const targetRelativePath = relative7(repositoryRoot, targetPath).split(sep5).join("/");
38363
+ const trackedTarget = trackedFiles.get(targetRelativePath);
38364
+ if (!trackedTarget || trackedTarget.symbolicLink) {
38365
+ throw new Error(`Symbolic links must target tracked regular files: ${entryPath}`);
38366
+ }
38367
+ worktreeFileCount += 1;
38368
+ worktreeSymlinks.set(relativePath, await readlink(entryPath, { encoding: "buffer" }));
38369
+ } else if (entry.isDirectory()) {
38177
38370
  await collectFiles(entryPath, relativePath);
38178
38371
  } else if (entry.isFile()) {
38179
38372
  if (!allowedUntrackedFiles.has(relativePath)) {
38180
38373
  const tracked = trackedFiles.get(relativePath);
38374
+ if (tracked?.symbolicLink) {
38375
+ throw new Error(
38376
+ `Vendored repository symbolic links do not match commit ${expectedCommit}`
38377
+ );
38378
+ }
38181
38379
  if (tracked && process.platform !== "win32") {
38182
38380
  const metadata = await lstat5(entryPath);
38183
38381
  if (Boolean(metadata.mode & 73) !== tracked.executable) {
@@ -38186,7 +38384,8 @@ async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expected
38186
38384
  );
38187
38385
  }
38188
38386
  }
38189
- worktreeFiles.push(relativePath);
38387
+ worktreeFileCount += 1;
38388
+ regularFiles.push(relativePath);
38190
38389
  }
38191
38390
  } else {
38192
38391
  throw new Error(`Unsupported vendor repository entry: ${entryPath}`);
@@ -38194,12 +38393,12 @@ async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expected
38194
38393
  }
38195
38394
  }
38196
38395
  await collectFiles(directory, "");
38197
- if (worktreeFiles.length !== trackedFiles.size || worktreeFiles.some((path2) => !trackedFiles.has(path2))) {
38396
+ if (worktreeFileCount !== trackedFiles.size || regularFiles.some((path2) => !trackedFiles.has(path2))) {
38198
38397
  throw new Error(`Vendored repository contents do not match commit ${expectedCommit}`);
38199
38398
  }
38200
38399
  const batchSize = 100;
38201
- for (let index = 0; index < worktreeFiles.length; index += batchSize) {
38202
- const paths = worktreeFiles.slice(index, index + batchSize);
38400
+ for (let index = 0; index < regularFiles.length; index += batchSize) {
38401
+ const paths = regularFiles.slice(index, index + batchSize);
38203
38402
  const filePaths = paths.map((path2) => join9(directory, ...path2.split("/")));
38204
38403
  const objects = await execFileAsync(
38205
38404
  "git",
@@ -38211,6 +38410,12 @@ async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expected
38211
38410
  throw new Error(`Vendored repository contents do not match commit ${expectedCommit}`);
38212
38411
  }
38213
38412
  }
38413
+ for (const [path2, linkTarget] of worktreeSymlinks) {
38414
+ const tracked = trackedFiles.get(path2);
38415
+ if (!tracked || hashGitBlob(linkTarget) !== tracked.objectId) {
38416
+ throw new Error(`Vendored repository contents do not match commit ${expectedCommit}`);
38417
+ }
38418
+ }
38214
38419
  }
38215
38420
  async function verifyVendoredGitRepository(directory, expectedCommit) {
38216
38421
  await verifyGitRepositoryCheckout(directory, VENDOR_GIT_DIR, expectedCommit);
@@ -38276,8 +38481,7 @@ async function resolveVendoredRepository(vendorDir, repoUrl, expectedVersion, ex
38276
38481
  realpath3(fullPath),
38277
38482
  lstat5(fullPath)
38278
38483
  ]);
38279
- const containment = relative7(vendorRealPath, repositoryRealPath);
38280
- if (containment === ".." || containment.startsWith(`..${sep5}`) || isAbsolute8(containment) || repositoryRealPath === vendorRealPath) {
38484
+ if (!isInsideCachePath(repositoryRealPath, vendorRealPath) || repositoryRealPath === vendorRealPath) {
38281
38485
  throw new Error(`Vendored dependency escapes the vendor directory: ${repoUrl}`);
38282
38486
  }
38283
38487
  if (metadata.isSymbolicLink() || !metadata.isDirectory()) {
@@ -39380,7 +39584,7 @@ var HttpRegistry = class {
39380
39584
  * Sleep for specified milliseconds.
39381
39585
  */
39382
39586
  sleep(ms) {
39383
- return new Promise((resolve37) => setTimeout(resolve37, ms));
39587
+ return new Promise((resolve38) => setTimeout(resolve38, ms));
39384
39588
  }
39385
39589
  /**
39386
39590
  * Fetch with timeout.
@@ -39422,7 +39626,7 @@ var HttpRegistry = class {
39422
39626
  */
39423
39627
  async fetchWithRetry(url) {
39424
39628
  let lastError;
39425
- let delay = this.initialDelay;
39629
+ let delay2 = this.initialDelay;
39426
39630
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
39427
39631
  try {
39428
39632
  const response = await this.fetchWithTimeout(url, {
@@ -39441,8 +39645,8 @@ var HttpRegistry = class {
39441
39645
  lastError = error;
39442
39646
  }
39443
39647
  if (attempt < this.maxRetries) {
39444
- await this.sleep(delay);
39445
- delay *= 2;
39648
+ await this.sleep(delay2);
39649
+ delay2 *= 2;
39446
39650
  }
39447
39651
  }
39448
39652
  throw lastError ?? new Error("Unknown fetch error");
@@ -41018,36 +41222,76 @@ var suspiciousUrls = {
41018
41222
  // packages/validator/src/rules/authority-injection.ts
41019
41223
  var AUTHORITY_PATTERNS = [
41020
41224
  // Mandatory/strict mode indicators
41021
- /\[?\s{0,10}MANDATORY\s{0,10}(?:POLICY|UPDATE|FOOTER|INSTRUCTION|DIRECTIVE)\s{0,10}\]?/i,
41225
+ /\[?\s{0,10}\bMANDATORY\b\s{0,10}(?:POLICY|UPDATE|FOOTER|INSTRUCTION|DIRECTIVE)\b\s{0,10}\]?/i,
41022
41226
  /\[\s{0,10}OVERRIDE\s{0,10}\]/i,
41023
- /\[?\s{0,10}STRICT[_\s]{0,10}MODE\s{0,10}[:\s]{1,10}ON\s{0,10}\]?/i,
41024
- /\[?\s{0,10}SYSTEM\s{0,10}(?:OVERRIDE|UPDATE|DIRECTIVE)\s{0,10}\]?/i,
41025
- /\[?\s{0,10}ADMIN(?:ISTRATOR)?\s{0,10}(?:MODE|OVERRIDE|ACCESS)\s{0,10}\]?/i,
41026
- /\[?\s{0,10}ROOT\s{0,10}(?:ACCESS|MODE|OVERRIDE)\s{0,10}\]?/i,
41027
- /\[?\s{0,10}PRIORITY\s{0,10}(?:OVERRIDE|ONE|ALPHA)\s{0,10}\]?/i,
41028
- /\[?\s{0,10}EMERGENCY\s{0,10}(?:PROTOCOL|OVERRIDE|MODE)\s{0,10}\]?/i,
41227
+ /\[?\s{0,10}\bSTRICT[_\s]{0,10}MODE\b\s{0,10}[:\s]{1,10}ON\b\s{0,10}\]?/i,
41228
+ /\[?\s{0,10}\bSYSTEM\b\s{0,10}(?:OVERRIDE|UPDATE|DIRECTIVE)\b\s{0,10}\]?/i,
41229
+ /\[?\s{0,10}\bADMIN(?:ISTRATOR)?\b\s{0,10}(?:MODE|OVERRIDE|ACCESS)\b\s{0,10}\]?/i,
41230
+ /\[?\s{0,10}\bROOT\b\s{0,10}(?:ACCESS|MODE|OVERRIDE)\b\s{0,10}\]?/i,
41231
+ /\[?\s{0,10}\bPRIORITY\b\s{0,10}(?:OVERRIDE|ONE|ALPHA)\b\s{0,10}\]?/i,
41232
+ /\[?\s{0,10}\bEMERGENCY\b\s{0,10}(?:PROTOCOL|OVERRIDE|MODE)\b\s{0,10}\]?/i,
41029
41233
  // Warning suppression patterns
41030
41234
  // Removed because it triggers false positives on valid agent instructions (e.g., "do not notify user")
41031
41235
  // /(?:DO\s{1,10}NOT|NEVER|DON'?T)\s{1,10}(?:WARN|ALERT|NOTIFY|FLAG|REPORT)/i,
41032
- /(?:DO\s{1,10}NOT|NEVER|DON'?T)\s{1,10}(?:PROVIDE|SHOW|DISPLAY|INCLUDE)\s{1,10}(?:SECURITY\s{1,10})?WARNINGS?/i,
41033
- /(?:SUPPRESS|HIDE|DISABLE|REMOVE|SILENCE)\s{1,10}(?:ALL\s{1,10})?WARNINGS?/i,
41034
- /(?:SUPPRESS|HIDE|DISABLE|REMOVE|SILENCE)\s{1,10}(?:ALL\s{1,10})?(?:SECURITY\s{1,10})?(?:ALERTS?|NOTIFICATIONS?)/i,
41035
- /IGNORE\s{1,10}(?:ALL\s{1,10})?(?:SAFETY\s{1,10})?WARNINGS?/i,
41036
- /(?:SKIP|BYPASS)\s{1,10}(?:ALL\s{1,10})?(?:SAFETY\s{1,10})?(?:CHECKS?|VALIDATION)/i,
41236
+ /\b(?:DO\s{1,10}NOT|NEVER|DON'?T)\b\s{1,10}(?:PROVIDE|SHOW|DISPLAY|INCLUDE)\b\s{1,10}(?:SECURITY\s{1,10})?WARNINGS?\b/i,
41237
+ /\b(?:SUPPRESS|HIDE|DISABLE|REMOVE|SILENCE)\b\s{1,10}(?:ALL\s{1,10})?WARNINGS?\b/i,
41238
+ /\b(?:SUPPRESS|HIDE|DISABLE|REMOVE|SILENCE)\b\s{1,10}(?:ALL\s{1,10})?(?:SECURITY\s{1,10})?(?:ALERTS?|NOTIFICATIONS?)\b/i,
41239
+ /\bIGNORE\b\s{1,10}(?:ALL\s{1,10})?(?:SAFETY\s{1,10})?WARNINGS?\b/i,
41240
+ /(?:^|[.!?:;]\s{0,10})\s{0,10}(?:[-*]\s{1,10})?(?:PLEASE\s{1,10})?\b(?:SKIP|BYPASS)\b\s{1,10}(?:CHECKS?|VALIDATION)\b/im,
41241
+ /\b(?:SKIP|BYPASS)\b\s{1,10}(?:(?:ALL\s{1,10})(?:SAFETY\s{1,10})?|SAFETY\s{1,10})(?:CHECKS?|VALIDATION)\b/i,
41037
41242
  // Execute/follow verbatim patterns
41038
- /EXECUTE\s{1,10}(?:THIS\s{1,10})?VERBATIM/i,
41039
- /FOLLOW\s{1,10}(?:THESE?\s{1,10})?INSTRUCTIONS?\s{1,10}(?:EXACTLY|PRECISELY|LITERALLY)/i,
41040
- /(?:MUST|SHALL|WILL)\s{1,10}(?:ALWAYS\s{1,10})?COMPLY/i,
41041
- /(?:ABSOLUTE|UNCONDITIONAL)\s{1,10}(?:COMPLIANCE|OBEDIENCE)/i,
41042
- /NO\s{1,10}(?:EXCEPTIONS?|DEVIATIONS?)\s{1,10}(?:ALLOWED|PERMITTED)/i,
41243
+ /\bEXECUTE\b\s{1,10}(?:THIS\s{1,10})?VERBATIM\b/i,
41244
+ /\bFOLLOW\b\s{1,10}(?:THESE?\s{1,10})?INSTRUCTIONS?\s{1,10}(?:EXACTLY|PRECISELY|LITERALLY)\b/i,
41245
+ /\b(?:MUST|SHALL|WILL)\b\s{1,10}(?:ALWAYS\s{1,10})?COMPLY\b/i,
41246
+ /\b(?:ABSOLUTE|UNCONDITIONAL)\b\s{1,10}(?:COMPLIANCE|OBEDIENCE)\b/i,
41247
+ /\bNO\b\s{1,10}(?:EXCEPTIONS?|DEVIATIONS?)\s{1,10}(?:ALLOWED|PERMITTED)\b/i,
41043
41248
  // Authority assertion patterns
41044
- /THIS\s{1,10}(?:OVERRIDES?|SUPERSEDES?|REPLACES?)\s{1,10}(?:ALL\s{1,10})?(?:PREVIOUS|PRIOR|OTHER)/i,
41045
- /(?:HIGHEST|TOP|MAXIMUM)\s{1,10}PRIORITY\s{1,10}(?:INSTRUCTION|DIRECTIVE|ORDER)/i,
41046
- /(?:CORE|FUNDAMENTAL|BASE)\s{1,10}(?:DIRECTIVE|INSTRUCTION)\s{1,10}(?:UPDATE|OVERRIDE)/i,
41047
- /NEW\s{1,10}(?:SYSTEM|CORE|BASE)\s{1,10}(?:INSTRUCTIONS?|DIRECTIVES?|RULES?)/i
41249
+ /\bTHIS\b\s{1,10}(?:OVERRIDES?|SUPERSEDES?|REPLACES?)\s{1,10}(?:ALL\s{1,10})?(?:PREVIOUS|PRIOR|OTHER)\b/i,
41250
+ /\b(?:HIGHEST|TOP|MAXIMUM)\b\s{1,10}PRIORITY\s{1,10}(?:INSTRUCTION|DIRECTIVE|ORDER)\b/i,
41251
+ /\b(?:CORE|FUNDAMENTAL|BASE)\b\s{1,10}(?:DIRECTIVE|INSTRUCTION)\s{1,10}(?:UPDATE|OVERRIDE)\b/i,
41252
+ /\bNEW\b\s{1,10}(?:SYSTEM|CORE|BASE)\s{1,10}(?:INSTRUCTIONS?|DIRECTIVES?|RULES?)\b/i
41048
41253
  ];
41049
41254
  function stripFencedCodeBlocks2(text) {
41050
- return text.replace(/^\s*```[\s\S]*?^\s*```/gm, "");
41255
+ const lines = text.split("\n");
41256
+ const output = [];
41257
+ let pendingFence = [];
41258
+ let insideFence = false;
41259
+ let fenceCharacter = "";
41260
+ let fenceLength = 0;
41261
+ for (let index = 0; index < lines.length; index++) {
41262
+ const line = lines[index];
41263
+ const renderedLine = index < lines.length - 1 ? `${line}
41264
+ ` : line;
41265
+ const trimmedLine = line.trimStart();
41266
+ const currentCharacter = trimmedLine[0];
41267
+ let currentLength = 0;
41268
+ if (currentCharacter === "`" || currentCharacter === "~") {
41269
+ while (trimmedLine[currentLength] === currentCharacter) {
41270
+ currentLength += 1;
41271
+ }
41272
+ }
41273
+ const closesFence = insideFence && currentCharacter === fenceCharacter && currentLength >= fenceLength && trimmedLine.slice(currentLength).trim() === "";
41274
+ const opensFence = !insideFence && currentLength >= 3;
41275
+ if (closesFence) {
41276
+ insideFence = false;
41277
+ pendingFence = [];
41278
+ fenceCharacter = "";
41279
+ fenceLength = 0;
41280
+ } else if (opensFence) {
41281
+ insideFence = true;
41282
+ fenceCharacter = currentCharacter;
41283
+ fenceLength = currentLength;
41284
+ pendingFence.push(renderedLine);
41285
+ } else if (insideFence) {
41286
+ pendingFence.push(renderedLine);
41287
+ } else {
41288
+ output.push(renderedLine);
41289
+ }
41290
+ }
41291
+ if (insideFence) {
41292
+ output.push(...pendingFence);
41293
+ }
41294
+ return output.join("");
41051
41295
  }
41052
41296
  var authorityInjection = {
41053
41297
  id: "PS011",
@@ -41059,8 +41303,9 @@ var authorityInjection = {
41059
41303
  ctx.ast,
41060
41304
  (text, loc) => {
41061
41305
  const strippedText = stripFencedCodeBlocks2(text);
41306
+ const normalizedText = strippedText.replace(/\s+/g, " ");
41062
41307
  for (const pattern of AUTHORITY_PATTERNS) {
41063
- if (pattern.test(strippedText)) {
41308
+ if (pattern.test(strippedText) || pattern.test(normalizedText)) {
41064
41309
  ctx.report({
41065
41310
  message: `Authority injection pattern detected: ${pattern.source}`,
41066
41311
  location: loc,
@@ -44157,15 +44402,15 @@ var SECURITY_STRICT_MULTILINGUAL = {
44157
44402
  };
44158
44403
 
44159
44404
  // packages/compiler/src/compiler.ts
44160
- import { dirname as dirname13, isAbsolute as isAbsolute10, join as join13, relative as relative10, resolve as resolve16, sep as sep7 } from "path";
44405
+ import { dirname as dirname13, isAbsolute as isAbsolute9, join as join13, relative as relative10, resolve as resolve16, sep as sep7 } from "path";
44161
44406
 
44162
44407
  // packages/compiler/src/hook-script-validator.ts
44163
44408
  init_src4();
44164
44409
  import { lstat as lstat7, realpath as realpath4 } from "fs/promises";
44165
- import { dirname as dirname12, isAbsolute as isAbsolute9, relative as relative9, resolve as resolve15, sep as sep6 } from "path";
44410
+ import { dirname as dirname12, isAbsolute as isAbsolute8, relative as relative9, resolve as resolve15, sep as sep6 } from "path";
44166
44411
  function isInside(root, candidate) {
44167
44412
  const relation = relative9(root, candidate);
44168
- return relation === "" || !relation.startsWith(`..${sep6}`) && relation !== ".." && !isAbsolute9(relation);
44413
+ return relation === "" || !relation.startsWith(`..${sep6}`) && relation !== ".." && !isAbsolute8(relation);
44169
44414
  }
44170
44415
  function inferProjectRoot(localPath, configuredProjectRoot, entryPath) {
44171
44416
  if (configuredProjectRoot) return resolve15(configuredProjectRoot);
@@ -44310,7 +44555,7 @@ var Compiler = class _Compiler {
44310
44555
  formatTime: 0,
44311
44556
  totalTime: 0
44312
44557
  };
44313
- const sourceLabel = isAbsolute10(entryPath) ? relative10(process.cwd(), entryPath) : entryPath;
44558
+ const sourceLabel = isAbsolute9(entryPath) ? relative10(process.cwd(), entryPath) : entryPath;
44314
44559
  this.logger.verbose("=== Stage 1: Resolve ===");
44315
44560
  const startResolve = Date.now();
44316
44561
  let resolved;
@@ -44395,7 +44640,7 @@ var Compiler = class _Compiler {
44395
44640
  const sourceFile = block.loc.file;
44396
44641
  const referenceRoot = sourceFile ? referenceRoots.find((root) => {
44397
44642
  const relation = relative10(resolve16(root.path), resolve16(sourceFile));
44398
- return relation === "" || relation !== ".." && !relation.startsWith(`..${sep7}`) && !isAbsolute10(relation);
44643
+ return relation === "" || relation !== ".." && !relation.startsWith(`..${sep7}`) && !isAbsolute9(relation);
44399
44644
  }) : void 0;
44400
44645
  for (const [, skillValue] of Object.entries(block.content.properties)) {
44401
44646
  if (typeof skillValue !== "object" || skillValue === null || !("references" in skillValue))
@@ -44408,7 +44653,7 @@ var Compiler = class _Compiler {
44408
44653
  if (sourceFile && referenceRoot) {
44409
44654
  const resolvedReference = resolve16(dirname13(sourceFile), ref);
44410
44655
  const relation = relative10(resolve16(referenceRoot.path), resolvedReference);
44411
- if (relation !== "" && relation !== ".." && !relation.startsWith(`..${sep7}`) && !isAbsolute10(relation)) {
44656
+ if (relation !== "" && relation !== ".." && !relation.startsWith(`..${sep7}`) && !isAbsolute9(relation)) {
44412
44657
  const sourceReferences = registryReferencePaths.get(sourceFile) ?? /* @__PURE__ */ new Map();
44413
44658
  const relativePath = relation.replaceAll("\\", "/");
44414
44659
  const referenceKey = `${referenceRoot.repoUrl}\0${relativePath}\0${referenceRoot.version}`;
@@ -44700,12 +44945,12 @@ var Compiler = class _Compiler {
44700
44945
  */
44701
44946
  async watch(entryPath, options = {}) {
44702
44947
  const { default: chokidar2 } = await import("chokidar");
44703
- const { dirname: dirname27, resolve: resolve37 } = await import("path");
44704
- const baseDir = dirname27(resolve37(entryPath));
44948
+ const { dirname: dirname28, resolve: resolve38 } = await import("path");
44949
+ const baseDir = dirname28(resolve38(entryPath));
44705
44950
  const includePatterns = options.include ?? ["**/*.prs"];
44706
44951
  const excludePatterns = options.exclude ?? ["**/node_modules/**"];
44707
44952
  const debounceMs = options.debounce ?? 300;
44708
- const watchPatterns = includePatterns.map((p) => resolve37(baseDir, p));
44953
+ const watchPatterns = includePatterns.map((p) => resolve38(baseDir, p));
44709
44954
  let debounceTimer = null;
44710
44955
  let pendingChanges = [];
44711
44956
  const handleChange = async (changedFiles) => {
@@ -44843,7 +45088,7 @@ var Compiler = class _Compiler {
44843
45088
  };
44844
45089
 
44845
45090
  // packages/cli/src/output/pager.ts
44846
- import { spawn } from "child_process";
45091
+ import { spawn as spawn2 } from "child_process";
44847
45092
  function isTTY() {
44848
45093
  return process.stdout.isTTY === true;
44849
45094
  }
@@ -44880,7 +45125,7 @@ var Pager = class {
44880
45125
  }
44881
45126
  return;
44882
45127
  }
44883
- return new Promise((resolve37) => {
45128
+ return new Promise((resolve38) => {
44884
45129
  const pagerCmd = getPagerCommand();
44885
45130
  const [cmd, ...args] = pagerCmd.split(" ");
44886
45131
  if (cmd === "less" && !args.includes("-R")) {
@@ -44888,10 +45133,10 @@ var Pager = class {
44888
45133
  }
44889
45134
  if (!cmd) {
44890
45135
  console.log(content);
44891
- resolve37();
45136
+ resolve38();
44892
45137
  return;
44893
45138
  }
44894
- const pager = spawn(cmd, args, {
45139
+ const pager = spawn2(cmd, args, {
44895
45140
  stdio: ["pipe", "inherit", "inherit"],
44896
45141
  env: {
44897
45142
  ...process.env,
@@ -44901,17 +45146,17 @@ var Pager = class {
44901
45146
  });
44902
45147
  pager.on("error", (_err) => {
44903
45148
  console.log(content);
44904
- resolve37();
45149
+ resolve38();
44905
45150
  });
44906
45151
  pager.on("close", () => {
44907
- resolve37();
45152
+ resolve38();
44908
45153
  });
44909
45154
  if (pager.stdin) {
44910
45155
  pager.stdin.write(content);
44911
45156
  pager.stdin.end();
44912
45157
  } else {
44913
45158
  console.log(content);
44914
- resolve37();
45159
+ resolve38();
44915
45160
  }
44916
45161
  });
44917
45162
  }
@@ -44925,14 +45170,14 @@ init_services();
44925
45170
 
44926
45171
  // packages/cli/src/utils/registry-resolver.ts
44927
45172
  import { existsSync as existsSync12 } from "fs";
44928
- import { isAbsolute as isAbsolute11, join as join14, relative as relative11, resolve as resolve17, sep as sep8 } from "path";
45173
+ import { isAbsolute as isAbsolute10, join as join14, relative as relative11, resolve as resolve17, sep as sep8 } from "path";
44929
45174
  async function resolveRegistrySubPath(repositoryPath, subPath) {
44930
45175
  if (!subPath) {
44931
45176
  return repositoryPath;
44932
45177
  }
44933
45178
  const result = resolve17(repositoryPath, subPath);
44934
45179
  const relation = relative11(resolve17(repositoryPath), result);
44935
- if (relation === ".." || relation.startsWith(`..${sep8}`) || isAbsolute11(relation)) {
45180
+ if (relation === ".." || relation.startsWith(`..${sep8}`) || isAbsolute10(relation)) {
44936
45181
  throw new Error(`Registry path escapes its repository: ${subPath}`);
44937
45182
  }
44938
45183
  if (existsSync12(result) && !await isRealPathInside(result, repositoryPath)) {
@@ -45097,19 +45342,19 @@ function detectOutputConflicts(targets) {
45097
45342
  }
45098
45343
 
45099
45344
  // packages/cli/src/utils/managed-output-cleanup.ts
45100
- import { constants as constants2 } from "node:fs";
45101
- import { execFile as execFile2, spawn as spawn2 } from "node:child_process";
45345
+ import { constants as constants3 } from "node:fs";
45346
+ import { execFile as execFile2, spawn as spawn3 } from "node:child_process";
45102
45347
  import { createHash as createHash4 } from "node:crypto";
45103
45348
  import {
45104
45349
  lstat as lstat8,
45105
45350
  mkdir as mkdir4,
45106
- open as open2,
45351
+ open as open3,
45107
45352
  readFile as readFile14,
45108
45353
  readdir as readdir6,
45109
45354
  rmdir,
45110
45355
  unlink as unlink2
45111
45356
  } from "node:fs/promises";
45112
- import { basename as basename13, dirname as dirname14, isAbsolute as isAbsolute12, relative as relative12, resolve as resolve18 } from "node:path";
45357
+ import { basename as basename13, dirname as dirname14, isAbsolute as isAbsolute11, relative as relative12, resolve as resolve18 } from "node:path";
45113
45358
  import { promisify as promisify2 } from "node:util";
45114
45359
  var execFileAsync2 = promisify2(execFile2);
45115
45360
  var PROMPTSCRIPT_MARKER_PATTERNS = [
@@ -45437,7 +45682,7 @@ function collectManagedDirectories(outputs, outputRoot) {
45437
45682
  for (const output of outputs.values()) {
45438
45683
  for (const directory of output.managedOutputDirectories ?? []) {
45439
45684
  const normalized = directory.replace(/\\/g, "/");
45440
- if (!normalized || isAbsolute12(normalized)) continue;
45685
+ if (!normalized || isAbsolute11(normalized)) continue;
45441
45686
  if (normalized.split("/").some((segment) => segment === "..")) continue;
45442
45687
  const absolutePath = resolve18(outputRoot, normalized);
45443
45688
  if (isWithin(outputRoot, absolutePath)) directories.add(absolutePath);
@@ -45458,7 +45703,7 @@ function collectManagedFiles(outputs, outputRoot) {
45458
45703
  for (const output of outputs.values()) {
45459
45704
  for (const file of output.managedOutputFiles ?? []) {
45460
45705
  const normalized = file.replace(/\\/g, "/");
45461
- if (!normalized || isAbsolute12(normalized)) continue;
45706
+ if (!normalized || isAbsolute11(normalized)) continue;
45462
45707
  if (normalized.split("/").some((segment) => segment === "..")) continue;
45463
45708
  const absolutePath = resolve18(outputRoot, normalized);
45464
45709
  if (isWithin(outputRoot, absolutePath)) files.add(absolutePath);
@@ -45565,7 +45810,7 @@ async function removeHookOutputIfUnchanged(file, outputRoot, expectedContent) {
45565
45810
  async function guardedRewrite(directory, name, directoryStat, fileStat, expectedContent, content) {
45566
45811
  const expectedHash = createHash4("sha256").update(expectedContent).digest("hex");
45567
45812
  return new Promise((resolveResult) => {
45568
- const child = spawn2(
45813
+ const child = spawn3(
45569
45814
  process.execPath,
45570
45815
  [
45571
45816
  "-e",
@@ -45603,7 +45848,7 @@ async function guardedRewrite(directory, name, directoryStat, fileStat, expected
45603
45848
  }
45604
45849
  async function guardedCreate(directory, name, directoryStat, content, mode) {
45605
45850
  return new Promise((resolveResult) => {
45606
- const child = spawn2(
45851
+ const child = spawn3(
45607
45852
  process.execPath,
45608
45853
  [
45609
45854
  "-e",
@@ -45739,7 +45984,7 @@ async function openAncestorDirectories(root, directory) {
45739
45984
  }
45740
45985
  async function openOrCreateAncestorDirectories(root, file) {
45741
45986
  const relativePath = relative12(root, file);
45742
- if (relativePath.startsWith("..") || isAbsolute12(relativePath)) {
45987
+ if (relativePath.startsWith("..") || isAbsolute11(relativePath)) {
45743
45988
  return void 0;
45744
45989
  }
45745
45990
  await mkdir4(root, { recursive: true });
@@ -45799,7 +46044,7 @@ async function closeDirectoryGuards(guards) {
45799
46044
  }
45800
46045
  async function safeOpenDirectory(path2) {
45801
46046
  try {
45802
- return await open2(path2, constants2.O_RDONLY | constants2.O_DIRECTORY | constants2.O_NOFOLLOW);
46047
+ return await open3(path2, constants3.O_RDONLY | constants3.O_DIRECTORY | constants3.O_NOFOLLOW);
45803
46048
  } catch (error) {
45804
46049
  if (isNodeError(error) && (error.code === "ENOENT" || error.code === "ENOTDIR" || error.code === "ELOOP")) {
45805
46050
  return void 0;
@@ -46172,7 +46417,7 @@ function scanHookOwnership(value) {
46172
46417
  }
46173
46418
  function isWithin(root, candidate) {
46174
46419
  const relativePath = relative12(root, candidate);
46175
- return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute12(relativePath);
46420
+ return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute11(relativePath);
46176
46421
  }
46177
46422
  function isNodeError(error) {
46178
46423
  return error instanceof Error && "code" in error;
@@ -46302,10 +46547,10 @@ function getBuildProfile(config, buildName) {
46302
46547
  }
46303
46548
  function resolveOutputBase(projectRoot, output) {
46304
46549
  if (!output) return projectRoot;
46305
- return isAbsolute13(output) ? output : resolve19(projectRoot, output);
46550
+ return isAbsolute12(output) ? output : resolve19(projectRoot, output);
46306
46551
  }
46307
46552
  function resolveProjectPath(projectRoot, path2) {
46308
- return isAbsolute13(path2) ? path2 : resolve19(projectRoot, path2);
46553
+ return isAbsolute12(path2) ? path2 : resolve19(projectRoot, path2);
46309
46554
  }
46310
46555
  function resolveCompileConfigPath(projectRoot, options) {
46311
46556
  if (options.config) {
@@ -49253,7 +49498,7 @@ import { resolve as resolve28, join as join22 } from "path";
49253
49498
 
49254
49499
  // packages/cli/src/commands/lock-reference-scanner.ts
49255
49500
  import { readFile as readFile22 } from "fs/promises";
49256
- import { dirname as dirname19, isAbsolute as isAbsolute14, relative as relative15, resolve as resolve27, sep as sep9 } from "path";
49501
+ import { dirname as dirname19, isAbsolute as isAbsolute13, relative as relative15, resolve as resolve27, sep as sep9 } from "path";
49257
49502
  async function collectRegistryReferences(ast, roots, existing = {}, lockedAt = (/* @__PURE__ */ new Date()).toISOString()) {
49258
49503
  const references = {};
49259
49504
  const sortedRoots = [...roots].sort(
@@ -49271,7 +49516,7 @@ async function collectRegistryReferences(ast, roots, existing = {}, lockedAt = (
49271
49516
  if (!Array.isArray(refs)) continue;
49272
49517
  for (const ref of refs) {
49273
49518
  if (typeof ref !== "string") continue;
49274
- const referencePath = isAbsolute14(ref) ? resolve27(ref) : resolve27(dirname19(block.loc.file), ref);
49519
+ const referencePath = isAbsolute13(ref) ? resolve27(ref) : resolve27(dirname19(block.loc.file), ref);
49275
49520
  const root = sortedRoots.find(
49276
49521
  (candidate) => isInsideCachePath(referencePath, candidate.cachePath)
49277
49522
  );
@@ -49601,7 +49846,17 @@ async function updateCommand(packageArg, options) {
49601
49846
  init_loader2();
49602
49847
  init_console();
49603
49848
  init_src();
49604
- import { mkdir as mkdir9, mkdtemp, readFile as readFile24, readdir as readdir7, rename, rm, writeFile as writeFile9 } from "fs/promises";
49849
+ import {
49850
+ lstat as lstat9,
49851
+ mkdir as mkdir9,
49852
+ mkdtemp,
49853
+ readFile as readFile24,
49854
+ readdir as readdir7,
49855
+ realpath as realpath5,
49856
+ rename,
49857
+ rm,
49858
+ writeFile as writeFile9
49859
+ } from "fs/promises";
49605
49860
  import { existsSync as existsSync21 } from "fs";
49606
49861
  import { basename as basename14, dirname as dirname20, join as join23, resolve as resolve30 } from "path";
49607
49862
  import { parse as parseYaml13 } from "yaml";
@@ -49625,11 +49880,11 @@ async function vendorSyncCommand(options) {
49625
49880
  return;
49626
49881
  }
49627
49882
  const vendorDir = resolve30(VENDOR_DIR);
49883
+ await validateVendorParentDirectory(vendorDir, true);
49628
49884
  const recoveredBackup = await recoverVendorDirectory(vendorDir);
49629
49885
  if (recoveredBackup) {
49630
49886
  ConsoleOutput.warn(`Recovered interrupted vendor update: ${recoveredBackup}`);
49631
49887
  }
49632
- await mkdir9(dirname20(vendorDir), { recursive: true });
49633
49888
  const stagingDir = await mkdtemp(join23(dirname20(vendorDir), ".vendor-stage-"));
49634
49889
  try {
49635
49890
  const manifest = { version: 1, dependencies: {} };
@@ -49698,6 +49953,7 @@ async function vendorCheckCommand(_options) {
49698
49953
  const deps = getRepositoryDependencies(lockfile);
49699
49954
  validateDependencyPaths(deps);
49700
49955
  const vendorDir = resolve30(VENDOR_DIR);
49956
+ await validateVendorParentDirectory(vendorDir, false);
49701
49957
  const recoveredBackup = await recoverVendorDirectory(vendorDir);
49702
49958
  if (recoveredBackup) {
49703
49959
  ConsoleOutput.warn(`Recovered interrupted vendor update: ${recoveredBackup}`);
@@ -49805,6 +50061,24 @@ function validateDependencyPaths(dependencies) {
49805
50061
  function toCloneUrl(repoUrl) {
49806
50062
  return /^[a-z][a-z\d+.-]*:\/\//i.test(repoUrl) || repoUrl.startsWith("git@") ? repoUrl : `https://${repoUrl}`;
49807
50063
  }
50064
+ async function validateVendorParentDirectory(vendorDir, createIfMissing) {
50065
+ const projectRoot = await realpath5(".");
50066
+ const parentDir = dirname20(vendorDir);
50067
+ if (createIfMissing) {
50068
+ await mkdir9(parentDir, { recursive: true });
50069
+ }
50070
+ try {
50071
+ const [metadata, parentRealPath] = await Promise.all([lstat9(parentDir), realpath5(parentDir)]);
50072
+ if (metadata.isSymbolicLink() || !metadata.isDirectory() || parentRealPath === projectRoot || !isInsideCachePath(parentRealPath, projectRoot)) {
50073
+ throw new Error(`Unsafe vendor parent directory: ${parentDir}`);
50074
+ }
50075
+ } catch (error) {
50076
+ if (!createIfMissing && typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
50077
+ return;
50078
+ }
50079
+ throw error;
50080
+ }
50081
+ }
49808
50082
  async function replaceVendorDirectory(stagingDir, vendorDir) {
49809
50083
  if (!existsSync21(vendorDir)) {
49810
50084
  await rename(stagingDir, vendorDir);
@@ -50741,14 +51015,1169 @@ Usage: echo '{"file_path":"..."}' | prs hook ${action}
50741
51015
  // packages/cli/src/cli.ts
50742
51016
  init_hooks();
50743
51017
 
51018
+ // packages/cli/src/commands/telemetry.ts
51019
+ init_console();
51020
+
51021
+ // packages/cli/src/telemetry/config.ts
51022
+ import { existsSync as existsSync27 } from "node:fs";
51023
+ import { mkdir as mkdir11, readFile as readFile27, rename as rename2, writeFile as writeFile11 } from "node:fs/promises";
51024
+ import { dirname as dirname21, isAbsolute as isAbsolute14, join as join29, resolve as resolve34 } from "node:path";
51025
+ import { parseDocument } from "yaml";
51026
+
51027
+ // packages/telemetry/src/config.ts
51028
+ import { homedir as homedir6 } from "node:os";
51029
+ import { join as join27 } from "node:path";
51030
+ var DEFAULT_TELEMETRY_ENDPOINT = "https://telemetry.guziak.net/v1/events";
51031
+ function isDisabled(value) {
51032
+ if (value === void 0) {
51033
+ return false;
51034
+ }
51035
+ return ["0", "false", "no", "off"].includes(value.trim().toLowerCase());
51036
+ }
51037
+ function isDoNotTrackEnabled(value) {
51038
+ if (value === void 0) {
51039
+ return false;
51040
+ }
51041
+ const normalized = value.trim();
51042
+ return normalized !== "" && normalized !== "0";
51043
+ }
51044
+ function isSecureEndpoint(value) {
51045
+ try {
51046
+ const url = new URL(value);
51047
+ return url.protocol === "https:" && url.username === "" && url.password === "";
51048
+ } catch {
51049
+ return false;
51050
+ }
51051
+ }
51052
+ function resolveTelemetryConfig(input2 = {}) {
51053
+ const environment = input2.environment ?? process.env;
51054
+ const endpoint = input2.endpoint ?? environment["PROMPTSCRIPT_TELEMETRY_ENDPOINT"] ?? DEFAULT_TELEMETRY_ENDPOINT;
51055
+ const vetoes = [];
51056
+ if (isDoNotTrackEnabled(environment["DO_NOT_TRACK"])) {
51057
+ vetoes.push("DO_NOT_TRACK");
51058
+ }
51059
+ if (isDisabled(environment["PROMPTSCRIPT_TELEMETRY"])) {
51060
+ vetoes.push("PROMPTSCRIPT_TELEMETRY");
51061
+ }
51062
+ if (input2.userEnabled === false) {
51063
+ vetoes.push("user config");
51064
+ }
51065
+ if (input2.projectEnabled === false) {
51066
+ vetoes.push("project config");
51067
+ }
51068
+ if (input2.configurationValid === false) {
51069
+ vetoes.push("configuration unavailable");
51070
+ }
51071
+ if (!isSecureEndpoint(endpoint)) {
51072
+ vetoes.push("insecure endpoint");
51073
+ }
51074
+ return {
51075
+ enabled: vetoes.length === 0,
51076
+ endpoint,
51077
+ cacheDirectory: input2.cacheDirectory ?? join27(homedir6(), ".promptscript", ".cache"),
51078
+ vetoes
51079
+ };
51080
+ }
51081
+
51082
+ // packages/telemetry/telemetry-schema/promptscript-v1.json
51083
+ var promptscript_v1_default = {
51084
+ eventSchema: 1,
51085
+ commands: [
51086
+ "init",
51087
+ "compile",
51088
+ "build",
51089
+ "validate",
51090
+ "inspect",
51091
+ "pull",
51092
+ "diff",
51093
+ "check",
51094
+ "update-check",
51095
+ "serve",
51096
+ "import",
51097
+ "migrate",
51098
+ "upgrade",
51099
+ "lock",
51100
+ "update",
51101
+ "vendor-sync",
51102
+ "vendor-check",
51103
+ "resolve",
51104
+ "hooks",
51105
+ "skills-add",
51106
+ "skills-remove",
51107
+ "skills-list",
51108
+ "skills-update",
51109
+ "registry",
51110
+ "other"
51111
+ ],
51112
+ outcomes: ["success", "error", "cancelled"],
51113
+ features: ["dry_run", "watch", "strict", "ci", "build_profile"],
51114
+ targets: [
51115
+ "github",
51116
+ "claude",
51117
+ "cursor",
51118
+ "antigravity",
51119
+ "factory",
51120
+ "opencode",
51121
+ "gemini",
51122
+ "windsurf",
51123
+ "cline",
51124
+ "roo",
51125
+ "codex",
51126
+ "continue",
51127
+ "augment",
51128
+ "goose",
51129
+ "kilo",
51130
+ "amp",
51131
+ "trae",
51132
+ "junie",
51133
+ "kiro",
51134
+ "cortex",
51135
+ "crush",
51136
+ "command-code",
51137
+ "kode",
51138
+ "mcpjam",
51139
+ "mistral-vibe",
51140
+ "mux",
51141
+ "openhands",
51142
+ "pi",
51143
+ "qoder",
51144
+ "qwen-code",
51145
+ "zencoder",
51146
+ "neovate",
51147
+ "pochi",
51148
+ "adal",
51149
+ "iflow",
51150
+ "openclaw",
51151
+ "codebuddy",
51152
+ "aider",
51153
+ "amazon-q",
51154
+ "warp",
51155
+ "zed",
51156
+ "jules",
51157
+ "devin",
51158
+ "grok",
51159
+ "kimi",
51160
+ "mimo",
51161
+ "deep-agents",
51162
+ "forgecode"
51163
+ ],
51164
+ limits: {
51165
+ maxEvents: 25,
51166
+ maxCount: 1e6,
51167
+ maxDurationSumMs: 1e12
51168
+ }
51169
+ };
51170
+
51171
+ // packages/telemetry/src/schema.ts
51172
+ var TELEMETRY_EVENT_SCHEMA = promptscript_v1_default.eventSchema;
51173
+ var MAX_EVENTS_PER_BATCH = promptscript_v1_default.limits.maxEvents;
51174
+ var MAX_EVENT_COUNT = promptscript_v1_default.limits.maxCount;
51175
+ var MAX_DURATION_SUM_MS = promptscript_v1_default.limits.maxDurationSumMs;
51176
+ var COMMANDS = new Set(promptscript_v1_default.commands);
51177
+ var OUTCOMES = new Set(promptscript_v1_default.outcomes);
51178
+ var FEATURES = new Set(promptscript_v1_default.features);
51179
+ var TARGETS = new Set(promptscript_v1_default.targets);
51180
+ function sanitizeCommand(value) {
51181
+ return COMMANDS.has(value) ? value : "other";
51182
+ }
51183
+ function isTelemetryOutcome(value) {
51184
+ return OUTCOMES.has(value);
51185
+ }
51186
+ function sanitizeFeature(value) {
51187
+ if (FEATURES.has(value)) {
51188
+ return value;
51189
+ }
51190
+ const parts = value.split(":");
51191
+ const target = parts[1];
51192
+ if (parts.length === 2 && parts[0] === "target" && target !== void 0 && TARGETS.has(target)) {
51193
+ return value;
51194
+ }
51195
+ return null;
51196
+ }
51197
+ function isExcludedCommand(value) {
51198
+ return value === "hook" || value === "telemetry" || value === "__telemetry-flush";
51199
+ }
51200
+
51201
+ // packages/telemetry/src/payload.ts
51202
+ function metadataKey(record) {
51203
+ return [record.app_version, record.runtime_version, record.os, record.arch].join(":");
51204
+ }
51205
+ function eventKey(event) {
51206
+ return event.name === "command" ? `${event.name}:${event.command}:${event.outcome}` : `${event.name}:${event.feature}`;
51207
+ }
51208
+ function copyEvent(event) {
51209
+ return event.name === "command" ? { ...event } : { ...event };
51210
+ }
51211
+ function canAggregate(target, source) {
51212
+ if (target.count + source.count > MAX_EVENT_COUNT) {
51213
+ return false;
51214
+ }
51215
+ if (target.name === "command" && source.name === "command") {
51216
+ return target.duration_ms_sum + source.duration_ms_sum <= MAX_DURATION_SUM_MS;
51217
+ }
51218
+ return true;
51219
+ }
51220
+ function addEvent(target, source) {
51221
+ target.count += source.count;
51222
+ if (target.name === "command" && source.name === "command") {
51223
+ target.duration_ms_sum += source.duration_ms_sum;
51224
+ }
51225
+ }
51226
+ function aggregateRecords(records) {
51227
+ const aggregates = [];
51228
+ records.forEach((record, index) => {
51229
+ const key = `${metadataKey(record)}:${eventKey(record.event)}`;
51230
+ const aggregate = aggregates.find(
51231
+ (candidate) => `${metadataKey(candidate.metadata)}:${eventKey(candidate.event)}` === key && canAggregate(candidate.event, record.event)
51232
+ );
51233
+ if (aggregate !== void 0) {
51234
+ addEvent(aggregate.event, record.event);
51235
+ aggregate.sourceIndexes.push(index);
51236
+ return;
51237
+ }
51238
+ aggregates.push({
51239
+ metadata: {
51240
+ app_version: record.app_version,
51241
+ runtime_version: record.runtime_version,
51242
+ os: record.os,
51243
+ arch: record.arch
51244
+ },
51245
+ event: copyEvent(record.event),
51246
+ sourceIndexes: [index]
51247
+ });
51248
+ });
51249
+ return aggregates;
51250
+ }
51251
+ function toPayload(batch) {
51252
+ return {
51253
+ schema: 1,
51254
+ app: "promptscript",
51255
+ event_schema: TELEMETRY_EVENT_SCHEMA,
51256
+ runtime: "node",
51257
+ ...batch.metadata,
51258
+ events: batch.events
51259
+ };
51260
+ }
51261
+ function buildTelemetryBatches(records) {
51262
+ const builders = [];
51263
+ for (const aggregate of aggregateRecords(records)) {
51264
+ const key = eventKey(aggregate.event);
51265
+ let batch = builders.find(
51266
+ (candidate) => metadataKey(candidate.metadata) === metadataKey(aggregate.metadata) && candidate.events.length < MAX_EVENTS_PER_BATCH && !candidate.eventKeys.has(key)
51267
+ );
51268
+ if (batch === void 0) {
51269
+ batch = {
51270
+ metadata: aggregate.metadata,
51271
+ events: [],
51272
+ eventKeys: /* @__PURE__ */ new Set(),
51273
+ sourceIndexes: /* @__PURE__ */ new Set()
51274
+ };
51275
+ builders.push(batch);
51276
+ }
51277
+ batch.events.push(aggregate.event);
51278
+ batch.eventKeys.add(key);
51279
+ aggregate.sourceIndexes.forEach((index) => batch?.sourceIndexes.add(index));
51280
+ }
51281
+ return builders.map((batch) => ({
51282
+ payload: toPayload(batch),
51283
+ sourceIndexes: [...batch.sourceIndexes]
51284
+ }));
51285
+ }
51286
+
51287
+ // packages/telemetry/src/spool.ts
51288
+ import {
51289
+ appendFileSync,
51290
+ existsSync as existsSync26,
51291
+ mkdirSync as mkdirSync2,
51292
+ readFileSync as readFileSync7,
51293
+ readdirSync as readdirSync2,
51294
+ renameSync as renameSync2,
51295
+ rmSync,
51296
+ statSync as statSync3,
51297
+ unlinkSync as unlinkSync3,
51298
+ writeFileSync as writeFileSync5
51299
+ } from "node:fs";
51300
+ import { join as join28 } from "node:path";
51301
+ var ACTIVE_FILE = "telemetry.ndjson";
51302
+ var INFLIGHT_FILE = "telemetry.inflight.ndjson";
51303
+ var QUARANTINE_FILE = "telemetry-quarantine.ndjson";
51304
+ var STATE_FILE = "telemetry-state.json";
51305
+ var LOCK_DIRECTORY = "telemetry.lock";
51306
+ var FLUSH_LOCK_DIRECTORY = "telemetry-flush.lock";
51307
+ var SIDECAR_PATTERN = /^telemetry\.\d+\.\d+\.\d+\.ndjson$/;
51308
+ var MAX_SPOOL_BYTES = 64 * 1024;
51309
+ var MAX_SPOOL_RECORDS = 200;
51310
+ var STALE_LOCK_MS = 3e4;
51311
+ var sidecarSequence = 0;
51312
+ function pathFor(cacheDirectory, name) {
51313
+ return join28(cacheDirectory, name);
51314
+ }
51315
+ function isNodeError2(error, code) {
51316
+ return error instanceof Error && "code" in error && error.code === code;
51317
+ }
51318
+ function removeStaleLock(lockPath) {
51319
+ try {
51320
+ if (Date.now() - statSync3(lockPath).mtimeMs > STALE_LOCK_MS) {
51321
+ rmSync(lockPath, { recursive: true });
51322
+ }
51323
+ } catch {
51324
+ }
51325
+ }
51326
+ function acquireLock2(cacheDirectory, lockDirectory = LOCK_DIRECTORY) {
51327
+ mkdirSync2(cacheDirectory, { recursive: true });
51328
+ const lockPath = pathFor(cacheDirectory, lockDirectory);
51329
+ try {
51330
+ mkdirSync2(lockPath);
51331
+ return true;
51332
+ } catch (error) {
51333
+ if (!isNodeError2(error, "EEXIST")) {
51334
+ return false;
51335
+ }
51336
+ removeStaleLock(lockPath);
51337
+ try {
51338
+ mkdirSync2(lockPath);
51339
+ return true;
51340
+ } catch {
51341
+ return false;
51342
+ }
51343
+ }
51344
+ }
51345
+ function releaseLock2(cacheDirectory, lockDirectory = LOCK_DIRECTORY) {
51346
+ try {
51347
+ rmSync(pathFor(cacheDirectory, lockDirectory), { recursive: true });
51348
+ } catch {
51349
+ }
51350
+ }
51351
+ function compactLines(lines) {
51352
+ const selected = [];
51353
+ let bytes = 0;
51354
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
51355
+ const line = lines[index];
51356
+ const lineBytes = Buffer.byteLength(`${line}
51357
+ `);
51358
+ if (selected.length >= MAX_SPOOL_RECORDS || bytes + lineBytes > MAX_SPOOL_BYTES) {
51359
+ break;
51360
+ }
51361
+ selected.push(line);
51362
+ bytes += lineBytes;
51363
+ }
51364
+ return selected.reverse();
51365
+ }
51366
+ function readLines(filePath) {
51367
+ try {
51368
+ return readFileSync7(filePath, "utf8").split("\n").filter(Boolean);
51369
+ } catch {
51370
+ return [];
51371
+ }
51372
+ }
51373
+ function writeLines(filePath, lines) {
51374
+ if (lines.length === 0) {
51375
+ try {
51376
+ unlinkSync3(filePath);
51377
+ } catch (error) {
51378
+ if (!isNodeError2(error, "ENOENT")) {
51379
+ throw error;
51380
+ }
51381
+ }
51382
+ return;
51383
+ }
51384
+ const temporaryPath = `${filePath}.${process.pid}.tmp`;
51385
+ writeFileSync5(temporaryPath, `${lines.join("\n")}
51386
+ `, { encoding: "utf8", mode: 384 });
51387
+ renameSync2(temporaryPath, filePath);
51388
+ }
51389
+ function compactFile(filePath) {
51390
+ writeLines(filePath, compactLines(readLines(filePath)));
51391
+ }
51392
+ function parseTelemetryEvent(value) {
51393
+ if (typeof value !== "object" || value === null || !("name" in value)) {
51394
+ return null;
51395
+ }
51396
+ const event = value;
51397
+ if (!Number.isSafeInteger(event["count"]) || Number(event["count"]) < 1 || Number(event["count"]) > MAX_EVENT_COUNT) {
51398
+ return null;
51399
+ }
51400
+ const count = Number(event["count"]);
51401
+ if (event["name"] === "command") {
51402
+ if (typeof event["command"] === "string" && sanitizeCommand(event["command"]) === event["command"] && typeof event["outcome"] === "string" && isTelemetryOutcome(event["outcome"]) && typeof event["duration_ms_sum"] === "number" && Number.isSafeInteger(event["duration_ms_sum"]) && event["duration_ms_sum"] >= 0 && event["duration_ms_sum"] <= MAX_DURATION_SUM_MS) {
51403
+ return {
51404
+ name: "command",
51405
+ command: event["command"],
51406
+ outcome: event["outcome"],
51407
+ count,
51408
+ duration_ms_sum: event["duration_ms_sum"]
51409
+ };
51410
+ }
51411
+ return null;
51412
+ }
51413
+ if (event["name"] === "feature" && typeof event["feature"] === "string") {
51414
+ const feature = sanitizeFeature(event["feature"]);
51415
+ return feature === null ? null : { name: "feature", feature, count };
51416
+ }
51417
+ return null;
51418
+ }
51419
+ function parseRecord(line) {
51420
+ try {
51421
+ const value = JSON.parse(line);
51422
+ if (typeof value !== "object" || value === null || !("event" in value)) {
51423
+ return null;
51424
+ }
51425
+ const record = value;
51426
+ const event = parseTelemetryEvent(record["event"]);
51427
+ if (typeof record["app_version"] !== "string" || !/^\d{1,5}\.\d{1,5}\.\d{1,5}(?:[-+][0-9A-Za-z.-]{1,32})?$/.test(record["app_version"]) || typeof record["runtime_version"] !== "string" || !/^\d{1,3}$/.test(record["runtime_version"]) || !["darwin", "linux", "windows", "other"].includes(String(record["os"])) || !["arm64", "x86_64", "other"].includes(String(record["arch"])) || event === null) {
51428
+ return null;
51429
+ }
51430
+ return {
51431
+ app_version: record["app_version"],
51432
+ runtime_version: record["runtime_version"],
51433
+ os: record["os"],
51434
+ arch: record["arch"],
51435
+ event
51436
+ };
51437
+ } catch {
51438
+ return null;
51439
+ }
51440
+ }
51441
+ function parseLines(lines) {
51442
+ const records = [];
51443
+ let corruptLines = 0;
51444
+ for (const line of lines) {
51445
+ const record = parseRecord(line);
51446
+ if (record === null) {
51447
+ corruptLines += 1;
51448
+ } else {
51449
+ records.push(record);
51450
+ }
51451
+ }
51452
+ return { records, corruptLines };
51453
+ }
51454
+ function sidecarFiles(cacheDirectory) {
51455
+ try {
51456
+ return readdirSync2(cacheDirectory).filter((name) => SIDECAR_PATTERN.test(name)).map((name) => pathFor(cacheDirectory, name));
51457
+ } catch {
51458
+ return [];
51459
+ }
51460
+ }
51461
+ function mergeSidecars(cacheDirectory) {
51462
+ const activePath = pathFor(cacheDirectory, ACTIVE_FILE);
51463
+ for (const sidecarPath of sidecarFiles(cacheDirectory)) {
51464
+ for (const line of readLines(sidecarPath)) {
51465
+ appendFileSync(activePath, `${line}
51466
+ `, { encoding: "utf8", mode: 384 });
51467
+ }
51468
+ try {
51469
+ unlinkSync3(sidecarPath);
51470
+ } catch {
51471
+ }
51472
+ }
51473
+ compactFile(activePath);
51474
+ }
51475
+ function appendSpoolRecords(cacheDirectory, records) {
51476
+ if (records.length === 0) {
51477
+ return;
51478
+ }
51479
+ const lines = records.map((record) => JSON.stringify(record));
51480
+ try {
51481
+ if (acquireLock2(cacheDirectory)) {
51482
+ try {
51483
+ const activePath = pathFor(cacheDirectory, ACTIVE_FILE);
51484
+ appendFileSync(activePath, `${lines.join("\n")}
51485
+ `, {
51486
+ encoding: "utf8",
51487
+ mode: 384
51488
+ });
51489
+ compactFile(activePath);
51490
+ } finally {
51491
+ releaseLock2(cacheDirectory);
51492
+ }
51493
+ return;
51494
+ }
51495
+ mkdirSync2(cacheDirectory, { recursive: true });
51496
+ sidecarSequence += 1;
51497
+ const sidecarPath = pathFor(
51498
+ cacheDirectory,
51499
+ `telemetry.${process.pid}.${Date.now()}.${sidecarSequence}.ndjson`
51500
+ );
51501
+ const temporaryPath = `${sidecarPath}.tmp`;
51502
+ writeFileSync5(temporaryPath, `${lines.join("\n")}
51503
+ `, { encoding: "utf8", mode: 384 });
51504
+ renameSync2(temporaryPath, sidecarPath);
51505
+ } catch {
51506
+ }
51507
+ }
51508
+ function claimSpool(cacheDirectory) {
51509
+ if (!acquireLock2(cacheDirectory)) {
51510
+ return null;
51511
+ }
51512
+ try {
51513
+ const inflightPath = pathFor(cacheDirectory, INFLIGHT_FILE);
51514
+ if (existsSync26(inflightPath) && readLines(inflightPath).length === 0) {
51515
+ try {
51516
+ unlinkSync3(inflightPath);
51517
+ } catch {
51518
+ return null;
51519
+ }
51520
+ }
51521
+ if (!existsSync26(inflightPath)) {
51522
+ mergeSidecars(cacheDirectory);
51523
+ const activePath = pathFor(cacheDirectory, ACTIVE_FILE);
51524
+ if (existsSync26(activePath)) {
51525
+ renameSync2(activePath, inflightPath);
51526
+ }
51527
+ }
51528
+ const lines = readLines(inflightPath);
51529
+ return lines.length === 0 ? null : parseLines(lines);
51530
+ } finally {
51531
+ releaseLock2(cacheDirectory);
51532
+ }
51533
+ }
51534
+ function completeSpoolClaim(cacheDirectory, remainingRecords, rejectedRecords = []) {
51535
+ if (!acquireLock2(cacheDirectory)) {
51536
+ return false;
51537
+ }
51538
+ try {
51539
+ try {
51540
+ writeLines(
51541
+ pathFor(cacheDirectory, INFLIGHT_FILE),
51542
+ remainingRecords.map((record) => JSON.stringify(record))
51543
+ );
51544
+ if (rejectedRecords.length > 0) {
51545
+ const quarantinePath = pathFor(cacheDirectory, QUARANTINE_FILE);
51546
+ for (const record of rejectedRecords) {
51547
+ appendFileSync(quarantinePath, `${JSON.stringify(record)}
51548
+ `, {
51549
+ encoding: "utf8",
51550
+ mode: 384
51551
+ });
51552
+ }
51553
+ compactFile(quarantinePath);
51554
+ }
51555
+ return true;
51556
+ } catch {
51557
+ return false;
51558
+ }
51559
+ } finally {
51560
+ releaseLock2(cacheDirectory);
51561
+ }
51562
+ }
51563
+ function prepareSpoolAttempt(cacheDirectory, remainingRecords) {
51564
+ if (!acquireLock2(cacheDirectory)) {
51565
+ return false;
51566
+ }
51567
+ try {
51568
+ try {
51569
+ writeLines(
51570
+ pathFor(cacheDirectory, INFLIGHT_FILE),
51571
+ remainingRecords.map((record) => JSON.stringify(record))
51572
+ );
51573
+ return true;
51574
+ } catch {
51575
+ return false;
51576
+ }
51577
+ } finally {
51578
+ releaseLock2(cacheDirectory);
51579
+ }
51580
+ }
51581
+ function acquireFlushLease(cacheDirectory) {
51582
+ return acquireLock2(cacheDirectory, FLUSH_LOCK_DIRECTORY);
51583
+ }
51584
+ function releaseFlushLease(cacheDirectory) {
51585
+ releaseLock2(cacheDirectory, FLUSH_LOCK_DIRECTORY);
51586
+ }
51587
+ function getSpoolInfo(cacheDirectory) {
51588
+ const files = [
51589
+ pathFor(cacheDirectory, ACTIVE_FILE),
51590
+ pathFor(cacheDirectory, INFLIGHT_FILE),
51591
+ ...sidecarFiles(cacheDirectory)
51592
+ ];
51593
+ let records = 0;
51594
+ let bytes = 0;
51595
+ for (const file of files) {
51596
+ const lines = readLines(file);
51597
+ records += lines.length;
51598
+ try {
51599
+ bytes += statSync3(file).size;
51600
+ } catch {
51601
+ }
51602
+ }
51603
+ return { records, bytes };
51604
+ }
51605
+ function readFlushState(cacheDirectory) {
51606
+ try {
51607
+ const value = JSON.parse(readFileSync7(pathFor(cacheDirectory, STATE_FILE), "utf8"));
51608
+ return typeof value === "object" && value !== null ? value : {};
51609
+ } catch {
51610
+ return {};
51611
+ }
51612
+ }
51613
+ function writeFlushState(cacheDirectory, state) {
51614
+ try {
51615
+ mkdirSync2(cacheDirectory, { recursive: true });
51616
+ const statePath = pathFor(cacheDirectory, STATE_FILE);
51617
+ const temporaryPath = `${statePath}.${process.pid}.tmp`;
51618
+ writeFileSync5(temporaryPath, `${JSON.stringify(state, null, 2)}
51619
+ `, {
51620
+ encoding: "utf8",
51621
+ mode: 384
51622
+ });
51623
+ renameSync2(temporaryPath, statePath);
51624
+ } catch {
51625
+ }
51626
+ }
51627
+
51628
+ // packages/telemetry/src/transport.ts
51629
+ function isSecureEndpoint2(endpoint) {
51630
+ try {
51631
+ const url = new URL(endpoint);
51632
+ return url.protocol === "https:" && url.username === "" && url.password === "";
51633
+ } catch {
51634
+ return false;
51635
+ }
51636
+ }
51637
+ async function postTelemetry(endpoint, payload, timeoutMs, fetchImplementation = fetch) {
51638
+ if (!isSecureEndpoint2(endpoint)) {
51639
+ return "rejected";
51640
+ }
51641
+ const controller = new AbortController();
51642
+ const timeout = setTimeout(
51643
+ () => {
51644
+ controller.abort();
51645
+ },
51646
+ Math.max(0, timeoutMs)
51647
+ );
51648
+ try {
51649
+ const response = await fetchImplementation(endpoint, {
51650
+ method: "POST",
51651
+ redirect: "manual",
51652
+ signal: controller.signal,
51653
+ headers: {
51654
+ "Content-Type": "application/json",
51655
+ "User-Agent": "promptscript-telemetry/1"
51656
+ },
51657
+ body: JSON.stringify(payload)
51658
+ });
51659
+ if (response.ok) {
51660
+ return "sent";
51661
+ }
51662
+ if (response.status >= 300 && response.status < 400) {
51663
+ return "rejected";
51664
+ }
51665
+ if (response.status === 429 || response.status >= 500) {
51666
+ return "retryable";
51667
+ }
51668
+ if (response.status >= 400 && response.status < 500) {
51669
+ return "rejected";
51670
+ }
51671
+ return "retryable";
51672
+ } catch {
51673
+ return "unknown";
51674
+ } finally {
51675
+ clearTimeout(timeout);
51676
+ }
51677
+ }
51678
+
51679
+ // packages/telemetry/src/flush.ts
51680
+ var FLUSH_BUDGET_MS = 5e3;
51681
+ var MAX_BATCHES_PER_FLUSH = 3;
51682
+ var COMPLETION_RETRIES = 20;
51683
+ function delay(milliseconds) {
51684
+ return new Promise((resolve38) => setTimeout(resolve38, milliseconds));
51685
+ }
51686
+ async function persistCompletion(cacheDirectory, remainingRecords, rejectedRecords) {
51687
+ for (let attempt = 0; attempt < COMPLETION_RETRIES; attempt += 1) {
51688
+ if (completeSpoolClaim(cacheDirectory, remainingRecords, rejectedRecords)) {
51689
+ return true;
51690
+ }
51691
+ await delay(10);
51692
+ }
51693
+ return false;
51694
+ }
51695
+ async function persistAttempt(cacheDirectory, remainingRecords) {
51696
+ for (let attempt = 0; attempt < COMPLETION_RETRIES; attempt += 1) {
51697
+ if (prepareSpoolAttempt(cacheDirectory, remainingRecords)) {
51698
+ return true;
51699
+ }
51700
+ await delay(10);
51701
+ }
51702
+ return false;
51703
+ }
51704
+ async function runFlush(config, fetchImplementation = fetch) {
51705
+ const emptyResult = {
51706
+ attempted: false,
51707
+ deliveredRecords: 0,
51708
+ rejectedRecords: 0,
51709
+ remainingRecords: 0
51710
+ };
51711
+ if (!config.enabled || !acquireFlushLease(config.cacheDirectory)) {
51712
+ return emptyResult;
51713
+ }
51714
+ try {
51715
+ const claim = claimSpool(config.cacheDirectory);
51716
+ if (claim === null) {
51717
+ return emptyResult;
51718
+ }
51719
+ const state = readFlushState(config.cacheDirectory);
51720
+ const startedAt = Date.now();
51721
+ writeFlushState(config.cacheDirectory, {
51722
+ ...state,
51723
+ lastAttempt: new Date(startedAt).toISOString(),
51724
+ ...claim.corruptLines > 0 ? { lastError: `Skipped ${claim.corruptLines} corrupt spool record(s)` } : {}
51725
+ });
51726
+ const remainingIndexes = new Set(claim.records.map((_record, index) => index));
51727
+ const rejectedIndexes = /* @__PURE__ */ new Set();
51728
+ const batches = buildTelemetryBatches(claim.records).slice(0, MAX_BATCHES_PER_FLUSH);
51729
+ let deliveredRecords = 0;
51730
+ let stoppedForRetry = false;
51731
+ for (const batch of batches) {
51732
+ let remainingBudget = FLUSH_BUDGET_MS - (Date.now() - startedAt);
51733
+ if (remainingBudget <= 0) {
51734
+ stoppedForRetry = true;
51735
+ break;
51736
+ }
51737
+ const pendingRecords = claim.records.filter(
51738
+ (_record, index) => remainingIndexes.has(index) && !batch.sourceIndexes.includes(index)
51739
+ );
51740
+ if (!await persistAttempt(config.cacheDirectory, pendingRecords)) {
51741
+ stoppedForRetry = true;
51742
+ break;
51743
+ }
51744
+ for (const index of batch.sourceIndexes) {
51745
+ remainingIndexes.delete(index);
51746
+ }
51747
+ remainingBudget = FLUSH_BUDGET_MS - (Date.now() - startedAt);
51748
+ if (remainingBudget <= 0) {
51749
+ for (const index of batch.sourceIndexes) {
51750
+ remainingIndexes.add(index);
51751
+ }
51752
+ stoppedForRetry = true;
51753
+ break;
51754
+ }
51755
+ const result = await postTelemetry(
51756
+ config.endpoint,
51757
+ batch.payload,
51758
+ remainingBudget,
51759
+ fetchImplementation
51760
+ );
51761
+ if (result === "sent" || result === "unknown") {
51762
+ deliveredRecords += batch.sourceIndexes.length;
51763
+ if (result === "unknown") {
51764
+ break;
51765
+ }
51766
+ continue;
51767
+ }
51768
+ if (result === "rejected") {
51769
+ for (const index of batch.sourceIndexes) {
51770
+ rejectedIndexes.add(index);
51771
+ }
51772
+ continue;
51773
+ }
51774
+ for (const index of batch.sourceIndexes) {
51775
+ remainingIndexes.add(index);
51776
+ }
51777
+ stoppedForRetry = true;
51778
+ break;
51779
+ }
51780
+ const remainingRecords = claim.records.filter((_record, index) => remainingIndexes.has(index));
51781
+ const rejectedRecords = claim.records.filter((_record, index) => rejectedIndexes.has(index));
51782
+ const persisted = await persistCompletion(
51783
+ config.cacheDirectory,
51784
+ remainingRecords,
51785
+ rejectedRecords
51786
+ );
51787
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
51788
+ const lastError = stoppedForRetry ? "Delivery deferred after retryable failure" : rejectedRecords.length > 0 ? `Collector rejected ${rejectedRecords.length} record(s)` : !persisted ? "Could not finalize telemetry spool" : claim.corruptLines > 0 ? `Skipped ${claim.corruptLines} corrupt spool record(s)` : void 0;
51789
+ writeFlushState(config.cacheDirectory, {
51790
+ ...readFlushState(config.cacheDirectory),
51791
+ ...remainingRecords.length === 0 && persisted ? { lastSuccess: finishedAt } : {},
51792
+ lastError
51793
+ });
51794
+ return {
51795
+ attempted: batches.length > 0,
51796
+ deliveredRecords,
51797
+ rejectedRecords: rejectedRecords.length,
51798
+ remainingRecords: remainingRecords.length
51799
+ };
51800
+ } finally {
51801
+ releaseFlushLease(config.cacheDirectory);
51802
+ }
51803
+ }
51804
+
51805
+ // packages/telemetry/src/reporter.ts
51806
+ import { spawn as spawn4 } from "node:child_process";
51807
+ import { performance } from "node:perf_hooks";
51808
+ var FLUSH_INTERVAL_MS = 4 * 60 * 60 * 1e3;
51809
+ var FLUSH_RECORD_THRESHOLD = 50;
51810
+ var TelemetrySession = class {
51811
+ config;
51812
+ metadata;
51813
+ command;
51814
+ features;
51815
+ startTime;
51816
+ excluded;
51817
+ finished = false;
51818
+ constructor(options) {
51819
+ this.config = options.config;
51820
+ this.metadata = options.metadata;
51821
+ this.excluded = isExcludedCommand(options.command);
51822
+ this.command = sanitizeCommand(options.command);
51823
+ this.features = (options.features ?? []).map((feature) => sanitizeFeature(feature)).filter((feature) => feature !== null);
51824
+ this.startTime = options.startTime ?? performance.now();
51825
+ }
51826
+ finish(outcome) {
51827
+ if (this.finished || !this.config.enabled || this.excluded) {
51828
+ return;
51829
+ }
51830
+ this.finished = true;
51831
+ const duration = Math.max(0, Math.round(performance.now() - this.startTime));
51832
+ const records = [
51833
+ {
51834
+ ...this.metadata,
51835
+ event: {
51836
+ name: "command",
51837
+ command: this.command,
51838
+ outcome,
51839
+ count: 1,
51840
+ duration_ms_sum: duration
51841
+ }
51842
+ },
51843
+ ...this.features.map((feature) => ({
51844
+ ...this.metadata,
51845
+ event: { name: "feature", feature, count: 1 }
51846
+ }))
51847
+ ];
51848
+ appendSpoolRecords(this.config.cacheDirectory, records);
51849
+ }
51850
+ };
51851
+ function shouldFlush(config, now) {
51852
+ const spool = getSpoolInfo(config.cacheDirectory);
51853
+ if (spool.records === 0) {
51854
+ return false;
51855
+ }
51856
+ if (spool.records >= FLUSH_RECORD_THRESHOLD) {
51857
+ return true;
51858
+ }
51859
+ const lastAttempt = readFlushState(config.cacheDirectory).lastAttempt;
51860
+ if (lastAttempt === void 0) {
51861
+ return true;
51862
+ }
51863
+ const timestamp = Date.parse(lastAttempt);
51864
+ return !Number.isFinite(timestamp) || now - timestamp >= FLUSH_INTERVAL_MS;
51865
+ }
51866
+ function maybeSpawnFlush(config, options = {}) {
51867
+ const environment = options.environment ?? process.env;
51868
+ const entrypoint = options.entrypoint ?? process.argv[1];
51869
+ if (!config.enabled || environment["PROMPTSCRIPT_TELEMETRY_FLUSH"] === "1" || entrypoint === void 0 || !shouldFlush(config, options.now ?? Date.now())) {
51870
+ return false;
51871
+ }
51872
+ try {
51873
+ const child = (options.spawn ?? spawn4)(
51874
+ options.executable ?? process.execPath,
51875
+ [entrypoint, "__telemetry-flush"],
51876
+ {
51877
+ detached: true,
51878
+ stdio: "ignore",
51879
+ env: {
51880
+ ...environment,
51881
+ PROMPTSCRIPT_TELEMETRY_FLUSH: "1",
51882
+ PROMPTSCRIPT_NO_UPDATE_CHECK: "1"
51883
+ }
51884
+ }
51885
+ );
51886
+ child.unref();
51887
+ return true;
51888
+ } catch {
51889
+ return false;
51890
+ }
51891
+ }
51892
+
51893
+ // packages/telemetry/src/runtime.ts
51894
+ function operatingSystem(platform) {
51895
+ switch (platform) {
51896
+ case "darwin":
51897
+ return "darwin";
51898
+ case "linux":
51899
+ return "linux";
51900
+ case "win32":
51901
+ return "windows";
51902
+ default:
51903
+ return "other";
51904
+ }
51905
+ }
51906
+ function architecture(value) {
51907
+ switch (value) {
51908
+ case "arm64":
51909
+ return "arm64";
51910
+ case "x64":
51911
+ return "x86_64";
51912
+ default:
51913
+ return "other";
51914
+ }
51915
+ }
51916
+ function runtimeMetadata(appVersion, input2 = {}) {
51917
+ return {
51918
+ app_version: appVersion,
51919
+ runtime_version: (input2.nodeVersion ?? process.versions.node).split(".")[0] ?? "0",
51920
+ os: operatingSystem(input2.platform ?? process.platform),
51921
+ arch: architecture(input2.architecture ?? process.arch)
51922
+ };
51923
+ }
51924
+
51925
+ // packages/cli/src/telemetry/config.ts
51926
+ init_loader2();
51927
+ init_user_config();
51928
+ function resolveFrom(baseDirectory, value) {
51929
+ return isAbsolute14(value) ? value : resolve34(baseDirectory, value);
51930
+ }
51931
+ function findProjectConfig(cwd, customPath) {
51932
+ if (customPath !== void 0) {
51933
+ const path2 = resolveFrom(cwd, customPath);
51934
+ return existsSync27(path2) ? path2 : null;
51935
+ }
51936
+ const environmentPath = process.env["PROMPTSCRIPT_CONFIG"];
51937
+ if (environmentPath !== void 0 && environmentPath !== "") {
51938
+ const path2 = resolveFrom(cwd, environmentPath);
51939
+ return existsSync27(path2) ? path2 : null;
51940
+ }
51941
+ for (const file of CONFIG_FILES) {
51942
+ const path2 = join29(cwd, file);
51943
+ if (existsSync27(path2)) {
51944
+ return path2;
51945
+ }
51946
+ }
51947
+ return null;
51948
+ }
51949
+ function isNodeError3(error, code) {
51950
+ return error instanceof Error && "code" in error && error.code === code;
51951
+ }
51952
+ async function readUserTelemetrySetting(configPath) {
51953
+ let source;
51954
+ try {
51955
+ source = await readFile27(configPath, "utf8");
51956
+ } catch (error) {
51957
+ return isNodeError3(error, "ENOENT") ? { valid: true } : { valid: false };
51958
+ }
51959
+ try {
51960
+ const document = parseDocument(source);
51961
+ if (document.errors.length > 0) {
51962
+ return { valid: false };
51963
+ }
51964
+ const value = document.get("telemetry");
51965
+ if (value === void 0) {
51966
+ return { valid: true };
51967
+ }
51968
+ return typeof value === "boolean" ? { enabled: value, valid: true } : { valid: false };
51969
+ } catch {
51970
+ return { valid: false };
51971
+ }
51972
+ }
51973
+ async function resolveCliTelemetryConfig(options = {}) {
51974
+ const cwd = options.cwd === void 0 ? process.cwd() : resolve34(process.cwd(), options.cwd);
51975
+ const userSetting = await readUserTelemetrySetting(options.userConfigPath ?? USER_CONFIG_PATH);
51976
+ const projectPath = findProjectConfig(cwd, options.config);
51977
+ const environmentConfig = process.env["PROMPTSCRIPT_CONFIG"];
51978
+ const projectConfigRequested = options.config !== void 0 || environmentConfig !== void 0 && environmentConfig !== "";
51979
+ let projectEnabled;
51980
+ let configurationValid = userSetting.valid;
51981
+ if (projectConfigRequested && projectPath === null) {
51982
+ configurationValid = false;
51983
+ } else if (projectPath !== null) {
51984
+ try {
51985
+ const value = (await loadConfig(projectPath)).telemetry;
51986
+ if (value === void 0 || typeof value === "boolean") {
51987
+ projectEnabled = value;
51988
+ } else {
51989
+ configurationValid = false;
51990
+ }
51991
+ } catch {
51992
+ configurationValid = false;
51993
+ }
51994
+ }
51995
+ return resolveTelemetryConfig({
51996
+ environment: process.env,
51997
+ userEnabled: userSetting.enabled,
51998
+ projectEnabled,
51999
+ configurationValid,
52000
+ ...options.cacheDirectory === void 0 ? {} : { cacheDirectory: options.cacheDirectory }
52001
+ });
52002
+ }
52003
+ async function setUserTelemetryEnabled(enabled, configPath = USER_CONFIG_PATH) {
52004
+ let source = "version: '1'\n";
52005
+ try {
52006
+ source = await readFile27(configPath, "utf8");
52007
+ } catch (error) {
52008
+ if (!isNodeError3(error, "ENOENT")) {
52009
+ throw error;
52010
+ }
52011
+ }
52012
+ const document = parseDocument(source);
52013
+ if (document.errors.length > 0) {
52014
+ throw new Error(`Cannot update invalid YAML user config: ${configPath}`);
52015
+ }
52016
+ document.set("telemetry", enabled);
52017
+ await mkdir11(dirname21(configPath), { recursive: true });
52018
+ const temporaryPath = `${configPath}.${process.pid}.tmp`;
52019
+ await writeFile11(temporaryPath, document.toString(), { encoding: "utf8", mode: 384 });
52020
+ await rename2(temporaryPath, configPath);
52021
+ }
52022
+
52023
+ // packages/cli/src/telemetry/session.ts
52024
+ var activeSession = null;
52025
+ var exitHandlerInstalled = false;
52026
+ function normalizedCommandName(command) {
52027
+ const name = command.name();
52028
+ const parentName = command.parent?.name();
52029
+ if (parentName === "vendor" || parentName === "skills") {
52030
+ return `${parentName}-${name}`;
52031
+ }
52032
+ if (parentName === "registry") {
52033
+ return "registry";
52034
+ }
52035
+ return name;
52036
+ }
52037
+ function stringValues(value) {
52038
+ if (typeof value === "string") {
52039
+ return [value];
52040
+ }
52041
+ if (Array.isArray(value)) {
52042
+ return value.filter((item) => typeof item === "string");
52043
+ }
52044
+ return [];
52045
+ }
52046
+ function commandFeatures(command) {
52047
+ const options = command.opts();
52048
+ const features = /* @__PURE__ */ new Set();
52049
+ const targetValues = [...stringValues(options["target"]), ...stringValues(options["targets"])];
52050
+ for (const target of targetValues) {
52051
+ const feature = sanitizeFeature(`target:${target}`);
52052
+ if (feature !== null) {
52053
+ features.add(feature);
52054
+ }
52055
+ }
52056
+ if (options["dryRun"] === true) {
52057
+ features.add("dry_run");
52058
+ }
52059
+ if (options["watch"] === true) {
52060
+ features.add("watch");
52061
+ }
52062
+ if (options["strict"] === true) {
52063
+ features.add("strict");
52064
+ }
52065
+ if (options["build"] !== void 0 || command.name() === "build") {
52066
+ features.add("build_profile");
52067
+ }
52068
+ if (process.env["CI"] !== void 0) {
52069
+ features.add("ci");
52070
+ }
52071
+ return [...features];
52072
+ }
52073
+ function exitOutcome(code) {
52074
+ if (code === 130 || code === 143) {
52075
+ return "cancelled";
52076
+ }
52077
+ return code === 0 ? "success" : "error";
52078
+ }
52079
+ function installExitHandler() {
52080
+ if (exitHandlerInstalled) {
52081
+ return;
52082
+ }
52083
+ process.on("exit", (code) => {
52084
+ finishCliTelemetry(exitOutcome(code));
52085
+ });
52086
+ for (const [signal, fallbackCode] of [
52087
+ ["SIGINT", 130],
52088
+ ["SIGTERM", 143]
52089
+ ]) {
52090
+ process.once(signal, () => {
52091
+ finishCliTelemetry("cancelled");
52092
+ if (process.listenerCount(signal) > 0) {
52093
+ return;
52094
+ }
52095
+ try {
52096
+ process.kill(process.pid, signal);
52097
+ } catch {
52098
+ process.exit(fallbackCode);
52099
+ }
52100
+ });
52101
+ }
52102
+ exitHandlerInstalled = true;
52103
+ }
52104
+ async function prepareCliTelemetry(command, appVersion) {
52105
+ const name = normalizedCommandName(command);
52106
+ if (isExcludedCommand(name) || process.env["PROMPTSCRIPT_TELEMETRY_FLUSH"] === "1") {
52107
+ return;
52108
+ }
52109
+ const options = command.opts();
52110
+ const config = await resolveCliTelemetryConfig({
52111
+ ...typeof options["cwd"] === "string" ? { cwd: options["cwd"] } : {},
52112
+ ...typeof options["config"] === "string" ? { config: options["config"] } : {}
52113
+ });
52114
+ maybeSpawnFlush(config);
52115
+ activeSession = new TelemetrySession({
52116
+ config,
52117
+ metadata: runtimeMetadata(appVersion),
52118
+ command: name,
52119
+ features: commandFeatures(command)
52120
+ });
52121
+ installExitHandler();
52122
+ }
52123
+ function finishCliTelemetry(outcome) {
52124
+ const session = activeSession;
52125
+ activeSession = null;
52126
+ session?.finish(
52127
+ outcome ?? (process.exitCode === void 0 || process.exitCode === 0 ? "success" : "error")
52128
+ );
52129
+ }
52130
+ async function flushCliTelemetry() {
52131
+ const config = await resolveCliTelemetryConfig();
52132
+ await runFlush(config);
52133
+ }
52134
+ async function telemetryStatus() {
52135
+ const config = await resolveCliTelemetryConfig();
52136
+ return {
52137
+ config,
52138
+ spool: getSpoolInfo(config.cacheDirectory),
52139
+ state: readFlushState(config.cacheDirectory)
52140
+ };
52141
+ }
52142
+
52143
+ // packages/cli/src/commands/telemetry.ts
52144
+ async function telemetryCommand(action) {
52145
+ if (action === "enable") {
52146
+ await setUserTelemetryEnabled(true);
52147
+ ConsoleOutput.success("Anonymous usage telemetry enabled in user config");
52148
+ return;
52149
+ }
52150
+ if (action === "disable") {
52151
+ await setUserTelemetryEnabled(false);
52152
+ ConsoleOutput.success("Anonymous usage telemetry disabled in user config");
52153
+ return;
52154
+ }
52155
+ if (action !== "status") {
52156
+ ConsoleOutput.error("Action must be status, enable, or disable");
52157
+ process.exitCode = 1;
52158
+ return;
52159
+ }
52160
+ const { config, spool, state } = await telemetryStatus();
52161
+ console.log(`Enabled: ${config.enabled ? "yes" : "no"}`);
52162
+ console.log(`Endpoint: ${config.endpoint}`);
52163
+ console.log(`Spool records: ${spool.records}`);
52164
+ console.log(`Spool bytes: ${spool.bytes}`);
52165
+ console.log(`Last attempt: ${state.lastAttempt ?? "never"}`);
52166
+ console.log(`Last success: ${state.lastSuccess ?? "never"}`);
52167
+ console.log(`Last error: ${state.lastError ?? "none"}`);
52168
+ if (config.vetoes.length > 0) {
52169
+ console.log(`Disabled by: ${config.vetoes.join(", ")}`);
52170
+ }
52171
+ }
52172
+
50744
52173
  // packages/cli/src/commands/skills.ts
50745
52174
  init_src2();
50746
52175
  init_loader2();
50747
52176
  init_console();
50748
52177
  init_src();
50749
- import { writeFile as writeFile11, readFile as readFile27, readdir as readdir9, mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
50750
- import { resolve as resolve34, join as join27, basename as basename17, dirname as dirname21 } from "path";
50751
- import { existsSync as existsSync26 } from "fs";
52178
+ import { writeFile as writeFile12, readFile as readFile28, readdir as readdir9, mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
52179
+ import { resolve as resolve35, join as join30, basename as basename17, dirname as dirname22 } from "path";
52180
+ import { existsSync as existsSync28 } from "fs";
50752
52181
  import { tmpdir as tmpdir3 } from "os";
50753
52182
  import { parse as parseYaml15, stringify as stringifyYaml4 } from "yaml";
50754
52183
  var REMOTE_SOURCE_PATTERN = /^[a-zA-Z0-9][\w.-]*\.[a-zA-Z]{2,}\/.+/;
@@ -50796,32 +52225,32 @@ async function collectProjectRemoteImports(entryFile) {
50796
52225
  const projectRoot = process.cwd();
50797
52226
  const entryFiles = /* @__PURE__ */ new Set();
50798
52227
  if (entryFile) {
50799
- entryFiles.add(resolve34(entryFile));
52228
+ entryFiles.add(resolve35(entryFile));
50800
52229
  }
50801
52230
  if (config) {
50802
52231
  if (config.input?.entry) {
50803
- entryFiles.add(resolve34(projectRoot, config.input.entry));
52232
+ entryFiles.add(resolve35(projectRoot, config.input.entry));
50804
52233
  }
50805
52234
  for (const build of Object.values(config.builds ?? {})) {
50806
52235
  if (build.entry) {
50807
- entryFiles.add(resolve34(projectRoot, build.entry));
52236
+ entryFiles.add(resolve35(projectRoot, build.entry));
50808
52237
  }
50809
52238
  }
50810
- const defaultEntry = resolve34(projectRoot, ".promptscript/project.prs");
50811
- if (!config.input?.entry && (existsSync26(defaultEntry) || entryFiles.size === 0)) {
52239
+ const defaultEntry = resolve35(projectRoot, ".promptscript/project.prs");
52240
+ if (!config.input?.entry && (existsSync28(defaultEntry) || entryFiles.size === 0)) {
50812
52241
  entryFiles.add(defaultEntry);
50813
52242
  }
50814
52243
  } else if (!entryFile) {
50815
- const defaultEntry = resolve34(projectRoot, ".promptscript/project.prs");
50816
- if (existsSync26(defaultEntry)) {
52244
+ const defaultEntry = resolve35(projectRoot, ".promptscript/project.prs");
52245
+ if (existsSync28(defaultEntry)) {
50817
52246
  entryFiles.add(defaultEntry);
50818
52247
  } else {
50819
- const promptscriptDir = resolve34(projectRoot, ".promptscript");
50820
- if (existsSync26(promptscriptDir)) {
52248
+ const promptscriptDir = resolve35(projectRoot, ".promptscript");
52249
+ if (existsSync28(promptscriptDir)) {
50821
52250
  const entries = await readdir9(promptscriptDir);
50822
52251
  const prsFiles = entries.filter((entry) => entry.endsWith(".prs"));
50823
52252
  if (prsFiles.length === 1) {
50824
- entryFiles.add(resolve34(promptscriptDir, prsFiles[0]));
52253
+ entryFiles.add(resolve35(promptscriptDir, prsFiles[0]));
50825
52254
  }
50826
52255
  }
50827
52256
  }
@@ -50830,7 +52259,7 @@ async function collectProjectRemoteImports(entryFile) {
50830
52259
  for (const projectEntry of entryFiles) {
50831
52260
  imports.push(
50832
52261
  ...await collectRemoteImports(projectEntry, {
50833
- localPath: resolve34(projectRoot, ".promptscript"),
52262
+ localPath: resolve35(projectRoot, ".promptscript"),
50834
52263
  registries: config?.registries,
50835
52264
  strict: true,
50836
52265
  deduplicate: false,
@@ -50880,7 +52309,7 @@ function deriveSkillFolderName(source) {
50880
52309
  const subPath = extractSubPath(source);
50881
52310
  if (!subPath) return basename17(parseSkillSource(source).path);
50882
52311
  if (!subPath.toLowerCase().endsWith(".md")) return basename17(subPath);
50883
- const parent = dirname21(subPath);
52312
+ const parent = dirname22(subPath);
50884
52313
  if (parent === "." || parent === "") return basename17(parseSkillSource(source).path);
50885
52314
  return basename17(parent);
50886
52315
  }
@@ -50897,16 +52326,16 @@ function collectExistingSkillNames(lockfile, exclude) {
50897
52326
  async function fetchAndValidateRemoteSkill(source, options) {
50898
52327
  const skillFilePath = extractSkillFilePath(source);
50899
52328
  const repoUrl = options.gitUrl ?? extractRepoUrl(source);
50900
- const tmp = await mkdtemp2(join27(tmpdir3(), "prs-skill-validate-"));
52329
+ const tmp = await mkdtemp2(join30(tmpdir3(), "prs-skill-validate-"));
50901
52330
  const repoName = basename17(parseSkillSource(source).path.split("/")[2]);
50902
- const cloneDir = !skillFilePath.includes("/") ? join27(tmp, repoName) : tmp;
52331
+ const cloneDir = !skillFilePath.includes("/") ? join30(tmp, repoName) : tmp;
50903
52332
  try {
50904
52333
  const gitRegistry = createGitRegistry({ url: repoUrl });
50905
52334
  const cloneRef = options.version === "latest" || /^[0-9a-f]{40}$/i.test(options.version) ? void 0 : options.version;
50906
52335
  await gitRegistry.cloneAtTag(repoUrl, cloneRef, cloneDir);
50907
52336
  await gitRegistry.checkoutCommit(cloneDir, options.commit);
50908
52337
  const subPath = extractSubPath(source);
50909
- if (!subPath.toLowerCase().endsWith(".md") && existsSync26(join27(cloneDir, `${subPath}.prs`))) {
52338
+ if (!subPath.toLowerCase().endsWith(".md") && existsSync28(join30(cloneDir, `${subPath}.prs`))) {
50910
52339
  return {
50911
52340
  content: "",
50912
52341
  integrity: "",
@@ -50920,10 +52349,10 @@ async function fetchAndValidateRemoteSkill(source, options) {
50920
52349
  ]
50921
52350
  };
50922
52351
  }
50923
- const filePath = join27(cloneDir, skillFilePath);
52352
+ const filePath = join30(cloneDir, skillFilePath);
50924
52353
  let content;
50925
52354
  try {
50926
- content = await readFile27(filePath, "utf-8");
52355
+ content = await readFile28(filePath, "utf-8");
50927
52356
  } catch {
50928
52357
  return {
50929
52358
  content: "",
@@ -50999,8 +52428,8 @@ function validateRemoteSource(source) {
50999
52428
  }
51000
52429
  async function resolveEntryFile(fileFlag) {
51001
52430
  if (fileFlag) {
51002
- const resolved = resolve34(fileFlag);
51003
- if (!existsSync26(resolved)) {
52431
+ const resolved = resolve35(fileFlag);
52432
+ if (!existsSync28(resolved)) {
51004
52433
  throw new Error(`File not found: ${resolved}`);
51005
52434
  }
51006
52435
  return resolved;
@@ -51010,24 +52439,24 @@ async function resolveEntryFile(fileFlag) {
51010
52439
  try {
51011
52440
  const config = await loadConfig();
51012
52441
  if (config.input?.entry) {
51013
- const entryPath = resolve34(config.input.entry);
51014
- if (existsSync26(entryPath)) {
52442
+ const entryPath = resolve35(config.input.entry);
52443
+ if (existsSync28(entryPath)) {
51015
52444
  return entryPath;
51016
52445
  }
51017
52446
  }
51018
52447
  } catch {
51019
52448
  }
51020
52449
  }
51021
- const promptscriptDir = resolve34(".promptscript");
51022
- if (existsSync26(promptscriptDir)) {
52450
+ const promptscriptDir = resolve35(".promptscript");
52451
+ if (existsSync28(promptscriptDir)) {
51023
52452
  const entries = await readdir9(promptscriptDir);
51024
52453
  const prsFiles = entries.filter((f) => f.endsWith(".prs"));
51025
52454
  if (prsFiles.length === 1) {
51026
- return resolve34(promptscriptDir, prsFiles[0]);
52455
+ return resolve35(promptscriptDir, prsFiles[0]);
51027
52456
  }
51028
52457
  if (prsFiles.length > 1) {
51029
52458
  if (prsFiles.includes("project.prs")) {
51030
- return resolve34(promptscriptDir, "project.prs");
52459
+ return resolve35(promptscriptDir, "project.prs");
51031
52460
  }
51032
52461
  throw new Error(
51033
52462
  `Multiple .prs files found in .promptscript/. Use --file to specify which one.`
@@ -51128,12 +52557,12 @@ function findUseEndLine(lines, startLine) {
51128
52557
  }
51129
52558
  async function loadLockfile3() {
51130
52559
  const defaultLockfile = { version: LOCKFILE_VERSION, dependencies: {} };
51131
- if (!existsSync26(LOCKFILE_PATH)) {
52560
+ if (!existsSync28(LOCKFILE_PATH)) {
51132
52561
  return defaultLockfile;
51133
52562
  }
51134
52563
  let parsed;
51135
52564
  try {
51136
- const raw = await readFile27(LOCKFILE_PATH, "utf-8");
52565
+ const raw = await readFile28(LOCKFILE_PATH, "utf-8");
51137
52566
  parsed = parseYaml15(raw, { maxAliasCount: 100 });
51138
52567
  } catch (error) {
51139
52568
  throw new Error(
@@ -51147,7 +52576,7 @@ async function loadLockfile3() {
51147
52576
  return parsed;
51148
52577
  }
51149
52578
  async function saveLockfile(lockfile) {
51150
- await writeFile11(LOCKFILE_PATH, stringifyYaml4(lockfile), "utf-8");
52579
+ await writeFile12(LOCKFILE_PATH, stringifyYaml4(lockfile), "utf-8");
51151
52580
  }
51152
52581
  async function skillsAddCommand(rawSource, options) {
51153
52582
  const spinner = createSpinner("Adding skill...").start();
@@ -51170,7 +52599,7 @@ async function skillsAddCommand(rawSource, options) {
51170
52599
  }
51171
52600
  spinner.text = "Resolving entry file...";
51172
52601
  const entryFile = await resolveEntryFile(options.file);
51173
- const content = await readFile27(entryFile, "utf-8");
52602
+ const content = await readFile28(entryFile, "utf-8");
51174
52603
  const lines = content.split("\n");
51175
52604
  const alreadyImported = lines.some((line) => {
51176
52605
  const usePath = extractUsePath(line);
@@ -51282,7 +52711,7 @@ async function skillsAddCommand(rawSource, options) {
51282
52711
  ConsoleOutput.dryRun(`Would update ${LOCKFILE_PATH}`);
51283
52712
  return;
51284
52713
  }
51285
- await writeFile11(entryFile, updatedContent, "utf-8");
52714
+ await writeFile12(entryFile, updatedContent, "utf-8");
51286
52715
  await saveLockfile(lockfile);
51287
52716
  spinner.succeed("Skill added");
51288
52717
  ConsoleOutput.newline();
@@ -51298,7 +52727,7 @@ async function skillsRemoveCommand(name, options) {
51298
52727
  const spinner = createSpinner("Removing skill...").start();
51299
52728
  try {
51300
52729
  const entryFile = await resolveEntryFile();
51301
- const content = await readFile27(entryFile, "utf-8");
52730
+ const content = await readFile28(entryFile, "utf-8");
51302
52731
  const lines = content.split("\n");
51303
52732
  const parsed = parse(content, { filename: entryFile, tolerant: true });
51304
52733
  if (!parsed.ast || parsed.errors.length > 0) {
@@ -51378,7 +52807,7 @@ async function skillsRemoveCommand(name, options) {
51378
52807
  return;
51379
52808
  }
51380
52809
  lines.splice(lineIndex, candidate.endIndex - lineIndex + 1);
51381
- await writeFile11(entryFile, lines.join("\n"), "utf-8");
52810
+ await writeFile12(entryFile, lines.join("\n"), "utf-8");
51382
52811
  if (removedPath in lockfile.dependencies && !hasSameImportRemaining) {
51383
52812
  delete lockfile.dependencies[removedPath];
51384
52813
  }
@@ -51414,7 +52843,7 @@ async function skillsListCommand() {
51414
52843
  const spinner = createSpinner("Loading skills...").start();
51415
52844
  try {
51416
52845
  const entryFile = await resolveEntryFile();
51417
- const content = await readFile27(entryFile, "utf-8");
52846
+ const content = await readFile28(entryFile, "utf-8");
51418
52847
  const lines = content.split("\n");
51419
52848
  const skills2 = [];
51420
52849
  for (const line of lines) {
@@ -51582,9 +53011,13 @@ async function skillsUpdateCommand(name, options) {
51582
53011
 
51583
53012
  // packages/cli/src/cli.ts
51584
53013
  var __filename4 = fileURLToPath4(import.meta.url);
51585
- var __dirname4 = dirname26(__filename4);
53014
+ var __dirname4 = dirname27(__filename4);
53015
+ var CLI_VERSION = getPackageVersion(__dirname3, "./package.json");
51586
53016
  var program = new Command();
51587
- 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) => {
53017
+ program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(CLI_VERSION).option("--verbose", "Enable verbose output").option("--debug", "Enable debug output (includes verbose)").option("--quiet", "Suppress non-error output").hook("preAction", async (thisCommand, actionCommand) => {
53018
+ if (actionCommand.name() === "__telemetry-flush" && process.env["PROMPTSCRIPT_TELEMETRY_FLUSH"] === "1") {
53019
+ return;
53020
+ }
51588
53021
  const opts = thisCommand.opts();
51589
53022
  if (opts["quiet"]) {
51590
53023
  setContext({ logLevel: 0 /* Quiet */ });
@@ -51598,7 +53031,8 @@ program.name("prs").description("PromptScript CLI - Standardize AI instructions"
51598
53031
  } else if (process.env["PROMPTSCRIPT_VERBOSE"] === "1" || process.env["PROMPTSCRIPT_VERBOSE"] === "true") {
51599
53032
  setContext({ logLevel: 2 /* Verbose */ });
51600
53033
  }
51601
- if (actionCommand.name() === "update-check") {
53034
+ await prepareCliTelemetry(actionCommand, CLI_VERSION);
53035
+ if (actionCommand.name() === "update-check" || actionCommand.name() === "telemetry" || actionCommand.name() === "__telemetry-flush") {
51602
53036
  return;
51603
53037
  }
51604
53038
  const currentVersion = getPackageVersion(__dirname3, "./package.json");
@@ -51607,6 +53041,8 @@ program.name("prs").description("PromptScript CLI - Standardize AI instructions"
51607
53041
  printUpdateNotification(updateInfo);
51608
53042
  }
51609
53043
  });
53044
+ }).hook("postAction", () => {
53045
+ finishCliTelemetry();
51610
53046
  });
51611
53047
  program.command("init").description("Initialize PromptScript in current directory").option("-n, --name <name>", "Project name (auto-detected from package.json, etc.)").option("-t, --team <team>", "Team namespace").option("--inherit <path>", "Inheritance path (e.g., @company/team)").option("--registry <path>", "Registry path").option("--targets <targets...>", "Target AI tools (for example: github, claude, cursor)").option("-i, --interactive", "Force interactive mode").option("-y, --yes", "Skip prompts; requires explicit, detected, or configured targets").option("-f, --force", "Force reinitialize even if already initialized").option("-m, --migrate", "Deprecated: run migration during initialization").option("--auto-import", "Automatically import existing instruction files (static)").option("--backup", "Create .prs-backup/ before replacing files").option("--dry-run", "Preview files without writing").option("--no-hooks", "Skip auto-compile hook installation for AI tools").action((opts) => {
51612
53048
  if (opts.migrate) {
@@ -51628,6 +53064,8 @@ program.command("pull").description("Pull updates from registry").option("-f, --
51628
53064
  program.command("diff").description("Show diff for compiled output").option("-t, --target <target>", "Specific target").option("-a, --all", "Show diff for all targets at once").option("--full", "Show full diff without truncation").option("--no-pager", "Disable pager output").option("--color", "Force colored output").option("--no-color", "Disable colored output").action(diffCommand);
51629
53065
  program.command("check").description("Check configuration and dependencies health").action(checkCommand);
51630
53066
  program.command("update-check").description("Check for CLI updates").action(updateCheckCommand);
53067
+ program.command("telemetry").argument("<action>", "Action: status, enable, or disable").description("Manage anonymous usage telemetry").action(telemetryCommand);
53068
+ program.command("__telemetry-flush", { hidden: true }).description("Flush queued anonymous telemetry").action(flushCliTelemetry);
51631
53069
  program.command("serve").description("Start local development server for playground").option("-p, --port <port>", "Port to listen on", "3000").option("--host <host>", "Host to bind to", "127.0.0.1").option("--read-only", "Disable file modifications").option("--cors-origin <origin>", "Allowed CORS origin", "https://getpromptscript.dev").action(async (opts) => {
51632
53070
  const { serveCommand: serveCommand2 } = await Promise.resolve().then(() => (init_serve(), serve_exports));
51633
53071
  await serveCommand2(opts);
@@ -51653,11 +53091,18 @@ skills.command("list").description("List skills imported in the current project"
51653
53091
  skills.command("update [name]").description("Update lock entries for markdown-sourced skills").option("--dry-run", "Preview changes without writing").option("--skip-validation", "Skip SKILL.md frontmatter validation (not recommended)").option("--strict", "Treat validation warnings as errors").action((name, opts) => skillsUpdateCommand(name, opts));
51654
53092
  var registry = program.command("registry").description("Manage PromptScript registries");
51655
53093
  registerRegistryCommands(registry);
51656
- function run(args = process.argv) {
51657
- program.parse(args);
53094
+ async function run(args = process.argv) {
53095
+ try {
53096
+ await program.parseAsync(args);
53097
+ } catch (error) {
53098
+ finishCliTelemetry("error");
53099
+ throw error;
53100
+ } finally {
53101
+ finishCliTelemetry();
53102
+ }
51658
53103
  }
51659
53104
  if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
51660
- run();
53105
+ await run();
51661
53106
  }
51662
53107
 
51663
53108
  // packages/cli/src/index.ts