ctxloom-pro 1.1.1 → 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 path41 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 path35 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 path42 of paths) {
3137
- this.nodeCounts.set(path42, (this.nodeCounts.get(path42) ?? 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 [path42, raw] of this.nodes) {
3285
- nodes[path42] = {
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 [path42, raw] of Object.entries(s.nodes)) {
3301
- idx.nodes.set(path42, {
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(path42) {
3315
- const existing = this.nodes.get(path42);
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(path42, 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 [path42, 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[path42] = { 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 [path42, 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(path42, { 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(path42) {
3432
- const existing = this.nodes.get(path42);
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(path42, 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: path42, errorMaps, issueData } = params;
4674
- const fullPath = [...path42, ...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, path42, key) {
4790
+ constructor(parent, value, path43, key) {
4791
4791
  this._cachedPath = [];
4792
4792
  this.parent = parent;
4793
4793
  this.data = value;
4794
- this._path = path42;
4794
+ this._path = path43;
4795
4795
  this._key = key;
4796
4796
  }
4797
4797
  get path() {
@@ -11268,6 +11268,11 @@ 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";
11273
11278
  var CTXLOOM_VERSION = typeof __CTXLOOM_VERSION__ === "string" && __CTXLOOM_VERSION__.length > 0 ? __CTXLOOM_VERSION__ : "dev";
@@ -11275,23 +11280,22 @@ var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (typeof __TELEMETRY_POSTHOG_
11275
11280
  var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (typeof __TELEMETRY_SENTRY_DSN__ === "string" ? __TELEMETRY_SENTRY_DSN__ : "");
11276
11281
 
11277
11282
  // ../../packages/core/src/server/ProjectState.ts
11278
- import path33 from "path";
11283
+ import path34 from "path";
11279
11284
 
11280
11285
  // ../../packages/core/src/server/projectId.ts
11281
11286
  import crypto5 from "crypto";
11282
- import path32 from "path";
11287
+ import path33 from "path";
11283
11288
 
11284
11289
  // ../../packages/core/src/server/ProjectStateManager.ts
11285
11290
  init_logger();
11286
- import os4 from "os";
11287
11291
 
11288
11292
  // ../../packages/core/src/server/resolveProjectRoot.ts
11289
11293
  import fs29 from "fs";
11290
- import path34 from "path";
11294
+ import path35 from "path";
11291
11295
 
11292
11296
  // server/loader.ts
11293
11297
  async function loadContext(root) {
11294
- const absRoot = path35.resolve(root);
11298
+ const absRoot = path36.resolve(root);
11295
11299
  const overlay = new GitOverlayStore(absRoot);
11296
11300
  const gitEnabled = await overlay.loadSnapshot();
11297
11301
  const graph = new DependencyGraph();
@@ -11545,20 +11549,20 @@ function buildOwnershipRouter(ctx) {
11545
11549
  // server/routes/file.ts
11546
11550
  import { Router as Router7 } from "express";
11547
11551
  import fs30 from "fs/promises";
11548
- import path36 from "path";
11552
+ import path37 from "path";
11549
11553
  function buildFileRouter(ctx) {
11550
11554
  const router = Router7();
11551
11555
  router.get("/", async (req, res) => {
11552
11556
  const rel = req.query.path;
11553
11557
  if (!rel) return res.status(400).json({ error: "missing path" });
11554
- const abs = path36.resolve(ctx.root, rel);
11555
- const rootBoundary = ctx.root.endsWith(path36.sep) ? ctx.root : ctx.root + path36.sep;
11558
+ const abs = path37.resolve(ctx.root, rel);
11559
+ const rootBoundary = ctx.root.endsWith(path37.sep) ? ctx.root : ctx.root + path37.sep;
11556
11560
  if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
11557
11561
  return res.status(403).json({ error: "forbidden" });
11558
11562
  }
11559
11563
  try {
11560
11564
  const content = await fs30.readFile(abs, "utf-8");
11561
- const ext = path36.extname(abs).slice(1);
11565
+ const ext = path37.extname(abs).slice(1);
11562
11566
  res.json({ content, lines: content.split("\n").length, ext });
11563
11567
  } catch {
11564
11568
  res.status(404).json({ error: "not found" });
@@ -11570,7 +11574,7 @@ function buildFileRouter(ctx) {
11570
11574
  // server/routes/open.ts
11571
11575
  import { Router as Router8 } from "express";
11572
11576
  import { execFile as execFile2 } from "child_process";
11573
- import path37 from "path";
11577
+ import path38 from "path";
11574
11578
  function tryOpen(bin, abs) {
11575
11579
  return new Promise((resolve) => {
11576
11580
  execFile2(bin, [abs], { timeout: 5e3 }, (err) => resolve(!err));
@@ -11581,8 +11585,8 @@ function buildOpenRouter(ctx) {
11581
11585
  router.post("/", async (req, res) => {
11582
11586
  const rel = req.body?.path;
11583
11587
  if (!rel || typeof rel !== "string") return res.status(400).json({ error: "missing path" });
11584
- const abs = path37.resolve(ctx.root, rel);
11585
- const rootBoundary = ctx.root.endsWith(path37.sep) ? ctx.root : ctx.root + path37.sep;
11588
+ const abs = path38.resolve(ctx.root, rel);
11589
+ const rootBoundary = ctx.root.endsWith(path38.sep) ? ctx.root : ctx.root + path38.sep;
11586
11590
  if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
11587
11591
  return res.status(403).json({ error: "forbidden" });
11588
11592
  }
@@ -11594,7 +11598,7 @@ function buildOpenRouter(ctx) {
11594
11598
 
11595
11599
  // server/routes/tokens.ts
11596
11600
  import { Router as Router9 } from "express";
11597
- import path38 from "path";
11601
+ import path39 from "path";
11598
11602
  import fs31 from "fs";
11599
11603
  var CHARS_PER_TOKEN = 4;
11600
11604
  var cache = null;
@@ -11610,7 +11614,7 @@ function buildTokensRouter(ctx) {
11610
11614
  let fullChars = 0;
11611
11615
  let skeletonChars = 0;
11612
11616
  for (const file of files) {
11613
- const absPath = path38.join(ctx.root, file);
11617
+ const absPath = path39.join(ctx.root, file);
11614
11618
  try {
11615
11619
  const content = fs31.readFileSync(absPath, "utf-8");
11616
11620
  fullChars += content.length;
@@ -11713,23 +11717,23 @@ function buildFileTrendsRouter(ctx) {
11713
11717
 
11714
11718
  // server/routes/projects.ts
11715
11719
  import { Router as Router12 } from "express";
11716
- import path40 from "path";
11720
+ import path41 from "path";
11717
11721
 
11718
11722
  // server/projects.ts
11719
- import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
11723
+ import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
11720
11724
  import os5 from "os";
11721
- import path39 from "path";
11725
+ import path40 from "path";
11722
11726
  import crypto6 from "crypto";
11723
11727
  var HOME = os5.homedir();
11724
- var REGISTRY_PATH = path39.join(HOME, ".ctxloom", "repos.json");
11728
+ var REGISTRY_PATH = path40.join(HOME, ".ctxloom", "repos.json");
11725
11729
  function slugFor(root) {
11726
- const abs = path39.resolve(root);
11730
+ const abs = path40.resolve(root);
11727
11731
  return crypto6.createHash("sha1").update(abs).digest("hex").slice(0, 12);
11728
11732
  }
11729
11733
  function readRegistry() {
11730
- if (!existsSync2(REGISTRY_PATH)) return [];
11734
+ if (!existsSync3(REGISTRY_PATH)) return [];
11731
11735
  try {
11732
- const raw = readFileSync3(REGISTRY_PATH, "utf-8");
11736
+ const raw = readFileSync4(REGISTRY_PATH, "utf-8");
11733
11737
  const parsed = JSON.parse(raw);
11734
11738
  if (!Array.isArray(parsed)) return [];
11735
11739
  return parsed.filter((r) => typeof r?.root === "string");
@@ -11738,27 +11742,27 @@ function readRegistry() {
11738
11742
  }
11739
11743
  }
11740
11744
  function listProjects(defaultRoot) {
11741
- const absDefault = path39.resolve(defaultRoot);
11745
+ const absDefault = path40.resolve(defaultRoot);
11742
11746
  const out = [
11743
11747
  {
11744
11748
  slug: slugFor(absDefault),
11745
- name: path39.basename(absDefault) || absDefault,
11749
+ name: path40.basename(absDefault) || absDefault,
11746
11750
  root: absDefault,
11747
11751
  isDefault: true,
11748
- hasSnapshot: existsSync2(path39.join(absDefault, ".ctxloom"))
11752
+ hasSnapshot: existsSync3(path40.join(absDefault, ".ctxloom"))
11749
11753
  }
11750
11754
  ];
11751
11755
  const seen = /* @__PURE__ */ new Set([absDefault]);
11752
11756
  for (const entry of readRegistry()) {
11753
- const abs = path39.resolve(entry.root);
11757
+ const abs = path40.resolve(entry.root);
11754
11758
  if (seen.has(abs)) continue;
11755
11759
  seen.add(abs);
11756
11760
  const item = {
11757
11761
  slug: slugFor(abs),
11758
- name: entry.name ?? (path39.basename(abs) || abs),
11762
+ name: entry.name ?? (path40.basename(abs) || abs),
11759
11763
  root: abs,
11760
11764
  isDefault: false,
11761
- hasSnapshot: existsSync2(path39.join(abs, ".ctxloom"))
11765
+ hasSnapshot: existsSync3(path40.join(abs, ".ctxloom"))
11762
11766
  };
11763
11767
  if (entry.alias !== void 0) item.alias = entry.alias;
11764
11768
  out.push(item);
@@ -11811,7 +11815,7 @@ function buildProjectsRouter(deps) {
11811
11815
  } catch (err) {
11812
11816
  const detail = err instanceof Error ? err.message : String(err);
11813
11817
  res.status(500).json({
11814
- error: `failed to switch to ${path40.basename(target.root)}: ${detail}`
11818
+ error: `failed to switch to ${path41.basename(target.root)}: ${detail}`
11815
11819
  });
11816
11820
  }
11817
11821
  });
@@ -11819,7 +11823,7 @@ function buildProjectsRouter(deps) {
11819
11823
  }
11820
11824
 
11821
11825
  // server/index.ts
11822
- var __dirname2 = path41.dirname(fileURLToPath2(import.meta.url));
11826
+ var __dirname2 = path42.dirname(fileURLToPath2(import.meta.url));
11823
11827
  async function startDashboard(options) {
11824
11828
  const { root, port, open } = options;
11825
11829
  console.log(`ctxloom dashboard \u2014 loading context from ${root}...`);
@@ -11878,7 +11882,7 @@ async function startDashboard(options) {
11878
11882
  }
11879
11883
  activeWatcher = null;
11880
11884
  }
11881
- const snapshotDir = path41.join(targetRoot, ".ctxloom");
11885
+ const snapshotDir = path42.join(targetRoot, ".ctxloom");
11882
11886
  try {
11883
11887
  activeWatcher = fs32.watch(snapshotDir, (_event, filename) => {
11884
11888
  if (!filename || !filename.includes("snapshot")) return;
@@ -11908,12 +11912,12 @@ async function startDashboard(options) {
11908
11912
  attachSnapshotWatcher(newRoot);
11909
11913
  }
11910
11914
  }));
11911
- const clientDist = path41.join(__dirname2, "../dashboard/client");
11912
- const clientDistExists = fs32.existsSync(path41.join(clientDist, "index.html"));
11915
+ const clientDist = path42.join(__dirname2, "../dashboard/client");
11916
+ const clientDistExists = fs32.existsSync(path42.join(clientDist, "index.html"));
11913
11917
  if (clientDistExists) {
11914
11918
  app.use(express.static(clientDist, { dotfiles: "allow" }));
11915
11919
  app.get(/.*/, (_req, res) => {
11916
- res.sendFile(path41.join(clientDist, "index.html"), { dotfiles: "allow" });
11920
+ res.sendFile(path42.join(clientDist, "index.html"), { dotfiles: "allow" });
11917
11921
  });
11918
11922
  } else {
11919
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 path33 of paths) {
3410
- this.nodeCounts.set(path33, (this.nodeCounts.get(path33) ?? 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 [path33, raw] of this.nodes) {
3558
- nodes[path33] = {
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 [path33, raw] of Object.entries(s.nodes)) {
3574
- idx.nodes.set(path33, {
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(path33) {
3588
- const existing = this.nodes.get(path33);
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(path33, 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 [path33, 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[path33] = { 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 [path33, 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(path33, { 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(path33) {
3705
- const existing = this.nodes.get(path33);
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(path33, fresh);
3708
+ this.nodes.set(path34, fresh);
3709
3709
  return fresh;
3710
3710
  }
3711
3711
  };
@@ -8794,19 +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";
8799
- var CTXLOOM_VERSION = "1.1.1".length > 0 ? "1.1.1" : "dev";
8841
+ var CTXLOOM_VERSION = "1.1.2".length > 0 ? "1.1.2" : "dev";
8800
8842
  var POSTHOG_HOST = "https://eu.i.posthog.com";
8801
8843
  var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
8802
8844
  var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
8803
- 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 = {}) {
8804
8856
  if (TELEMETRY_DISABLED || !POSTHOG_KEY) return;
8805
- 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
+ }
8806
8864
  }
8807
8865
  function captureError(err, context = {}) {
8808
8866
  if (TELEMETRY_DISABLED || !SENTRY_DSN) return;
8809
- void sendSentry(err, context);
8867
+ const record = resolveDistinctId();
8868
+ const augmented = record ? { ...context, distinct_id: record.id } : context;
8869
+ void sendSentry(err, augmented);
8810
8870
  }
8811
8871
  async function sendPostHog(event, distinctId, props) {
8812
8872
  try {
@@ -8828,6 +8888,33 @@ async function sendPostHog(event, distinctId, props) {
8828
8888
  } catch {
8829
8889
  }
8830
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
+ }
8831
8918
  async function sendSentry(err, context) {
8832
8919
  try {
8833
8920
  const dsn = parseDsn(SENTRY_DSN);
@@ -8883,13 +8970,13 @@ function parseStack(stack) {
8883
8970
  }
8884
8971
 
8885
8972
  // packages/core/src/server/ProjectState.ts
8886
- import path31 from "path";
8973
+ import path32 from "path";
8887
8974
 
8888
8975
  // packages/core/src/server/projectId.ts
8889
8976
  import crypto5 from "crypto";
8890
- import path30 from "path";
8977
+ import path31 from "path";
8891
8978
  function hashProjectRoot(absPath) {
8892
- const canonical = path30.resolve(absPath);
8979
+ const canonical = path31.resolve(absPath);
8893
8980
  return crypto5.createHash("sha256").update(canonical).digest("hex").slice(0, 16);
8894
8981
  }
8895
8982
 
@@ -8897,7 +8984,7 @@ function hashProjectRoot(absPath) {
8897
8984
  function createProjectState(projectRoot, opts = {}) {
8898
8985
  return {
8899
8986
  projectRoot,
8900
- dbPath: path31.join(projectRoot, ".ctxloom", "vectors.lancedb"),
8987
+ dbPath: path32.join(projectRoot, ".ctxloom", "vectors.lancedb"),
8901
8988
  pinned: opts.pinned ?? false,
8902
8989
  lastTouchedAt: Date.now(),
8903
8990
  vectorsInitialized: false,
@@ -8949,7 +9036,6 @@ async function disposeProjectState(state) {
8949
9036
  }
8950
9037
 
8951
9038
  // packages/core/src/server/ProjectStateManager.ts
8952
- import os4 from "os";
8953
9039
  var DEFAULT_MAX_PROJECTS = 5;
8954
9040
  var ProjectStateManager = class {
8955
9041
  map = /* @__PURE__ */ new Map();
@@ -9020,7 +9106,7 @@ var ProjectStateManager = class {
9020
9106
  }
9021
9107
  this.map.delete(victim.projectRoot);
9022
9108
  const pinnedCount = Array.from(this.map.values()).filter((s) => s.pinned).length;
9023
- track("project_evicted", os4.hostname(), {
9109
+ track("project_evicted", {
9024
9110
  project_id: hashProjectRoot(victim.projectRoot),
9025
9111
  pinned_count: pinnedCount,
9026
9112
  cap: this.maxProjects
@@ -9051,7 +9137,7 @@ var ProjectStateManager = class {
9051
9137
 
9052
9138
  // packages/core/src/server/resolveProjectRoot.ts
9053
9139
  import fs27 from "fs";
9054
- import path32 from "path";
9140
+ import path33 from "path";
9055
9141
  var PATH_SEPARATOR_PATTERN = /[/\\~]|^[A-Za-z]:/;
9056
9142
  function looksLikePath(value) {
9057
9143
  return PATH_SEPARATOR_PATTERN.test(value);
@@ -9076,9 +9162,9 @@ function resolvePathSafely(p, cwd) {
9076
9162
  let expanded = p;
9077
9163
  if (p === "~" || p.startsWith("~/")) {
9078
9164
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
9079
- expanded = p === "~" ? home : path32.join(home, p.slice(2));
9165
+ expanded = p === "~" ? home : path33.join(home, p.slice(2));
9080
9166
  }
9081
- return path32.isAbsolute(expanded) ? path32.resolve(expanded) : path32.resolve(cwd, expanded);
9167
+ return path33.isAbsolute(expanded) ? path33.resolve(expanded) : path33.resolve(cwd, expanded);
9082
9168
  }
9083
9169
  function realpathOrSame(p) {
9084
9170
  try {
@@ -9150,7 +9236,7 @@ function validateDefaultRoot(candidate) {
9150
9236
  } catch {
9151
9237
  return false;
9152
9238
  }
9153
- return PROJECT_MARKERS.some((m) => fs27.existsSync(path32.join(candidate, m)));
9239
+ return PROJECT_MARKERS.some((m) => fs27.existsSync(path33.join(candidate, m)));
9154
9240
  }
9155
9241
 
9156
9242
  // packages/core/src/server/structuredErrors.ts
@@ -9303,6 +9389,8 @@ export {
9303
9389
  activateLicense,
9304
9390
  deactivateLicense,
9305
9391
  startTrial,
9392
+ getOrCreateDistinctId,
9393
+ markAliasSent,
9306
9394
  track,
9307
9395
  captureError,
9308
9396
  hashProjectRoot,
@@ -9321,4 +9409,4 @@ export {
9321
9409
  FirstTouchTracker,
9322
9410
  EmittedOnceTracker
9323
9411
  };
9324
- //# sourceMappingURL=chunk-CXKKREER.js.map
9412
+ //# sourceMappingURL=chunk-RGO3M3IR.js.map
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ import {
45
45
  validateDefaultRoot,
46
46
  wrapWithIndexingEnvelope,
47
47
  writeCODEOWNERS
48
- } from "./chunk-CXKKREER.js";
48
+ } from "./chunk-RGO3M3IR.js";
49
49
  import {
50
50
  VectorStore
51
51
  } from "./chunk-NEHYSE2Y.js";
@@ -63,7 +63,6 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
63
63
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
64
64
  import path from "path";
65
65
  import fs from "fs";
66
- import os from "os";
67
66
  var PROJECT_ROOT = (() => {
68
67
  if (process.env.CTXLOOM_ROOT) return process.env.CTXLOOM_ROOT;
69
68
  const cwd = process.cwd();
@@ -181,14 +180,14 @@ function buildContext(defaultRoot, noDefaultMode) {
181
180
  try {
182
181
  const projectId = hashProjectRoot(state.projectRoot);
183
182
  if (emittedOnceTracker.markAndCheck(`project_resolved:${projectId}`)) {
184
- track("project_resolved", os.hostname(), {
183
+ track("project_resolved", {
185
184
  project_id: projectId,
186
185
  source,
187
186
  via_alias: source === "alias"
188
187
  });
189
188
  }
190
189
  if (stateManager.size() >= 2 && emittedOnceTracker.markAndCheck("multi_project_active")) {
191
- track("multi_project_active", os.hostname(), {
190
+ track("multi_project_active", {
192
191
  active_count: stateManager.size(),
193
192
  cap: stateManager.max
194
193
  });
@@ -274,7 +273,7 @@ function createServer() {
274
273
  if (graphFirstTouch) {
275
274
  try {
276
275
  const graphInst = state.graphPromise ? await state.graphPromise : null;
277
- track("project_first_touch", os.hostname(), {
276
+ track("project_first_touch", {
278
277
  project_id: hashProjectRoot(root),
279
278
  tier: "graph",
280
279
  duration_ms: durationMs,
@@ -293,7 +292,7 @@ function createServer() {
293
292
  const vectorsFirstTouch = firstTouchTracker.markAndCheck(root, "vectors");
294
293
  if (vectorsFirstTouch) {
295
294
  try {
296
- track("project_first_touch", os.hostname(), {
295
+ track("project_first_touch", {
297
296
  project_id: hashProjectRoot(root),
298
297
  tier: "vectors",
299
298
  duration_ms: durationMs
@@ -315,7 +314,7 @@ function createServer() {
315
314
  const projectRootArg2 = args2?.project_root;
316
315
  if (!ctx.noDefaultMode || projectRootArg2 !== void 0) {
317
316
  const sampleState = resolveOrDefault(projectRootArg2);
318
- track("tool_dispatched", os.hostname(), {
317
+ track("tool_dispatched", {
319
318
  project_id: hashProjectRoot(sampleState.projectRoot),
320
319
  tool: name,
321
320
  duration_ms: durationMs
@@ -329,7 +328,7 @@ function createServer() {
329
328
  if (err instanceof Error && err.message === "no_default_project") {
330
329
  const hadArg10 = args2?.project_root !== void 0;
331
330
  try {
332
- track("project_resolution_failed", os.hostname(), {
331
+ track("project_resolution_failed", {
333
332
  error_code: "no_default_project",
334
333
  had_arg: hadArg10
335
334
  });
@@ -347,7 +346,7 @@ function createServer() {
347
346
  const parsed2 = JSON.parse(err.message);
348
347
  if (parsed2.kind === "alias_not_found") {
349
348
  try {
350
- track("project_resolution_failed", os.hostname(), {
349
+ track("project_resolution_failed", {
351
350
  error_code: "alias_not_found",
352
351
  had_arg: true
353
352
  });
@@ -361,7 +360,7 @@ function createServer() {
361
360
  }
362
361
  if (parsed2.kind === "project_root_not_found") {
363
362
  try {
364
- track("project_resolution_failed", os.hostname(), {
363
+ track("project_resolution_failed", {
365
364
  error_code: "project_root_not_found",
366
365
  had_arg: true
367
366
  });
@@ -410,7 +409,7 @@ async function startServer(opts = {}) {
410
409
  logger.warn(
411
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."
412
411
  );
413
- track("kill_switch_active", os.hostname(), { cap: 1 });
412
+ track("kill_switch_active", { cap: 1 });
414
413
  }
415
414
  try {
416
415
  const { execSync: execSync2 } = await import("child_process");
@@ -881,7 +880,7 @@ ${body}
881
880
  // src/index.ts
882
881
  import { execSync } from "child_process";
883
882
  import * as readline from "readline";
884
- import os2 from "os";
883
+ import os from "os";
885
884
  import path3 from "path";
886
885
  try {
887
886
  const proc = process;
@@ -893,7 +892,7 @@ try {
893
892
  } catch {
894
893
  }
895
894
  var args = process.argv.slice(2);
896
- var ctxloomVersion = "1.1.1".length > 0 ? "1.1.1" : "dev";
895
+ var ctxloomVersion = "1.1.2".length > 0 ? "1.1.2" : "dev";
897
896
  if (args.includes("--version") || args.includes("-v")) {
898
897
  process.stdout.write(`ctxloom ${ctxloomVersion}
899
898
  `);
@@ -966,7 +965,7 @@ async function checkLicense() {
966
965
  if (command !== void 0 && LICENSE_GATE_BYPASS_COMMANDS.has(command)) return;
967
966
  const ciKey = process.env["CTXLOOM_LICENSE_KEY"];
968
967
  if (ciKey) {
969
- const { ApiClient } = await import("./src-JI5LH2V7.js");
968
+ const { ApiClient } = await import("./src-5MVUPLMZ.js");
970
969
  const client = new ApiClient(process.env["CTXLOOM_API_BASE"]);
971
970
  try {
972
971
  const result = await client.validate(ciKey, "ci-ephemeral");
@@ -987,7 +986,7 @@ ctxloom license is ${result.status}.
987
986
  }
988
987
  const active = await isActive();
989
988
  if (!active) {
990
- track("license_gate_hit", os2.hostname());
989
+ track("license_gate_hit");
991
990
  process.stderr.write(errorBlock("ctxloom requires an active license.", [
992
991
  `${style.bold("ctxloom trial")} ${style.dim("\u2014 start a 7-day free trial")}`,
993
992
  `${style.bold("ctxloom activate <KEY>")} ${style.dim("\u2014 activate a purchased key")}`,
@@ -1036,7 +1035,7 @@ async function runTrial() {
1036
1035
  process.stdout.write(` ${style.dim(`Your license key will arrive at ${email} after checkout.`)}
1037
1036
  `);
1038
1037
  process.stdout.write(nextStep("Activate on this machine", "ctxloom activate <KEY>"));
1039
- track("trial_started", os2.hostname(), { email });
1038
+ track("trial_started", { email });
1040
1039
  } catch (err) {
1041
1040
  if (err instanceof FingerprintAlreadyUsedError) {
1042
1041
  process.stdout.write(errorBlock("A trial has already been used on this machine.", [
@@ -1078,10 +1077,10 @@ async function runActivate(key) {
1078
1077
  process.stdout.write(kvTable([
1079
1078
  ["Tier", tier],
1080
1079
  ["Expires", expires],
1081
- ["Machine", `${os2.hostname()} (${os2.platform()}-${os2.arch()})`]
1080
+ ["Machine", `${os.hostname()} (${os.platform()}-${os.arch()})`]
1082
1081
  ]));
1083
1082
  process.stdout.write(nextStep("Configure your AI tools", "ctxloom setup"));
1084
- track("license_activated", os2.hostname(), { tier: license.tier });
1083
+ track("license_activated", { tier: license.tier });
1085
1084
  } catch (err) {
1086
1085
  if (err instanceof SeatLimitError) {
1087
1086
  process.stdout.write(errorBlock("Seat limit reached.", [
@@ -1118,7 +1117,7 @@ async function runDeactivate() {
1118
1117
  process.stdout.write(` ${success("Deactivated")}
1119
1118
  `);
1120
1119
  process.stdout.write(nextStep("Activate on another machine", "ctxloom activate <KEY>"));
1121
- track("license_deactivated", os2.hostname());
1120
+ track("license_deactivated");
1122
1121
  } catch (err) {
1123
1122
  if (err instanceof NetworkError) {
1124
1123
  process.stderr.write(errorBlock("Deactivation failed \u2014 network error.", [
@@ -1165,7 +1164,7 @@ async function runStatus() {
1165
1164
  ["Tier", style.bold(tier)],
1166
1165
  ["Status", statusColored],
1167
1166
  ["Expires", expiresLabel],
1168
- ["Machine", `${os2.hostname()} ${style.dim(`(${os2.platform()}-${os2.arch()})`)}`],
1167
+ ["Machine", `${os.hostname()} ${style.dim(`(${os.platform()}-${os.arch()})`)}`],
1169
1168
  ["Last sync", style.dim(lastCheck)]
1170
1169
  ]));
1171
1170
  process.stdout.write("\n");
@@ -1319,7 +1318,7 @@ async function main() {
1319
1318
  process.exit(1);
1320
1319
  }
1321
1320
  if (alias !== void 0) {
1322
- const { validateAlias } = await import("./src-JI5LH2V7.js");
1321
+ const { validateAlias } = await import("./src-5MVUPLMZ.js");
1323
1322
  const v = validateAlias(alias);
1324
1323
  if (!v.ok) {
1325
1324
  console.error(`[ctxloom] Invalid alias: ${v.reason}`);
@@ -1327,7 +1326,7 @@ async function main() {
1327
1326
  }
1328
1327
  }
1329
1328
  const dbPath = path3.join(absPath, ".ctxloom", "vectors.lancedb");
1330
- const registryPath = path3.join(os2.homedir(), ".ctxloom", "repos.json");
1329
+ const registryPath = path3.join(os.homedir(), ".ctxloom", "repos.json");
1331
1330
  const reg = new RepoRegistry(registryPath);
1332
1331
  try {
1333
1332
  reg.register(absPath, dbPath, alias !== void 0 ? { alias } : {});
@@ -1336,7 +1335,7 @@ async function main() {
1336
1335
  process.exit(1);
1337
1336
  }
1338
1337
  if (alias !== void 0) {
1339
- track("alias_registered", os2.hostname(), {
1338
+ track("alias_registered", {
1340
1339
  alias_length: alias.length,
1341
1340
  was_collision: false
1342
1341
  });
@@ -1347,7 +1346,7 @@ async function main() {
1347
1346
  break;
1348
1347
  }
1349
1348
  case "repos": {
1350
- const registryPath = path3.join(os2.homedir(), ".ctxloom", "repos.json");
1349
+ const registryPath = path3.join(os.homedir(), ".ctxloom", "repos.json");
1351
1350
  const reg = new RepoRegistry(registryPath);
1352
1351
  const repos = reg.list();
1353
1352
  if (repos.length === 0) {
@@ -1563,7 +1562,7 @@ Suggested reviewers for ${files.length} file(s):`);
1563
1562
  process.stderr.write("[ctxloom] --limit must be a non-negative integer (0 for unlimited)\n");
1564
1563
  process.exit(2);
1565
1564
  }
1566
- const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-JI5LH2V7.js");
1565
+ const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-5MVUPLMZ.js");
1567
1566
  let config;
1568
1567
  try {
1569
1568
  config = await loadRulesConfig(root);
@@ -1587,7 +1586,7 @@ Suggested reviewers for ${files.length} file(s):`);
1587
1586
  }
1588
1587
  let graph;
1589
1588
  if (useSnapshot) {
1590
- const { DependencyGraph: DG } = await import("./src-JI5LH2V7.js");
1589
+ const { DependencyGraph: DG } = await import("./src-5MVUPLMZ.js");
1591
1590
  graph = new DG();
1592
1591
  const loaded = await graph.loadSnapshotOnly(root);
1593
1592
  if (!loaded) {
@@ -1596,7 +1595,7 @@ Suggested reviewers for ${files.length} file(s):`);
1596
1595
  }
1597
1596
  } else {
1598
1597
  process.stderr.write("[ctxloom] Building dependency graph...\n");
1599
- const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-JI5LH2V7.js");
1598
+ const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-5MVUPLMZ.js");
1600
1599
  let parser;
1601
1600
  try {
1602
1601
  parser = new ASTParser2();
@@ -67,6 +67,7 @@ import {
67
67
  generateCODEOWNERS,
68
68
  getImpactRadius,
69
69
  getLicenseInfo,
70
+ getOrCreateDistinctId,
70
71
  hashProjectRoot,
71
72
  isActive,
72
73
  isSiloed,
@@ -75,6 +76,7 @@ import {
75
76
  loadReviewConfig,
76
77
  loadRulesConfig,
77
78
  loadTrendSeries,
79
+ markAliasSent,
78
80
  maybePrintExpiryWarning,
79
81
  mergeIntoFile,
80
82
  noDefaultProjectError,
@@ -97,7 +99,7 @@ import {
97
99
  validateDefaultRoot,
98
100
  wrapWithIndexingEnvelope,
99
101
  writeCODEOWNERS
100
- } from "./chunk-CXKKREER.js";
102
+ } from "./chunk-RGO3M3IR.js";
101
103
  import {
102
104
  VectorStore
103
105
  } from "./chunk-NEHYSE2Y.js";
@@ -183,6 +185,7 @@ export {
183
185
  generateEmbedding,
184
186
  getImpactRadius,
185
187
  getLicenseInfo,
188
+ getOrCreateDistinctId,
186
189
  hashProjectRoot,
187
190
  indexDirectory,
188
191
  isActive,
@@ -193,6 +196,7 @@ export {
193
196
  loadRulesConfig,
194
197
  loadTrendSeries,
195
198
  logger,
199
+ markAliasSent,
196
200
  maybePrintExpiryWarning,
197
201
  mergeIntoFile,
198
202
  noDefaultProjectError,
@@ -216,4 +220,4 @@ export {
216
220
  wrapWithIndexingEnvelope,
217
221
  writeCODEOWNERS
218
222
  };
219
- //# sourceMappingURL=src-JI5LH2V7.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.1",
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",