iteration-cli 0.10.0 → 0.11.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.
@@ -932,8 +932,8 @@ var require_src = __commonJS({
932
932
 
933
933
  // src/cursor-hook-entry.ts
934
934
  import { randomUUID } from "node:crypto";
935
- import { promises as fs } from "node:fs";
936
- import path3 from "node:path";
935
+ import { promises as fs2 } from "node:fs";
936
+ import path4 from "node:path";
937
937
  import { StringDecoder } from "node:string_decoder";
938
938
 
939
939
  // ../../node_modules/.pnpm/zod@4.6.4/node_modules/zod/v4/core/util.js
@@ -1172,10 +1172,10 @@ function mergeDefs(...defs) {
1172
1172
  function cloneDef(schema) {
1173
1173
  return mergeDefs(schema._zod.def);
1174
1174
  }
1175
- function getElementAtPath(obj, path4) {
1176
- if (!path4)
1175
+ function getElementAtPath(obj, path5) {
1176
+ if (!path5)
1177
1177
  return obj;
1178
- return path4.reduce((acc, key) => acc?.[key], obj);
1178
+ return path5.reduce((acc, key) => acc?.[key], obj);
1179
1179
  }
1180
1180
  function promiseAllObject(promisesObj) {
1181
1181
  const keys = Object.keys(promisesObj);
@@ -1515,11 +1515,11 @@ function explicitlyAborted(x, startIndex = 0) {
1515
1515
  }
1516
1516
  return false;
1517
1517
  }
1518
- function prefixIssues(path4, issues) {
1518
+ function prefixIssues(path5, issues) {
1519
1519
  return issues.map((iss) => {
1520
1520
  var _a3;
1521
1521
  (_a3 = iss).path ?? (_a3.path = []);
1522
- iss.path.unshift(path4);
1522
+ iss.path.unshift(path5);
1523
1523
  return iss;
1524
1524
  });
1525
1525
  }
@@ -1965,16 +1965,16 @@ function flattenError(error2, mapper = (issue2) => issue2.message) {
1965
1965
  }
1966
1966
  function formatError(error2, mapper = (issue2) => issue2.message) {
1967
1967
  const fieldErrors = { _errors: [] };
1968
- const processError = (error3, path4 = []) => {
1968
+ const processError = (error3, path5 = []) => {
1969
1969
  for (const issue2 of error3.issues) {
1970
1970
  if (issue2.code === "invalid_union" && issue2.errors.length) {
1971
- issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
1971
+ issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
1972
1972
  } else if (issue2.code === "invalid_key") {
1973
- processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
1973
+ processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1974
1974
  } else if (issue2.code === "invalid_element") {
1975
- processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
1975
+ processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1976
1976
  } else {
1977
- const fullpath = [...path4, ...issue2.path];
1977
+ const fullpath = [...path5, ...issue2.path];
1978
1978
  if (fullpath.length === 0) {
1979
1979
  fieldErrors._errors.push(mapper(issue2));
1980
1980
  } else {
@@ -7321,6 +7321,25 @@ var TRACE_EVENT_TYPES = [
7321
7321
  ];
7322
7322
  var COLLECTOR_SOURCES = ["codex", "claude", "cursor"];
7323
7323
  var MAX_TRACE_EVENTS_PER_BATCH = 100;
7324
+ var cursorBillingRequestSchema = object({
7325
+ conversationId: uuid2(),
7326
+ reconcileFromMs: number2().int().nonnegative().nullable(),
7327
+ events: array(object({
7328
+ timestamp: number2().int().nonnegative(),
7329
+ kind: _enum([
7330
+ "included",
7331
+ "paid",
7332
+ "byok",
7333
+ "not_charged",
7334
+ "unknown"
7335
+ ]),
7336
+ inputTokens: number2().int().nonnegative().nullable(),
7337
+ outputTokens: number2().int().nonnegative().nullable(),
7338
+ cacheReadTokens: number2().int().nonnegative().nullable(),
7339
+ cacheWriteTokens: number2().int().nonnegative().nullable(),
7340
+ paidUsd: number2().finite().nonnegative().nullable()
7341
+ }).strict()).max(5e3)
7342
+ }).strict();
7324
7343
  var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
7325
7344
  var TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
7326
7345
  var unpairedSurrogate = /[\uD800-\uDFFF]/u;
@@ -8542,11 +8561,11 @@ function requireLodash_sortby() {
8542
8561
  function baseForOwn(object2, iteratee) {
8543
8562
  return object2 && baseFor(object2, iteratee, keys);
8544
8563
  }
8545
- function baseGet(object2, path4) {
8546
- path4 = isKey(path4, object2) ? [path4] : castPath(path4);
8547
- var index = 0, length = path4.length;
8564
+ function baseGet(object2, path5) {
8565
+ path5 = isKey(path5, object2) ? [path5] : castPath(path5);
8566
+ var index = 0, length = path5.length;
8548
8567
  while (object2 != null && index < length) {
8549
- object2 = object2[toKey(path4[index++])];
8568
+ object2 = object2[toKey(path5[index++])];
8550
8569
  }
8551
8570
  return index && index == length ? object2 : void 0;
8552
8571
  }
@@ -8673,13 +8692,13 @@ function requireLodash_sortby() {
8673
8692
  return object2 === source || baseIsMatch(object2, source, matchData);
8674
8693
  };
8675
8694
  }
8676
- function baseMatchesProperty(path4, srcValue) {
8677
- if (isKey(path4) && isStrictComparable(srcValue)) {
8678
- return matchesStrictComparable(toKey(path4), srcValue);
8695
+ function baseMatchesProperty(path5, srcValue) {
8696
+ if (isKey(path5) && isStrictComparable(srcValue)) {
8697
+ return matchesStrictComparable(toKey(path5), srcValue);
8679
8698
  }
8680
8699
  return function(object2) {
8681
- var objValue = get(object2, path4);
8682
- return objValue === void 0 && objValue === srcValue ? hasIn(object2, path4) : baseIsEqual(srcValue, objValue, void 0, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);
8700
+ var objValue = get(object2, path5);
8701
+ return objValue === void 0 && objValue === srcValue ? hasIn(object2, path5) : baseIsEqual(srcValue, objValue, void 0, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);
8683
8702
  };
8684
8703
  }
8685
8704
  function baseOrderBy(collection, iteratees, orders) {
@@ -8695,9 +8714,9 @@ function requireLodash_sortby() {
8695
8714
  return compareMultiple(object2, other, orders);
8696
8715
  });
8697
8716
  }
8698
- function basePropertyDeep(path4) {
8717
+ function basePropertyDeep(path5) {
8699
8718
  return function(object2) {
8700
- return baseGet(object2, path4);
8719
+ return baseGet(object2, path5);
8701
8720
  };
8702
8721
  }
8703
8722
  function baseRest(func, start) {
@@ -8952,11 +8971,11 @@ function requireLodash_sortby() {
8952
8971
  return result;
8953
8972
  };
8954
8973
  }
8955
- function hasPath(object2, path4, hasFunc) {
8956
- path4 = isKey(path4, object2) ? [path4] : castPath(path4);
8957
- var result, index = -1, length = path4.length;
8974
+ function hasPath(object2, path5, hasFunc) {
8975
+ path5 = isKey(path5, object2) ? [path5] : castPath(path5);
8976
+ var result, index = -1, length = path5.length;
8958
8977
  while (++index < length) {
8959
- var key = toKey(path4[index]);
8978
+ var key = toKey(path5[index]);
8960
8979
  if (!(result = object2 != null && hasFunc(object2, key))) {
8961
8980
  break;
8962
8981
  }
@@ -9111,12 +9130,12 @@ function requireLodash_sortby() {
9111
9130
  function toString(value) {
9112
9131
  return value == null ? "" : baseToString(value);
9113
9132
  }
9114
- function get(object2, path4, defaultValue) {
9115
- var result = object2 == null ? void 0 : baseGet(object2, path4);
9133
+ function get(object2, path5, defaultValue) {
9134
+ var result = object2 == null ? void 0 : baseGet(object2, path5);
9116
9135
  return result === void 0 ? defaultValue : result;
9117
9136
  }
9118
- function hasIn(object2, path4) {
9119
- return object2 != null && hasPath(object2, path4, baseHasIn);
9137
+ function hasIn(object2, path5) {
9138
+ return object2 != null && hasPath(object2, path5, baseHasIn);
9120
9139
  }
9121
9140
  function keys(object2) {
9122
9141
  return isArrayLike(object2) ? arrayLikeKeys(object2) : baseKeys(object2);
@@ -9124,8 +9143,8 @@ function requireLodash_sortby() {
9124
9143
  function identity(value) {
9125
9144
  return value;
9126
9145
  }
9127
- function property(path4) {
9128
- return isKey(path4) ? baseProperty(toKey(path4)) : basePropertyDeep(path4);
9146
+ function property(path5) {
9147
+ return isKey(path5) ? baseProperty(toKey(path5)) : basePropertyDeep(path5);
9129
9148
  }
9130
9149
  module.exports = sortBy;
9131
9150
  })(lodash_sortby, lodash_sortby.exports);
@@ -9556,16 +9575,16 @@ async function reportIfFoundPrivateKeyP12Format({ source, context, t }) {
9556
9575
  return;
9557
9576
  }
9558
9577
  try {
9559
- const fs2 = await import("node:fs");
9560
- const path4 = await import("node:path");
9561
- const pfxBytes = new Uint8Array(fs2.readFileSync(source.filePath));
9578
+ const fs3 = await import("node:fs");
9579
+ const path5 = await import("node:path");
9580
+ const pfxBytes = new Uint8Array(fs3.readFileSync(source.filePath));
9562
9581
  const isGcpServiceAccountP12 = await verifyPkcs12Mac(pfxBytes, GCP_SERVICE_ACCOUNT_P12_PASSWORD);
9563
9582
  if (!isGcpServiceAccountP12) {
9564
9583
  return;
9565
9584
  }
9566
9585
  context.report({
9567
9586
  message: t("PrivateKeyP12", {
9568
- FILE_NAME: path4.basename(source.filePath)
9587
+ FILE_NAME: path5.basename(source.filePath)
9569
9588
  }),
9570
9589
  range: [0, source.content.length]
9571
9590
  });
@@ -11521,6 +11540,87 @@ var hookFileSchema = object({
11521
11540
  hooks: record(string2(), array(hookEntrySchema)).optional()
11522
11541
  }).loose();
11523
11542
 
11543
+ // src/git-repository.ts
11544
+ import fs from "node:fs";
11545
+ import path3 from "node:path";
11546
+ var repositoryUrls = /* @__PURE__ */ new Map();
11547
+ function missing(error2) {
11548
+ const code = error2.code;
11549
+ return code === "ENOENT" || code === "ENOTDIR";
11550
+ }
11551
+ function readText(file) {
11552
+ try {
11553
+ return fs.readFileSync(file, "utf8");
11554
+ } catch (error2) {
11555
+ if (missing(error2)) return null;
11556
+ throw error2;
11557
+ }
11558
+ }
11559
+ function gitFiles(cwd) {
11560
+ try {
11561
+ if (!fs.statSync(cwd).isDirectory()) return null;
11562
+ } catch (error2) {
11563
+ if (missing(error2)) return null;
11564
+ throw error2;
11565
+ }
11566
+ for (let directory = cwd; ; directory = path3.dirname(directory)) {
11567
+ const dotGit = path3.join(directory, ".git");
11568
+ let stats;
11569
+ try {
11570
+ stats = fs.statSync(dotGit);
11571
+ } catch (error2) {
11572
+ if (!missing(error2)) throw error2;
11573
+ if (path3.dirname(directory) === directory) return null;
11574
+ continue;
11575
+ }
11576
+ if (stats.isDirectory())
11577
+ return { directory: dotGit, config: path3.join(dotGit, "config") };
11578
+ const pointer = /^gitdir:\s*(.+?)\s*$/m.exec(readText(dotGit) ?? "");
11579
+ if (pointer === null) return null;
11580
+ const gitDirectory = path3.resolve(directory, pointer[1]);
11581
+ const common = readText(path3.join(gitDirectory, "commondir"))?.trim();
11582
+ return {
11583
+ directory: gitDirectory,
11584
+ config: path3.join(
11585
+ common ? path3.resolve(gitDirectory, common) : gitDirectory,
11586
+ "config"
11587
+ )
11588
+ };
11589
+ }
11590
+ }
11591
+ function originUrl(config2) {
11592
+ let inOrigin = false;
11593
+ for (const line of config2.split(/\r?\n/)) {
11594
+ const section = /^\s*\[\s*([^\]]+?)\s*\]/.exec(line);
11595
+ if (section !== null) {
11596
+ inOrigin = /^remote\s+"origin"$/i.test(section[1]);
11597
+ continue;
11598
+ }
11599
+ const url = inOrigin ? /^\s*url\s*=\s*(.+?)\s*$/i.exec(line) : null;
11600
+ if (url !== null) return url[1].replace(/^"(.*)"$/, "$1");
11601
+ }
11602
+ return null;
11603
+ }
11604
+ function withoutCredentials(url) {
11605
+ return url.replace(/^([a-z][a-z0-9+.-]*:\/\/)[^/@]*@/i, "$1");
11606
+ }
11607
+ function gitRepositoryUrl(cwd) {
11608
+ const known = repositoryUrls.get(cwd);
11609
+ if (known !== void 0) return known;
11610
+ const files = gitFiles(cwd);
11611
+ const config2 = files === null ? null : readText(files.config);
11612
+ const origin = config2 === null ? null : originUrl(config2);
11613
+ const url = origin === null ? null : withoutCredentials(origin);
11614
+ if (url !== null) repositoryUrls.set(cwd, url);
11615
+ return url;
11616
+ }
11617
+ function gitBranch(cwd) {
11618
+ const files = gitFiles(cwd);
11619
+ const head = files === null ? null : readText(path3.join(files.directory, "HEAD"));
11620
+ const branch = /^ref: refs\/heads\/(.+)\s*$/.exec(head ?? "")?.[1];
11621
+ return branch ?? null;
11622
+ }
11623
+
11524
11624
  // src/cursor-hook-entry.ts
11525
11625
  var MAX_INPUT_BYTES = 16 * 1024 * 1024;
11526
11626
  var MAX_QUEUE_BYTES = 256 * 1024 * 1024;
@@ -11531,11 +11631,12 @@ var hookInputSchema = object({
11531
11631
  conversation_id: string2().optional(),
11532
11632
  parent_conversation_id: string2().optional(),
11533
11633
  subagent_id: string2().optional(),
11634
+ workspace_roots: array(string2()).optional(),
11534
11635
  transcript_path: string2().nullable().optional(),
11535
11636
  agent_transcript_path: string2().nullable().optional()
11536
11637
  }).loose();
11537
11638
  async function assertQueueCapacity(directory, incomingBytes) {
11538
- const names = (await fs.readdir(directory)).filter(
11639
+ const names = (await fs2.readdir(directory)).filter(
11539
11640
  (name) => name.endsWith(".jsonl") || name.endsWith(".tmp")
11540
11641
  );
11541
11642
  if (names.length >= MAX_QUEUE_FILES)
@@ -11545,7 +11646,7 @@ async function assertQueueCapacity(directory, incomingBytes) {
11545
11646
  const sizes = await Promise.all(
11546
11647
  names.slice(start, start + 64).map(async (name) => {
11547
11648
  try {
11548
- return (await fs.stat(path3.join(directory, name))).size;
11649
+ return (await fs2.stat(path4.join(directory, name))).size;
11549
11650
  } catch (error2) {
11550
11651
  if (error2 instanceof Error && "code" in error2 && error2.code === "ENOENT")
11551
11652
  return 0;
@@ -11565,28 +11666,28 @@ async function writeRecord(directory, value) {
11565
11666
  throw new Error("Cursor hook record exceeds the capture limit.");
11566
11667
  await assertQueueCapacity(directory, recordBytes);
11567
11668
  const id = randomUUID();
11568
- const temporary = path3.join(directory, `${id}.tmp`);
11569
- const filename = path3.join(directory, `${id}.jsonl`);
11570
- const handle = await fs.open(temporary, "wx", 384);
11669
+ const temporary = path4.join(directory, `${id}.tmp`);
11670
+ const filename = path4.join(directory, `${id}.jsonl`);
11671
+ const handle = await fs2.open(temporary, "wx", 384);
11571
11672
  try {
11572
11673
  await handle.writeFile(serialized, "utf8");
11573
11674
  await handle.sync();
11574
11675
  } catch (error2) {
11575
- await fs.rm(temporary, { force: true });
11676
+ await fs2.rm(temporary, { force: true });
11576
11677
  throw error2;
11577
11678
  } finally {
11578
11679
  await handle.close();
11579
11680
  }
11580
- await fs.rename(temporary, filename);
11681
+ await fs2.rename(temporary, filename);
11581
11682
  }
11582
11683
  async function captureSubagentTranscript(directory, input, capturedAt, transcriptBytes) {
11583
11684
  const filename = input.agent_transcript_path;
11584
- if (filename === null || filename === void 0 || !path3.isAbsolute(filename))
11685
+ if (filename === null || filename === void 0 || !path4.isAbsolute(filename))
11585
11686
  return;
11586
- const stats = await fs.stat(filename);
11687
+ const stats = await fs2.stat(filename);
11587
11688
  if (!stats.isFile() || stats.size > MAX_SUBAGENT_BYTES)
11588
11689
  throw new Error("Cursor subagent transcript exceeds the capture limit.");
11589
- const contents = await fs.readFile(filename);
11690
+ const contents = await fs2.readFile(filename);
11590
11691
  const parts = Math.max(1, Math.ceil(contents.length / SUBAGENT_CHUNK_BYTES));
11591
11692
  const decoder = new StringDecoder("utf8");
11592
11693
  for (let index = 0; index < parts; index += 1) {
@@ -11635,16 +11736,24 @@ async function main() {
11635
11736
  let transcriptBytes = null;
11636
11737
  if (transcriptPath !== null) {
11637
11738
  try {
11638
- transcriptBytes = (await fs.stat(transcriptPath)).size;
11739
+ transcriptBytes = (await fs2.stat(transcriptPath)).size;
11639
11740
  } catch (error2) {
11640
11741
  if (!(error2 instanceof Error && "code" in error2 && error2.code === "ENOENT"))
11641
11742
  throw error2;
11642
11743
  }
11643
11744
  }
11644
11745
  const directory = cursorHookDirectory(dataDirectory);
11645
- await fs.mkdir(directory, { recursive: true, mode: 448 });
11746
+ await fs2.mkdir(directory, { recursive: true, mode: 448 });
11646
11747
  const capturedAt = (/* @__PURE__ */ new Date()).toISOString();
11647
- await writeRecord(directory, { hook, capturedAt, transcriptBytes, input });
11748
+ const cwd = input.workspace_roots?.[0] ?? null;
11749
+ const git = (hook === "sessionStart" || hook === "beforeSubmitPrompt") && cwd !== null ? { branch: gitBranch(cwd), repository_url: gitRepositoryUrl(cwd) } : null;
11750
+ await writeRecord(directory, {
11751
+ hook,
11752
+ capturedAt,
11753
+ transcriptBytes,
11754
+ input,
11755
+ git
11756
+ });
11648
11757
  if (hook === "subagentStop")
11649
11758
  await captureSubagentTranscript(
11650
11759
  directory,
@@ -11661,15 +11770,15 @@ main().catch(async (error2) => {
11661
11770
  const dataDirectory = process.argv[3];
11662
11771
  if (dataDirectory) {
11663
11772
  try {
11664
- await fs.mkdir(dataDirectory, { recursive: true, mode: 448 });
11773
+ await fs2.mkdir(dataDirectory, { recursive: true, mode: 448 });
11665
11774
  const temporary = `${cursorHookFailurePath(dataDirectory)}.${randomUUID()}.tmp`;
11666
- await fs.writeFile(
11775
+ await fs2.writeFile(
11667
11776
  temporary,
11668
11777
  `${JSON.stringify({ atMs: Date.now() })}
11669
11778
  `,
11670
11779
  { mode: 384 }
11671
11780
  );
11672
- await fs.rename(temporary, cursorHookFailurePath(dataDirectory));
11781
+ await fs2.rename(temporary, cursorHookFailurePath(dataDirectory));
11673
11782
  } catch (statusError) {
11674
11783
  process.stderr.write(
11675
11784
  `Iteration could not save the Cursor capture failure: ${statusError instanceof Error ? statusError.message : "unknown error"}
package/dist/launcher.js CHANGED
@@ -5779,6 +5779,9 @@ var ZodUUID = /* @__PURE__ */ $constructor("ZodUUID", (inst, def) => {
5779
5779
  $ZodUUID.init(inst, def);
5780
5780
  ZodStringFormat.init(inst, def);
5781
5781
  });
5782
+ function uuid2(params) {
5783
+ return _uuid(ZodUUID, params);
5784
+ }
5782
5785
  var ZodURL = /* @__PURE__ */ $constructor("ZodURL", (inst, def) => {
5783
5786
  $ZodURL.init(inst, def);
5784
5787
  ZodStringFormat.init(inst, def);
@@ -6394,6 +6397,25 @@ var TRACE_EVENT_TYPES = [
6394
6397
  ];
6395
6398
  var COLLECTOR_SOURCES = ["codex", "claude", "cursor"];
6396
6399
  var MAX_TRACE_EVENTS_PER_BATCH = 100;
6400
+ var cursorBillingRequestSchema = object({
6401
+ conversationId: uuid2(),
6402
+ reconcileFromMs: number2().int().nonnegative().nullable(),
6403
+ events: array(object({
6404
+ timestamp: number2().int().nonnegative(),
6405
+ kind: _enum([
6406
+ "included",
6407
+ "paid",
6408
+ "byok",
6409
+ "not_charged",
6410
+ "unknown"
6411
+ ]),
6412
+ inputTokens: number2().int().nonnegative().nullable(),
6413
+ outputTokens: number2().int().nonnegative().nullable(),
6414
+ cacheReadTokens: number2().int().nonnegative().nullable(),
6415
+ cacheWriteTokens: number2().int().nonnegative().nullable(),
6416
+ paidUsd: number2().finite().nonnegative().nullable()
6417
+ }).strict()).max(5e3)
6418
+ }).strict();
6397
6419
  var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
6398
6420
  var TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
6399
6421
  var unpairedSurrogate = /[\uD800-\uDFFF]/u;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iteration-cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,7 +22,7 @@
22
22
  "@secretlint/secretlint-rule-preset-recommend": "13.0.6",
23
23
  "esbuild": "0.28.2",
24
24
  "zod": "4.6.4",
25
- "iteration-trace-contracts": "0.7.0"
25
+ "iteration-trace-contracts": "0.8.0"
26
26
  },
27
27
  "scripts": {
28
28
  "build": "pnpm --filter iteration-trace-contracts build && tsc -p tsconfig.json --noEmit && node build.mjs"