ctxloom-pro 1.1.0 → 1.1.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.
@@ -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 path41 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 path35 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 path42 of paths) {
3137
+ this.nodeCounts.set(path42, (this.nodeCounts.get(path42) ?? 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 [path42, raw] of this.nodes) {
3285
+ nodes[path42] = {
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 [path42, raw] of Object.entries(s.nodes)) {
3301
+ idx.nodes.set(path42, {
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(path42) {
3315
+ const existing = this.nodes.get(path42);
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(path42, 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 [path42, 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[path42] = { 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 [path42, 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(path42, { 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(path42) {
3432
+ const existing = this.nodes.get(path42);
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(path42, 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: path42, errorMaps, issueData } = params;
4674
+ const fullPath = [...path42, ...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, path42, key) {
4791
4791
  this._cachedPath = [];
4792
4792
  this.parent = parent;
4793
4793
  this.data = value;
4794
- this._path = path41;
4794
+ this._path = path42;
4795
4795
  this._key = key;
4796
4796
  }
4797
4797
  get path() {
@@ -11270,22 +11270,28 @@ import os3 from "os";
11270
11270
 
11271
11271
  // ../../packages/core/src/license/telemetry.ts
11272
11272
  var TELEMETRY_DISABLED = process.env["CTXLOOM_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1";
11273
+ var CTXLOOM_VERSION = typeof __CTXLOOM_VERSION__ === "string" && __CTXLOOM_VERSION__.length > 0 ? __CTXLOOM_VERSION__ : "dev";
11273
11274
  var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (typeof __TELEMETRY_POSTHOG_KEY__ === "string" ? __TELEMETRY_POSTHOG_KEY__ : "");
11274
11275
  var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (typeof __TELEMETRY_SENTRY_DSN__ === "string" ? __TELEMETRY_SENTRY_DSN__ : "");
11275
11276
 
11276
11277
  // ../../packages/core/src/server/ProjectState.ts
11278
+ import path33 from "path";
11279
+
11280
+ // ../../packages/core/src/server/projectId.ts
11281
+ import crypto5 from "crypto";
11277
11282
  import path32 from "path";
11278
11283
 
11279
11284
  // ../../packages/core/src/server/ProjectStateManager.ts
11280
11285
  init_logger();
11286
+ import os4 from "os";
11281
11287
 
11282
11288
  // ../../packages/core/src/server/resolveProjectRoot.ts
11283
11289
  import fs29 from "fs";
11284
- import path33 from "path";
11290
+ import path34 from "path";
11285
11291
 
11286
11292
  // server/loader.ts
11287
11293
  async function loadContext(root) {
11288
- const absRoot = path34.resolve(root);
11294
+ const absRoot = path35.resolve(root);
11289
11295
  const overlay = new GitOverlayStore(absRoot);
11290
11296
  const gitEnabled = await overlay.loadSnapshot();
11291
11297
  const graph = new DependencyGraph();
@@ -11539,20 +11545,20 @@ function buildOwnershipRouter(ctx) {
11539
11545
  // server/routes/file.ts
11540
11546
  import { Router as Router7 } from "express";
11541
11547
  import fs30 from "fs/promises";
11542
- import path35 from "path";
11548
+ import path36 from "path";
11543
11549
  function buildFileRouter(ctx) {
11544
11550
  const router = Router7();
11545
11551
  router.get("/", async (req, res) => {
11546
11552
  const rel = req.query.path;
11547
11553
  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;
11554
+ const abs = path36.resolve(ctx.root, rel);
11555
+ const rootBoundary = ctx.root.endsWith(path36.sep) ? ctx.root : ctx.root + path36.sep;
11550
11556
  if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
11551
11557
  return res.status(403).json({ error: "forbidden" });
11552
11558
  }
11553
11559
  try {
11554
11560
  const content = await fs30.readFile(abs, "utf-8");
11555
- const ext = path35.extname(abs).slice(1);
11561
+ const ext = path36.extname(abs).slice(1);
11556
11562
  res.json({ content, lines: content.split("\n").length, ext });
11557
11563
  } catch {
11558
11564
  res.status(404).json({ error: "not found" });
@@ -11564,7 +11570,7 @@ function buildFileRouter(ctx) {
11564
11570
  // server/routes/open.ts
11565
11571
  import { Router as Router8 } from "express";
11566
11572
  import { execFile as execFile2 } from "child_process";
11567
- import path36 from "path";
11573
+ import path37 from "path";
11568
11574
  function tryOpen(bin, abs) {
11569
11575
  return new Promise((resolve) => {
11570
11576
  execFile2(bin, [abs], { timeout: 5e3 }, (err) => resolve(!err));
@@ -11575,8 +11581,8 @@ function buildOpenRouter(ctx) {
11575
11581
  router.post("/", async (req, res) => {
11576
11582
  const rel = req.body?.path;
11577
11583
  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;
11584
+ const abs = path37.resolve(ctx.root, rel);
11585
+ const rootBoundary = ctx.root.endsWith(path37.sep) ? ctx.root : ctx.root + path37.sep;
11580
11586
  if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
11581
11587
  return res.status(403).json({ error: "forbidden" });
11582
11588
  }
@@ -11588,7 +11594,7 @@ function buildOpenRouter(ctx) {
11588
11594
 
11589
11595
  // server/routes/tokens.ts
11590
11596
  import { Router as Router9 } from "express";
11591
- import path37 from "path";
11597
+ import path38 from "path";
11592
11598
  import fs31 from "fs";
11593
11599
  var CHARS_PER_TOKEN = 4;
11594
11600
  var cache = null;
@@ -11604,7 +11610,7 @@ function buildTokensRouter(ctx) {
11604
11610
  let fullChars = 0;
11605
11611
  let skeletonChars = 0;
11606
11612
  for (const file of files) {
11607
- const absPath = path37.join(ctx.root, file);
11613
+ const absPath = path38.join(ctx.root, file);
11608
11614
  try {
11609
11615
  const content = fs31.readFileSync(absPath, "utf-8");
11610
11616
  fullChars += content.length;
@@ -11707,18 +11713,18 @@ function buildFileTrendsRouter(ctx) {
11707
11713
 
11708
11714
  // server/routes/projects.ts
11709
11715
  import { Router as Router12 } from "express";
11710
- import path39 from "path";
11716
+ import path40 from "path";
11711
11717
 
11712
11718
  // server/projects.ts
11713
11719
  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");
11720
+ import os5 from "os";
11721
+ import path39 from "path";
11722
+ import crypto6 from "crypto";
11723
+ var HOME = os5.homedir();
11724
+ var REGISTRY_PATH = path39.join(HOME, ".ctxloom", "repos.json");
11719
11725
  function slugFor(root) {
11720
- const abs = path38.resolve(root);
11721
- return crypto5.createHash("sha1").update(abs).digest("hex").slice(0, 12);
11726
+ const abs = path39.resolve(root);
11727
+ return crypto6.createHash("sha1").update(abs).digest("hex").slice(0, 12);
11722
11728
  }
11723
11729
  function readRegistry() {
11724
11730
  if (!existsSync2(REGISTRY_PATH)) return [];
@@ -11732,27 +11738,27 @@ function readRegistry() {
11732
11738
  }
11733
11739
  }
11734
11740
  function listProjects(defaultRoot) {
11735
- const absDefault = path38.resolve(defaultRoot);
11741
+ const absDefault = path39.resolve(defaultRoot);
11736
11742
  const out = [
11737
11743
  {
11738
11744
  slug: slugFor(absDefault),
11739
- name: path38.basename(absDefault) || absDefault,
11745
+ name: path39.basename(absDefault) || absDefault,
11740
11746
  root: absDefault,
11741
11747
  isDefault: true,
11742
- hasSnapshot: existsSync2(path38.join(absDefault, ".ctxloom"))
11748
+ hasSnapshot: existsSync2(path39.join(absDefault, ".ctxloom"))
11743
11749
  }
11744
11750
  ];
11745
11751
  const seen = /* @__PURE__ */ new Set([absDefault]);
11746
11752
  for (const entry of readRegistry()) {
11747
- const abs = path38.resolve(entry.root);
11753
+ const abs = path39.resolve(entry.root);
11748
11754
  if (seen.has(abs)) continue;
11749
11755
  seen.add(abs);
11750
11756
  const item = {
11751
11757
  slug: slugFor(abs),
11752
- name: entry.name ?? (path38.basename(abs) || abs),
11758
+ name: entry.name ?? (path39.basename(abs) || abs),
11753
11759
  root: abs,
11754
11760
  isDefault: false,
11755
- hasSnapshot: existsSync2(path38.join(abs, ".ctxloom"))
11761
+ hasSnapshot: existsSync2(path39.join(abs, ".ctxloom"))
11756
11762
  };
11757
11763
  if (entry.alias !== void 0) item.alias = entry.alias;
11758
11764
  out.push(item);
@@ -11805,7 +11811,7 @@ function buildProjectsRouter(deps) {
11805
11811
  } catch (err) {
11806
11812
  const detail = err instanceof Error ? err.message : String(err);
11807
11813
  res.status(500).json({
11808
- error: `failed to switch to ${path39.basename(target.root)}: ${detail}`
11814
+ error: `failed to switch to ${path40.basename(target.root)}: ${detail}`
11809
11815
  });
11810
11816
  }
11811
11817
  });
@@ -11813,7 +11819,7 @@ function buildProjectsRouter(deps) {
11813
11819
  }
11814
11820
 
11815
11821
  // server/index.ts
11816
- var __dirname2 = path40.dirname(fileURLToPath2(import.meta.url));
11822
+ var __dirname2 = path41.dirname(fileURLToPath2(import.meta.url));
11817
11823
  async function startDashboard(options) {
11818
11824
  const { root, port, open } = options;
11819
11825
  console.log(`ctxloom dashboard \u2014 loading context from ${root}...`);
@@ -11872,7 +11878,7 @@ async function startDashboard(options) {
11872
11878
  }
11873
11879
  activeWatcher = null;
11874
11880
  }
11875
- const snapshotDir = path40.join(targetRoot, ".ctxloom");
11881
+ const snapshotDir = path41.join(targetRoot, ".ctxloom");
11876
11882
  try {
11877
11883
  activeWatcher = fs32.watch(snapshotDir, (_event, filename) => {
11878
11884
  if (!filename || !filename.includes("snapshot")) return;
@@ -11902,12 +11908,12 @@ async function startDashboard(options) {
11902
11908
  attachSnapshotWatcher(newRoot);
11903
11909
  }
11904
11910
  }));
11905
- const clientDist = path40.join(__dirname2, "../dashboard/client");
11906
- const clientDistExists = fs32.existsSync(path40.join(clientDist, "index.html"));
11911
+ const clientDist = path41.join(__dirname2, "../dashboard/client");
11912
+ const clientDistExists = fs32.existsSync(path41.join(clientDist, "index.html"));
11907
11913
  if (clientDistExists) {
11908
11914
  app.use(express.static(clientDist, { dotfiles: "allow" }));
11909
11915
  app.get(/.*/, (_req, res) => {
11910
- res.sendFile(path40.join(clientDist, "index.html"), { dotfiles: "allow" });
11916
+ res.sendFile(path41.join(clientDist, "index.html"), { dotfiles: "allow" });
11911
11917
  });
11912
11918
  } else {
11913
11919
  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 path33 of paths) {
3410
+ this.nodeCounts.set(path33, (this.nodeCounts.get(path33) ?? 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 [path33, raw] of this.nodes) {
3558
+ nodes[path33] = {
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 [path33, raw] of Object.entries(s.nodes)) {
3574
+ idx.nodes.set(path33, {
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(path33) {
3588
+ const existing = this.nodes.get(path33);
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(path33, 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 [path33, 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[path33] = { 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 [path33, 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(path33, { 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(path33) {
3705
+ const existing = this.nodes.get(path33);
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(path33, fresh);
3709
3709
  return fresh;
3710
3710
  }
3711
3711
  };
@@ -8796,6 +8796,7 @@ async function startTrial(email, opts = {}) {
8796
8796
 
8797
8797
  // packages/core/src/license/telemetry.ts
8798
8798
  var TELEMETRY_DISABLED = process.env["CTXLOOM_NO_TELEMETRY"] === "1" || process.env["DO_NOT_TRACK"] === "1";
8799
+ var CTXLOOM_VERSION = "1.1.1".length > 0 ? "1.1.1" : "dev";
8799
8800
  var POSTHOG_HOST = "https://eu.i.posthog.com";
8800
8801
  var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
8801
8802
  var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
@@ -8818,6 +8819,7 @@ async function sendPostHog(event, distinctId, props) {
8818
8819
  event,
8819
8820
  properties: {
8820
8821
  $lib: "ctxloom-cli",
8822
+ release: CTXLOOM_VERSION,
8821
8823
  ...props
8822
8824
  }
8823
8825
  }),
@@ -8853,7 +8855,7 @@ async function sendSentry(err, context) {
8853
8855
  ]
8854
8856
  },
8855
8857
  extra: context,
8856
- tags: { runtime: "node", component: "cli-license" }
8858
+ tags: { runtime: "node", component: "cli-license", release: CTXLOOM_VERSION }
8857
8859
  }),
8858
8860
  signal: AbortSignal.timeout(4e3)
8859
8861
  });
@@ -8869,20 +8871,33 @@ function parseDsn(dsn) {
8869
8871
  return null;
8870
8872
  }
8871
8873
  }
8874
+ function scrubPath(filename) {
8875
+ return filename.replace(/^\/Users\/[^/]+\//, "/Users/~/").replace(/^\/home\/[^/]+\//, "/home/~/").replace(/^([A-Z]:\\\\Users\\\\)[^\\]+\\\\/, "$1~\\\\").replace(/^([A-Z]:\\Users\\)[^\\]+\\/, "$1~\\");
8876
+ }
8872
8877
  function parseStack(stack) {
8873
8878
  return stack.split("\n").slice(1).map((line) => {
8874
8879
  const m = line.trim().match(/at (.+?) \((.+?):(\d+):\d+\)/);
8875
8880
  if (!m) return null;
8876
- return { function: m[1] ?? "", filename: m[2] ?? "", lineno: Number(m[3]) };
8881
+ return { function: m[1] ?? "", filename: scrubPath(m[2] ?? ""), lineno: Number(m[3]) };
8877
8882
  }).filter((f) => f !== null).slice(0, 20);
8878
8883
  }
8879
8884
 
8880
8885
  // packages/core/src/server/ProjectState.ts
8886
+ import path31 from "path";
8887
+
8888
+ // packages/core/src/server/projectId.ts
8889
+ import crypto5 from "crypto";
8881
8890
  import path30 from "path";
8891
+ function hashProjectRoot(absPath) {
8892
+ const canonical = path30.resolve(absPath);
8893
+ return crypto5.createHash("sha256").update(canonical).digest("hex").slice(0, 16);
8894
+ }
8895
+
8896
+ // packages/core/src/server/ProjectState.ts
8882
8897
  function createProjectState(projectRoot, opts = {}) {
8883
8898
  return {
8884
8899
  projectRoot,
8885
- dbPath: path30.join(projectRoot, ".ctxloom", "vectors.lancedb"),
8900
+ dbPath: path31.join(projectRoot, ".ctxloom", "vectors.lancedb"),
8886
8901
  pinned: opts.pinned ?? false,
8887
8902
  lastTouchedAt: Date.now(),
8888
8903
  vectorsInitialized: false,
@@ -8900,8 +8915,16 @@ function createProjectState(projectRoot, opts = {}) {
8900
8915
  async function ensureVectorsInitialized(state) {
8901
8916
  if (state.vectorsInitialized) return;
8902
8917
  if (!state.storePromise) return;
8903
- await state.storePromise;
8904
- state.vectorsInitialized = true;
8918
+ try {
8919
+ await state.storePromise;
8920
+ state.vectorsInitialized = true;
8921
+ } catch (err) {
8922
+ captureError(err, {
8923
+ project_id: hashProjectRoot(state.projectRoot),
8924
+ phase: "vector_init"
8925
+ });
8926
+ throw err;
8927
+ }
8905
8928
  }
8906
8929
  async function disposeProjectState(state) {
8907
8930
  try {
@@ -8926,6 +8949,7 @@ async function disposeProjectState(state) {
8926
8949
  }
8927
8950
 
8928
8951
  // packages/core/src/server/ProjectStateManager.ts
8952
+ import os4 from "os";
8929
8953
  var DEFAULT_MAX_PROJECTS = 5;
8930
8954
  var ProjectStateManager = class {
8931
8955
  map = /* @__PURE__ */ new Map();
@@ -8995,12 +9019,23 @@ var ProjectStateManager = class {
8995
9019
  );
8996
9020
  }
8997
9021
  this.map.delete(victim.projectRoot);
9022
+ const pinnedCount = Array.from(this.map.values()).filter((s) => s.pinned).length;
9023
+ track("project_evicted", os4.hostname(), {
9024
+ project_id: hashProjectRoot(victim.projectRoot),
9025
+ pinned_count: pinnedCount,
9026
+ cap: this.maxProjects
9027
+ });
8998
9028
  void this.onDispose(victim).then(() => {
8999
9029
  logger.info("project.evicted", {
9000
9030
  root: victim.projectRoot,
9001
9031
  reason: "lru_cap_reached",
9002
9032
  ttl_seconds: Math.round((Date.now() - victim.lastTouchedAt) / 1e3)
9003
9033
  });
9034
+ }).catch((err) => {
9035
+ captureError(err, {
9036
+ project_id: hashProjectRoot(victim.projectRoot),
9037
+ phase: "dispose"
9038
+ });
9004
9039
  });
9005
9040
  }
9006
9041
  /**
@@ -9016,7 +9051,7 @@ var ProjectStateManager = class {
9016
9051
 
9017
9052
  // packages/core/src/server/resolveProjectRoot.ts
9018
9053
  import fs27 from "fs";
9019
- import path31 from "path";
9054
+ import path32 from "path";
9020
9055
  var PATH_SEPARATOR_PATTERN = /[/\\~]|^[A-Za-z]:/;
9021
9056
  function looksLikePath(value) {
9022
9057
  return PATH_SEPARATOR_PATTERN.test(value);
@@ -9041,9 +9076,9 @@ function resolvePathSafely(p, cwd) {
9041
9076
  let expanded = p;
9042
9077
  if (p === "~" || p.startsWith("~/")) {
9043
9078
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
9044
- expanded = p === "~" ? home : path31.join(home, p.slice(2));
9079
+ expanded = p === "~" ? home : path32.join(home, p.slice(2));
9045
9080
  }
9046
- return path31.isAbsolute(expanded) ? path31.resolve(expanded) : path31.resolve(cwd, expanded);
9081
+ return path32.isAbsolute(expanded) ? path32.resolve(expanded) : path32.resolve(cwd, expanded);
9047
9082
  }
9048
9083
  function realpathOrSame(p) {
9049
9084
  try {
@@ -9115,7 +9150,7 @@ function validateDefaultRoot(candidate) {
9115
9150
  } catch {
9116
9151
  return false;
9117
9152
  }
9118
- return PROJECT_MARKERS.some((m) => fs27.existsSync(path31.join(candidate, m)));
9153
+ return PROJECT_MARKERS.some((m) => fs27.existsSync(path32.join(candidate, m)));
9119
9154
  }
9120
9155
 
9121
9156
  // packages/core/src/server/structuredErrors.ts
@@ -9171,6 +9206,21 @@ var FirstTouchTracker = class {
9171
9206
  }
9172
9207
  };
9173
9208
 
9209
+ // packages/core/src/server/EmittedOnceTracker.ts
9210
+ var EmittedOnceTracker = class {
9211
+ seen = /* @__PURE__ */ new Set();
9212
+ /** Returns true the first time `key` is seen, false thereafter. */
9213
+ markAndCheck(key) {
9214
+ if (this.seen.has(key)) return false;
9215
+ this.seen.add(key);
9216
+ return true;
9217
+ }
9218
+ /** Clear all keys. Used by tests. */
9219
+ reset() {
9220
+ this.seen.clear();
9221
+ }
9222
+ };
9223
+
9174
9224
  export {
9175
9225
  GRAMMAR_MANIFEST,
9176
9226
  findGrammar,
@@ -9255,6 +9305,7 @@ export {
9255
9305
  startTrial,
9256
9306
  track,
9257
9307
  captureError,
9308
+ hashProjectRoot,
9258
9309
  createProjectState,
9259
9310
  ensureVectorsInitialized,
9260
9311
  disposeProjectState,
@@ -9267,6 +9318,7 @@ export {
9267
9318
  aliasNotFoundError,
9268
9319
  noParseableSourcesWarning,
9269
9320
  wrapWithIndexingEnvelope,
9270
- FirstTouchTracker
9321
+ FirstTouchTracker,
9322
+ EmittedOnceTracker
9271
9323
  };
9272
- //# sourceMappingURL=chunk-FFSSKDV4.js.map
9324
+ //# sourceMappingURL=chunk-CXKKREER.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-CXKKREER.js";
47
49
  import {
48
50
  VectorStore
49
51
  } from "./chunk-NEHYSE2Y.js";
@@ -61,6 +63,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
61
63
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
62
64
  import path from "path";
63
65
  import fs from "fs";
66
+ import os from "os";
64
67
  var PROJECT_ROOT = (() => {
65
68
  if (process.env.CTXLOOM_ROOT) return process.env.CTXLOOM_ROOT;
66
69
  const cwd = process.cwd();
@@ -87,6 +90,7 @@ var stateManager = new ProjectStateManager({
87
90
  maxProjects: DISABLE_MULTIPROJECT ? 1 : MAX_PROJECTS
88
91
  });
89
92
  var firstTouchTracker = new FirstTouchTracker();
93
+ var emittedOnceTracker = new EmittedOnceTracker();
90
94
  async function initStore(state) {
91
95
  if (!state.storePromise) {
92
96
  state.storePromise = (async () => {
@@ -110,12 +114,20 @@ async function initParser(state) {
110
114
  async function initGraph(state) {
111
115
  if (!state.graphPromise) {
112
116
  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;
117
+ try {
118
+ const parser = await initParser(state);
119
+ const graph = new DependencyGraph();
120
+ graph.setParser(parser);
121
+ await graph.buildFromDirectory(state.projectRoot);
122
+ state.graphInitialized = true;
123
+ return graph;
124
+ } catch (err) {
125
+ captureError(err, {
126
+ project_id: hashProjectRoot(state.projectRoot),
127
+ phase: "graph_init"
128
+ });
129
+ throw err;
130
+ }
119
131
  })();
120
132
  }
121
133
  return state.graphPromise;
@@ -130,31 +142,60 @@ async function initSkeletonizer(state) {
130
142
  }
131
143
  return state.skeletonizerPromise;
132
144
  }
145
+ function classifyResolutionSource(arg, env) {
146
+ if (arg !== void 0) {
147
+ return /[/\\~]|^[A-Za-z]:/.test(arg) ? "arg-path" : "alias";
148
+ }
149
+ return env ? "env" : "cwd";
150
+ }
133
151
  function buildContext(defaultRoot, noDefaultMode) {
134
152
  const repoRegistry = new RepoRegistry(repoRegistryPath);
135
153
  function resolveOrDefault(arg) {
154
+ let state;
155
+ let source;
136
156
  if (DISABLE_MULTIPROJECT) {
137
157
  if (!defaultRoot) {
138
158
  throw new Error("CTXLOOM_DISABLE_MULTIPROJECT=1 but server has no default root.");
139
159
  }
140
- return stateManager.get(defaultRoot);
141
- }
142
- if (arg === void 0) {
160
+ state = stateManager.get(defaultRoot);
161
+ source = "env";
162
+ } else if (arg === void 0) {
143
163
  if (!defaultRoot) {
144
164
  throw new Error("no_default_project");
145
165
  }
146
- return stateManager.get(defaultRoot);
166
+ state = stateManager.get(defaultRoot);
167
+ source = classifyResolutionSource(void 0, process.env.CTXLOOM_ROOT);
168
+ } else {
169
+ const outcome = resolveProjectRoot({
170
+ arg,
171
+ env: process.env.CTXLOOM_ROOT,
172
+ cwd: process.cwd(),
173
+ registry: repoRegistry
174
+ });
175
+ if (outcome.kind !== "ok") {
176
+ throw new Error(JSON.stringify(outcome));
177
+ }
178
+ state = stateManager.get(outcome.root);
179
+ source = classifyResolutionSource(arg, process.env.CTXLOOM_ROOT);
147
180
  }
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));
181
+ try {
182
+ const projectId = hashProjectRoot(state.projectRoot);
183
+ if (emittedOnceTracker.markAndCheck(`project_resolved:${projectId}`)) {
184
+ track("project_resolved", os.hostname(), {
185
+ project_id: projectId,
186
+ source,
187
+ via_alias: source === "alias"
188
+ });
189
+ }
190
+ if (stateManager.size() >= 2 && emittedOnceTracker.markAndCheck("multi_project_active")) {
191
+ track("multi_project_active", os.hostname(), {
192
+ active_count: stateManager.size(),
193
+ cap: stateManager.max
194
+ });
195
+ }
196
+ } catch {
156
197
  }
157
- return stateManager.get(outcome.root);
198
+ return state;
158
199
  }
159
200
  const ctx = {
160
201
  projectRoot: defaultRoot ?? "",
@@ -231,6 +272,17 @@ function createServer() {
231
272
  const root = state.projectRoot;
232
273
  const graphFirstTouch = firstTouchTracker.markAndCheck(root, "graph");
233
274
  if (graphFirstTouch) {
275
+ try {
276
+ const graphInst = state.graphPromise ? await state.graphPromise : null;
277
+ track("project_first_touch", os.hostname(), {
278
+ project_id: hashProjectRoot(root),
279
+ tier: "graph",
280
+ duration_ms: durationMs,
281
+ nodes: graphInst?.allFiles().length ?? null,
282
+ edges: graphInst?.edgeCount() ?? null
283
+ });
284
+ } catch {
285
+ }
234
286
  const wrapped = wrapWithIndexingEnvelope(
235
287
  { firstTouch: true, projectRoot: root, tier: "graph", durationMs },
236
288
  text
@@ -240,6 +292,14 @@ function createServer() {
240
292
  if (state.vectorsInitialized) {
241
293
  const vectorsFirstTouch = firstTouchTracker.markAndCheck(root, "vectors");
242
294
  if (vectorsFirstTouch) {
295
+ try {
296
+ track("project_first_touch", os.hostname(), {
297
+ project_id: hashProjectRoot(root),
298
+ tier: "vectors",
299
+ duration_ms: durationMs
300
+ });
301
+ } catch {
302
+ }
243
303
  const wrapped = wrapWithIndexingEnvelope(
244
304
  { firstTouch: true, projectRoot: root, tier: "vectors", durationMs },
245
305
  text
@@ -250,9 +310,31 @@ function createServer() {
250
310
  } catch {
251
311
  }
252
312
  }
313
+ if (Math.random() < 0.25) {
314
+ try {
315
+ const projectRootArg2 = args2?.project_root;
316
+ if (!ctx.noDefaultMode || projectRootArg2 !== void 0) {
317
+ const sampleState = resolveOrDefault(projectRootArg2);
318
+ track("tool_dispatched", os.hostname(), {
319
+ project_id: hashProjectRoot(sampleState.projectRoot),
320
+ tool: name,
321
+ duration_ms: durationMs
322
+ });
323
+ }
324
+ } catch {
325
+ }
326
+ }
253
327
  return { content: [{ type: "text", text }] };
254
328
  } catch (err) {
255
329
  if (err instanceof Error && err.message === "no_default_project") {
330
+ const hadArg10 = args2?.project_root !== void 0;
331
+ try {
332
+ track("project_resolution_failed", os.hostname(), {
333
+ error_code: "no_default_project",
334
+ had_arg: hadArg10
335
+ });
336
+ } catch {
337
+ }
256
338
  const xml = noDefaultProjectError({
257
339
  attemptedRoot: PROJECT_ROOT,
258
340
  resolutionChain: "CTXLOOM_ROOT env var\u2192unset, fallback_cwd\u2192" + PROJECT_ROOT,
@@ -264,6 +346,13 @@ function createServer() {
264
346
  try {
265
347
  const parsed2 = JSON.parse(err.message);
266
348
  if (parsed2.kind === "alias_not_found") {
349
+ try {
350
+ track("project_resolution_failed", os.hostname(), {
351
+ error_code: "alias_not_found",
352
+ had_arg: true
353
+ });
354
+ } catch {
355
+ }
267
356
  const xml = aliasNotFoundError({
268
357
  alias: String(parsed2.alias ?? ""),
269
358
  didYouMean: Array.isArray(parsed2.didYouMean) ? parsed2.didYouMean : []
@@ -271,6 +360,13 @@ function createServer() {
271
360
  return { content: [{ type: "text", text: xml }], isError: true };
272
361
  }
273
362
  if (parsed2.kind === "project_root_not_found") {
363
+ try {
364
+ track("project_resolution_failed", os.hostname(), {
365
+ error_code: "project_root_not_found",
366
+ had_arg: true
367
+ });
368
+ } catch {
369
+ }
274
370
  const xml = projectRootNotFoundError({
275
371
  path: String(parsed2.attemptedPath ?? ""),
276
372
  resolutionChain: String(parsed2.resolutionChain ?? "")
@@ -280,6 +376,20 @@ function createServer() {
280
376
  } catch {
281
377
  }
282
378
  }
379
+ try {
380
+ const projectRootArg13 = args2?.project_root;
381
+ let projectIdForCtx;
382
+ try {
383
+ const fallbackState = resolveOrDefault(projectRootArg13);
384
+ projectIdForCtx = hashProjectRoot(fallbackState.projectRoot);
385
+ } catch {
386
+ }
387
+ captureError(err, {
388
+ tool: name,
389
+ ...projectIdForCtx ? { project_id: projectIdForCtx } : {}
390
+ });
391
+ } catch {
392
+ }
283
393
  return {
284
394
  content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
285
395
  isError: true
@@ -300,6 +410,7 @@ async function startServer(opts = {}) {
300
410
  logger.warn(
301
411
  "[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
412
  );
413
+ track("kill_switch_active", os.hostname(), { cap: 1 });
303
414
  }
304
415
  try {
305
416
  const { execSync: execSync2 } = await import("child_process");
@@ -770,7 +881,7 @@ ${body}
770
881
  // src/index.ts
771
882
  import { execSync } from "child_process";
772
883
  import * as readline from "readline";
773
- import os from "os";
884
+ import os2 from "os";
774
885
  import path3 from "path";
775
886
  try {
776
887
  const proc = process;
@@ -782,7 +893,7 @@ try {
782
893
  } catch {
783
894
  }
784
895
  var args = process.argv.slice(2);
785
- var ctxloomVersion = "1.1.0".length > 0 ? "1.1.0" : "dev";
896
+ var ctxloomVersion = "1.1.1".length > 0 ? "1.1.1" : "dev";
786
897
  if (args.includes("--version") || args.includes("-v")) {
787
898
  process.stdout.write(`ctxloom ${ctxloomVersion}
788
899
  `);
@@ -855,7 +966,7 @@ async function checkLicense() {
855
966
  if (command !== void 0 && LICENSE_GATE_BYPASS_COMMANDS.has(command)) return;
856
967
  const ciKey = process.env["CTXLOOM_LICENSE_KEY"];
857
968
  if (ciKey) {
858
- const { ApiClient } = await import("./src-XX7SUUMW.js");
969
+ const { ApiClient } = await import("./src-JI5LH2V7.js");
859
970
  const client = new ApiClient(process.env["CTXLOOM_API_BASE"]);
860
971
  try {
861
972
  const result = await client.validate(ciKey, "ci-ephemeral");
@@ -876,7 +987,7 @@ ctxloom license is ${result.status}.
876
987
  }
877
988
  const active = await isActive();
878
989
  if (!active) {
879
- track("license_gate_hit", os.hostname());
990
+ track("license_gate_hit", os2.hostname());
880
991
  process.stderr.write(errorBlock("ctxloom requires an active license.", [
881
992
  `${style.bold("ctxloom trial")} ${style.dim("\u2014 start a 7-day free trial")}`,
882
993
  `${style.bold("ctxloom activate <KEY>")} ${style.dim("\u2014 activate a purchased key")}`,
@@ -925,7 +1036,7 @@ async function runTrial() {
925
1036
  process.stdout.write(` ${style.dim(`Your license key will arrive at ${email} after checkout.`)}
926
1037
  `);
927
1038
  process.stdout.write(nextStep("Activate on this machine", "ctxloom activate <KEY>"));
928
- track("trial_started", os.hostname(), { email });
1039
+ track("trial_started", os2.hostname(), { email });
929
1040
  } catch (err) {
930
1041
  if (err instanceof FingerprintAlreadyUsedError) {
931
1042
  process.stdout.write(errorBlock("A trial has already been used on this machine.", [
@@ -967,10 +1078,10 @@ async function runActivate(key) {
967
1078
  process.stdout.write(kvTable([
968
1079
  ["Tier", tier],
969
1080
  ["Expires", expires],
970
- ["Machine", `${os.hostname()} (${os.platform()}-${os.arch()})`]
1081
+ ["Machine", `${os2.hostname()} (${os2.platform()}-${os2.arch()})`]
971
1082
  ]));
972
1083
  process.stdout.write(nextStep("Configure your AI tools", "ctxloom setup"));
973
- track("license_activated", os.hostname(), { tier: license.tier });
1084
+ track("license_activated", os2.hostname(), { tier: license.tier });
974
1085
  } catch (err) {
975
1086
  if (err instanceof SeatLimitError) {
976
1087
  process.stdout.write(errorBlock("Seat limit reached.", [
@@ -1007,7 +1118,7 @@ async function runDeactivate() {
1007
1118
  process.stdout.write(` ${success("Deactivated")}
1008
1119
  `);
1009
1120
  process.stdout.write(nextStep("Activate on another machine", "ctxloom activate <KEY>"));
1010
- track("license_deactivated", os.hostname());
1121
+ track("license_deactivated", os2.hostname());
1011
1122
  } catch (err) {
1012
1123
  if (err instanceof NetworkError) {
1013
1124
  process.stderr.write(errorBlock("Deactivation failed \u2014 network error.", [
@@ -1054,7 +1165,7 @@ async function runStatus() {
1054
1165
  ["Tier", style.bold(tier)],
1055
1166
  ["Status", statusColored],
1056
1167
  ["Expires", expiresLabel],
1057
- ["Machine", `${os.hostname()} ${style.dim(`(${os.platform()}-${os.arch()})`)}`],
1168
+ ["Machine", `${os2.hostname()} ${style.dim(`(${os2.platform()}-${os2.arch()})`)}`],
1058
1169
  ["Last sync", style.dim(lastCheck)]
1059
1170
  ]));
1060
1171
  process.stdout.write("\n");
@@ -1208,7 +1319,7 @@ async function main() {
1208
1319
  process.exit(1);
1209
1320
  }
1210
1321
  if (alias !== void 0) {
1211
- const { validateAlias } = await import("./src-XX7SUUMW.js");
1322
+ const { validateAlias } = await import("./src-JI5LH2V7.js");
1212
1323
  const v = validateAlias(alias);
1213
1324
  if (!v.ok) {
1214
1325
  console.error(`[ctxloom] Invalid alias: ${v.reason}`);
@@ -1216,7 +1327,7 @@ async function main() {
1216
1327
  }
1217
1328
  }
1218
1329
  const dbPath = path3.join(absPath, ".ctxloom", "vectors.lancedb");
1219
- const registryPath = path3.join(os.homedir(), ".ctxloom", "repos.json");
1330
+ const registryPath = path3.join(os2.homedir(), ".ctxloom", "repos.json");
1220
1331
  const reg = new RepoRegistry(registryPath);
1221
1332
  try {
1222
1333
  reg.register(absPath, dbPath, alias !== void 0 ? { alias } : {});
@@ -1224,13 +1335,19 @@ async function main() {
1224
1335
  console.error(`[ctxloom] ${err instanceof Error ? err.message : String(err)}`);
1225
1336
  process.exit(1);
1226
1337
  }
1338
+ if (alias !== void 0) {
1339
+ track("alias_registered", os2.hostname(), {
1340
+ alias_length: alias.length,
1341
+ was_collision: false
1342
+ });
1343
+ }
1227
1344
  console.log(`[ctxloom] Registered repo: ${absPath}${alias ? ` (alias: ${alias})` : ""}`);
1228
1345
  console.log(`[ctxloom] LanceDB path: ${dbPath}`);
1229
1346
  console.log(`[ctxloom] Registry: ${registryPath}`);
1230
1347
  break;
1231
1348
  }
1232
1349
  case "repos": {
1233
- const registryPath = path3.join(os.homedir(), ".ctxloom", "repos.json");
1350
+ const registryPath = path3.join(os2.homedir(), ".ctxloom", "repos.json");
1234
1351
  const reg = new RepoRegistry(registryPath);
1235
1352
  const repos = reg.list();
1236
1353
  if (repos.length === 0) {
@@ -1446,7 +1563,7 @@ Suggested reviewers for ${files.length} file(s):`);
1446
1563
  process.stderr.write("[ctxloom] --limit must be a non-negative integer (0 for unlimited)\n");
1447
1564
  process.exit(2);
1448
1565
  }
1449
- const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-XX7SUUMW.js");
1566
+ const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-JI5LH2V7.js");
1450
1567
  let config;
1451
1568
  try {
1452
1569
  config = await loadRulesConfig(root);
@@ -1470,7 +1587,7 @@ Suggested reviewers for ${files.length} file(s):`);
1470
1587
  }
1471
1588
  let graph;
1472
1589
  if (useSnapshot) {
1473
- const { DependencyGraph: DG } = await import("./src-XX7SUUMW.js");
1590
+ const { DependencyGraph: DG } = await import("./src-JI5LH2V7.js");
1474
1591
  graph = new DG();
1475
1592
  const loaded = await graph.loadSnapshotOnly(root);
1476
1593
  if (!loaded) {
@@ -1479,7 +1596,7 @@ Suggested reviewers for ${files.length} file(s):`);
1479
1596
  }
1480
1597
  } else {
1481
1598
  process.stderr.write("[ctxloom] Building dependency graph...\n");
1482
- const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-XX7SUUMW.js");
1599
+ const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-JI5LH2V7.js");
1483
1600
  let parser;
1484
1601
  try {
1485
1602
  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,7 @@ import {
66
67
  generateCODEOWNERS,
67
68
  getImpactRadius,
68
69
  getLicenseInfo,
70
+ hashProjectRoot,
69
71
  isActive,
70
72
  isSiloed,
71
73
  listNamedSnapshots,
@@ -95,7 +97,7 @@ import {
95
97
  validateDefaultRoot,
96
98
  wrapWithIndexingEnvelope,
97
99
  writeCODEOWNERS
98
- } from "./chunk-FFSSKDV4.js";
100
+ } from "./chunk-CXKKREER.js";
99
101
  import {
100
102
  VectorStore
101
103
  } from "./chunk-NEHYSE2Y.js";
@@ -121,6 +123,7 @@ export {
121
123
  DependencyGraph,
122
124
  EMBEDDING_DIMENSION,
123
125
  EmailAlreadyUsedError,
126
+ EmittedOnceTracker,
124
127
  FileWatcher,
125
128
  Fingerprint,
126
129
  FingerprintAlreadyUsedError,
@@ -180,6 +183,7 @@ export {
180
183
  generateEmbedding,
181
184
  getImpactRadius,
182
185
  getLicenseInfo,
186
+ hashProjectRoot,
183
187
  indexDirectory,
184
188
  isActive,
185
189
  isSiloed,
@@ -212,4 +216,4 @@ export {
212
216
  wrapWithIndexingEnvelope,
213
217
  writeCODEOWNERS
214
218
  };
215
- //# sourceMappingURL=src-XX7SUUMW.js.map
219
+ //# sourceMappingURL=src-JI5LH2V7.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.1",
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",