ctxloom-pro 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -169,12 +169,12 @@ var init_VectorStore = __esm({
169
169
  // server/index.ts
170
170
  import express from "express";
171
171
  import cors from "cors";
172
- import path40 from "path";
172
+ import path42 from "path";
173
173
  import fs32 from "fs";
174
174
  import { fileURLToPath as fileURLToPath2 } from "url";
175
175
 
176
176
  // server/loader.ts
177
- import path34 from "path";
177
+ import path36 from "path";
178
178
 
179
179
  // ../../packages/core/src/graph/DependencyGraph.ts
180
180
  import fs7 from "fs";
@@ -3133,8 +3133,8 @@ var CoChangeIndex = class _CoChangeIndex {
3133
3133
  if (event.isBulk || event.isMerge) return;
3134
3134
  const paths = event.files.map((f) => f.path);
3135
3135
  if (paths.length === 0) return;
3136
- for (const path41 of paths) {
3137
- this.nodeCounts.set(path41, (this.nodeCounts.get(path41) ?? 0) + 1);
3136
+ for (const path43 of paths) {
3137
+ this.nodeCounts.set(path43, (this.nodeCounts.get(path43) ?? 0) + 1);
3138
3138
  }
3139
3139
  for (let i = 0; i < paths.length; i++) {
3140
3140
  for (let j = i + 1; j < paths.length; j++) {
@@ -3281,8 +3281,8 @@ var ChurnIndex = class _ChurnIndex {
3281
3281
  */
3282
3282
  snapshot() {
3283
3283
  const nodes = {};
3284
- for (const [path41, raw] of this.nodes) {
3285
- nodes[path41] = {
3284
+ for (const [path43, raw] of this.nodes) {
3285
+ nodes[path43] = {
3286
3286
  commits: raw.commits,
3287
3287
  churnLines: raw.churnLines,
3288
3288
  bugCommits: raw.bugCommits,
@@ -3297,8 +3297,8 @@ var ChurnIndex = class _ChurnIndex {
3297
3297
  */
3298
3298
  static load(s) {
3299
3299
  const idx = new _ChurnIndex();
3300
- for (const [path41, raw] of Object.entries(s.nodes)) {
3301
- idx.nodes.set(path41, {
3300
+ for (const [path43, raw] of Object.entries(s.nodes)) {
3301
+ idx.nodes.set(path43, {
3302
3302
  commits: raw.commits,
3303
3303
  churnLines: raw.churnLines,
3304
3304
  bugCommits: raw.bugCommits,
@@ -3311,8 +3311,8 @@ var ChurnIndex = class _ChurnIndex {
3311
3311
  // -------------------------------------------------------------------------
3312
3312
  // Private helpers
3313
3313
  // -------------------------------------------------------------------------
3314
- getOrCreate(path41) {
3315
- const existing = this.nodes.get(path41);
3314
+ getOrCreate(path43) {
3315
+ const existing = this.nodes.get(path43);
3316
3316
  if (existing !== void 0) return existing;
3317
3317
  const fresh = {
3318
3318
  commits: 0,
@@ -3321,7 +3321,7 @@ var ChurnIndex = class _ChurnIndex {
3321
3321
  authorCounts: {},
3322
3322
  lastTouch: 0
3323
3323
  };
3324
- this.nodes.set(path41, fresh);
3324
+ this.nodes.set(path43, fresh);
3325
3325
  return fresh;
3326
3326
  }
3327
3327
  };
@@ -3402,12 +3402,12 @@ var OwnershipIndex = class _OwnershipIndex {
3402
3402
  */
3403
3403
  snapshot() {
3404
3404
  const nodes = {};
3405
- for (const [path41, raw] of this.nodes) {
3405
+ for (const [path43, raw] of this.nodes) {
3406
3406
  const authorWeights = {};
3407
3407
  for (const [email, entry] of Object.entries(raw.authorWeights)) {
3408
3408
  authorWeights[email] = { ...entry };
3409
3409
  }
3410
- nodes[path41] = { authorWeights, lastTouch: raw.lastTouch };
3410
+ nodes[path43] = { authorWeights, lastTouch: raw.lastTouch };
3411
3411
  }
3412
3412
  return { version: 1, nodes };
3413
3413
  }
@@ -3416,23 +3416,23 @@ var OwnershipIndex = class _OwnershipIndex {
3416
3416
  */
3417
3417
  static load(s) {
3418
3418
  const idx = new _OwnershipIndex();
3419
- for (const [path41, raw] of Object.entries(s.nodes)) {
3419
+ for (const [path43, raw] of Object.entries(s.nodes)) {
3420
3420
  const authorWeights = {};
3421
3421
  for (const [email, entry] of Object.entries(raw.authorWeights)) {
3422
3422
  authorWeights[email] = { ...entry };
3423
3423
  }
3424
- idx.nodes.set(path41, { authorWeights, lastTouch: raw.lastTouch });
3424
+ idx.nodes.set(path43, { authorWeights, lastTouch: raw.lastTouch });
3425
3425
  }
3426
3426
  return idx;
3427
3427
  }
3428
3428
  // -------------------------------------------------------------------------
3429
3429
  // Private helpers
3430
3430
  // -------------------------------------------------------------------------
3431
- getOrCreate(path41) {
3432
- const existing = this.nodes.get(path41);
3431
+ getOrCreate(path43) {
3432
+ const existing = this.nodes.get(path43);
3433
3433
  if (existing !== void 0) return existing;
3434
3434
  const fresh = { authorWeights: {}, lastTouch: 0 };
3435
- this.nodes.set(path41, fresh);
3435
+ this.nodes.set(path43, fresh);
3436
3436
  return fresh;
3437
3437
  }
3438
3438
  };
@@ -4670,8 +4670,8 @@ function getErrorMap() {
4670
4670
 
4671
4671
  // ../../node_modules/zod/v3/helpers/parseUtil.js
4672
4672
  var makeIssue = (params) => {
4673
- const { data, path: path41, errorMaps, issueData } = params;
4674
- const fullPath = [...path41, ...issueData.path || []];
4673
+ const { data, path: path43, errorMaps, issueData } = params;
4674
+ const fullPath = [...path43, ...issueData.path || []];
4675
4675
  const fullIssue = {
4676
4676
  ...issueData,
4677
4677
  path: fullPath
@@ -4787,11 +4787,11 @@ var errorUtil;
4787
4787
 
4788
4788
  // ../../node_modules/zod/v3/types.js
4789
4789
  var ParseInputLazyPath = class {
4790
- constructor(parent, value, path41, key) {
4790
+ constructor(parent, value, path43, key) {
4791
4791
  this._cachedPath = [];
4792
4792
  this.parent = parent;
4793
4793
  this.data = value;
4794
- this._path = path41;
4794
+ this._path = path43;
4795
4795
  this._key = key;
4796
4796
  }
4797
4797
  get path() {
@@ -11268,24 +11268,34 @@ import { readFileSync as readFileSync2 } from "fs";
11268
11268
  // ../../packages/core/src/license/index.ts
11269
11269
  import os3 from "os";
11270
11270
 
11271
+ // ../../packages/core/src/license/DistinctIdStore.ts
11272
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
11273
+ import path32 from "path";
11274
+ import os4 from "os";
11275
+
11271
11276
  // ../../packages/core/src/license/telemetry.ts
11272
11277
  var TELEMETRY_DISABLED = process.env["CTXLOOM_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1";
11278
+ var CTXLOOM_VERSION = typeof __CTXLOOM_VERSION__ === "string" && __CTXLOOM_VERSION__.length > 0 ? __CTXLOOM_VERSION__ : "dev";
11273
11279
  var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (typeof __TELEMETRY_POSTHOG_KEY__ === "string" ? __TELEMETRY_POSTHOG_KEY__ : "");
11274
11280
  var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (typeof __TELEMETRY_SENTRY_DSN__ === "string" ? __TELEMETRY_SENTRY_DSN__ : "");
11275
11281
 
11276
11282
  // ../../packages/core/src/server/ProjectState.ts
11277
- import path32 from "path";
11283
+ import path34 from "path";
11284
+
11285
+ // ../../packages/core/src/server/projectId.ts
11286
+ import crypto5 from "crypto";
11287
+ import path33 from "path";
11278
11288
 
11279
11289
  // ../../packages/core/src/server/ProjectStateManager.ts
11280
11290
  init_logger();
11281
11291
 
11282
11292
  // ../../packages/core/src/server/resolveProjectRoot.ts
11283
11293
  import fs29 from "fs";
11284
- import path33 from "path";
11294
+ import path35 from "path";
11285
11295
 
11286
11296
  // server/loader.ts
11287
11297
  async function loadContext(root) {
11288
- const absRoot = path34.resolve(root);
11298
+ const absRoot = path36.resolve(root);
11289
11299
  const overlay = new GitOverlayStore(absRoot);
11290
11300
  const gitEnabled = await overlay.loadSnapshot();
11291
11301
  const graph = new DependencyGraph();
@@ -11539,20 +11549,20 @@ function buildOwnershipRouter(ctx) {
11539
11549
  // server/routes/file.ts
11540
11550
  import { Router as Router7 } from "express";
11541
11551
  import fs30 from "fs/promises";
11542
- import path35 from "path";
11552
+ import path37 from "path";
11543
11553
  function buildFileRouter(ctx) {
11544
11554
  const router = Router7();
11545
11555
  router.get("/", async (req, res) => {
11546
11556
  const rel = req.query.path;
11547
11557
  if (!rel) return res.status(400).json({ error: "missing path" });
11548
- const abs = path35.resolve(ctx.root, rel);
11549
- const rootBoundary = ctx.root.endsWith(path35.sep) ? ctx.root : ctx.root + path35.sep;
11558
+ const abs = path37.resolve(ctx.root, rel);
11559
+ const rootBoundary = ctx.root.endsWith(path37.sep) ? ctx.root : ctx.root + path37.sep;
11550
11560
  if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
11551
11561
  return res.status(403).json({ error: "forbidden" });
11552
11562
  }
11553
11563
  try {
11554
11564
  const content = await fs30.readFile(abs, "utf-8");
11555
- const ext = path35.extname(abs).slice(1);
11565
+ const ext = path37.extname(abs).slice(1);
11556
11566
  res.json({ content, lines: content.split("\n").length, ext });
11557
11567
  } catch {
11558
11568
  res.status(404).json({ error: "not found" });
@@ -11564,7 +11574,7 @@ function buildFileRouter(ctx) {
11564
11574
  // server/routes/open.ts
11565
11575
  import { Router as Router8 } from "express";
11566
11576
  import { execFile as execFile2 } from "child_process";
11567
- import path36 from "path";
11577
+ import path38 from "path";
11568
11578
  function tryOpen(bin, abs) {
11569
11579
  return new Promise((resolve) => {
11570
11580
  execFile2(bin, [abs], { timeout: 5e3 }, (err) => resolve(!err));
@@ -11575,8 +11585,8 @@ function buildOpenRouter(ctx) {
11575
11585
  router.post("/", async (req, res) => {
11576
11586
  const rel = req.body?.path;
11577
11587
  if (!rel || typeof rel !== "string") return res.status(400).json({ error: "missing path" });
11578
- const abs = path36.resolve(ctx.root, rel);
11579
- const rootBoundary = ctx.root.endsWith(path36.sep) ? ctx.root : ctx.root + path36.sep;
11588
+ const abs = path38.resolve(ctx.root, rel);
11589
+ const rootBoundary = ctx.root.endsWith(path38.sep) ? ctx.root : ctx.root + path38.sep;
11580
11590
  if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
11581
11591
  return res.status(403).json({ error: "forbidden" });
11582
11592
  }
@@ -11588,7 +11598,7 @@ function buildOpenRouter(ctx) {
11588
11598
 
11589
11599
  // server/routes/tokens.ts
11590
11600
  import { Router as Router9 } from "express";
11591
- import path37 from "path";
11601
+ import path39 from "path";
11592
11602
  import fs31 from "fs";
11593
11603
  var CHARS_PER_TOKEN = 4;
11594
11604
  var cache = null;
@@ -11604,7 +11614,7 @@ function buildTokensRouter(ctx) {
11604
11614
  let fullChars = 0;
11605
11615
  let skeletonChars = 0;
11606
11616
  for (const file of files) {
11607
- const absPath = path37.join(ctx.root, file);
11617
+ const absPath = path39.join(ctx.root, file);
11608
11618
  try {
11609
11619
  const content = fs31.readFileSync(absPath, "utf-8");
11610
11620
  fullChars += content.length;
@@ -11707,23 +11717,23 @@ function buildFileTrendsRouter(ctx) {
11707
11717
 
11708
11718
  // server/routes/projects.ts
11709
11719
  import { Router as Router12 } from "express";
11710
- import path39 from "path";
11720
+ import path41 from "path";
11711
11721
 
11712
11722
  // server/projects.ts
11713
- import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
11714
- import os4 from "os";
11715
- import path38 from "path";
11716
- import crypto5 from "crypto";
11717
- var HOME = os4.homedir();
11718
- var REGISTRY_PATH = path38.join(HOME, ".ctxloom", "repos.json");
11723
+ import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
11724
+ import os5 from "os";
11725
+ import path40 from "path";
11726
+ import crypto6 from "crypto";
11727
+ var HOME = os5.homedir();
11728
+ var REGISTRY_PATH = path40.join(HOME, ".ctxloom", "repos.json");
11719
11729
  function slugFor(root) {
11720
- const abs = path38.resolve(root);
11721
- return crypto5.createHash("sha1").update(abs).digest("hex").slice(0, 12);
11730
+ const abs = path40.resolve(root);
11731
+ return crypto6.createHash("sha1").update(abs).digest("hex").slice(0, 12);
11722
11732
  }
11723
11733
  function readRegistry() {
11724
- if (!existsSync2(REGISTRY_PATH)) return [];
11734
+ if (!existsSync3(REGISTRY_PATH)) return [];
11725
11735
  try {
11726
- const raw = readFileSync3(REGISTRY_PATH, "utf-8");
11736
+ const raw = readFileSync4(REGISTRY_PATH, "utf-8");
11727
11737
  const parsed = JSON.parse(raw);
11728
11738
  if (!Array.isArray(parsed)) return [];
11729
11739
  return parsed.filter((r) => typeof r?.root === "string");
@@ -11732,27 +11742,27 @@ function readRegistry() {
11732
11742
  }
11733
11743
  }
11734
11744
  function listProjects(defaultRoot) {
11735
- const absDefault = path38.resolve(defaultRoot);
11745
+ const absDefault = path40.resolve(defaultRoot);
11736
11746
  const out = [
11737
11747
  {
11738
11748
  slug: slugFor(absDefault),
11739
- name: path38.basename(absDefault) || absDefault,
11749
+ name: path40.basename(absDefault) || absDefault,
11740
11750
  root: absDefault,
11741
11751
  isDefault: true,
11742
- hasSnapshot: existsSync2(path38.join(absDefault, ".ctxloom"))
11752
+ hasSnapshot: existsSync3(path40.join(absDefault, ".ctxloom"))
11743
11753
  }
11744
11754
  ];
11745
11755
  const seen = /* @__PURE__ */ new Set([absDefault]);
11746
11756
  for (const entry of readRegistry()) {
11747
- const abs = path38.resolve(entry.root);
11757
+ const abs = path40.resolve(entry.root);
11748
11758
  if (seen.has(abs)) continue;
11749
11759
  seen.add(abs);
11750
11760
  const item = {
11751
11761
  slug: slugFor(abs),
11752
- name: entry.name ?? (path38.basename(abs) || abs),
11762
+ name: entry.name ?? (path40.basename(abs) || abs),
11753
11763
  root: abs,
11754
11764
  isDefault: false,
11755
- hasSnapshot: existsSync2(path38.join(abs, ".ctxloom"))
11765
+ hasSnapshot: existsSync3(path40.join(abs, ".ctxloom"))
11756
11766
  };
11757
11767
  if (entry.alias !== void 0) item.alias = entry.alias;
11758
11768
  out.push(item);
@@ -11805,7 +11815,7 @@ function buildProjectsRouter(deps) {
11805
11815
  } catch (err) {
11806
11816
  const detail = err instanceof Error ? err.message : String(err);
11807
11817
  res.status(500).json({
11808
- error: `failed to switch to ${path39.basename(target.root)}: ${detail}`
11818
+ error: `failed to switch to ${path41.basename(target.root)}: ${detail}`
11809
11819
  });
11810
11820
  }
11811
11821
  });
@@ -11813,7 +11823,7 @@ function buildProjectsRouter(deps) {
11813
11823
  }
11814
11824
 
11815
11825
  // server/index.ts
11816
- var __dirname2 = path40.dirname(fileURLToPath2(import.meta.url));
11826
+ var __dirname2 = path42.dirname(fileURLToPath2(import.meta.url));
11817
11827
  async function startDashboard(options) {
11818
11828
  const { root, port, open } = options;
11819
11829
  console.log(`ctxloom dashboard \u2014 loading context from ${root}...`);
@@ -11872,7 +11882,7 @@ async function startDashboard(options) {
11872
11882
  }
11873
11883
  activeWatcher = null;
11874
11884
  }
11875
- const snapshotDir = path40.join(targetRoot, ".ctxloom");
11885
+ const snapshotDir = path42.join(targetRoot, ".ctxloom");
11876
11886
  try {
11877
11887
  activeWatcher = fs32.watch(snapshotDir, (_event, filename) => {
11878
11888
  if (!filename || !filename.includes("snapshot")) return;
@@ -11902,12 +11912,12 @@ async function startDashboard(options) {
11902
11912
  attachSnapshotWatcher(newRoot);
11903
11913
  }
11904
11914
  }));
11905
- const clientDist = path40.join(__dirname2, "../dashboard/client");
11906
- const clientDistExists = fs32.existsSync(path40.join(clientDist, "index.html"));
11915
+ const clientDist = path42.join(__dirname2, "../dashboard/client");
11916
+ const clientDistExists = fs32.existsSync(path42.join(clientDist, "index.html"));
11907
11917
  if (clientDistExists) {
11908
11918
  app.use(express.static(clientDist, { dotfiles: "allow" }));
11909
11919
  app.get(/.*/, (_req, res) => {
11910
- res.sendFile(path40.join(clientDist, "index.html"), { dotfiles: "allow" });
11920
+ res.sendFile(path42.join(clientDist, "index.html"), { dotfiles: "allow" });
11911
11921
  });
11912
11922
  } else {
11913
11923
  app.get(/^\/(?!api\/).*/, (_req, res) => {
@@ -3406,8 +3406,8 @@ var CoChangeIndex = class _CoChangeIndex {
3406
3406
  if (event.isBulk || event.isMerge) return;
3407
3407
  const paths = event.files.map((f) => f.path);
3408
3408
  if (paths.length === 0) return;
3409
- for (const path32 of paths) {
3410
- this.nodeCounts.set(path32, (this.nodeCounts.get(path32) ?? 0) + 1);
3409
+ for (const path34 of paths) {
3410
+ this.nodeCounts.set(path34, (this.nodeCounts.get(path34) ?? 0) + 1);
3411
3411
  }
3412
3412
  for (let i = 0; i < paths.length; i++) {
3413
3413
  for (let j = i + 1; j < paths.length; j++) {
@@ -3554,8 +3554,8 @@ var ChurnIndex = class _ChurnIndex {
3554
3554
  */
3555
3555
  snapshot() {
3556
3556
  const nodes = {};
3557
- for (const [path32, raw] of this.nodes) {
3558
- nodes[path32] = {
3557
+ for (const [path34, raw] of this.nodes) {
3558
+ nodes[path34] = {
3559
3559
  commits: raw.commits,
3560
3560
  churnLines: raw.churnLines,
3561
3561
  bugCommits: raw.bugCommits,
@@ -3570,8 +3570,8 @@ var ChurnIndex = class _ChurnIndex {
3570
3570
  */
3571
3571
  static load(s) {
3572
3572
  const idx = new _ChurnIndex();
3573
- for (const [path32, raw] of Object.entries(s.nodes)) {
3574
- idx.nodes.set(path32, {
3573
+ for (const [path34, raw] of Object.entries(s.nodes)) {
3574
+ idx.nodes.set(path34, {
3575
3575
  commits: raw.commits,
3576
3576
  churnLines: raw.churnLines,
3577
3577
  bugCommits: raw.bugCommits,
@@ -3584,8 +3584,8 @@ var ChurnIndex = class _ChurnIndex {
3584
3584
  // -------------------------------------------------------------------------
3585
3585
  // Private helpers
3586
3586
  // -------------------------------------------------------------------------
3587
- getOrCreate(path32) {
3588
- const existing = this.nodes.get(path32);
3587
+ getOrCreate(path34) {
3588
+ const existing = this.nodes.get(path34);
3589
3589
  if (existing !== void 0) return existing;
3590
3590
  const fresh = {
3591
3591
  commits: 0,
@@ -3594,7 +3594,7 @@ var ChurnIndex = class _ChurnIndex {
3594
3594
  authorCounts: {},
3595
3595
  lastTouch: 0
3596
3596
  };
3597
- this.nodes.set(path32, fresh);
3597
+ this.nodes.set(path34, fresh);
3598
3598
  return fresh;
3599
3599
  }
3600
3600
  };
@@ -3675,12 +3675,12 @@ var OwnershipIndex = class _OwnershipIndex {
3675
3675
  */
3676
3676
  snapshot() {
3677
3677
  const nodes = {};
3678
- for (const [path32, raw] of this.nodes) {
3678
+ for (const [path34, raw] of this.nodes) {
3679
3679
  const authorWeights = {};
3680
3680
  for (const [email, entry] of Object.entries(raw.authorWeights)) {
3681
3681
  authorWeights[email] = { ...entry };
3682
3682
  }
3683
- nodes[path32] = { authorWeights, lastTouch: raw.lastTouch };
3683
+ nodes[path34] = { authorWeights, lastTouch: raw.lastTouch };
3684
3684
  }
3685
3685
  return { version: 1, nodes };
3686
3686
  }
@@ -3689,23 +3689,23 @@ var OwnershipIndex = class _OwnershipIndex {
3689
3689
  */
3690
3690
  static load(s) {
3691
3691
  const idx = new _OwnershipIndex();
3692
- for (const [path32, raw] of Object.entries(s.nodes)) {
3692
+ for (const [path34, raw] of Object.entries(s.nodes)) {
3693
3693
  const authorWeights = {};
3694
3694
  for (const [email, entry] of Object.entries(raw.authorWeights)) {
3695
3695
  authorWeights[email] = { ...entry };
3696
3696
  }
3697
- idx.nodes.set(path32, { authorWeights, lastTouch: raw.lastTouch });
3697
+ idx.nodes.set(path34, { authorWeights, lastTouch: raw.lastTouch });
3698
3698
  }
3699
3699
  return idx;
3700
3700
  }
3701
3701
  // -------------------------------------------------------------------------
3702
3702
  // Private helpers
3703
3703
  // -------------------------------------------------------------------------
3704
- getOrCreate(path32) {
3705
- const existing = this.nodes.get(path32);
3704
+ getOrCreate(path34) {
3705
+ const existing = this.nodes.get(path34);
3706
3706
  if (existing !== void 0) return existing;
3707
3707
  const fresh = { authorWeights: {}, lastTouch: 0 };
3708
- this.nodes.set(path32, fresh);
3708
+ this.nodes.set(path34, fresh);
3709
3709
  return fresh;
3710
3710
  }
3711
3711
  };
@@ -8794,18 +8794,79 @@ async function startTrial(email, opts = {}) {
8794
8794
  return result;
8795
8795
  }
8796
8796
 
8797
+ // packages/core/src/license/DistinctIdStore.ts
8798
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
8799
+ import path30 from "path";
8800
+ import os4 from "os";
8801
+ var UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
8802
+ function distinctIdPath(home) {
8803
+ return path30.join(home ?? os4.homedir(), ".ctxloom", "distinct_id");
8804
+ }
8805
+ function isValidV4(id) {
8806
+ return typeof id === "string" && UUID_V4_REGEX.test(id);
8807
+ }
8808
+ function getOrCreateDistinctId(home) {
8809
+ const filePath = distinctIdPath(home);
8810
+ if (existsSync2(filePath)) {
8811
+ try {
8812
+ const raw = readFileSync3(filePath, "utf8");
8813
+ const parsed = JSON.parse(raw);
8814
+ if (parsed !== null && typeof parsed === "object" && isValidV4(parsed["id"])) {
8815
+ return parsed;
8816
+ }
8817
+ } catch {
8818
+ }
8819
+ }
8820
+ const record = {
8821
+ id: crypto.randomUUID(),
8822
+ alias_pending: os4.hostname()
8823
+ };
8824
+ mkdirSync2(path30.dirname(filePath), { recursive: true });
8825
+ writeFileSync2(filePath, JSON.stringify(record), { mode: 384 });
8826
+ return record;
8827
+ }
8828
+ function markAliasSent(home) {
8829
+ const filePath = distinctIdPath(home);
8830
+ try {
8831
+ const raw = readFileSync3(filePath, "utf8");
8832
+ const parsed = JSON.parse(raw);
8833
+ const { alias_pending: _dropped, ...rest } = parsed;
8834
+ writeFileSync2(filePath, JSON.stringify(rest), { mode: 384 });
8835
+ } catch {
8836
+ }
8837
+ }
8838
+
8797
8839
  // packages/core/src/license/telemetry.ts
8798
8840
  var TELEMETRY_DISABLED = process.env["CTXLOOM_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1";
8841
+ var CTXLOOM_VERSION = "1.1.2".length > 0 ? "1.1.2" : "dev";
8799
8842
  var POSTHOG_HOST = "https://eu.i.posthog.com";
8800
8843
  var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
8801
8844
  var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
8802
- function track(event, distinctId, props = {}) {
8845
+ var cachedDistinctId = null;
8846
+ function resolveDistinctId() {
8847
+ if (cachedDistinctId) return cachedDistinctId;
8848
+ try {
8849
+ cachedDistinctId = getOrCreateDistinctId();
8850
+ return cachedDistinctId;
8851
+ } catch {
8852
+ return null;
8853
+ }
8854
+ }
8855
+ function track(event, props = {}) {
8803
8856
  if (TELEMETRY_DISABLED || !POSTHOG_KEY) return;
8804
- void sendPostHog(event, distinctId, props);
8857
+ const record = resolveDistinctId();
8858
+ if (!record) return;
8859
+ void sendPostHog(event, record.id, props);
8860
+ if (record.alias_pending) {
8861
+ const oldAlias = record.alias_pending;
8862
+ void sendAlias(record.id, oldAlias);
8863
+ }
8805
8864
  }
8806
8865
  function captureError(err, context = {}) {
8807
8866
  if (TELEMETRY_DISABLED || !SENTRY_DSN) return;
8808
- void sendSentry(err, context);
8867
+ const record = resolveDistinctId();
8868
+ const augmented = record ? { ...context, distinct_id: record.id } : context;
8869
+ void sendSentry(err, augmented);
8809
8870
  }
8810
8871
  async function sendPostHog(event, distinctId, props) {
8811
8872
  try {
@@ -8818,6 +8879,7 @@ async function sendPostHog(event, distinctId, props) {
8818
8879
  event,
8819
8880
  properties: {
8820
8881
  $lib: "ctxloom-cli",
8882
+ release: CTXLOOM_VERSION,
8821
8883
  ...props
8822
8884
  }
8823
8885
  }),
@@ -8826,6 +8888,33 @@ async function sendPostHog(event, distinctId, props) {
8826
8888
  } catch {
8827
8889
  }
8828
8890
  }
8891
+ async function sendAlias(newId, oldAlias) {
8892
+ try {
8893
+ const res = await fetch(`${POSTHOG_HOST}/capture/`, {
8894
+ method: "POST",
8895
+ headers: { "Content-Type": "application/json" },
8896
+ body: JSON.stringify({
8897
+ api_key: POSTHOG_KEY,
8898
+ distinct_id: newId,
8899
+ event: "$create_alias",
8900
+ properties: {
8901
+ $lib: "ctxloom-cli",
8902
+ release: CTXLOOM_VERSION,
8903
+ alias: oldAlias
8904
+ }
8905
+ }),
8906
+ signal: AbortSignal.timeout(4e3)
8907
+ });
8908
+ if (res.ok) {
8909
+ try {
8910
+ markAliasSent();
8911
+ } catch {
8912
+ }
8913
+ if (cachedDistinctId) cachedDistinctId = { id: cachedDistinctId.id };
8914
+ }
8915
+ } catch {
8916
+ }
8917
+ }
8829
8918
  async function sendSentry(err, context) {
8830
8919
  try {
8831
8920
  const dsn = parseDsn(SENTRY_DSN);
@@ -8853,7 +8942,7 @@ async function sendSentry(err, context) {
8853
8942
  ]
8854
8943
  },
8855
8944
  extra: context,
8856
- tags: { runtime: "node", component: "cli-license" }
8945
+ tags: { runtime: "node", component: "cli-license", release: CTXLOOM_VERSION }
8857
8946
  }),
8858
8947
  signal: AbortSignal.timeout(4e3)
8859
8948
  });
@@ -8869,20 +8958,33 @@ function parseDsn(dsn) {
8869
8958
  return null;
8870
8959
  }
8871
8960
  }
8961
+ function scrubPath(filename) {
8962
+ return filename.replace(/^\/Users\/[^/]+\//, "/Users/~/").replace(/^\/home\/[^/]+\//, "/home/~/").replace(/^([A-Z]:\\\\Users\\\\)[^\\]+\\\\/, "$1~\\\\").replace(/^([A-Z]:\\Users\\)[^\\]+\\/, "$1~\\");
8963
+ }
8872
8964
  function parseStack(stack) {
8873
8965
  return stack.split("\n").slice(1).map((line) => {
8874
8966
  const m = line.trim().match(/at (.+?) \((.+?):(\d+):\d+\)/);
8875
8967
  if (!m) return null;
8876
- return { function: m[1] ?? "", filename: m[2] ?? "", lineno: Number(m[3]) };
8968
+ return { function: m[1] ?? "", filename: scrubPath(m[2] ?? ""), lineno: Number(m[3]) };
8877
8969
  }).filter((f) => f !== null).slice(0, 20);
8878
8970
  }
8879
8971
 
8880
8972
  // packages/core/src/server/ProjectState.ts
8881
- import path30 from "path";
8973
+ import path32 from "path";
8974
+
8975
+ // packages/core/src/server/projectId.ts
8976
+ import crypto5 from "crypto";
8977
+ import path31 from "path";
8978
+ function hashProjectRoot(absPath) {
8979
+ const canonical = path31.resolve(absPath);
8980
+ return crypto5.createHash("sha256").update(canonical).digest("hex").slice(0, 16);
8981
+ }
8982
+
8983
+ // packages/core/src/server/ProjectState.ts
8882
8984
  function createProjectState(projectRoot, opts = {}) {
8883
8985
  return {
8884
8986
  projectRoot,
8885
- dbPath: path30.join(projectRoot, ".ctxloom", "vectors.lancedb"),
8987
+ dbPath: path32.join(projectRoot, ".ctxloom", "vectors.lancedb"),
8886
8988
  pinned: opts.pinned ?? false,
8887
8989
  lastTouchedAt: Date.now(),
8888
8990
  vectorsInitialized: false,
@@ -8900,8 +9002,16 @@ function createProjectState(projectRoot, opts = {}) {
8900
9002
  async function ensureVectorsInitialized(state) {
8901
9003
  if (state.vectorsInitialized) return;
8902
9004
  if (!state.storePromise) return;
8903
- await state.storePromise;
8904
- state.vectorsInitialized = true;
9005
+ try {
9006
+ await state.storePromise;
9007
+ state.vectorsInitialized = true;
9008
+ } catch (err) {
9009
+ captureError(err, {
9010
+ project_id: hashProjectRoot(state.projectRoot),
9011
+ phase: "vector_init"
9012
+ });
9013
+ throw err;
9014
+ }
8905
9015
  }
8906
9016
  async function disposeProjectState(state) {
8907
9017
  try {
@@ -8995,12 +9105,23 @@ var ProjectStateManager = class {
8995
9105
  );
8996
9106
  }
8997
9107
  this.map.delete(victim.projectRoot);
9108
+ const pinnedCount = Array.from(this.map.values()).filter((s) => s.pinned).length;
9109
+ track("project_evicted", {
9110
+ project_id: hashProjectRoot(victim.projectRoot),
9111
+ pinned_count: pinnedCount,
9112
+ cap: this.maxProjects
9113
+ });
8998
9114
  void this.onDispose(victim).then(() => {
8999
9115
  logger.info("project.evicted", {
9000
9116
  root: victim.projectRoot,
9001
9117
  reason: "lru_cap_reached",
9002
9118
  ttl_seconds: Math.round((Date.now() - victim.lastTouchedAt) / 1e3)
9003
9119
  });
9120
+ }).catch((err) => {
9121
+ captureError(err, {
9122
+ project_id: hashProjectRoot(victim.projectRoot),
9123
+ phase: "dispose"
9124
+ });
9004
9125
  });
9005
9126
  }
9006
9127
  /**
@@ -9016,7 +9137,7 @@ var ProjectStateManager = class {
9016
9137
 
9017
9138
  // packages/core/src/server/resolveProjectRoot.ts
9018
9139
  import fs27 from "fs";
9019
- import path31 from "path";
9140
+ import path33 from "path";
9020
9141
  var PATH_SEPARATOR_PATTERN = /[/\\~]|^[A-Za-z]:/;
9021
9142
  function looksLikePath(value) {
9022
9143
  return PATH_SEPARATOR_PATTERN.test(value);
@@ -9041,9 +9162,9 @@ function resolvePathSafely(p, cwd) {
9041
9162
  let expanded = p;
9042
9163
  if (p === "~" || p.startsWith("~/")) {
9043
9164
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
9044
- expanded = p === "~" ? home : path31.join(home, p.slice(2));
9165
+ expanded = p === "~" ? home : path33.join(home, p.slice(2));
9045
9166
  }
9046
- return path31.isAbsolute(expanded) ? path31.resolve(expanded) : path31.resolve(cwd, expanded);
9167
+ return path33.isAbsolute(expanded) ? path33.resolve(expanded) : path33.resolve(cwd, expanded);
9047
9168
  }
9048
9169
  function realpathOrSame(p) {
9049
9170
  try {
@@ -9115,7 +9236,7 @@ function validateDefaultRoot(candidate) {
9115
9236
  } catch {
9116
9237
  return false;
9117
9238
  }
9118
- return PROJECT_MARKERS.some((m) => fs27.existsSync(path31.join(candidate, m)));
9239
+ return PROJECT_MARKERS.some((m) => fs27.existsSync(path33.join(candidate, m)));
9119
9240
  }
9120
9241
 
9121
9242
  // packages/core/src/server/structuredErrors.ts
@@ -9171,6 +9292,21 @@ var FirstTouchTracker = class {
9171
9292
  }
9172
9293
  };
9173
9294
 
9295
+ // packages/core/src/server/EmittedOnceTracker.ts
9296
+ var EmittedOnceTracker = class {
9297
+ seen = /* @__PURE__ */ new Set();
9298
+ /** Returns true the first time `key` is seen, false thereafter. */
9299
+ markAndCheck(key) {
9300
+ if (this.seen.has(key)) return false;
9301
+ this.seen.add(key);
9302
+ return true;
9303
+ }
9304
+ /** Clear all keys. Used by tests. */
9305
+ reset() {
9306
+ this.seen.clear();
9307
+ }
9308
+ };
9309
+
9174
9310
  export {
9175
9311
  GRAMMAR_MANIFEST,
9176
9312
  findGrammar,
@@ -9253,8 +9389,11 @@ export {
9253
9389
  activateLicense,
9254
9390
  deactivateLicense,
9255
9391
  startTrial,
9392
+ getOrCreateDistinctId,
9393
+ markAliasSent,
9256
9394
  track,
9257
9395
  captureError,
9396
+ hashProjectRoot,
9258
9397
  createProjectState,
9259
9398
  ensureVectorsInitialized,
9260
9399
  disposeProjectState,
@@ -9267,6 +9406,7 @@ export {
9267
9406
  aliasNotFoundError,
9268
9407
  noParseableSourcesWarning,
9269
9408
  wrapWithIndexingEnvelope,
9270
- FirstTouchTracker
9409
+ FirstTouchTracker,
9410
+ EmittedOnceTracker
9271
9411
  };
9272
- //# sourceMappingURL=chunk-FFSSKDV4.js.map
9412
+ //# sourceMappingURL=chunk-RGO3M3IR.js.map
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  AuthorResolver,
9
9
  DependencyGraph,
10
10
  EmailAlreadyUsedError,
11
+ EmittedOnceTracker,
11
12
  FileWatcher,
12
13
  FingerprintAlreadyUsedError,
13
14
  FirstTouchTracker,
@@ -30,6 +31,7 @@ import {
30
31
  ensureVectorsInitialized,
31
32
  generateCODEOWNERS,
32
33
  getLicenseInfo,
34
+ hashProjectRoot,
33
35
  isActive,
34
36
  loadReviewConfig,
35
37
  noDefaultProjectError,
@@ -43,7 +45,7 @@ import {
43
45
  validateDefaultRoot,
44
46
  wrapWithIndexingEnvelope,
45
47
  writeCODEOWNERS
46
- } from "./chunk-FFSSKDV4.js";
48
+ } from "./chunk-RGO3M3IR.js";
47
49
  import {
48
50
  VectorStore
49
51
  } from "./chunk-NEHYSE2Y.js";
@@ -87,6 +89,7 @@ var stateManager = new ProjectStateManager({
87
89
  maxProjects: DISABLE_MULTIPROJECT ? 1 : MAX_PROJECTS
88
90
  });
89
91
  var firstTouchTracker = new FirstTouchTracker();
92
+ var emittedOnceTracker = new EmittedOnceTracker();
90
93
  async function initStore(state) {
91
94
  if (!state.storePromise) {
92
95
  state.storePromise = (async () => {
@@ -110,12 +113,20 @@ async function initParser(state) {
110
113
  async function initGraph(state) {
111
114
  if (!state.graphPromise) {
112
115
  state.graphPromise = (async () => {
113
- const parser = await initParser(state);
114
- const graph = new DependencyGraph();
115
- graph.setParser(parser);
116
- await graph.buildFromDirectory(state.projectRoot);
117
- state.graphInitialized = true;
118
- return graph;
116
+ try {
117
+ const parser = await initParser(state);
118
+ const graph = new DependencyGraph();
119
+ graph.setParser(parser);
120
+ await graph.buildFromDirectory(state.projectRoot);
121
+ state.graphInitialized = true;
122
+ return graph;
123
+ } catch (err) {
124
+ captureError(err, {
125
+ project_id: hashProjectRoot(state.projectRoot),
126
+ phase: "graph_init"
127
+ });
128
+ throw err;
129
+ }
119
130
  })();
120
131
  }
121
132
  return state.graphPromise;
@@ -130,31 +141,60 @@ async function initSkeletonizer(state) {
130
141
  }
131
142
  return state.skeletonizerPromise;
132
143
  }
144
+ function classifyResolutionSource(arg, env) {
145
+ if (arg !== void 0) {
146
+ return /[/\\~]|^[A-Za-z]:/.test(arg) ? "arg-path" : "alias";
147
+ }
148
+ return env ? "env" : "cwd";
149
+ }
133
150
  function buildContext(defaultRoot, noDefaultMode) {
134
151
  const repoRegistry = new RepoRegistry(repoRegistryPath);
135
152
  function resolveOrDefault(arg) {
153
+ let state;
154
+ let source;
136
155
  if (DISABLE_MULTIPROJECT) {
137
156
  if (!defaultRoot) {
138
157
  throw new Error("CTXLOOM_DISABLE_MULTIPROJECT=1 but server has no default root.");
139
158
  }
140
- return stateManager.get(defaultRoot);
141
- }
142
- if (arg === void 0) {
159
+ state = stateManager.get(defaultRoot);
160
+ source = "env";
161
+ } else if (arg === void 0) {
143
162
  if (!defaultRoot) {
144
163
  throw new Error("no_default_project");
145
164
  }
146
- return stateManager.get(defaultRoot);
165
+ state = stateManager.get(defaultRoot);
166
+ source = classifyResolutionSource(void 0, process.env.CTXLOOM_ROOT);
167
+ } else {
168
+ const outcome = resolveProjectRoot({
169
+ arg,
170
+ env: process.env.CTXLOOM_ROOT,
171
+ cwd: process.cwd(),
172
+ registry: repoRegistry
173
+ });
174
+ if (outcome.kind !== "ok") {
175
+ throw new Error(JSON.stringify(outcome));
176
+ }
177
+ state = stateManager.get(outcome.root);
178
+ source = classifyResolutionSource(arg, process.env.CTXLOOM_ROOT);
147
179
  }
148
- const outcome = resolveProjectRoot({
149
- arg,
150
- env: process.env.CTXLOOM_ROOT,
151
- cwd: process.cwd(),
152
- registry: repoRegistry
153
- });
154
- if (outcome.kind !== "ok") {
155
- throw new Error(JSON.stringify(outcome));
180
+ try {
181
+ const projectId = hashProjectRoot(state.projectRoot);
182
+ if (emittedOnceTracker.markAndCheck(`project_resolved:${projectId}`)) {
183
+ track("project_resolved", {
184
+ project_id: projectId,
185
+ source,
186
+ via_alias: source === "alias"
187
+ });
188
+ }
189
+ if (stateManager.size() >= 2 && emittedOnceTracker.markAndCheck("multi_project_active")) {
190
+ track("multi_project_active", {
191
+ active_count: stateManager.size(),
192
+ cap: stateManager.max
193
+ });
194
+ }
195
+ } catch {
156
196
  }
157
- return stateManager.get(outcome.root);
197
+ return state;
158
198
  }
159
199
  const ctx = {
160
200
  projectRoot: defaultRoot ?? "",
@@ -231,6 +271,17 @@ function createServer() {
231
271
  const root = state.projectRoot;
232
272
  const graphFirstTouch = firstTouchTracker.markAndCheck(root, "graph");
233
273
  if (graphFirstTouch) {
274
+ try {
275
+ const graphInst = state.graphPromise ? await state.graphPromise : null;
276
+ track("project_first_touch", {
277
+ project_id: hashProjectRoot(root),
278
+ tier: "graph",
279
+ duration_ms: durationMs,
280
+ nodes: graphInst?.allFiles().length ?? null,
281
+ edges: graphInst?.edgeCount() ?? null
282
+ });
283
+ } catch {
284
+ }
234
285
  const wrapped = wrapWithIndexingEnvelope(
235
286
  { firstTouch: true, projectRoot: root, tier: "graph", durationMs },
236
287
  text
@@ -240,6 +291,14 @@ function createServer() {
240
291
  if (state.vectorsInitialized) {
241
292
  const vectorsFirstTouch = firstTouchTracker.markAndCheck(root, "vectors");
242
293
  if (vectorsFirstTouch) {
294
+ try {
295
+ track("project_first_touch", {
296
+ project_id: hashProjectRoot(root),
297
+ tier: "vectors",
298
+ duration_ms: durationMs
299
+ });
300
+ } catch {
301
+ }
243
302
  const wrapped = wrapWithIndexingEnvelope(
244
303
  { firstTouch: true, projectRoot: root, tier: "vectors", durationMs },
245
304
  text
@@ -250,9 +309,31 @@ function createServer() {
250
309
  } catch {
251
310
  }
252
311
  }
312
+ if (Math.random() < 0.25) {
313
+ try {
314
+ const projectRootArg2 = args2?.project_root;
315
+ if (!ctx.noDefaultMode || projectRootArg2 !== void 0) {
316
+ const sampleState = resolveOrDefault(projectRootArg2);
317
+ track("tool_dispatched", {
318
+ project_id: hashProjectRoot(sampleState.projectRoot),
319
+ tool: name,
320
+ duration_ms: durationMs
321
+ });
322
+ }
323
+ } catch {
324
+ }
325
+ }
253
326
  return { content: [{ type: "text", text }] };
254
327
  } catch (err) {
255
328
  if (err instanceof Error && err.message === "no_default_project") {
329
+ const hadArg10 = args2?.project_root !== void 0;
330
+ try {
331
+ track("project_resolution_failed", {
332
+ error_code: "no_default_project",
333
+ had_arg: hadArg10
334
+ });
335
+ } catch {
336
+ }
256
337
  const xml = noDefaultProjectError({
257
338
  attemptedRoot: PROJECT_ROOT,
258
339
  resolutionChain: "CTXLOOM_ROOT env var\u2192unset, fallback_cwd\u2192" + PROJECT_ROOT,
@@ -264,6 +345,13 @@ function createServer() {
264
345
  try {
265
346
  const parsed2 = JSON.parse(err.message);
266
347
  if (parsed2.kind === "alias_not_found") {
348
+ try {
349
+ track("project_resolution_failed", {
350
+ error_code: "alias_not_found",
351
+ had_arg: true
352
+ });
353
+ } catch {
354
+ }
267
355
  const xml = aliasNotFoundError({
268
356
  alias: String(parsed2.alias ?? ""),
269
357
  didYouMean: Array.isArray(parsed2.didYouMean) ? parsed2.didYouMean : []
@@ -271,6 +359,13 @@ function createServer() {
271
359
  return { content: [{ type: "text", text: xml }], isError: true };
272
360
  }
273
361
  if (parsed2.kind === "project_root_not_found") {
362
+ try {
363
+ track("project_resolution_failed", {
364
+ error_code: "project_root_not_found",
365
+ had_arg: true
366
+ });
367
+ } catch {
368
+ }
274
369
  const xml = projectRootNotFoundError({
275
370
  path: String(parsed2.attemptedPath ?? ""),
276
371
  resolutionChain: String(parsed2.resolutionChain ?? "")
@@ -280,6 +375,20 @@ function createServer() {
280
375
  } catch {
281
376
  }
282
377
  }
378
+ try {
379
+ const projectRootArg13 = args2?.project_root;
380
+ let projectIdForCtx;
381
+ try {
382
+ const fallbackState = resolveOrDefault(projectRootArg13);
383
+ projectIdForCtx = hashProjectRoot(fallbackState.projectRoot);
384
+ } catch {
385
+ }
386
+ captureError(err, {
387
+ tool: name,
388
+ ...projectIdForCtx ? { project_id: projectIdForCtx } : {}
389
+ });
390
+ } catch {
391
+ }
283
392
  return {
284
393
  content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
285
394
  isError: true
@@ -300,6 +409,7 @@ async function startServer(opts = {}) {
300
409
  logger.warn(
301
410
  "[DEPRECATED] CTXLOOM_DISABLE_MULTIPROJECT=1 is set \u2014 multi-project support is disabled. maxProjects is capped at 1 and project_root arguments are ignored. This kill switch will be removed in a future release."
302
411
  );
412
+ track("kill_switch_active", { cap: 1 });
303
413
  }
304
414
  try {
305
415
  const { execSync: execSync2 } = await import("child_process");
@@ -782,7 +892,7 @@ try {
782
892
  } catch {
783
893
  }
784
894
  var args = process.argv.slice(2);
785
- var ctxloomVersion = "1.1.0".length > 0 ? "1.1.0" : "dev";
895
+ var ctxloomVersion = "1.1.2".length > 0 ? "1.1.2" : "dev";
786
896
  if (args.includes("--version") || args.includes("-v")) {
787
897
  process.stdout.write(`ctxloom ${ctxloomVersion}
788
898
  `);
@@ -855,7 +965,7 @@ async function checkLicense() {
855
965
  if (command !== void 0 && LICENSE_GATE_BYPASS_COMMANDS.has(command)) return;
856
966
  const ciKey = process.env["CTXLOOM_LICENSE_KEY"];
857
967
  if (ciKey) {
858
- const { ApiClient } = await import("./src-XX7SUUMW.js");
968
+ const { ApiClient } = await import("./src-5MVUPLMZ.js");
859
969
  const client = new ApiClient(process.env["CTXLOOM_API_BASE"]);
860
970
  try {
861
971
  const result = await client.validate(ciKey, "ci-ephemeral");
@@ -876,7 +986,7 @@ ctxloom license is ${result.status}.
876
986
  }
877
987
  const active = await isActive();
878
988
  if (!active) {
879
- track("license_gate_hit", os.hostname());
989
+ track("license_gate_hit");
880
990
  process.stderr.write(errorBlock("ctxloom requires an active license.", [
881
991
  `${style.bold("ctxloom trial")} ${style.dim("\u2014 start a 7-day free trial")}`,
882
992
  `${style.bold("ctxloom activate <KEY>")} ${style.dim("\u2014 activate a purchased key")}`,
@@ -925,7 +1035,7 @@ async function runTrial() {
925
1035
  process.stdout.write(` ${style.dim(`Your license key will arrive at ${email} after checkout.`)}
926
1036
  `);
927
1037
  process.stdout.write(nextStep("Activate on this machine", "ctxloom activate <KEY>"));
928
- track("trial_started", os.hostname(), { email });
1038
+ track("trial_started", { email });
929
1039
  } catch (err) {
930
1040
  if (err instanceof FingerprintAlreadyUsedError) {
931
1041
  process.stdout.write(errorBlock("A trial has already been used on this machine.", [
@@ -970,7 +1080,7 @@ async function runActivate(key) {
970
1080
  ["Machine", `${os.hostname()} (${os.platform()}-${os.arch()})`]
971
1081
  ]));
972
1082
  process.stdout.write(nextStep("Configure your AI tools", "ctxloom setup"));
973
- track("license_activated", os.hostname(), { tier: license.tier });
1083
+ track("license_activated", { tier: license.tier });
974
1084
  } catch (err) {
975
1085
  if (err instanceof SeatLimitError) {
976
1086
  process.stdout.write(errorBlock("Seat limit reached.", [
@@ -1007,7 +1117,7 @@ async function runDeactivate() {
1007
1117
  process.stdout.write(` ${success("Deactivated")}
1008
1118
  `);
1009
1119
  process.stdout.write(nextStep("Activate on another machine", "ctxloom activate <KEY>"));
1010
- track("license_deactivated", os.hostname());
1120
+ track("license_deactivated");
1011
1121
  } catch (err) {
1012
1122
  if (err instanceof NetworkError) {
1013
1123
  process.stderr.write(errorBlock("Deactivation failed \u2014 network error.", [
@@ -1208,7 +1318,7 @@ async function main() {
1208
1318
  process.exit(1);
1209
1319
  }
1210
1320
  if (alias !== void 0) {
1211
- const { validateAlias } = await import("./src-XX7SUUMW.js");
1321
+ const { validateAlias } = await import("./src-5MVUPLMZ.js");
1212
1322
  const v = validateAlias(alias);
1213
1323
  if (!v.ok) {
1214
1324
  console.error(`[ctxloom] Invalid alias: ${v.reason}`);
@@ -1224,6 +1334,12 @@ async function main() {
1224
1334
  console.error(`[ctxloom] ${err instanceof Error ? err.message : String(err)}`);
1225
1335
  process.exit(1);
1226
1336
  }
1337
+ if (alias !== void 0) {
1338
+ track("alias_registered", {
1339
+ alias_length: alias.length,
1340
+ was_collision: false
1341
+ });
1342
+ }
1227
1343
  console.log(`[ctxloom] Registered repo: ${absPath}${alias ? ` (alias: ${alias})` : ""}`);
1228
1344
  console.log(`[ctxloom] LanceDB path: ${dbPath}`);
1229
1345
  console.log(`[ctxloom] Registry: ${registryPath}`);
@@ -1446,7 +1562,7 @@ Suggested reviewers for ${files.length} file(s):`);
1446
1562
  process.stderr.write("[ctxloom] --limit must be a non-negative integer (0 for unlimited)\n");
1447
1563
  process.exit(2);
1448
1564
  }
1449
- const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-XX7SUUMW.js");
1565
+ const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-5MVUPLMZ.js");
1450
1566
  let config;
1451
1567
  try {
1452
1568
  config = await loadRulesConfig(root);
@@ -1470,7 +1586,7 @@ Suggested reviewers for ${files.length} file(s):`);
1470
1586
  }
1471
1587
  let graph;
1472
1588
  if (useSnapshot) {
1473
- const { DependencyGraph: DG } = await import("./src-XX7SUUMW.js");
1589
+ const { DependencyGraph: DG } = await import("./src-5MVUPLMZ.js");
1474
1590
  graph = new DG();
1475
1591
  const loaded = await graph.loadSnapshotOnly(root);
1476
1592
  if (!loaded) {
@@ -1479,7 +1595,7 @@ Suggested reviewers for ${files.length} file(s):`);
1479
1595
  }
1480
1596
  } else {
1481
1597
  process.stderr.write("[ctxloom] Building dependency graph...\n");
1482
- const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-XX7SUUMW.js");
1598
+ const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-5MVUPLMZ.js");
1483
1599
  let parser;
1484
1600
  try {
1485
1601
  parser = new ASTParser2();
@@ -10,6 +10,7 @@ import {
10
10
  DEFAULT_REVIEW_CONFIG,
11
11
  DependencyGraph,
12
12
  EmailAlreadyUsedError,
13
+ EmittedOnceTracker,
13
14
  FileWatcher,
14
15
  Fingerprint,
15
16
  FingerprintAlreadyUsedError,
@@ -66,6 +67,8 @@ import {
66
67
  generateCODEOWNERS,
67
68
  getImpactRadius,
68
69
  getLicenseInfo,
70
+ getOrCreateDistinctId,
71
+ hashProjectRoot,
69
72
  isActive,
70
73
  isSiloed,
71
74
  listNamedSnapshots,
@@ -73,6 +76,7 @@ import {
73
76
  loadReviewConfig,
74
77
  loadRulesConfig,
75
78
  loadTrendSeries,
79
+ markAliasSent,
76
80
  maybePrintExpiryWarning,
77
81
  mergeIntoFile,
78
82
  noDefaultProjectError,
@@ -95,7 +99,7 @@ import {
95
99
  validateDefaultRoot,
96
100
  wrapWithIndexingEnvelope,
97
101
  writeCODEOWNERS
98
- } from "./chunk-FFSSKDV4.js";
102
+ } from "./chunk-RGO3M3IR.js";
99
103
  import {
100
104
  VectorStore
101
105
  } from "./chunk-NEHYSE2Y.js";
@@ -121,6 +125,7 @@ export {
121
125
  DependencyGraph,
122
126
  EMBEDDING_DIMENSION,
123
127
  EmailAlreadyUsedError,
128
+ EmittedOnceTracker,
124
129
  FileWatcher,
125
130
  Fingerprint,
126
131
  FingerprintAlreadyUsedError,
@@ -180,6 +185,8 @@ export {
180
185
  generateEmbedding,
181
186
  getImpactRadius,
182
187
  getLicenseInfo,
188
+ getOrCreateDistinctId,
189
+ hashProjectRoot,
183
190
  indexDirectory,
184
191
  isActive,
185
192
  isSiloed,
@@ -189,6 +196,7 @@ export {
189
196
  loadRulesConfig,
190
197
  loadTrendSeries,
191
198
  logger,
199
+ markAliasSent,
192
200
  maybePrintExpiryWarning,
193
201
  mergeIntoFile,
194
202
  noDefaultProjectError,
@@ -212,4 +220,4 @@ export {
212
220
  wrapWithIndexingEnvelope,
213
221
  writeCODEOWNERS
214
222
  };
215
- //# sourceMappingURL=src-XX7SUUMW.js.map
223
+ //# sourceMappingURL=src-5MVUPLMZ.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ctxloom-pro",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "ctxloom — The Universal Code Context Engine. A local-first MCP server providing intelligent code context via hybrid Vector + AST + Graph search with Skeletonization (92% token reduction).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",