@promptscript/cli 1.16.0 → 1.17.0

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,8 +4437,8 @@ 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");
@@ -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);
@@ -31082,9 +31110,9 @@ var require_rate_limit = __commonJS({
31082
31110
  throw new Error("The configured rate-limit store does not implement a `read` method, which is required to use `{ increment: false }`");
31083
31111
  }
31084
31112
  try {
31085
- const res = await new Promise((resolve37, reject) => {
31113
+ const res = await new Promise((resolve38, reject) => {
31086
31114
  store[storeMethod](key, (err, res2) => {
31087
- err ? reject(err) : resolve37(res2);
31115
+ err ? reject(err) : resolve38(res2);
31088
31116
  }, timeWindow, max);
31089
31117
  });
31090
31118
  current = res.current;
@@ -31191,13 +31219,13 @@ var init_health = __esm({
31191
31219
  });
31192
31220
 
31193
31221
  // packages/server/src/source-dirs.ts
31194
- import { readFile as readFile28 } from "fs/promises";
31195
- import { join as join28, dirname as dirname22 } from "path";
31222
+ import { readFile as readFile29 } from "fs/promises";
31223
+ import { join as join31, dirname as dirname23 } from "path";
31196
31224
  import { parse as parseYaml16 } from "yaml";
31197
31225
  async function readProjectConfig(workspace) {
31198
31226
  for (const filename of CONFIG_FILENAMES) {
31199
31227
  try {
31200
- const raw = await readFile28(join28(workspace, filename), "utf-8");
31228
+ const raw = await readFile29(join31(workspace, filename), "utf-8");
31201
31229
  const config = parseYaml16(raw);
31202
31230
  if (config) return config;
31203
31231
  } catch {
@@ -31208,10 +31236,10 @@ async function readProjectConfig(workspace) {
31208
31236
  async function resolveSourceDir(workspace) {
31209
31237
  for (const filename of CONFIG_FILENAMES) {
31210
31238
  try {
31211
- const raw = await readFile28(join28(workspace, filename), "utf-8");
31239
+ const raw = await readFile29(join31(workspace, filename), "utf-8");
31212
31240
  const config = parseYaml16(raw);
31213
31241
  if (config?.input?.entry) {
31214
- return dirname22(config.input.entry);
31242
+ return dirname23(config.input.entry);
31215
31243
  }
31216
31244
  } catch {
31217
31245
  }
@@ -31225,9 +31253,9 @@ async function resolveFileGlobs(workspace) {
31225
31253
  async function resolveWatchPaths(workspace) {
31226
31254
  const sourceDir = await resolveSourceDir(workspace);
31227
31255
  return [
31228
- join28(workspace, sourceDir),
31229
- join28(workspace, "promptscript.yaml"),
31230
- join28(workspace, "promptscript.yml")
31256
+ join31(workspace, sourceDir),
31257
+ join31(workspace, "promptscript.yaml"),
31258
+ join31(workspace, "promptscript.yml")
31231
31259
  ];
31232
31260
  }
31233
31261
  var DEFAULT_SOURCE_DIR, CONFIG_FILENAMES;
@@ -31254,16 +31282,16 @@ var init_config2 = __esm({
31254
31282
  });
31255
31283
 
31256
31284
  // 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";
31285
+ import { resolve as resolve36, relative as relative16, isAbsolute as isAbsolute16, dirname as dirname24, basename as basename18 } from "path";
31258
31286
  import { realpath as realpath5 } from "fs/promises";
31259
31287
  async function resolveSafePath(workspace, requestedPath) {
31260
31288
  const decoded = decodeURIComponent(requestedPath);
31261
- if (isAbsolute15(decoded)) {
31289
+ if (isAbsolute16(decoded)) {
31262
31290
  throw new PathTraversalError(requestedPath);
31263
31291
  }
31264
- const resolved = resolve35(workspace, decoded);
31292
+ const resolved = resolve36(workspace, decoded);
31265
31293
  const rel = relative16(workspace, resolved);
31266
- if (rel.startsWith("..") || isAbsolute15(rel)) {
31294
+ if (rel.startsWith("..") || isAbsolute16(rel)) {
31267
31295
  throw new PathTraversalError(requestedPath);
31268
31296
  }
31269
31297
  let realWorkspace;
@@ -31278,8 +31306,8 @@ async function resolveSafePath(workspace, requestedPath) {
31278
31306
  } catch (err) {
31279
31307
  if (err instanceof Error && "code" in err && err.code === "ENOENT") {
31280
31308
  try {
31281
- const realParent = await realpath5(dirname23(resolved));
31282
- real = resolve35(realParent, basename18(resolved));
31309
+ const realParent = await realpath5(dirname24(resolved));
31310
+ real = resolve36(realParent, basename18(resolved));
31283
31311
  } catch {
31284
31312
  return resolved;
31285
31313
  }
@@ -31288,7 +31316,7 @@ async function resolveSafePath(workspace, requestedPath) {
31288
31316
  }
31289
31317
  }
31290
31318
  const realRel = relative16(realWorkspace, real);
31291
- if (realRel.startsWith("..") || isAbsolute15(realRel)) {
31319
+ if (realRel.startsWith("..") || isAbsolute16(realRel)) {
31292
31320
  throw new PathTraversalError(requestedPath);
31293
31321
  }
31294
31322
  return resolved;
@@ -31307,8 +31335,8 @@ var init_path_guard = __esm({
31307
31335
  });
31308
31336
 
31309
31337
  // 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";
31338
+ import { readFile as readFile30, writeFile as writeFile13, unlink as unlink3, stat as stat3, mkdir as mkdir12 } from "fs/promises";
31339
+ import { dirname as dirname25 } from "path";
31312
31340
  import fg from "fast-glob";
31313
31341
  function registerRoutes(app, workspace, readOnly) {
31314
31342
  app.get("/api/files", async () => {
@@ -31328,7 +31356,7 @@ function registerRoutes(app, workspace, readOnly) {
31328
31356
  const filePath = request.params["*"];
31329
31357
  try {
31330
31358
  const resolved = await resolveSafePath(workspace, filePath);
31331
- const content = await readFile29(resolved, "utf-8");
31359
+ const content = await readFile30(resolved, "utf-8");
31332
31360
  return { path: filePath, content };
31333
31361
  } catch (err) {
31334
31362
  if (err instanceof PathTraversalError) {
@@ -31345,7 +31373,7 @@ function registerRoutes(app, workspace, readOnly) {
31345
31373
  const filePath = request.params["*"];
31346
31374
  try {
31347
31375
  const resolved = await resolveSafePath(workspace, filePath);
31348
- await writeFile12(resolved, request.body.content, "utf-8");
31376
+ await writeFile13(resolved, request.body.content, "utf-8");
31349
31377
  return { path: filePath, status: "updated" };
31350
31378
  } catch (err) {
31351
31379
  if (err instanceof PathTraversalError) {
@@ -31368,8 +31396,8 @@ function registerRoutes(app, workspace, readOnly) {
31368
31396
  return reply.status(409).send({ error: "File already exists" });
31369
31397
  } catch {
31370
31398
  }
31371
- await mkdir11(dirname24(resolved), { recursive: true });
31372
- await writeFile12(resolved, request.body.content, "utf-8");
31399
+ await mkdir12(dirname25(resolved), { recursive: true });
31400
+ await writeFile13(resolved, request.body.content, "utf-8");
31373
31401
  return reply.status(201).send({ path: filePath, status: "created" });
31374
31402
  } catch (err) {
31375
31403
  if (err instanceof PathTraversalError) {
@@ -31597,7 +31625,7 @@ var migrate_exports = {};
31597
31625
  __export(migrate_exports, {
31598
31626
  migrateCommand: () => migrateCommand
31599
31627
  });
31600
- import { dirname as dirname25, relative as relative18, resolve as resolve36 } from "path";
31628
+ import { dirname as dirname26, relative as relative18, resolve as resolve37 } from "path";
31601
31629
  import { parse as parseYaml17 } from "yaml";
31602
31630
  async function migrateCommand(options, services = createDefaultServices()) {
31603
31631
  const previousOutputStream = getContext().outputStream;
@@ -31617,7 +31645,7 @@ async function runMigrateCommand(options, services) {
31617
31645
  ConsoleOutput.info("No migration candidates found. No files changed.");
31618
31646
  return;
31619
31647
  }
31620
- const configPath = findProjectConfig(services);
31648
+ const configPath = findProjectConfig2(services);
31621
31649
  if (!configPath) {
31622
31650
  await initCommand(
31623
31651
  {
@@ -31730,7 +31758,7 @@ function validateOptions(options) {
31730
31758
  throw new Error("--static and --llm cannot be used together");
31731
31759
  }
31732
31760
  }
31733
- function findProjectConfig(services) {
31761
+ function findProjectConfig2(services) {
31734
31762
  const envConfig = process.env["PROMPTSCRIPT_CONFIG"];
31735
31763
  if (envConfig && services.fs.existsSync(envConfig)) {
31736
31764
  return envConfig;
@@ -31781,7 +31809,7 @@ function extractTargetNames(targets) {
31781
31809
  }
31782
31810
  async function createStaticMigrationWrites(candidates, config, services) {
31783
31811
  const result = await importMultipleFiles(
31784
- candidates.map((candidate) => resolve36(services.cwd, candidate.path)),
31812
+ candidates.map((candidate) => resolve37(services.cwd, candidate.path)),
31785
31813
  {
31786
31814
  projectName: config.id,
31787
31815
  syntaxVersion: config.syntax || getLatestSyntaxVersion()
@@ -31873,7 +31901,7 @@ ${content}`;
31873
31901
  }
31874
31902
  function toUsePath(entryPath, targetPath) {
31875
31903
  const withoutExtension = targetPath.replace(/\.prs$/, "");
31876
- const path2 = relative18(dirname25(entryPath), withoutExtension).replace(/\\/g, "/");
31904
+ const path2 = relative18(dirname26(entryPath), withoutExtension).replace(/\\/g, "/");
31877
31905
  return path2.startsWith(".") ? path2 : `./${path2}`;
31878
31906
  }
31879
31907
  function validateProjectPath(path2) {
@@ -31933,7 +31961,7 @@ init_src();
31933
31961
  init_init();
31934
31962
  import { Command } from "commander";
31935
31963
  import { fileURLToPath as fileURLToPath4, pathToFileURL } from "url";
31936
- import { dirname as dirname26 } from "path";
31964
+ import { dirname as dirname27 } from "path";
31937
31965
 
31938
31966
  // packages/cli/src/commands/compile.ts
31939
31967
  import { resolve as resolve19, dirname as dirname15, isAbsolute as isAbsolute13, relative as relative13 } from "path";
@@ -31942,7 +31970,7 @@ import { chmod, writeFile as writeFile4, mkdir as mkdir5, readFile as readFile15
31942
31970
  import { existsSync as existsSync13 } from "fs";
31943
31971
  import chokidar from "chokidar";
31944
31972
 
31945
- // node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js
31973
+ // node_modules/.pnpm/balanced-match@4.0.3/node_modules/balanced-match/dist/esm/index.js
31946
31974
  var balanced = (a, b, str) => {
31947
31975
  const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
31948
31976
  const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
@@ -38017,7 +38045,7 @@ async function discoverNativeContent(dir) {
38017
38045
 
38018
38046
  // packages/resolver/src/vendor-manifest.ts
38019
38047
  import { createHash as createHash3 } from "crypto";
38020
- import { execFile } from "child_process";
38048
+ import { execFile, spawn } from "child_process";
38021
38049
  import { lstat as lstat5, readFile as readFile12, readdir as readdir4, realpath as realpath3 } from "fs/promises";
38022
38050
  import { basename as basename10, dirname as dirname9, isAbsolute as isAbsolute8, join as join9, posix as posix2, relative as relative7, sep as sep5 } from "path";
38023
38051
  import { promisify } from "util";
@@ -38025,6 +38053,8 @@ var VENDOR_MANIFEST_FILE = ".vendor-manifest.json";
38025
38053
  var VENDOR_GIT_DIR = ".promptscript-git";
38026
38054
  var execFileAsync = promisify(execFile);
38027
38055
  var NULL_DEVICE = process.platform === "win32" ? "NUL" : "/dev/null";
38056
+ var MAX_GIT_STDERR_BYTES = 64 * 1024;
38057
+ var MAX_GIT_TREE_RECORD_BYTES = 1024 * 1024;
38028
38058
  function getVendorRepositoryRelativePath(repoUrl) {
38029
38059
  const sshMatch = /^git@([^:]+):(.+)$/.exec(repoUrl);
38030
38060
  const candidate = sshMatch ? `https://${sshMatch[1]}/${sshMatch[2]}` : /^[a-z][a-z\d+.-]*:\/\//i.test(repoUrl) ? repoUrl : `https://${repoUrl}`;
@@ -38141,25 +38171,91 @@ async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expected
38141
38171
  if (head.stdout.trim().toLowerCase() !== expectedCommit.toLowerCase()) {
38142
38172
  throw new Error(`Vendored repository commit does not match the lockfile: ${directory}`);
38143
38173
  }
38144
- const tree = await execFileAsync("git", [...commonArgs, "ls-tree", "-r", "-z", expectedCommit], {
38145
- env: environment
38146
- });
38147
38174
  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"
38175
+ const treeProcess = spawn("git", [...commonArgs, "ls-tree", "-r", "-z", expectedCommit], {
38176
+ env: environment,
38177
+ stdio: ["ignore", "pipe", "pipe"]
38178
+ });
38179
+ let processError = null;
38180
+ const processCompletion = new Promise((resolveProcess) => {
38181
+ treeProcess.once("error", (error) => {
38182
+ processError = new Error(`Failed to run git ls-tree: ${error.message}`, { cause: error });
38183
+ resolveProcess({ code: null, signal: null });
38162
38184
  });
38185
+ treeProcess.once("close", (code2, signal2) => {
38186
+ resolveProcess({ code: code2, signal: signal2 });
38187
+ });
38188
+ });
38189
+ const stderrChunks = [];
38190
+ let stderrBytes = 0;
38191
+ let stderrTruncated = false;
38192
+ const stderrCompletion = (async () => {
38193
+ for await (const chunk of treeProcess.stderr) {
38194
+ const remaining = MAX_GIT_STDERR_BYTES - stderrBytes;
38195
+ if (remaining > 0) {
38196
+ const captured = chunk.subarray(0, remaining);
38197
+ stderrChunks.push(Buffer.from(captured));
38198
+ stderrBytes += captured.length;
38199
+ }
38200
+ if (chunk.length > remaining) {
38201
+ stderrTruncated = true;
38202
+ }
38203
+ }
38204
+ })();
38205
+ let pendingRecord = Buffer.alloc(0);
38206
+ let stdoutError = null;
38207
+ try {
38208
+ for await (const chunk of treeProcess.stdout) {
38209
+ const output = pendingRecord.length > 0 ? Buffer.concat([pendingRecord, chunk]) : chunk;
38210
+ let recordStart = 0;
38211
+ let separator = output.indexOf(0, recordStart);
38212
+ while (separator >= 0) {
38213
+ if (separator > recordStart) {
38214
+ const row = output.toString("utf8", recordStart, separator);
38215
+ const metadataSeparator = row.indexOf(" ");
38216
+ const metadata = row.slice(0, metadataSeparator).split(" ");
38217
+ const path2 = row.slice(metadataSeparator + 1);
38218
+ const [mode, entryType, objectId, ...extraFields] = metadata;
38219
+ if (metadataSeparator < 0 || entryType !== "blob" || extraFields.length > 0 || mode === void 0 || objectId === void 0) {
38220
+ throw new Error(`Unsupported Git tree entry in vendored repository: ${path2}`);
38221
+ }
38222
+ if (mode !== "100644" && mode !== "100755") {
38223
+ throw new Error(`Unsupported Git tree mode in vendored repository: ${path2}`);
38224
+ }
38225
+ trackedFiles.set(path2, {
38226
+ objectId,
38227
+ executable: mode === "100755"
38228
+ });
38229
+ }
38230
+ recordStart = separator + 1;
38231
+ separator = output.indexOf(0, recordStart);
38232
+ }
38233
+ pendingRecord = recordStart < output.length ? Buffer.from(output.subarray(recordStart)) : Buffer.alloc(0);
38234
+ if (pendingRecord.length > MAX_GIT_TREE_RECORD_BYTES) {
38235
+ throw new Error(`Git ls-tree record exceeds ${MAX_GIT_TREE_RECORD_BYTES} bytes`);
38236
+ }
38237
+ }
38238
+ } catch (error) {
38239
+ stdoutError = error instanceof Error ? error : new Error(String(error), { cause: error });
38240
+ treeProcess.kill();
38241
+ }
38242
+ const [{ code, signal }] = await Promise.all([processCompletion, stderrCompletion]);
38243
+ if (processError) {
38244
+ throw processError;
38245
+ }
38246
+ if (stdoutError) {
38247
+ throw stdoutError;
38248
+ }
38249
+ if (code !== 0) {
38250
+ const stderrOutput = Buffer.concat(stderrChunks, stderrBytes).toString("utf8");
38251
+ const stderrDetails = stderrTruncated ? `${stderrOutput}
38252
+ [stderr truncated]` : stderrOutput;
38253
+ const details = stderrDetails.trim();
38254
+ const status = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
38255
+ throw new Error(`Git ls-tree failed with ${status}${details ? `: ${details}` : ""}`);
38256
+ }
38257
+ if (pendingRecord.length > 0) {
38258
+ throw new Error("Git ls-tree output ended with an unterminated record");
38163
38259
  }
38164
38260
  const worktreeFiles = [];
38165
38261
  async function collectFiles(currentDirectory, prefix) {
@@ -39380,7 +39476,7 @@ var HttpRegistry = class {
39380
39476
  * Sleep for specified milliseconds.
39381
39477
  */
39382
39478
  sleep(ms) {
39383
- return new Promise((resolve37) => setTimeout(resolve37, ms));
39479
+ return new Promise((resolve38) => setTimeout(resolve38, ms));
39384
39480
  }
39385
39481
  /**
39386
39482
  * Fetch with timeout.
@@ -39422,7 +39518,7 @@ var HttpRegistry = class {
39422
39518
  */
39423
39519
  async fetchWithRetry(url) {
39424
39520
  let lastError;
39425
- let delay = this.initialDelay;
39521
+ let delay2 = this.initialDelay;
39426
39522
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
39427
39523
  try {
39428
39524
  const response = await this.fetchWithTimeout(url, {
@@ -39441,8 +39537,8 @@ var HttpRegistry = class {
39441
39537
  lastError = error;
39442
39538
  }
39443
39539
  if (attempt < this.maxRetries) {
39444
- await this.sleep(delay);
39445
- delay *= 2;
39540
+ await this.sleep(delay2);
39541
+ delay2 *= 2;
39446
39542
  }
39447
39543
  }
39448
39544
  throw lastError ?? new Error("Unknown fetch error");
@@ -44700,12 +44796,12 @@ var Compiler = class _Compiler {
44700
44796
  */
44701
44797
  async watch(entryPath, options = {}) {
44702
44798
  const { default: chokidar2 } = await import("chokidar");
44703
- const { dirname: dirname27, resolve: resolve37 } = await import("path");
44704
- const baseDir = dirname27(resolve37(entryPath));
44799
+ const { dirname: dirname28, resolve: resolve38 } = await import("path");
44800
+ const baseDir = dirname28(resolve38(entryPath));
44705
44801
  const includePatterns = options.include ?? ["**/*.prs"];
44706
44802
  const excludePatterns = options.exclude ?? ["**/node_modules/**"];
44707
44803
  const debounceMs = options.debounce ?? 300;
44708
- const watchPatterns = includePatterns.map((p) => resolve37(baseDir, p));
44804
+ const watchPatterns = includePatterns.map((p) => resolve38(baseDir, p));
44709
44805
  let debounceTimer = null;
44710
44806
  let pendingChanges = [];
44711
44807
  const handleChange = async (changedFiles) => {
@@ -44843,7 +44939,7 @@ var Compiler = class _Compiler {
44843
44939
  };
44844
44940
 
44845
44941
  // packages/cli/src/output/pager.ts
44846
- import { spawn } from "child_process";
44942
+ import { spawn as spawn2 } from "child_process";
44847
44943
  function isTTY() {
44848
44944
  return process.stdout.isTTY === true;
44849
44945
  }
@@ -44880,7 +44976,7 @@ var Pager = class {
44880
44976
  }
44881
44977
  return;
44882
44978
  }
44883
- return new Promise((resolve37) => {
44979
+ return new Promise((resolve38) => {
44884
44980
  const pagerCmd = getPagerCommand();
44885
44981
  const [cmd, ...args] = pagerCmd.split(" ");
44886
44982
  if (cmd === "less" && !args.includes("-R")) {
@@ -44888,10 +44984,10 @@ var Pager = class {
44888
44984
  }
44889
44985
  if (!cmd) {
44890
44986
  console.log(content);
44891
- resolve37();
44987
+ resolve38();
44892
44988
  return;
44893
44989
  }
44894
- const pager = spawn(cmd, args, {
44990
+ const pager = spawn2(cmd, args, {
44895
44991
  stdio: ["pipe", "inherit", "inherit"],
44896
44992
  env: {
44897
44993
  ...process.env,
@@ -44901,17 +44997,17 @@ var Pager = class {
44901
44997
  });
44902
44998
  pager.on("error", (_err) => {
44903
44999
  console.log(content);
44904
- resolve37();
45000
+ resolve38();
44905
45001
  });
44906
45002
  pager.on("close", () => {
44907
- resolve37();
45003
+ resolve38();
44908
45004
  });
44909
45005
  if (pager.stdin) {
44910
45006
  pager.stdin.write(content);
44911
45007
  pager.stdin.end();
44912
45008
  } else {
44913
45009
  console.log(content);
44914
- resolve37();
45010
+ resolve38();
44915
45011
  }
44916
45012
  });
44917
45013
  }
@@ -45098,7 +45194,7 @@ function detectOutputConflicts(targets) {
45098
45194
 
45099
45195
  // packages/cli/src/utils/managed-output-cleanup.ts
45100
45196
  import { constants as constants2 } from "node:fs";
45101
- import { execFile as execFile2, spawn as spawn2 } from "node:child_process";
45197
+ import { execFile as execFile2, spawn as spawn3 } from "node:child_process";
45102
45198
  import { createHash as createHash4 } from "node:crypto";
45103
45199
  import {
45104
45200
  lstat as lstat8,
@@ -45565,7 +45661,7 @@ async function removeHookOutputIfUnchanged(file, outputRoot, expectedContent) {
45565
45661
  async function guardedRewrite(directory, name, directoryStat, fileStat, expectedContent, content) {
45566
45662
  const expectedHash = createHash4("sha256").update(expectedContent).digest("hex");
45567
45663
  return new Promise((resolveResult) => {
45568
- const child = spawn2(
45664
+ const child = spawn3(
45569
45665
  process.execPath,
45570
45666
  [
45571
45667
  "-e",
@@ -45603,7 +45699,7 @@ async function guardedRewrite(directory, name, directoryStat, fileStat, expected
45603
45699
  }
45604
45700
  async function guardedCreate(directory, name, directoryStat, content, mode) {
45605
45701
  return new Promise((resolveResult) => {
45606
- const child = spawn2(
45702
+ const child = spawn3(
45607
45703
  process.execPath,
45608
45704
  [
45609
45705
  "-e",
@@ -50741,14 +50837,1169 @@ Usage: echo '{"file_path":"..."}' | prs hook ${action}
50741
50837
  // packages/cli/src/cli.ts
50742
50838
  init_hooks();
50743
50839
 
50840
+ // packages/cli/src/commands/telemetry.ts
50841
+ init_console();
50842
+
50843
+ // packages/cli/src/telemetry/config.ts
50844
+ import { existsSync as existsSync27 } from "node:fs";
50845
+ import { mkdir as mkdir11, readFile as readFile27, rename as rename2, writeFile as writeFile11 } from "node:fs/promises";
50846
+ import { dirname as dirname21, isAbsolute as isAbsolute15, join as join29, resolve as resolve34 } from "node:path";
50847
+ import { parseDocument } from "yaml";
50848
+
50849
+ // packages/telemetry/src/config.ts
50850
+ import { homedir as homedir6 } from "node:os";
50851
+ import { join as join27 } from "node:path";
50852
+ var DEFAULT_TELEMETRY_ENDPOINT = "https://telemetry.guziak.net/v1/events";
50853
+ function isDisabled(value) {
50854
+ if (value === void 0) {
50855
+ return false;
50856
+ }
50857
+ return ["0", "false", "no", "off"].includes(value.trim().toLowerCase());
50858
+ }
50859
+ function isDoNotTrackEnabled(value) {
50860
+ if (value === void 0) {
50861
+ return false;
50862
+ }
50863
+ const normalized = value.trim();
50864
+ return normalized !== "" && normalized !== "0";
50865
+ }
50866
+ function isSecureEndpoint(value) {
50867
+ try {
50868
+ const url = new URL(value);
50869
+ return url.protocol === "https:" && url.username === "" && url.password === "";
50870
+ } catch {
50871
+ return false;
50872
+ }
50873
+ }
50874
+ function resolveTelemetryConfig(input2 = {}) {
50875
+ const environment = input2.environment ?? process.env;
50876
+ const endpoint = input2.endpoint ?? environment["PROMPTSCRIPT_TELEMETRY_ENDPOINT"] ?? DEFAULT_TELEMETRY_ENDPOINT;
50877
+ const vetoes = [];
50878
+ if (isDoNotTrackEnabled(environment["DO_NOT_TRACK"])) {
50879
+ vetoes.push("DO_NOT_TRACK");
50880
+ }
50881
+ if (isDisabled(environment["PROMPTSCRIPT_TELEMETRY"])) {
50882
+ vetoes.push("PROMPTSCRIPT_TELEMETRY");
50883
+ }
50884
+ if (input2.userEnabled === false) {
50885
+ vetoes.push("user config");
50886
+ }
50887
+ if (input2.projectEnabled === false) {
50888
+ vetoes.push("project config");
50889
+ }
50890
+ if (input2.configurationValid === false) {
50891
+ vetoes.push("configuration unavailable");
50892
+ }
50893
+ if (!isSecureEndpoint(endpoint)) {
50894
+ vetoes.push("insecure endpoint");
50895
+ }
50896
+ return {
50897
+ enabled: vetoes.length === 0,
50898
+ endpoint,
50899
+ cacheDirectory: input2.cacheDirectory ?? join27(homedir6(), ".promptscript", ".cache"),
50900
+ vetoes
50901
+ };
50902
+ }
50903
+
50904
+ // packages/telemetry/telemetry-schema/promptscript-v1.json
50905
+ var promptscript_v1_default = {
50906
+ eventSchema: 1,
50907
+ commands: [
50908
+ "init",
50909
+ "compile",
50910
+ "build",
50911
+ "validate",
50912
+ "inspect",
50913
+ "pull",
50914
+ "diff",
50915
+ "check",
50916
+ "update-check",
50917
+ "serve",
50918
+ "import",
50919
+ "migrate",
50920
+ "upgrade",
50921
+ "lock",
50922
+ "update",
50923
+ "vendor-sync",
50924
+ "vendor-check",
50925
+ "resolve",
50926
+ "hooks",
50927
+ "skills-add",
50928
+ "skills-remove",
50929
+ "skills-list",
50930
+ "skills-update",
50931
+ "registry",
50932
+ "other"
50933
+ ],
50934
+ outcomes: ["success", "error", "cancelled"],
50935
+ features: ["dry_run", "watch", "strict", "ci", "build_profile"],
50936
+ targets: [
50937
+ "github",
50938
+ "claude",
50939
+ "cursor",
50940
+ "antigravity",
50941
+ "factory",
50942
+ "opencode",
50943
+ "gemini",
50944
+ "windsurf",
50945
+ "cline",
50946
+ "roo",
50947
+ "codex",
50948
+ "continue",
50949
+ "augment",
50950
+ "goose",
50951
+ "kilo",
50952
+ "amp",
50953
+ "trae",
50954
+ "junie",
50955
+ "kiro",
50956
+ "cortex",
50957
+ "crush",
50958
+ "command-code",
50959
+ "kode",
50960
+ "mcpjam",
50961
+ "mistral-vibe",
50962
+ "mux",
50963
+ "openhands",
50964
+ "pi",
50965
+ "qoder",
50966
+ "qwen-code",
50967
+ "zencoder",
50968
+ "neovate",
50969
+ "pochi",
50970
+ "adal",
50971
+ "iflow",
50972
+ "openclaw",
50973
+ "codebuddy",
50974
+ "aider",
50975
+ "amazon-q",
50976
+ "warp",
50977
+ "zed",
50978
+ "jules",
50979
+ "devin",
50980
+ "grok",
50981
+ "kimi",
50982
+ "mimo",
50983
+ "deep-agents",
50984
+ "forgecode"
50985
+ ],
50986
+ limits: {
50987
+ maxEvents: 25,
50988
+ maxCount: 1e6,
50989
+ maxDurationSumMs: 1e12
50990
+ }
50991
+ };
50992
+
50993
+ // packages/telemetry/src/schema.ts
50994
+ var TELEMETRY_EVENT_SCHEMA = promptscript_v1_default.eventSchema;
50995
+ var MAX_EVENTS_PER_BATCH = promptscript_v1_default.limits.maxEvents;
50996
+ var MAX_EVENT_COUNT = promptscript_v1_default.limits.maxCount;
50997
+ var MAX_DURATION_SUM_MS = promptscript_v1_default.limits.maxDurationSumMs;
50998
+ var COMMANDS = new Set(promptscript_v1_default.commands);
50999
+ var OUTCOMES = new Set(promptscript_v1_default.outcomes);
51000
+ var FEATURES = new Set(promptscript_v1_default.features);
51001
+ var TARGETS = new Set(promptscript_v1_default.targets);
51002
+ function sanitizeCommand(value) {
51003
+ return COMMANDS.has(value) ? value : "other";
51004
+ }
51005
+ function isTelemetryOutcome(value) {
51006
+ return OUTCOMES.has(value);
51007
+ }
51008
+ function sanitizeFeature(value) {
51009
+ if (FEATURES.has(value)) {
51010
+ return value;
51011
+ }
51012
+ const parts = value.split(":");
51013
+ const target = parts[1];
51014
+ if (parts.length === 2 && parts[0] === "target" && target !== void 0 && TARGETS.has(target)) {
51015
+ return value;
51016
+ }
51017
+ return null;
51018
+ }
51019
+ function isExcludedCommand(value) {
51020
+ return value === "hook" || value === "telemetry" || value === "__telemetry-flush";
51021
+ }
51022
+
51023
+ // packages/telemetry/src/payload.ts
51024
+ function metadataKey(record) {
51025
+ return [record.app_version, record.runtime_version, record.os, record.arch].join(":");
51026
+ }
51027
+ function eventKey(event) {
51028
+ return event.name === "command" ? `${event.name}:${event.command}:${event.outcome}` : `${event.name}:${event.feature}`;
51029
+ }
51030
+ function copyEvent(event) {
51031
+ return event.name === "command" ? { ...event } : { ...event };
51032
+ }
51033
+ function canAggregate(target, source) {
51034
+ if (target.count + source.count > MAX_EVENT_COUNT) {
51035
+ return false;
51036
+ }
51037
+ if (target.name === "command" && source.name === "command") {
51038
+ return target.duration_ms_sum + source.duration_ms_sum <= MAX_DURATION_SUM_MS;
51039
+ }
51040
+ return true;
51041
+ }
51042
+ function addEvent(target, source) {
51043
+ target.count += source.count;
51044
+ if (target.name === "command" && source.name === "command") {
51045
+ target.duration_ms_sum += source.duration_ms_sum;
51046
+ }
51047
+ }
51048
+ function aggregateRecords(records) {
51049
+ const aggregates = [];
51050
+ records.forEach((record, index) => {
51051
+ const key = `${metadataKey(record)}:${eventKey(record.event)}`;
51052
+ const aggregate = aggregates.find(
51053
+ (candidate) => `${metadataKey(candidate.metadata)}:${eventKey(candidate.event)}` === key && canAggregate(candidate.event, record.event)
51054
+ );
51055
+ if (aggregate !== void 0) {
51056
+ addEvent(aggregate.event, record.event);
51057
+ aggregate.sourceIndexes.push(index);
51058
+ return;
51059
+ }
51060
+ aggregates.push({
51061
+ metadata: {
51062
+ app_version: record.app_version,
51063
+ runtime_version: record.runtime_version,
51064
+ os: record.os,
51065
+ arch: record.arch
51066
+ },
51067
+ event: copyEvent(record.event),
51068
+ sourceIndexes: [index]
51069
+ });
51070
+ });
51071
+ return aggregates;
51072
+ }
51073
+ function toPayload(batch) {
51074
+ return {
51075
+ schema: 1,
51076
+ app: "promptscript",
51077
+ event_schema: TELEMETRY_EVENT_SCHEMA,
51078
+ runtime: "node",
51079
+ ...batch.metadata,
51080
+ events: batch.events
51081
+ };
51082
+ }
51083
+ function buildTelemetryBatches(records) {
51084
+ const builders = [];
51085
+ for (const aggregate of aggregateRecords(records)) {
51086
+ const key = eventKey(aggregate.event);
51087
+ let batch = builders.find(
51088
+ (candidate) => metadataKey(candidate.metadata) === metadataKey(aggregate.metadata) && candidate.events.length < MAX_EVENTS_PER_BATCH && !candidate.eventKeys.has(key)
51089
+ );
51090
+ if (batch === void 0) {
51091
+ batch = {
51092
+ metadata: aggregate.metadata,
51093
+ events: [],
51094
+ eventKeys: /* @__PURE__ */ new Set(),
51095
+ sourceIndexes: /* @__PURE__ */ new Set()
51096
+ };
51097
+ builders.push(batch);
51098
+ }
51099
+ batch.events.push(aggregate.event);
51100
+ batch.eventKeys.add(key);
51101
+ aggregate.sourceIndexes.forEach((index) => batch?.sourceIndexes.add(index));
51102
+ }
51103
+ return builders.map((batch) => ({
51104
+ payload: toPayload(batch),
51105
+ sourceIndexes: [...batch.sourceIndexes]
51106
+ }));
51107
+ }
51108
+
51109
+ // packages/telemetry/src/spool.ts
51110
+ import {
51111
+ appendFileSync,
51112
+ existsSync as existsSync26,
51113
+ mkdirSync as mkdirSync2,
51114
+ readFileSync as readFileSync7,
51115
+ readdirSync as readdirSync2,
51116
+ renameSync as renameSync2,
51117
+ rmSync,
51118
+ statSync as statSync3,
51119
+ unlinkSync as unlinkSync3,
51120
+ writeFileSync as writeFileSync5
51121
+ } from "node:fs";
51122
+ import { join as join28 } from "node:path";
51123
+ var ACTIVE_FILE = "telemetry.ndjson";
51124
+ var INFLIGHT_FILE = "telemetry.inflight.ndjson";
51125
+ var QUARANTINE_FILE = "telemetry-quarantine.ndjson";
51126
+ var STATE_FILE = "telemetry-state.json";
51127
+ var LOCK_DIRECTORY = "telemetry.lock";
51128
+ var FLUSH_LOCK_DIRECTORY = "telemetry-flush.lock";
51129
+ var SIDECAR_PATTERN = /^telemetry\.\d+\.\d+\.\d+\.ndjson$/;
51130
+ var MAX_SPOOL_BYTES = 64 * 1024;
51131
+ var MAX_SPOOL_RECORDS = 200;
51132
+ var STALE_LOCK_MS = 3e4;
51133
+ var sidecarSequence = 0;
51134
+ function pathFor(cacheDirectory, name) {
51135
+ return join28(cacheDirectory, name);
51136
+ }
51137
+ function isNodeError2(error, code) {
51138
+ return error instanceof Error && "code" in error && error.code === code;
51139
+ }
51140
+ function removeStaleLock(lockPath) {
51141
+ try {
51142
+ if (Date.now() - statSync3(lockPath).mtimeMs > STALE_LOCK_MS) {
51143
+ rmSync(lockPath, { recursive: true });
51144
+ }
51145
+ } catch {
51146
+ }
51147
+ }
51148
+ function acquireLock2(cacheDirectory, lockDirectory = LOCK_DIRECTORY) {
51149
+ mkdirSync2(cacheDirectory, { recursive: true });
51150
+ const lockPath = pathFor(cacheDirectory, lockDirectory);
51151
+ try {
51152
+ mkdirSync2(lockPath);
51153
+ return true;
51154
+ } catch (error) {
51155
+ if (!isNodeError2(error, "EEXIST")) {
51156
+ return false;
51157
+ }
51158
+ removeStaleLock(lockPath);
51159
+ try {
51160
+ mkdirSync2(lockPath);
51161
+ return true;
51162
+ } catch {
51163
+ return false;
51164
+ }
51165
+ }
51166
+ }
51167
+ function releaseLock2(cacheDirectory, lockDirectory = LOCK_DIRECTORY) {
51168
+ try {
51169
+ rmSync(pathFor(cacheDirectory, lockDirectory), { recursive: true });
51170
+ } catch {
51171
+ }
51172
+ }
51173
+ function compactLines(lines) {
51174
+ const selected = [];
51175
+ let bytes = 0;
51176
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
51177
+ const line = lines[index];
51178
+ const lineBytes = Buffer.byteLength(`${line}
51179
+ `);
51180
+ if (selected.length >= MAX_SPOOL_RECORDS || bytes + lineBytes > MAX_SPOOL_BYTES) {
51181
+ break;
51182
+ }
51183
+ selected.push(line);
51184
+ bytes += lineBytes;
51185
+ }
51186
+ return selected.reverse();
51187
+ }
51188
+ function readLines(filePath) {
51189
+ try {
51190
+ return readFileSync7(filePath, "utf8").split("\n").filter(Boolean);
51191
+ } catch {
51192
+ return [];
51193
+ }
51194
+ }
51195
+ function writeLines(filePath, lines) {
51196
+ if (lines.length === 0) {
51197
+ try {
51198
+ unlinkSync3(filePath);
51199
+ } catch (error) {
51200
+ if (!isNodeError2(error, "ENOENT")) {
51201
+ throw error;
51202
+ }
51203
+ }
51204
+ return;
51205
+ }
51206
+ const temporaryPath = `${filePath}.${process.pid}.tmp`;
51207
+ writeFileSync5(temporaryPath, `${lines.join("\n")}
51208
+ `, { encoding: "utf8", mode: 384 });
51209
+ renameSync2(temporaryPath, filePath);
51210
+ }
51211
+ function compactFile(filePath) {
51212
+ writeLines(filePath, compactLines(readLines(filePath)));
51213
+ }
51214
+ function parseTelemetryEvent(value) {
51215
+ if (typeof value !== "object" || value === null || !("name" in value)) {
51216
+ return null;
51217
+ }
51218
+ const event = value;
51219
+ if (!Number.isSafeInteger(event["count"]) || Number(event["count"]) < 1 || Number(event["count"]) > MAX_EVENT_COUNT) {
51220
+ return null;
51221
+ }
51222
+ const count = Number(event["count"]);
51223
+ if (event["name"] === "command") {
51224
+ 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) {
51225
+ return {
51226
+ name: "command",
51227
+ command: event["command"],
51228
+ outcome: event["outcome"],
51229
+ count,
51230
+ duration_ms_sum: event["duration_ms_sum"]
51231
+ };
51232
+ }
51233
+ return null;
51234
+ }
51235
+ if (event["name"] === "feature" && typeof event["feature"] === "string") {
51236
+ const feature = sanitizeFeature(event["feature"]);
51237
+ return feature === null ? null : { name: "feature", feature, count };
51238
+ }
51239
+ return null;
51240
+ }
51241
+ function parseRecord(line) {
51242
+ try {
51243
+ const value = JSON.parse(line);
51244
+ if (typeof value !== "object" || value === null || !("event" in value)) {
51245
+ return null;
51246
+ }
51247
+ const record = value;
51248
+ const event = parseTelemetryEvent(record["event"]);
51249
+ 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) {
51250
+ return null;
51251
+ }
51252
+ return {
51253
+ app_version: record["app_version"],
51254
+ runtime_version: record["runtime_version"],
51255
+ os: record["os"],
51256
+ arch: record["arch"],
51257
+ event
51258
+ };
51259
+ } catch {
51260
+ return null;
51261
+ }
51262
+ }
51263
+ function parseLines(lines) {
51264
+ const records = [];
51265
+ let corruptLines = 0;
51266
+ for (const line of lines) {
51267
+ const record = parseRecord(line);
51268
+ if (record === null) {
51269
+ corruptLines += 1;
51270
+ } else {
51271
+ records.push(record);
51272
+ }
51273
+ }
51274
+ return { records, corruptLines };
51275
+ }
51276
+ function sidecarFiles(cacheDirectory) {
51277
+ try {
51278
+ return readdirSync2(cacheDirectory).filter((name) => SIDECAR_PATTERN.test(name)).map((name) => pathFor(cacheDirectory, name));
51279
+ } catch {
51280
+ return [];
51281
+ }
51282
+ }
51283
+ function mergeSidecars(cacheDirectory) {
51284
+ const activePath = pathFor(cacheDirectory, ACTIVE_FILE);
51285
+ for (const sidecarPath of sidecarFiles(cacheDirectory)) {
51286
+ for (const line of readLines(sidecarPath)) {
51287
+ appendFileSync(activePath, `${line}
51288
+ `, { encoding: "utf8", mode: 384 });
51289
+ }
51290
+ try {
51291
+ unlinkSync3(sidecarPath);
51292
+ } catch {
51293
+ }
51294
+ }
51295
+ compactFile(activePath);
51296
+ }
51297
+ function appendSpoolRecords(cacheDirectory, records) {
51298
+ if (records.length === 0) {
51299
+ return;
51300
+ }
51301
+ const lines = records.map((record) => JSON.stringify(record));
51302
+ try {
51303
+ if (acquireLock2(cacheDirectory)) {
51304
+ try {
51305
+ const activePath = pathFor(cacheDirectory, ACTIVE_FILE);
51306
+ appendFileSync(activePath, `${lines.join("\n")}
51307
+ `, {
51308
+ encoding: "utf8",
51309
+ mode: 384
51310
+ });
51311
+ compactFile(activePath);
51312
+ } finally {
51313
+ releaseLock2(cacheDirectory);
51314
+ }
51315
+ return;
51316
+ }
51317
+ mkdirSync2(cacheDirectory, { recursive: true });
51318
+ sidecarSequence += 1;
51319
+ const sidecarPath = pathFor(
51320
+ cacheDirectory,
51321
+ `telemetry.${process.pid}.${Date.now()}.${sidecarSequence}.ndjson`
51322
+ );
51323
+ const temporaryPath = `${sidecarPath}.tmp`;
51324
+ writeFileSync5(temporaryPath, `${lines.join("\n")}
51325
+ `, { encoding: "utf8", mode: 384 });
51326
+ renameSync2(temporaryPath, sidecarPath);
51327
+ } catch {
51328
+ }
51329
+ }
51330
+ function claimSpool(cacheDirectory) {
51331
+ if (!acquireLock2(cacheDirectory)) {
51332
+ return null;
51333
+ }
51334
+ try {
51335
+ const inflightPath = pathFor(cacheDirectory, INFLIGHT_FILE);
51336
+ if (existsSync26(inflightPath) && readLines(inflightPath).length === 0) {
51337
+ try {
51338
+ unlinkSync3(inflightPath);
51339
+ } catch {
51340
+ return null;
51341
+ }
51342
+ }
51343
+ if (!existsSync26(inflightPath)) {
51344
+ mergeSidecars(cacheDirectory);
51345
+ const activePath = pathFor(cacheDirectory, ACTIVE_FILE);
51346
+ if (existsSync26(activePath)) {
51347
+ renameSync2(activePath, inflightPath);
51348
+ }
51349
+ }
51350
+ const lines = readLines(inflightPath);
51351
+ return lines.length === 0 ? null : parseLines(lines);
51352
+ } finally {
51353
+ releaseLock2(cacheDirectory);
51354
+ }
51355
+ }
51356
+ function completeSpoolClaim(cacheDirectory, remainingRecords, rejectedRecords = []) {
51357
+ if (!acquireLock2(cacheDirectory)) {
51358
+ return false;
51359
+ }
51360
+ try {
51361
+ try {
51362
+ writeLines(
51363
+ pathFor(cacheDirectory, INFLIGHT_FILE),
51364
+ remainingRecords.map((record) => JSON.stringify(record))
51365
+ );
51366
+ if (rejectedRecords.length > 0) {
51367
+ const quarantinePath = pathFor(cacheDirectory, QUARANTINE_FILE);
51368
+ for (const record of rejectedRecords) {
51369
+ appendFileSync(quarantinePath, `${JSON.stringify(record)}
51370
+ `, {
51371
+ encoding: "utf8",
51372
+ mode: 384
51373
+ });
51374
+ }
51375
+ compactFile(quarantinePath);
51376
+ }
51377
+ return true;
51378
+ } catch {
51379
+ return false;
51380
+ }
51381
+ } finally {
51382
+ releaseLock2(cacheDirectory);
51383
+ }
51384
+ }
51385
+ function prepareSpoolAttempt(cacheDirectory, remainingRecords) {
51386
+ if (!acquireLock2(cacheDirectory)) {
51387
+ return false;
51388
+ }
51389
+ try {
51390
+ try {
51391
+ writeLines(
51392
+ pathFor(cacheDirectory, INFLIGHT_FILE),
51393
+ remainingRecords.map((record) => JSON.stringify(record))
51394
+ );
51395
+ return true;
51396
+ } catch {
51397
+ return false;
51398
+ }
51399
+ } finally {
51400
+ releaseLock2(cacheDirectory);
51401
+ }
51402
+ }
51403
+ function acquireFlushLease(cacheDirectory) {
51404
+ return acquireLock2(cacheDirectory, FLUSH_LOCK_DIRECTORY);
51405
+ }
51406
+ function releaseFlushLease(cacheDirectory) {
51407
+ releaseLock2(cacheDirectory, FLUSH_LOCK_DIRECTORY);
51408
+ }
51409
+ function getSpoolInfo(cacheDirectory) {
51410
+ const files = [
51411
+ pathFor(cacheDirectory, ACTIVE_FILE),
51412
+ pathFor(cacheDirectory, INFLIGHT_FILE),
51413
+ ...sidecarFiles(cacheDirectory)
51414
+ ];
51415
+ let records = 0;
51416
+ let bytes = 0;
51417
+ for (const file of files) {
51418
+ const lines = readLines(file);
51419
+ records += lines.length;
51420
+ try {
51421
+ bytes += statSync3(file).size;
51422
+ } catch {
51423
+ }
51424
+ }
51425
+ return { records, bytes };
51426
+ }
51427
+ function readFlushState(cacheDirectory) {
51428
+ try {
51429
+ const value = JSON.parse(readFileSync7(pathFor(cacheDirectory, STATE_FILE), "utf8"));
51430
+ return typeof value === "object" && value !== null ? value : {};
51431
+ } catch {
51432
+ return {};
51433
+ }
51434
+ }
51435
+ function writeFlushState(cacheDirectory, state) {
51436
+ try {
51437
+ mkdirSync2(cacheDirectory, { recursive: true });
51438
+ const statePath = pathFor(cacheDirectory, STATE_FILE);
51439
+ const temporaryPath = `${statePath}.${process.pid}.tmp`;
51440
+ writeFileSync5(temporaryPath, `${JSON.stringify(state, null, 2)}
51441
+ `, {
51442
+ encoding: "utf8",
51443
+ mode: 384
51444
+ });
51445
+ renameSync2(temporaryPath, statePath);
51446
+ } catch {
51447
+ }
51448
+ }
51449
+
51450
+ // packages/telemetry/src/transport.ts
51451
+ function isSecureEndpoint2(endpoint) {
51452
+ try {
51453
+ const url = new URL(endpoint);
51454
+ return url.protocol === "https:" && url.username === "" && url.password === "";
51455
+ } catch {
51456
+ return false;
51457
+ }
51458
+ }
51459
+ async function postTelemetry(endpoint, payload, timeoutMs, fetchImplementation = fetch) {
51460
+ if (!isSecureEndpoint2(endpoint)) {
51461
+ return "rejected";
51462
+ }
51463
+ const controller = new AbortController();
51464
+ const timeout = setTimeout(
51465
+ () => {
51466
+ controller.abort();
51467
+ },
51468
+ Math.max(0, timeoutMs)
51469
+ );
51470
+ try {
51471
+ const response = await fetchImplementation(endpoint, {
51472
+ method: "POST",
51473
+ redirect: "manual",
51474
+ signal: controller.signal,
51475
+ headers: {
51476
+ "Content-Type": "application/json",
51477
+ "User-Agent": "promptscript-telemetry/1"
51478
+ },
51479
+ body: JSON.stringify(payload)
51480
+ });
51481
+ if (response.ok) {
51482
+ return "sent";
51483
+ }
51484
+ if (response.status >= 300 && response.status < 400) {
51485
+ return "rejected";
51486
+ }
51487
+ if (response.status === 429 || response.status >= 500) {
51488
+ return "retryable";
51489
+ }
51490
+ if (response.status >= 400 && response.status < 500) {
51491
+ return "rejected";
51492
+ }
51493
+ return "retryable";
51494
+ } catch {
51495
+ return "unknown";
51496
+ } finally {
51497
+ clearTimeout(timeout);
51498
+ }
51499
+ }
51500
+
51501
+ // packages/telemetry/src/flush.ts
51502
+ var FLUSH_BUDGET_MS = 5e3;
51503
+ var MAX_BATCHES_PER_FLUSH = 3;
51504
+ var COMPLETION_RETRIES = 20;
51505
+ function delay(milliseconds) {
51506
+ return new Promise((resolve38) => setTimeout(resolve38, milliseconds));
51507
+ }
51508
+ async function persistCompletion(cacheDirectory, remainingRecords, rejectedRecords) {
51509
+ for (let attempt = 0; attempt < COMPLETION_RETRIES; attempt += 1) {
51510
+ if (completeSpoolClaim(cacheDirectory, remainingRecords, rejectedRecords)) {
51511
+ return true;
51512
+ }
51513
+ await delay(10);
51514
+ }
51515
+ return false;
51516
+ }
51517
+ async function persistAttempt(cacheDirectory, remainingRecords) {
51518
+ for (let attempt = 0; attempt < COMPLETION_RETRIES; attempt += 1) {
51519
+ if (prepareSpoolAttempt(cacheDirectory, remainingRecords)) {
51520
+ return true;
51521
+ }
51522
+ await delay(10);
51523
+ }
51524
+ return false;
51525
+ }
51526
+ async function runFlush(config, fetchImplementation = fetch) {
51527
+ const emptyResult = {
51528
+ attempted: false,
51529
+ deliveredRecords: 0,
51530
+ rejectedRecords: 0,
51531
+ remainingRecords: 0
51532
+ };
51533
+ if (!config.enabled || !acquireFlushLease(config.cacheDirectory)) {
51534
+ return emptyResult;
51535
+ }
51536
+ try {
51537
+ const claim = claimSpool(config.cacheDirectory);
51538
+ if (claim === null) {
51539
+ return emptyResult;
51540
+ }
51541
+ const state = readFlushState(config.cacheDirectory);
51542
+ const startedAt = Date.now();
51543
+ writeFlushState(config.cacheDirectory, {
51544
+ ...state,
51545
+ lastAttempt: new Date(startedAt).toISOString(),
51546
+ ...claim.corruptLines > 0 ? { lastError: `Skipped ${claim.corruptLines} corrupt spool record(s)` } : {}
51547
+ });
51548
+ const remainingIndexes = new Set(claim.records.map((_record, index) => index));
51549
+ const rejectedIndexes = /* @__PURE__ */ new Set();
51550
+ const batches = buildTelemetryBatches(claim.records).slice(0, MAX_BATCHES_PER_FLUSH);
51551
+ let deliveredRecords = 0;
51552
+ let stoppedForRetry = false;
51553
+ for (const batch of batches) {
51554
+ let remainingBudget = FLUSH_BUDGET_MS - (Date.now() - startedAt);
51555
+ if (remainingBudget <= 0) {
51556
+ stoppedForRetry = true;
51557
+ break;
51558
+ }
51559
+ const pendingRecords = claim.records.filter(
51560
+ (_record, index) => remainingIndexes.has(index) && !batch.sourceIndexes.includes(index)
51561
+ );
51562
+ if (!await persistAttempt(config.cacheDirectory, pendingRecords)) {
51563
+ stoppedForRetry = true;
51564
+ break;
51565
+ }
51566
+ for (const index of batch.sourceIndexes) {
51567
+ remainingIndexes.delete(index);
51568
+ }
51569
+ remainingBudget = FLUSH_BUDGET_MS - (Date.now() - startedAt);
51570
+ if (remainingBudget <= 0) {
51571
+ for (const index of batch.sourceIndexes) {
51572
+ remainingIndexes.add(index);
51573
+ }
51574
+ stoppedForRetry = true;
51575
+ break;
51576
+ }
51577
+ const result = await postTelemetry(
51578
+ config.endpoint,
51579
+ batch.payload,
51580
+ remainingBudget,
51581
+ fetchImplementation
51582
+ );
51583
+ if (result === "sent" || result === "unknown") {
51584
+ deliveredRecords += batch.sourceIndexes.length;
51585
+ if (result === "unknown") {
51586
+ break;
51587
+ }
51588
+ continue;
51589
+ }
51590
+ if (result === "rejected") {
51591
+ for (const index of batch.sourceIndexes) {
51592
+ rejectedIndexes.add(index);
51593
+ }
51594
+ continue;
51595
+ }
51596
+ for (const index of batch.sourceIndexes) {
51597
+ remainingIndexes.add(index);
51598
+ }
51599
+ stoppedForRetry = true;
51600
+ break;
51601
+ }
51602
+ const remainingRecords = claim.records.filter((_record, index) => remainingIndexes.has(index));
51603
+ const rejectedRecords = claim.records.filter((_record, index) => rejectedIndexes.has(index));
51604
+ const persisted = await persistCompletion(
51605
+ config.cacheDirectory,
51606
+ remainingRecords,
51607
+ rejectedRecords
51608
+ );
51609
+ const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
51610
+ 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;
51611
+ writeFlushState(config.cacheDirectory, {
51612
+ ...readFlushState(config.cacheDirectory),
51613
+ ...remainingRecords.length === 0 && persisted ? { lastSuccess: finishedAt } : {},
51614
+ lastError
51615
+ });
51616
+ return {
51617
+ attempted: batches.length > 0,
51618
+ deliveredRecords,
51619
+ rejectedRecords: rejectedRecords.length,
51620
+ remainingRecords: remainingRecords.length
51621
+ };
51622
+ } finally {
51623
+ releaseFlushLease(config.cacheDirectory);
51624
+ }
51625
+ }
51626
+
51627
+ // packages/telemetry/src/reporter.ts
51628
+ import { spawn as spawn4 } from "node:child_process";
51629
+ import { performance } from "node:perf_hooks";
51630
+ var FLUSH_INTERVAL_MS = 4 * 60 * 60 * 1e3;
51631
+ var FLUSH_RECORD_THRESHOLD = 50;
51632
+ var TelemetrySession = class {
51633
+ config;
51634
+ metadata;
51635
+ command;
51636
+ features;
51637
+ startTime;
51638
+ excluded;
51639
+ finished = false;
51640
+ constructor(options) {
51641
+ this.config = options.config;
51642
+ this.metadata = options.metadata;
51643
+ this.excluded = isExcludedCommand(options.command);
51644
+ this.command = sanitizeCommand(options.command);
51645
+ this.features = (options.features ?? []).map((feature) => sanitizeFeature(feature)).filter((feature) => feature !== null);
51646
+ this.startTime = options.startTime ?? performance.now();
51647
+ }
51648
+ finish(outcome) {
51649
+ if (this.finished || !this.config.enabled || this.excluded) {
51650
+ return;
51651
+ }
51652
+ this.finished = true;
51653
+ const duration = Math.max(0, Math.round(performance.now() - this.startTime));
51654
+ const records = [
51655
+ {
51656
+ ...this.metadata,
51657
+ event: {
51658
+ name: "command",
51659
+ command: this.command,
51660
+ outcome,
51661
+ count: 1,
51662
+ duration_ms_sum: duration
51663
+ }
51664
+ },
51665
+ ...this.features.map((feature) => ({
51666
+ ...this.metadata,
51667
+ event: { name: "feature", feature, count: 1 }
51668
+ }))
51669
+ ];
51670
+ appendSpoolRecords(this.config.cacheDirectory, records);
51671
+ }
51672
+ };
51673
+ function shouldFlush(config, now) {
51674
+ const spool = getSpoolInfo(config.cacheDirectory);
51675
+ if (spool.records === 0) {
51676
+ return false;
51677
+ }
51678
+ if (spool.records >= FLUSH_RECORD_THRESHOLD) {
51679
+ return true;
51680
+ }
51681
+ const lastAttempt = readFlushState(config.cacheDirectory).lastAttempt;
51682
+ if (lastAttempt === void 0) {
51683
+ return true;
51684
+ }
51685
+ const timestamp = Date.parse(lastAttempt);
51686
+ return !Number.isFinite(timestamp) || now - timestamp >= FLUSH_INTERVAL_MS;
51687
+ }
51688
+ function maybeSpawnFlush(config, options = {}) {
51689
+ const environment = options.environment ?? process.env;
51690
+ const entrypoint = options.entrypoint ?? process.argv[1];
51691
+ if (!config.enabled || environment["PROMPTSCRIPT_TELEMETRY_FLUSH"] === "1" || entrypoint === void 0 || !shouldFlush(config, options.now ?? Date.now())) {
51692
+ return false;
51693
+ }
51694
+ try {
51695
+ const child = (options.spawn ?? spawn4)(
51696
+ options.executable ?? process.execPath,
51697
+ [entrypoint, "__telemetry-flush"],
51698
+ {
51699
+ detached: true,
51700
+ stdio: "ignore",
51701
+ env: {
51702
+ ...environment,
51703
+ PROMPTSCRIPT_TELEMETRY_FLUSH: "1",
51704
+ PROMPTSCRIPT_NO_UPDATE_CHECK: "1"
51705
+ }
51706
+ }
51707
+ );
51708
+ child.unref();
51709
+ return true;
51710
+ } catch {
51711
+ return false;
51712
+ }
51713
+ }
51714
+
51715
+ // packages/telemetry/src/runtime.ts
51716
+ function operatingSystem(platform) {
51717
+ switch (platform) {
51718
+ case "darwin":
51719
+ return "darwin";
51720
+ case "linux":
51721
+ return "linux";
51722
+ case "win32":
51723
+ return "windows";
51724
+ default:
51725
+ return "other";
51726
+ }
51727
+ }
51728
+ function architecture(value) {
51729
+ switch (value) {
51730
+ case "arm64":
51731
+ return "arm64";
51732
+ case "x64":
51733
+ return "x86_64";
51734
+ default:
51735
+ return "other";
51736
+ }
51737
+ }
51738
+ function runtimeMetadata(appVersion, input2 = {}) {
51739
+ return {
51740
+ app_version: appVersion,
51741
+ runtime_version: (input2.nodeVersion ?? process.versions.node).split(".")[0] ?? "0",
51742
+ os: operatingSystem(input2.platform ?? process.platform),
51743
+ arch: architecture(input2.architecture ?? process.arch)
51744
+ };
51745
+ }
51746
+
51747
+ // packages/cli/src/telemetry/config.ts
51748
+ init_loader2();
51749
+ init_user_config();
51750
+ function resolveFrom(baseDirectory, value) {
51751
+ return isAbsolute15(value) ? value : resolve34(baseDirectory, value);
51752
+ }
51753
+ function findProjectConfig(cwd, customPath) {
51754
+ if (customPath !== void 0) {
51755
+ const path2 = resolveFrom(cwd, customPath);
51756
+ return existsSync27(path2) ? path2 : null;
51757
+ }
51758
+ const environmentPath = process.env["PROMPTSCRIPT_CONFIG"];
51759
+ if (environmentPath !== void 0 && environmentPath !== "") {
51760
+ const path2 = resolveFrom(cwd, environmentPath);
51761
+ return existsSync27(path2) ? path2 : null;
51762
+ }
51763
+ for (const file of CONFIG_FILES) {
51764
+ const path2 = join29(cwd, file);
51765
+ if (existsSync27(path2)) {
51766
+ return path2;
51767
+ }
51768
+ }
51769
+ return null;
51770
+ }
51771
+ function isNodeError3(error, code) {
51772
+ return error instanceof Error && "code" in error && error.code === code;
51773
+ }
51774
+ async function readUserTelemetrySetting(configPath) {
51775
+ let source;
51776
+ try {
51777
+ source = await readFile27(configPath, "utf8");
51778
+ } catch (error) {
51779
+ return isNodeError3(error, "ENOENT") ? { valid: true } : { valid: false };
51780
+ }
51781
+ try {
51782
+ const document = parseDocument(source);
51783
+ if (document.errors.length > 0) {
51784
+ return { valid: false };
51785
+ }
51786
+ const value = document.get("telemetry");
51787
+ if (value === void 0) {
51788
+ return { valid: true };
51789
+ }
51790
+ return typeof value === "boolean" ? { enabled: value, valid: true } : { valid: false };
51791
+ } catch {
51792
+ return { valid: false };
51793
+ }
51794
+ }
51795
+ async function resolveCliTelemetryConfig(options = {}) {
51796
+ const cwd = options.cwd === void 0 ? process.cwd() : resolve34(process.cwd(), options.cwd);
51797
+ const userSetting = await readUserTelemetrySetting(options.userConfigPath ?? USER_CONFIG_PATH);
51798
+ const projectPath = findProjectConfig(cwd, options.config);
51799
+ const environmentConfig = process.env["PROMPTSCRIPT_CONFIG"];
51800
+ const projectConfigRequested = options.config !== void 0 || environmentConfig !== void 0 && environmentConfig !== "";
51801
+ let projectEnabled;
51802
+ let configurationValid = userSetting.valid;
51803
+ if (projectConfigRequested && projectPath === null) {
51804
+ configurationValid = false;
51805
+ } else if (projectPath !== null) {
51806
+ try {
51807
+ const value = (await loadConfig(projectPath)).telemetry;
51808
+ if (value === void 0 || typeof value === "boolean") {
51809
+ projectEnabled = value;
51810
+ } else {
51811
+ configurationValid = false;
51812
+ }
51813
+ } catch {
51814
+ configurationValid = false;
51815
+ }
51816
+ }
51817
+ return resolveTelemetryConfig({
51818
+ environment: process.env,
51819
+ userEnabled: userSetting.enabled,
51820
+ projectEnabled,
51821
+ configurationValid,
51822
+ ...options.cacheDirectory === void 0 ? {} : { cacheDirectory: options.cacheDirectory }
51823
+ });
51824
+ }
51825
+ async function setUserTelemetryEnabled(enabled, configPath = USER_CONFIG_PATH) {
51826
+ let source = "version: '1'\n";
51827
+ try {
51828
+ source = await readFile27(configPath, "utf8");
51829
+ } catch (error) {
51830
+ if (!isNodeError3(error, "ENOENT")) {
51831
+ throw error;
51832
+ }
51833
+ }
51834
+ const document = parseDocument(source);
51835
+ if (document.errors.length > 0) {
51836
+ throw new Error(`Cannot update invalid YAML user config: ${configPath}`);
51837
+ }
51838
+ document.set("telemetry", enabled);
51839
+ await mkdir11(dirname21(configPath), { recursive: true });
51840
+ const temporaryPath = `${configPath}.${process.pid}.tmp`;
51841
+ await writeFile11(temporaryPath, document.toString(), { encoding: "utf8", mode: 384 });
51842
+ await rename2(temporaryPath, configPath);
51843
+ }
51844
+
51845
+ // packages/cli/src/telemetry/session.ts
51846
+ var activeSession = null;
51847
+ var exitHandlerInstalled = false;
51848
+ function normalizedCommandName(command) {
51849
+ const name = command.name();
51850
+ const parentName = command.parent?.name();
51851
+ if (parentName === "vendor" || parentName === "skills") {
51852
+ return `${parentName}-${name}`;
51853
+ }
51854
+ if (parentName === "registry") {
51855
+ return "registry";
51856
+ }
51857
+ return name;
51858
+ }
51859
+ function stringValues(value) {
51860
+ if (typeof value === "string") {
51861
+ return [value];
51862
+ }
51863
+ if (Array.isArray(value)) {
51864
+ return value.filter((item) => typeof item === "string");
51865
+ }
51866
+ return [];
51867
+ }
51868
+ function commandFeatures(command) {
51869
+ const options = command.opts();
51870
+ const features = /* @__PURE__ */ new Set();
51871
+ const targetValues = [...stringValues(options["target"]), ...stringValues(options["targets"])];
51872
+ for (const target of targetValues) {
51873
+ const feature = sanitizeFeature(`target:${target}`);
51874
+ if (feature !== null) {
51875
+ features.add(feature);
51876
+ }
51877
+ }
51878
+ if (options["dryRun"] === true) {
51879
+ features.add("dry_run");
51880
+ }
51881
+ if (options["watch"] === true) {
51882
+ features.add("watch");
51883
+ }
51884
+ if (options["strict"] === true) {
51885
+ features.add("strict");
51886
+ }
51887
+ if (options["build"] !== void 0 || command.name() === "build") {
51888
+ features.add("build_profile");
51889
+ }
51890
+ if (process.env["CI"] !== void 0) {
51891
+ features.add("ci");
51892
+ }
51893
+ return [...features];
51894
+ }
51895
+ function exitOutcome(code) {
51896
+ if (code === 130 || code === 143) {
51897
+ return "cancelled";
51898
+ }
51899
+ return code === 0 ? "success" : "error";
51900
+ }
51901
+ function installExitHandler() {
51902
+ if (exitHandlerInstalled) {
51903
+ return;
51904
+ }
51905
+ process.on("exit", (code) => {
51906
+ finishCliTelemetry(exitOutcome(code));
51907
+ });
51908
+ for (const [signal, fallbackCode] of [
51909
+ ["SIGINT", 130],
51910
+ ["SIGTERM", 143]
51911
+ ]) {
51912
+ process.once(signal, () => {
51913
+ finishCliTelemetry("cancelled");
51914
+ if (process.listenerCount(signal) > 0) {
51915
+ return;
51916
+ }
51917
+ try {
51918
+ process.kill(process.pid, signal);
51919
+ } catch {
51920
+ process.exit(fallbackCode);
51921
+ }
51922
+ });
51923
+ }
51924
+ exitHandlerInstalled = true;
51925
+ }
51926
+ async function prepareCliTelemetry(command, appVersion) {
51927
+ const name = normalizedCommandName(command);
51928
+ if (isExcludedCommand(name) || process.env["PROMPTSCRIPT_TELEMETRY_FLUSH"] === "1") {
51929
+ return;
51930
+ }
51931
+ const options = command.opts();
51932
+ const config = await resolveCliTelemetryConfig({
51933
+ ...typeof options["cwd"] === "string" ? { cwd: options["cwd"] } : {},
51934
+ ...typeof options["config"] === "string" ? { config: options["config"] } : {}
51935
+ });
51936
+ maybeSpawnFlush(config);
51937
+ activeSession = new TelemetrySession({
51938
+ config,
51939
+ metadata: runtimeMetadata(appVersion),
51940
+ command: name,
51941
+ features: commandFeatures(command)
51942
+ });
51943
+ installExitHandler();
51944
+ }
51945
+ function finishCliTelemetry(outcome) {
51946
+ const session = activeSession;
51947
+ activeSession = null;
51948
+ session?.finish(
51949
+ outcome ?? (process.exitCode === void 0 || process.exitCode === 0 ? "success" : "error")
51950
+ );
51951
+ }
51952
+ async function flushCliTelemetry() {
51953
+ const config = await resolveCliTelemetryConfig();
51954
+ await runFlush(config);
51955
+ }
51956
+ async function telemetryStatus() {
51957
+ const config = await resolveCliTelemetryConfig();
51958
+ return {
51959
+ config,
51960
+ spool: getSpoolInfo(config.cacheDirectory),
51961
+ state: readFlushState(config.cacheDirectory)
51962
+ };
51963
+ }
51964
+
51965
+ // packages/cli/src/commands/telemetry.ts
51966
+ async function telemetryCommand(action) {
51967
+ if (action === "enable") {
51968
+ await setUserTelemetryEnabled(true);
51969
+ ConsoleOutput.success("Anonymous usage telemetry enabled in user config");
51970
+ return;
51971
+ }
51972
+ if (action === "disable") {
51973
+ await setUserTelemetryEnabled(false);
51974
+ ConsoleOutput.success("Anonymous usage telemetry disabled in user config");
51975
+ return;
51976
+ }
51977
+ if (action !== "status") {
51978
+ ConsoleOutput.error("Action must be status, enable, or disable");
51979
+ process.exitCode = 1;
51980
+ return;
51981
+ }
51982
+ const { config, spool, state } = await telemetryStatus();
51983
+ console.log(`Enabled: ${config.enabled ? "yes" : "no"}`);
51984
+ console.log(`Endpoint: ${config.endpoint}`);
51985
+ console.log(`Spool records: ${spool.records}`);
51986
+ console.log(`Spool bytes: ${spool.bytes}`);
51987
+ console.log(`Last attempt: ${state.lastAttempt ?? "never"}`);
51988
+ console.log(`Last success: ${state.lastSuccess ?? "never"}`);
51989
+ console.log(`Last error: ${state.lastError ?? "none"}`);
51990
+ if (config.vetoes.length > 0) {
51991
+ console.log(`Disabled by: ${config.vetoes.join(", ")}`);
51992
+ }
51993
+ }
51994
+
50744
51995
  // packages/cli/src/commands/skills.ts
50745
51996
  init_src2();
50746
51997
  init_loader2();
50747
51998
  init_console();
50748
51999
  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";
52000
+ import { writeFile as writeFile12, readFile as readFile28, readdir as readdir9, mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
52001
+ import { resolve as resolve35, join as join30, basename as basename17, dirname as dirname22 } from "path";
52002
+ import { existsSync as existsSync28 } from "fs";
50752
52003
  import { tmpdir as tmpdir3 } from "os";
50753
52004
  import { parse as parseYaml15, stringify as stringifyYaml4 } from "yaml";
50754
52005
  var REMOTE_SOURCE_PATTERN = /^[a-zA-Z0-9][\w.-]*\.[a-zA-Z]{2,}\/.+/;
@@ -50796,32 +52047,32 @@ async function collectProjectRemoteImports(entryFile) {
50796
52047
  const projectRoot = process.cwd();
50797
52048
  const entryFiles = /* @__PURE__ */ new Set();
50798
52049
  if (entryFile) {
50799
- entryFiles.add(resolve34(entryFile));
52050
+ entryFiles.add(resolve35(entryFile));
50800
52051
  }
50801
52052
  if (config) {
50802
52053
  if (config.input?.entry) {
50803
- entryFiles.add(resolve34(projectRoot, config.input.entry));
52054
+ entryFiles.add(resolve35(projectRoot, config.input.entry));
50804
52055
  }
50805
52056
  for (const build of Object.values(config.builds ?? {})) {
50806
52057
  if (build.entry) {
50807
- entryFiles.add(resolve34(projectRoot, build.entry));
52058
+ entryFiles.add(resolve35(projectRoot, build.entry));
50808
52059
  }
50809
52060
  }
50810
- const defaultEntry = resolve34(projectRoot, ".promptscript/project.prs");
50811
- if (!config.input?.entry && (existsSync26(defaultEntry) || entryFiles.size === 0)) {
52061
+ const defaultEntry = resolve35(projectRoot, ".promptscript/project.prs");
52062
+ if (!config.input?.entry && (existsSync28(defaultEntry) || entryFiles.size === 0)) {
50812
52063
  entryFiles.add(defaultEntry);
50813
52064
  }
50814
52065
  } else if (!entryFile) {
50815
- const defaultEntry = resolve34(projectRoot, ".promptscript/project.prs");
50816
- if (existsSync26(defaultEntry)) {
52066
+ const defaultEntry = resolve35(projectRoot, ".promptscript/project.prs");
52067
+ if (existsSync28(defaultEntry)) {
50817
52068
  entryFiles.add(defaultEntry);
50818
52069
  } else {
50819
- const promptscriptDir = resolve34(projectRoot, ".promptscript");
50820
- if (existsSync26(promptscriptDir)) {
52070
+ const promptscriptDir = resolve35(projectRoot, ".promptscript");
52071
+ if (existsSync28(promptscriptDir)) {
50821
52072
  const entries = await readdir9(promptscriptDir);
50822
52073
  const prsFiles = entries.filter((entry) => entry.endsWith(".prs"));
50823
52074
  if (prsFiles.length === 1) {
50824
- entryFiles.add(resolve34(promptscriptDir, prsFiles[0]));
52075
+ entryFiles.add(resolve35(promptscriptDir, prsFiles[0]));
50825
52076
  }
50826
52077
  }
50827
52078
  }
@@ -50830,7 +52081,7 @@ async function collectProjectRemoteImports(entryFile) {
50830
52081
  for (const projectEntry of entryFiles) {
50831
52082
  imports.push(
50832
52083
  ...await collectRemoteImports(projectEntry, {
50833
- localPath: resolve34(projectRoot, ".promptscript"),
52084
+ localPath: resolve35(projectRoot, ".promptscript"),
50834
52085
  registries: config?.registries,
50835
52086
  strict: true,
50836
52087
  deduplicate: false,
@@ -50880,7 +52131,7 @@ function deriveSkillFolderName(source) {
50880
52131
  const subPath = extractSubPath(source);
50881
52132
  if (!subPath) return basename17(parseSkillSource(source).path);
50882
52133
  if (!subPath.toLowerCase().endsWith(".md")) return basename17(subPath);
50883
- const parent = dirname21(subPath);
52134
+ const parent = dirname22(subPath);
50884
52135
  if (parent === "." || parent === "") return basename17(parseSkillSource(source).path);
50885
52136
  return basename17(parent);
50886
52137
  }
@@ -50897,16 +52148,16 @@ function collectExistingSkillNames(lockfile, exclude) {
50897
52148
  async function fetchAndValidateRemoteSkill(source, options) {
50898
52149
  const skillFilePath = extractSkillFilePath(source);
50899
52150
  const repoUrl = options.gitUrl ?? extractRepoUrl(source);
50900
- const tmp = await mkdtemp2(join27(tmpdir3(), "prs-skill-validate-"));
52151
+ const tmp = await mkdtemp2(join30(tmpdir3(), "prs-skill-validate-"));
50901
52152
  const repoName = basename17(parseSkillSource(source).path.split("/")[2]);
50902
- const cloneDir = !skillFilePath.includes("/") ? join27(tmp, repoName) : tmp;
52153
+ const cloneDir = !skillFilePath.includes("/") ? join30(tmp, repoName) : tmp;
50903
52154
  try {
50904
52155
  const gitRegistry = createGitRegistry({ url: repoUrl });
50905
52156
  const cloneRef = options.version === "latest" || /^[0-9a-f]{40}$/i.test(options.version) ? void 0 : options.version;
50906
52157
  await gitRegistry.cloneAtTag(repoUrl, cloneRef, cloneDir);
50907
52158
  await gitRegistry.checkoutCommit(cloneDir, options.commit);
50908
52159
  const subPath = extractSubPath(source);
50909
- if (!subPath.toLowerCase().endsWith(".md") && existsSync26(join27(cloneDir, `${subPath}.prs`))) {
52160
+ if (!subPath.toLowerCase().endsWith(".md") && existsSync28(join30(cloneDir, `${subPath}.prs`))) {
50910
52161
  return {
50911
52162
  content: "",
50912
52163
  integrity: "",
@@ -50920,10 +52171,10 @@ async function fetchAndValidateRemoteSkill(source, options) {
50920
52171
  ]
50921
52172
  };
50922
52173
  }
50923
- const filePath = join27(cloneDir, skillFilePath);
52174
+ const filePath = join30(cloneDir, skillFilePath);
50924
52175
  let content;
50925
52176
  try {
50926
- content = await readFile27(filePath, "utf-8");
52177
+ content = await readFile28(filePath, "utf-8");
50927
52178
  } catch {
50928
52179
  return {
50929
52180
  content: "",
@@ -50999,8 +52250,8 @@ function validateRemoteSource(source) {
50999
52250
  }
51000
52251
  async function resolveEntryFile(fileFlag) {
51001
52252
  if (fileFlag) {
51002
- const resolved = resolve34(fileFlag);
51003
- if (!existsSync26(resolved)) {
52253
+ const resolved = resolve35(fileFlag);
52254
+ if (!existsSync28(resolved)) {
51004
52255
  throw new Error(`File not found: ${resolved}`);
51005
52256
  }
51006
52257
  return resolved;
@@ -51010,24 +52261,24 @@ async function resolveEntryFile(fileFlag) {
51010
52261
  try {
51011
52262
  const config = await loadConfig();
51012
52263
  if (config.input?.entry) {
51013
- const entryPath = resolve34(config.input.entry);
51014
- if (existsSync26(entryPath)) {
52264
+ const entryPath = resolve35(config.input.entry);
52265
+ if (existsSync28(entryPath)) {
51015
52266
  return entryPath;
51016
52267
  }
51017
52268
  }
51018
52269
  } catch {
51019
52270
  }
51020
52271
  }
51021
- const promptscriptDir = resolve34(".promptscript");
51022
- if (existsSync26(promptscriptDir)) {
52272
+ const promptscriptDir = resolve35(".promptscript");
52273
+ if (existsSync28(promptscriptDir)) {
51023
52274
  const entries = await readdir9(promptscriptDir);
51024
52275
  const prsFiles = entries.filter((f) => f.endsWith(".prs"));
51025
52276
  if (prsFiles.length === 1) {
51026
- return resolve34(promptscriptDir, prsFiles[0]);
52277
+ return resolve35(promptscriptDir, prsFiles[0]);
51027
52278
  }
51028
52279
  if (prsFiles.length > 1) {
51029
52280
  if (prsFiles.includes("project.prs")) {
51030
- return resolve34(promptscriptDir, "project.prs");
52281
+ return resolve35(promptscriptDir, "project.prs");
51031
52282
  }
51032
52283
  throw new Error(
51033
52284
  `Multiple .prs files found in .promptscript/. Use --file to specify which one.`
@@ -51128,12 +52379,12 @@ function findUseEndLine(lines, startLine) {
51128
52379
  }
51129
52380
  async function loadLockfile3() {
51130
52381
  const defaultLockfile = { version: LOCKFILE_VERSION, dependencies: {} };
51131
- if (!existsSync26(LOCKFILE_PATH)) {
52382
+ if (!existsSync28(LOCKFILE_PATH)) {
51132
52383
  return defaultLockfile;
51133
52384
  }
51134
52385
  let parsed;
51135
52386
  try {
51136
- const raw = await readFile27(LOCKFILE_PATH, "utf-8");
52387
+ const raw = await readFile28(LOCKFILE_PATH, "utf-8");
51137
52388
  parsed = parseYaml15(raw, { maxAliasCount: 100 });
51138
52389
  } catch (error) {
51139
52390
  throw new Error(
@@ -51147,7 +52398,7 @@ async function loadLockfile3() {
51147
52398
  return parsed;
51148
52399
  }
51149
52400
  async function saveLockfile(lockfile) {
51150
- await writeFile11(LOCKFILE_PATH, stringifyYaml4(lockfile), "utf-8");
52401
+ await writeFile12(LOCKFILE_PATH, stringifyYaml4(lockfile), "utf-8");
51151
52402
  }
51152
52403
  async function skillsAddCommand(rawSource, options) {
51153
52404
  const spinner = createSpinner("Adding skill...").start();
@@ -51170,7 +52421,7 @@ async function skillsAddCommand(rawSource, options) {
51170
52421
  }
51171
52422
  spinner.text = "Resolving entry file...";
51172
52423
  const entryFile = await resolveEntryFile(options.file);
51173
- const content = await readFile27(entryFile, "utf-8");
52424
+ const content = await readFile28(entryFile, "utf-8");
51174
52425
  const lines = content.split("\n");
51175
52426
  const alreadyImported = lines.some((line) => {
51176
52427
  const usePath = extractUsePath(line);
@@ -51282,7 +52533,7 @@ async function skillsAddCommand(rawSource, options) {
51282
52533
  ConsoleOutput.dryRun(`Would update ${LOCKFILE_PATH}`);
51283
52534
  return;
51284
52535
  }
51285
- await writeFile11(entryFile, updatedContent, "utf-8");
52536
+ await writeFile12(entryFile, updatedContent, "utf-8");
51286
52537
  await saveLockfile(lockfile);
51287
52538
  spinner.succeed("Skill added");
51288
52539
  ConsoleOutput.newline();
@@ -51298,7 +52549,7 @@ async function skillsRemoveCommand(name, options) {
51298
52549
  const spinner = createSpinner("Removing skill...").start();
51299
52550
  try {
51300
52551
  const entryFile = await resolveEntryFile();
51301
- const content = await readFile27(entryFile, "utf-8");
52552
+ const content = await readFile28(entryFile, "utf-8");
51302
52553
  const lines = content.split("\n");
51303
52554
  const parsed = parse(content, { filename: entryFile, tolerant: true });
51304
52555
  if (!parsed.ast || parsed.errors.length > 0) {
@@ -51378,7 +52629,7 @@ async function skillsRemoveCommand(name, options) {
51378
52629
  return;
51379
52630
  }
51380
52631
  lines.splice(lineIndex, candidate.endIndex - lineIndex + 1);
51381
- await writeFile11(entryFile, lines.join("\n"), "utf-8");
52632
+ await writeFile12(entryFile, lines.join("\n"), "utf-8");
51382
52633
  if (removedPath in lockfile.dependencies && !hasSameImportRemaining) {
51383
52634
  delete lockfile.dependencies[removedPath];
51384
52635
  }
@@ -51414,7 +52665,7 @@ async function skillsListCommand() {
51414
52665
  const spinner = createSpinner("Loading skills...").start();
51415
52666
  try {
51416
52667
  const entryFile = await resolveEntryFile();
51417
- const content = await readFile27(entryFile, "utf-8");
52668
+ const content = await readFile28(entryFile, "utf-8");
51418
52669
  const lines = content.split("\n");
51419
52670
  const skills2 = [];
51420
52671
  for (const line of lines) {
@@ -51582,9 +52833,13 @@ async function skillsUpdateCommand(name, options) {
51582
52833
 
51583
52834
  // packages/cli/src/cli.ts
51584
52835
  var __filename4 = fileURLToPath4(import.meta.url);
51585
- var __dirname4 = dirname26(__filename4);
52836
+ var __dirname4 = dirname27(__filename4);
52837
+ var CLI_VERSION = getPackageVersion(__dirname3, "./package.json");
51586
52838
  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) => {
52839
+ 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) => {
52840
+ if (actionCommand.name() === "__telemetry-flush" && process.env["PROMPTSCRIPT_TELEMETRY_FLUSH"] === "1") {
52841
+ return;
52842
+ }
51588
52843
  const opts = thisCommand.opts();
51589
52844
  if (opts["quiet"]) {
51590
52845
  setContext({ logLevel: 0 /* Quiet */ });
@@ -51598,7 +52853,8 @@ program.name("prs").description("PromptScript CLI - Standardize AI instructions"
51598
52853
  } else if (process.env["PROMPTSCRIPT_VERBOSE"] === "1" || process.env["PROMPTSCRIPT_VERBOSE"] === "true") {
51599
52854
  setContext({ logLevel: 2 /* Verbose */ });
51600
52855
  }
51601
- if (actionCommand.name() === "update-check") {
52856
+ await prepareCliTelemetry(actionCommand, CLI_VERSION);
52857
+ if (actionCommand.name() === "update-check" || actionCommand.name() === "telemetry" || actionCommand.name() === "__telemetry-flush") {
51602
52858
  return;
51603
52859
  }
51604
52860
  const currentVersion = getPackageVersion(__dirname3, "./package.json");
@@ -51607,6 +52863,8 @@ program.name("prs").description("PromptScript CLI - Standardize AI instructions"
51607
52863
  printUpdateNotification(updateInfo);
51608
52864
  }
51609
52865
  });
52866
+ }).hook("postAction", () => {
52867
+ finishCliTelemetry();
51610
52868
  });
51611
52869
  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
52870
  if (opts.migrate) {
@@ -51628,6 +52886,8 @@ program.command("pull").description("Pull updates from registry").option("-f, --
51628
52886
  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
52887
  program.command("check").description("Check configuration and dependencies health").action(checkCommand);
51630
52888
  program.command("update-check").description("Check for CLI updates").action(updateCheckCommand);
52889
+ program.command("telemetry").argument("<action>", "Action: status, enable, or disable").description("Manage anonymous usage telemetry").action(telemetryCommand);
52890
+ program.command("__telemetry-flush", { hidden: true }).description("Flush queued anonymous telemetry").action(flushCliTelemetry);
51631
52891
  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
52892
  const { serveCommand: serveCommand2 } = await Promise.resolve().then(() => (init_serve(), serve_exports));
51633
52893
  await serveCommand2(opts);
@@ -51653,11 +52913,18 @@ skills.command("list").description("List skills imported in the current project"
51653
52913
  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
52914
  var registry = program.command("registry").description("Manage PromptScript registries");
51655
52915
  registerRegistryCommands(registry);
51656
- function run(args = process.argv) {
51657
- program.parse(args);
52916
+ async function run(args = process.argv) {
52917
+ try {
52918
+ await program.parseAsync(args);
52919
+ } catch (error) {
52920
+ finishCliTelemetry("error");
52921
+ throw error;
52922
+ } finally {
52923
+ finishCliTelemetry();
52924
+ }
51658
52925
  }
51659
52926
  if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
51660
- run();
52927
+ await run();
51661
52928
  }
51662
52929
 
51663
52930
  // packages/cli/src/index.ts