ctxloom-pro 1.7.8 → 1.7.10

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.
@@ -1,11 +1,12 @@
1
1
  import {
2
- VectorStore
3
- } from "./chunk-Z3NQHCWG.js";
2
+ VectorStore,
3
+ isCorruptionError
4
+ } from "./chunk-UWQKNIZO.js";
4
5
  import {
5
6
  collectFiles,
6
7
  generateEmbedding,
7
8
  isIgnoredDir
8
- } from "./chunk-R32CUQAL.js";
9
+ } from "./chunk-XJJHX227.js";
9
10
  import {
10
11
  diskSink,
11
12
  readEvents
@@ -2705,7 +2706,7 @@ var CallGraphIndex = class _CallGraphIndex {
2705
2706
  var TS_EXTENSIONS2 = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".vue"]);
2706
2707
  var PY_EXTENSIONS = /* @__PURE__ */ new Set([".py", ".ipynb"]);
2707
2708
  var AST_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".py", ".go", ".rs", ".java", ".cs", ".rb", ".kt", ".kts", ".swift", ".ipynb", ".php", ".dart"]);
2708
- var CTXLOOM_VERSION = "1.7.8".length > 0 ? "1.7.8" : "dev";
2709
+ var CTXLOOM_VERSION = "1.7.10".length > 0 ? "1.7.10" : "dev";
2709
2710
  var SNAPSHOT_SCHEMA_VERSION = 2;
2710
2711
  function compareCtxloomVersions(snapshotVer, currentVer) {
2711
2712
  if (snapshotVer === currentVer) return "same";
@@ -4218,8 +4219,8 @@ var CoChangeIndex = class _CoChangeIndex {
4218
4219
  if (event.isBulk || event.isMerge) return;
4219
4220
  const paths = event.files.map((f) => f.path);
4220
4221
  if (paths.length === 0) return;
4221
- for (const path38 of paths) {
4222
- this.nodeCounts.set(path38, (this.nodeCounts.get(path38) ?? 0) + 1);
4222
+ for (const path39 of paths) {
4223
+ this.nodeCounts.set(path39, (this.nodeCounts.get(path39) ?? 0) + 1);
4223
4224
  }
4224
4225
  for (let i = 0; i < paths.length; i++) {
4225
4226
  for (let j = i + 1; j < paths.length; j++) {
@@ -4366,8 +4367,8 @@ var ChurnIndex = class _ChurnIndex {
4366
4367
  */
4367
4368
  snapshot() {
4368
4369
  const nodes = {};
4369
- for (const [path38, raw] of this.nodes) {
4370
- nodes[path38] = {
4370
+ for (const [path39, raw] of this.nodes) {
4371
+ nodes[path39] = {
4371
4372
  commits: raw.commits,
4372
4373
  churnLines: raw.churnLines,
4373
4374
  bugCommits: raw.bugCommits,
@@ -4382,8 +4383,8 @@ var ChurnIndex = class _ChurnIndex {
4382
4383
  */
4383
4384
  static load(s) {
4384
4385
  const idx = new _ChurnIndex();
4385
- for (const [path38, raw] of Object.entries(s.nodes)) {
4386
- idx.nodes.set(path38, {
4386
+ for (const [path39, raw] of Object.entries(s.nodes)) {
4387
+ idx.nodes.set(path39, {
4387
4388
  commits: raw.commits,
4388
4389
  churnLines: raw.churnLines,
4389
4390
  bugCommits: raw.bugCommits,
@@ -4396,8 +4397,8 @@ var ChurnIndex = class _ChurnIndex {
4396
4397
  // -------------------------------------------------------------------------
4397
4398
  // Private helpers
4398
4399
  // -------------------------------------------------------------------------
4399
- getOrCreate(path38) {
4400
- const existing = this.nodes.get(path38);
4400
+ getOrCreate(path39) {
4401
+ const existing = this.nodes.get(path39);
4401
4402
  if (existing !== void 0) return existing;
4402
4403
  const fresh = {
4403
4404
  commits: 0,
@@ -4406,7 +4407,7 @@ var ChurnIndex = class _ChurnIndex {
4406
4407
  authorCounts: {},
4407
4408
  lastTouch: 0
4408
4409
  };
4409
- this.nodes.set(path38, fresh);
4410
+ this.nodes.set(path39, fresh);
4410
4411
  return fresh;
4411
4412
  }
4412
4413
  };
@@ -4487,12 +4488,12 @@ var OwnershipIndex = class _OwnershipIndex {
4487
4488
  */
4488
4489
  snapshot() {
4489
4490
  const nodes = {};
4490
- for (const [path38, raw] of this.nodes) {
4491
+ for (const [path39, raw] of this.nodes) {
4491
4492
  const authorWeights = {};
4492
4493
  for (const [email, entry] of Object.entries(raw.authorWeights)) {
4493
4494
  authorWeights[email] = { ...entry };
4494
4495
  }
4495
- nodes[path38] = { authorWeights, lastTouch: raw.lastTouch };
4496
+ nodes[path39] = { authorWeights, lastTouch: raw.lastTouch };
4496
4497
  }
4497
4498
  return { version: 1, nodes };
4498
4499
  }
@@ -4501,23 +4502,23 @@ var OwnershipIndex = class _OwnershipIndex {
4501
4502
  */
4502
4503
  static load(s) {
4503
4504
  const idx = new _OwnershipIndex();
4504
- for (const [path38, raw] of Object.entries(s.nodes)) {
4505
+ for (const [path39, raw] of Object.entries(s.nodes)) {
4505
4506
  const authorWeights = {};
4506
4507
  for (const [email, entry] of Object.entries(raw.authorWeights)) {
4507
4508
  authorWeights[email] = { ...entry };
4508
4509
  }
4509
- idx.nodes.set(path38, { authorWeights, lastTouch: raw.lastTouch });
4510
+ idx.nodes.set(path39, { authorWeights, lastTouch: raw.lastTouch });
4510
4511
  }
4511
4512
  return idx;
4512
4513
  }
4513
4514
  // -------------------------------------------------------------------------
4514
4515
  // Private helpers
4515
4516
  // -------------------------------------------------------------------------
4516
- getOrCreate(path38) {
4517
- const existing = this.nodes.get(path38);
4517
+ getOrCreate(path39) {
4518
+ const existing = this.nodes.get(path39);
4518
4519
  if (existing !== void 0) return existing;
4519
4520
  const fresh = { authorWeights: {}, lastTouch: 0 };
4520
- this.nodes.set(path38, fresh);
4521
+ this.nodes.set(path39, fresh);
4521
4522
  return fresh;
4522
4523
  }
4523
4524
  };
@@ -5341,7 +5342,7 @@ var ProjectRootField = z.string().optional().describe(PROJECT_ROOT_DESCRIPTION);
5341
5342
  function escapeXML2(text) {
5342
5343
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
5343
5344
  }
5344
- function renderStatusXml(input) {
5345
+ async function renderStatusXml(input) {
5345
5346
  const { defaultRoot, manager, registry } = input;
5346
5347
  const lines = ["<ctx_status>"];
5347
5348
  if (defaultRoot) {
@@ -5361,9 +5362,21 @@ function renderStatusXml(input) {
5361
5362
  const reg = registry.list().find((r) => r.root === s.projectRoot);
5362
5363
  const alias = reg?.alias ? ` alias="${escapeXML2(reg.alias)}"` : "";
5363
5364
  const graphState = s.graphInitialized ? "ready" : s.graphPromise ? "building" : "cold";
5364
- const vectorsState = s.vectorsInitialized ? "ready" : s.storePromise ? "building" : "cold";
5365
+ let vectorsState = s.vectorsInitialized ? "ready" : s.storePromise ? "building" : "cold";
5366
+ let vectorsHint = "";
5367
+ if (s.vectorsInitialized && s.storePromise) {
5368
+ try {
5369
+ const store = await s.storePromise;
5370
+ await store.probe();
5371
+ } catch (probeErr) {
5372
+ if (isCorruptionError(probeErr)) {
5373
+ vectorsState = "corrupt";
5374
+ vectorsHint = ' vectors_hint="run: ctxloom vectors-cleanup &amp;&amp; ctxloom index"';
5375
+ }
5376
+ }
5377
+ }
5365
5378
  lines.push(
5366
- ` <project root="${escapeXML2(s.projectRoot)}"${alias} pinned="${s.pinned}" graph="${graphState}" vectors="${vectorsState}" last_touched_at="${new Date(s.lastTouchedAt).toISOString()}" />`
5379
+ ` <project root="${escapeXML2(s.projectRoot)}"${alias} pinned="${s.pinned}" graph="${graphState}" vectors="${vectorsState}"${vectorsHint} last_touched_at="${new Date(s.lastTouchedAt).toISOString()}" />`
5367
5380
  );
5368
5381
  }
5369
5382
  lines.push(" </active_projects>");
@@ -5394,7 +5407,7 @@ function registerStatusTool(registry, ctx) {
5394
5407
  async (args) => {
5395
5408
  const { project_root } = Schema31.parse(args ?? {});
5396
5409
  void project_root;
5397
- return renderStatusXml({
5410
+ return await renderStatusXml({
5398
5411
  defaultRoot: ctx.noDefaultMode ? null : ctx.projectRoot,
5399
5412
  manager: ctx.stateManager,
5400
5413
  registry: ctx.registry
@@ -7081,7 +7094,8 @@ function registerBlastRadiusTool(registry, ctx) {
7081
7094
  return '<blast_radius changed_files="0">\n <!-- No changed files detected -->\n</blast_radius>';
7082
7095
  }
7083
7096
  const result = await computeBlastRadius({ changedFiles: files, depth, projectRoot: gitRoot, graph });
7084
- const report = getImpactRadius({ graph, overlay: ctx.overlay, changedFiles: files, depth });
7097
+ const overlay = await ctx.getOverlay(project_root) ?? void 0;
7098
+ const report = getImpactRadius({ graph, overlay, changedFiles: files, depth });
7085
7099
  return buildBlastRadiusXml(result, depth, detail_level, report.historicalCoupling);
7086
7100
  }
7087
7101
  );
@@ -8718,15 +8732,16 @@ function registerDetectChangesTool(registry, ctx) {
8718
8732
  if (files.length === 0) {
8719
8733
  return '<detect_changes count="0">\n <!-- No changed files detected -->\n</detect_changes>';
8720
8734
  }
8735
+ const overlay = await ctx.getOverlay(project_root) ?? void 0;
8721
8736
  const { changedFiles: scored, summary } = detectChanges({
8722
8737
  graph,
8723
- overlay: ctx.overlay,
8738
+ overlay,
8724
8739
  changedFiles: files
8725
8740
  });
8726
8741
  if (detail_level === "minimal") {
8727
8742
  return `<detect_changes count="${scored.length}" critical="${summary.critical}" high="${summary.high}" medium="${summary.medium}" low="${summary.low}" detail_level="minimal" />`;
8728
8743
  }
8729
- const hasOverlay = ctx.overlay !== void 0;
8744
+ const hasOverlay = overlay !== void 0;
8730
8745
  const xml = [
8731
8746
  `<detect_changes count="${scored.length}" critical="${summary.critical}" high="${summary.high}" medium="${summary.medium}" low="${summary.low}">`
8732
8747
  ];
@@ -8867,7 +8882,7 @@ function registerFullTextSearchTool(registry, ctx) {
8867
8882
  };
8868
8883
  if (mode === "semantic") {
8869
8884
  try {
8870
- const { generateEmbedding: generateEmbedding2 } = await import("./embedder-E2GDBGOH.js");
8885
+ const { generateEmbedding: generateEmbedding2 } = await import("./embedder-WQMRK5T7.js");
8871
8886
  const store = await ctx.getStore(project_root);
8872
8887
  const embedding = await generateEmbedding2(query);
8873
8888
  const results = await store.search(embedding, limit);
@@ -8905,7 +8920,7 @@ function registerFullTextSearchTool(registry, ctx) {
8905
8920
  let merged = keywordResults.slice(0, limit);
8906
8921
  if (mode === "hybrid") {
8907
8922
  try {
8908
- const { generateEmbedding: generateEmbedding2 } = await import("./embedder-E2GDBGOH.js");
8923
+ const { generateEmbedding: generateEmbedding2 } = await import("./embedder-WQMRK5T7.js");
8909
8924
  const store = await ctx.getStore(project_root);
8910
8925
  const embedding = await generateEmbedding2(query);
8911
8926
  const vectorResults = await store.search(embedding, Math.ceil(limit / 2));
@@ -9436,6 +9451,28 @@ function registerFindLargeFunctionsTool(registry, ctx) {
9436
9451
 
9437
9452
  // packages/core/src/tools/git-coupling.ts
9438
9453
  import { z as z31 } from "zod";
9454
+
9455
+ // packages/core/src/tools/overlayNote.ts
9456
+ import fs22 from "fs";
9457
+ import path24 from "path";
9458
+ async function overlayUnavailableNote(ctx, projectRoot) {
9459
+ let rootDir = "";
9460
+ try {
9461
+ const graph = await ctx.getGraph(projectRoot);
9462
+ rootDir = graph.getRootDir();
9463
+ } catch {
9464
+ }
9465
+ if (rootDir) {
9466
+ const overlayFile = path24.join(rootDir, ".ctxloom", "git-overlay.json");
9467
+ if (fs22.existsSync(overlayFile)) {
9468
+ return `Git overlay exists at ${overlayFile} but could not be loaded into the server. Restart the MCP server (or check its logs for a git/overlay error); a re-index is not required.`;
9469
+ }
9470
+ return `No git overlay for ${rootDir}. Build it by running \`ctxloom index\` in that repo (the git overlay is created automatically when git is enabled). If the server was started with --no-git, restart it without that flag.`;
9471
+ }
9472
+ return "Git overlay not available. Run `ctxloom index` with git enabled to build it.";
9473
+ }
9474
+
9475
+ // packages/core/src/tools/git-coupling.ts
9439
9476
  var Schema26 = z31.object({
9440
9477
  file: z31.string().describe("File path to look up co-changed siblings for"),
9441
9478
  limit: z31.number().int().min(1).max(50).default(10),
@@ -9443,11 +9480,11 @@ var Schema26 = z31.object({
9443
9480
  half_life_days: z31.number().int().min(1).max(3650).default(90),
9444
9481
  project_root: ProjectRootField
9445
9482
  });
9446
- function overlayUnavailableResponse(file) {
9483
+ function overlayUnavailableResponse(file, note) {
9447
9484
  return {
9448
9485
  file,
9449
9486
  coupledFiles: [],
9450
- note: "Git overlay not available. Re-index with --with-git to enable coupling data."
9487
+ note
9451
9488
  };
9452
9489
  }
9453
9490
  function buildExplanation(sharedCommits, lastSharedDaysAgo) {
@@ -9472,13 +9509,18 @@ function registerGitCouplingTool(registry, ctx) {
9472
9509
  }
9473
9510
  },
9474
9511
  async (args) => {
9475
- const { file, limit, min_confidence, half_life_days } = Schema26.parse(args);
9476
- if (!ctx.overlay) {
9477
- return JSON.stringify(overlayUnavailableResponse(file));
9512
+ const { file, limit, min_confidence, half_life_days, project_root } = Schema26.parse(args);
9513
+ const overlay = await ctx.getOverlay(project_root);
9514
+ if (!overlay) {
9515
+ return JSON.stringify(
9516
+ overlayUnavailableResponse(file, await overlayUnavailableNote(ctx, project_root))
9517
+ );
9478
9518
  }
9479
- const coChange = ctx.overlay.coChange;
9519
+ const coChange = overlay.coChange;
9480
9520
  if (coChange.size().pairs === 0) {
9481
- return JSON.stringify(overlayUnavailableResponse(file));
9521
+ return JSON.stringify(
9522
+ overlayUnavailableResponse(file, await overlayUnavailableNote(ctx, project_root))
9523
+ );
9482
9524
  }
9483
9525
  const nowSec = Math.floor(Date.now() / 1e3);
9484
9526
  const pairs = coChange.topFor({
@@ -9543,8 +9585,9 @@ function registerRiskOverlayTool(registry, ctx) {
9543
9585
  }
9544
9586
  },
9545
9587
  async (args) => {
9546
- const { nodes } = Schema27.parse(args);
9547
- if (!ctx.overlay) {
9588
+ const { nodes, project_root } = Schema27.parse(args);
9589
+ const overlay = await ctx.getOverlay(project_root);
9590
+ if (!overlay) {
9548
9591
  const response2 = {
9549
9592
  nodes: nodes.map((file) => ({
9550
9593
  file,
@@ -9558,11 +9601,11 @@ function registerRiskOverlayTool(registry, ctx) {
9558
9601
  note: "no git data"
9559
9602
  })),
9560
9603
  overallRiskScore: 0,
9561
- note: "Git overlay not available. Re-index with --with-git to enable risk data."
9604
+ note: await overlayUnavailableNote(ctx, project_root)
9562
9605
  };
9563
9606
  return JSON.stringify(response2);
9564
9607
  }
9565
- const { churn, ownership, coChange } = ctx.overlay;
9608
+ const { churn, ownership, coChange } = overlay;
9566
9609
  const nowSec = Math.floor(Date.now() / 1e3);
9567
9610
  const nodeEntries = nodes.map((file) => {
9568
9611
  const churnStats = churn.statsFor(file);
@@ -9627,8 +9670,8 @@ var RulesConfigError = class extends Error {
9627
9670
  };
9628
9671
 
9629
9672
  // packages/core/src/rules/loadConfig.ts
9630
- import fs22 from "fs/promises";
9631
- import path24 from "path";
9673
+ import fs23 from "fs/promises";
9674
+ import path25 from "path";
9632
9675
  import yaml from "js-yaml";
9633
9676
  import { z as z33 } from "zod";
9634
9677
  var RuleSchema = z33.object({
@@ -9643,10 +9686,10 @@ var RulesConfigSchema = z33.object({
9643
9686
  rules: z33.array(RuleSchema).default([])
9644
9687
  });
9645
9688
  async function loadRulesConfig(rootDir) {
9646
- const filePath = path24.join(rootDir, ".ctxloom", "rules.yml");
9689
+ const filePath = path25.join(rootDir, ".ctxloom", "rules.yml");
9647
9690
  let raw;
9648
9691
  try {
9649
- raw = await fs22.readFile(filePath, "utf-8");
9692
+ raw = await fs23.readFile(filePath, "utf-8");
9650
9693
  } catch (err) {
9651
9694
  if (err.code === "ENOENT") return null;
9652
9695
  throw new RulesConfigError(`Failed to read rules config: ${String(err)}`);
@@ -10059,11 +10102,11 @@ function readRecentChanges(projectRoot) {
10059
10102
  return lines.slice(0, 20).map((line) => {
10060
10103
  const x = line[0];
10061
10104
  const y = line[1];
10062
- const path38 = line.slice(3).trim();
10105
+ const path39 = line.slice(3).trim();
10063
10106
  let status = "?";
10064
10107
  const xy = x === " " ? y : x;
10065
10108
  if (xy === "M" || xy === "A" || xy === "D" || xy === "R") status = xy;
10066
- return { file: path38, status };
10109
+ return { file: path39, status };
10067
10110
  });
10068
10111
  } catch {
10069
10112
  return [];
@@ -10290,8 +10333,8 @@ function createToolRegistry(ctx) {
10290
10333
  }
10291
10334
 
10292
10335
  // packages/core/src/tools/ruleManager.ts
10293
- import fs23 from "fs";
10294
- import path25 from "path";
10336
+ import fs24 from "fs";
10337
+ import path26 from "path";
10295
10338
  var RULE_FILES = [
10296
10339
  ".cursorrules",
10297
10340
  "CLAUDE.md",
@@ -10315,30 +10358,30 @@ var RuleManager = class {
10315
10358
  if (this.cachedRules) return this.cachedRules;
10316
10359
  const rules = [];
10317
10360
  for (const ruleFile of RULE_FILES) {
10318
- const fullPath = path25.join(this.projectRoot, ruleFile);
10361
+ const fullPath = path26.join(this.projectRoot, ruleFile);
10319
10362
  try {
10320
10363
  this.pathValidator.validate(fullPath);
10321
- if (fs23.existsSync(fullPath)) {
10322
- const stat = fs23.statSync(fullPath);
10364
+ if (fs24.existsSync(fullPath)) {
10365
+ const stat = fs24.statSync(fullPath);
10323
10366
  if (stat.isFile()) {
10324
- const content = fs23.readFileSync(fullPath, "utf-8");
10367
+ const content = fs24.readFileSync(fullPath, "utf-8");
10325
10368
  rules.push({
10326
10369
  name: ruleFile,
10327
10370
  path: ruleFile,
10328
10371
  content
10329
10372
  });
10330
10373
  } else if (stat.isDirectory()) {
10331
- const dirEntries = fs23.readdirSync(fullPath);
10374
+ const dirEntries = fs24.readdirSync(fullPath);
10332
10375
  for (const entry of dirEntries) {
10333
- const entryPath = path25.join(fullPath, entry);
10376
+ const entryPath = path26.join(fullPath, entry);
10334
10377
  try {
10335
10378
  this.pathValidator.validate(entryPath);
10336
10379
  } catch {
10337
10380
  continue;
10338
10381
  }
10339
- const entryStat = fs23.statSync(entryPath);
10382
+ const entryStat = fs24.statSync(entryPath);
10340
10383
  if (entryStat.isFile()) {
10341
- const content = fs23.readFileSync(entryPath, "utf-8");
10384
+ const content = fs24.readFileSync(entryPath, "utf-8");
10342
10385
  rules.push({
10343
10386
  name: `${ruleFile}/${entry}`,
10344
10387
  path: `${ruleFile}/${entry}`,
@@ -10381,8 +10424,8 @@ var RuleManager = class {
10381
10424
  };
10382
10425
 
10383
10426
  // packages/core/src/review/AuthorResolver.ts
10384
- import fs24 from "fs/promises";
10385
- import path26 from "path";
10427
+ import fs25 from "fs/promises";
10428
+ import path27 from "path";
10386
10429
  import yaml2 from "js-yaml";
10387
10430
  var AuthorResolver = class {
10388
10431
  constructor(ctxloomDir) {
@@ -10407,8 +10450,8 @@ var AuthorResolver = class {
10407
10450
  /** Write a new mapping to the cache file. */
10408
10451
  async writeCache(email, handle) {
10409
10452
  this.cache = { ...this.cache, [email]: handle };
10410
- await fs24.writeFile(
10411
- path26.join(this.ctxloomDir, "authors-cache.json"),
10453
+ await fs25.writeFile(
10454
+ path27.join(this.ctxloomDir, "authors-cache.json"),
10412
10455
  JSON.stringify(this.cache, null, 2)
10413
10456
  );
10414
10457
  }
@@ -10417,9 +10460,9 @@ var AuthorResolver = class {
10417
10460
  return emails.filter((e) => this.resolve(e) === void 0);
10418
10461
  }
10419
10462
  async loadYml() {
10420
- const file = path26.join(this.ctxloomDir, "authors.yml");
10463
+ const file = path27.join(this.ctxloomDir, "authors.yml");
10421
10464
  try {
10422
- const raw = await fs24.readFile(file, "utf8");
10465
+ const raw = await fs25.readFile(file, "utf8");
10423
10466
  const parsed = yaml2.load(raw);
10424
10467
  if (!parsed) return;
10425
10468
  this.mappings = parsed.mappings ?? {};
@@ -10428,9 +10471,9 @@ var AuthorResolver = class {
10428
10471
  }
10429
10472
  }
10430
10473
  async loadCache() {
10431
- const file = path26.join(this.ctxloomDir, "authors-cache.json");
10474
+ const file = path27.join(this.ctxloomDir, "authors-cache.json");
10432
10475
  try {
10433
- const raw = await fs24.readFile(file, "utf8");
10476
+ const raw = await fs25.readFile(file, "utf8");
10434
10477
  this.cache = JSON.parse(raw);
10435
10478
  } catch {
10436
10479
  }
@@ -10455,8 +10498,8 @@ async function resolveViaGitHubApi(email, owner, repo, token) {
10455
10498
  }
10456
10499
 
10457
10500
  // packages/core/src/review/CodeownersWriter.ts
10458
- import fs25 from "fs/promises";
10459
- import path27 from "path";
10501
+ import fs26 from "fs/promises";
10502
+ import path28 from "path";
10460
10503
  var MARKER_START = "# <ctxloom:start> \u2014 managed by ctxloom review-suggest; do not edit between markers";
10461
10504
  var MARKER_START_DETECT = "# <ctxloom:start>";
10462
10505
  var MARKER_END = "# <ctxloom:end>";
@@ -10488,15 +10531,15 @@ ${block}
10488
10531
  async function generateCODEOWNERS(codeownersPath, rules) {
10489
10532
  let existing = "";
10490
10533
  try {
10491
- existing = await fs25.readFile(codeownersPath, "utf8");
10534
+ existing = await fs26.readFile(codeownersPath, "utf8");
10492
10535
  } catch {
10493
10536
  }
10494
10537
  const block = buildCodeownersBlock(rules);
10495
10538
  return mergeIntoFile(existing, block);
10496
10539
  }
10497
10540
  async function writeCODEOWNERS(codeownersPath, content) {
10498
- await fs25.mkdir(path27.dirname(codeownersPath), { recursive: true });
10499
- await fs25.writeFile(codeownersPath, content, "utf8");
10541
+ await fs26.mkdir(path28.dirname(codeownersPath), { recursive: true });
10542
+ await fs26.writeFile(codeownersPath, content, "utf8");
10500
10543
  }
10501
10544
 
10502
10545
  // packages/core/src/review/types.ts
@@ -10681,8 +10724,8 @@ function matchGlob(pattern, value) {
10681
10724
  }
10682
10725
 
10683
10726
  // packages/core/src/review/loadConfig.ts
10684
- import fs26 from "fs/promises";
10685
- import path28 from "path";
10727
+ import fs27 from "fs/promises";
10728
+ import path29 from "path";
10686
10729
  import yaml3 from "js-yaml";
10687
10730
  function freshDefaults() {
10688
10731
  return {
@@ -10693,9 +10736,9 @@ function freshDefaults() {
10693
10736
  };
10694
10737
  }
10695
10738
  async function loadReviewConfig(root) {
10696
- const file = path28.join(root, ".ctxloom", "review.yml");
10739
+ const file = path29.join(root, ".ctxloom", "review.yml");
10697
10740
  try {
10698
- const raw = await fs26.readFile(file, "utf8");
10741
+ const raw = await fs27.readFile(file, "utf8");
10699
10742
  const parsed = yaml3.load(raw);
10700
10743
  if (!parsed) return freshDefaults();
10701
10744
  return {
@@ -10710,13 +10753,13 @@ async function loadReviewConfig(root) {
10710
10753
  }
10711
10754
 
10712
10755
  // packages/core/src/security/PathValidator.ts
10713
- import path29 from "path";
10714
- import fs27 from "fs";
10756
+ import path30 from "path";
10757
+ import fs28 from "fs";
10715
10758
  var MAX_FILE_SIZE = 5 * 1024 * 1024;
10716
10759
  var PathValidator = class {
10717
10760
  canonicalRoot;
10718
10761
  constructor(projectRoot) {
10719
- this.canonicalRoot = fs27.realpathSync(path29.resolve(projectRoot));
10762
+ this.canonicalRoot = fs28.realpathSync(path30.resolve(projectRoot));
10720
10763
  }
10721
10764
  /**
10722
10765
  * Validates that the given input path resolves within the project root.
@@ -10726,14 +10769,14 @@ var PathValidator = class {
10726
10769
  * @throws Error if the path escapes the project root
10727
10770
  */
10728
10771
  validate(inputPath) {
10729
- const resolved = path29.resolve(this.canonicalRoot, inputPath);
10772
+ const resolved = path30.resolve(this.canonicalRoot, inputPath);
10730
10773
  let canonical;
10731
10774
  try {
10732
- canonical = fs27.realpathSync(resolved);
10775
+ canonical = fs28.realpathSync(resolved);
10733
10776
  } catch {
10734
10777
  canonical = resolved;
10735
10778
  }
10736
- if (!canonical.startsWith(this.canonicalRoot + path29.sep) && canonical !== this.canonicalRoot) {
10779
+ if (!canonical.startsWith(this.canonicalRoot + path30.sep) && canonical !== this.canonicalRoot) {
10737
10780
  throw new Error(
10738
10781
  `Path traversal blocked: "${inputPath}" resolves outside of the project root`
10739
10782
  );
@@ -10750,7 +10793,7 @@ var PathValidator = class {
10750
10793
  * Converts an absolute path to a relative path from the project root.
10751
10794
  */
10752
10795
  toRelative(absolutePath) {
10753
- return path29.relative(this.canonicalRoot, absolutePath);
10796
+ return path30.relative(this.canonicalRoot, absolutePath);
10754
10797
  }
10755
10798
  /**
10756
10799
  * Validates and reads a file, returning its content.
@@ -10758,11 +10801,11 @@ var PathValidator = class {
10758
10801
  */
10759
10802
  readFile(inputPath) {
10760
10803
  const absPath = this.validate(inputPath);
10761
- const stat = fs27.statSync(absPath);
10804
+ const stat = fs28.statSync(absPath);
10762
10805
  if (stat.size > MAX_FILE_SIZE) {
10763
10806
  throw new Error(`File too large: ${inputPath} (${Math.round(stat.size / 1024)}KB, max ${MAX_FILE_SIZE / 1024 / 1024}MB)`);
10764
10807
  }
10765
- return fs27.readFileSync(absPath, "utf-8");
10808
+ return fs28.readFileSync(absPath, "utf-8");
10766
10809
  }
10767
10810
  /**
10768
10811
  * Checks if a path exists and is within the project root.
@@ -10913,7 +10956,7 @@ var EmailAlreadyUsedError = class extends Error {
10913
10956
 
10914
10957
  // packages/core/src/license/LicenseStore.ts
10915
10958
  import { readFileSync, writeFileSync, unlinkSync, mkdirSync, chmodSync, existsSync } from "fs";
10916
- import path30 from "path";
10959
+ import path31 from "path";
10917
10960
 
10918
10961
  // packages/core/src/license/types.ts
10919
10962
  import { z as z37 } from "zod";
@@ -10934,7 +10977,7 @@ var LicenseFileSchema = z37.object({
10934
10977
 
10935
10978
  // packages/core/src/license/LicenseStore.ts
10936
10979
  function licenseFilePath(home) {
10937
- return path30.join(home, ".ctxloom", "license.json");
10980
+ return path31.join(home, ".ctxloom", "license.json");
10938
10981
  }
10939
10982
  var LicenseStore = class {
10940
10983
  filePath;
@@ -10957,7 +11000,7 @@ var LicenseStore = class {
10957
11000
  }
10958
11001
  }
10959
11002
  async write(license) {
10960
- mkdirSync(path30.dirname(this.filePath), { recursive: true });
11003
+ mkdirSync(path31.dirname(this.filePath), { recursive: true });
10961
11004
  writeFileSync(this.filePath, JSON.stringify(license, null, 2), {
10962
11005
  encoding: "utf8",
10963
11006
  mode: 384
@@ -11088,11 +11131,11 @@ import os5 from "os";
11088
11131
 
11089
11132
  // packages/core/src/license/DistinctIdStore.ts
11090
11133
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
11091
- import path31 from "path";
11134
+ import path32 from "path";
11092
11135
  import os3 from "os";
11093
11136
  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;
11094
11137
  function distinctIdPath(home) {
11095
- return path31.join(home ?? os3.homedir(), ".ctxloom", "distinct_id");
11138
+ return path32.join(home ?? os3.homedir(), ".ctxloom", "distinct_id");
11096
11139
  }
11097
11140
  function isValidV4(id) {
11098
11141
  return typeof id === "string" && UUID_V4_REGEX.test(id);
@@ -11113,7 +11156,7 @@ function getOrCreateDistinctId(home) {
11113
11156
  id: crypto.randomUUID(),
11114
11157
  alias_pending: os3.hostname()
11115
11158
  };
11116
- mkdirSync2(path31.dirname(filePath), { recursive: true });
11159
+ mkdirSync2(path32.dirname(filePath), { recursive: true });
11117
11160
  writeFileSync2(filePath, JSON.stringify(record), { mode: 384 });
11118
11161
  return record;
11119
11162
  }
@@ -11142,7 +11185,7 @@ var TELEMETRY_DISABLED = TELEMETRY_LEVEL === "off";
11142
11185
  function getTelemetryLevel() {
11143
11186
  return TELEMETRY_LEVEL;
11144
11187
  }
11145
- var CTXLOOM_VERSION2 = "1.7.8".length > 0 ? "1.7.8" : "dev";
11188
+ var CTXLOOM_VERSION2 = "1.7.10".length > 0 ? "1.7.10" : "dev";
11146
11189
  var POSTHOG_HOST = "https://eu.i.posthog.com";
11147
11190
  var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
11148
11191
  var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
@@ -11275,17 +11318,17 @@ function parseStack(stack) {
11275
11318
 
11276
11319
  // packages/core/src/license/FunnelMilestones.ts
11277
11320
  import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
11278
- import path32 from "path";
11321
+ import path33 from "path";
11279
11322
  import os4 from "os";
11280
11323
  var INSTALL_MARKER = "installed_at";
11281
11324
  var FIRST_REVIEW_MARKER = "first_review_at";
11282
11325
  function writeMarker(filePath) {
11283
- mkdirSync3(path32.dirname(filePath), { recursive: true });
11326
+ mkdirSync3(path33.dirname(filePath), { recursive: true });
11284
11327
  writeFileSync3(filePath, (/* @__PURE__ */ new Date()).toISOString(), { mode: 384 });
11285
11328
  }
11286
11329
  function shouldEmitInstallCompleted(home) {
11287
11330
  const root = home ?? os4.homedir();
11288
- const marker = path32.join(root, ".ctxloom", INSTALL_MARKER);
11331
+ const marker = path33.join(root, ".ctxloom", INSTALL_MARKER);
11289
11332
  if (existsSync3(marker)) return false;
11290
11333
  try {
11291
11334
  writeMarker(marker);
@@ -11294,7 +11337,7 @@ function shouldEmitInstallCompleted(home) {
11294
11337
  return true;
11295
11338
  }
11296
11339
  function shouldEmitFirstReviewRun(projectRoot) {
11297
- const marker = path32.join(projectRoot, ".ctxloom", FIRST_REVIEW_MARKER);
11340
+ const marker = path33.join(projectRoot, ".ctxloom", FIRST_REVIEW_MARKER);
11298
11341
  if (existsSync3(marker)) return false;
11299
11342
  try {
11300
11343
  writeMarker(marker);
@@ -11412,16 +11455,16 @@ async function startTrial(email, opts = {}) {
11412
11455
 
11413
11456
  // packages/core/src/license/TelemetryNotice.ts
11414
11457
  import { existsSync as existsSync4, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
11415
- import path33 from "path";
11458
+ import path34 from "path";
11416
11459
  import os6 from "os";
11417
11460
  function noticePath(home) {
11418
- return path33.join(home ?? os6.homedir(), ".ctxloom", "telemetry_notice_shown");
11461
+ return path34.join(home ?? os6.homedir(), ".ctxloom", "telemetry_notice_shown");
11419
11462
  }
11420
11463
  function shouldShowTelemetryNotice(home) {
11421
11464
  const filePath = noticePath(home);
11422
11465
  if (existsSync4(filePath)) return false;
11423
11466
  try {
11424
- mkdirSync4(path33.dirname(filePath), { recursive: true });
11467
+ mkdirSync4(path34.dirname(filePath), { recursive: true });
11425
11468
  writeFileSync4(filePath, (/* @__PURE__ */ new Date()).toISOString(), { mode: 384 });
11426
11469
  } catch {
11427
11470
  }
@@ -11429,13 +11472,13 @@ function shouldShowTelemetryNotice(home) {
11429
11472
  }
11430
11473
 
11431
11474
  // packages/core/src/server/ProjectState.ts
11432
- import path35 from "path";
11475
+ import path36 from "path";
11433
11476
 
11434
11477
  // packages/core/src/server/projectId.ts
11435
11478
  import crypto5 from "crypto";
11436
- import path34 from "path";
11479
+ import path35 from "path";
11437
11480
  function hashProjectRoot(absPath) {
11438
- const canonical = path34.resolve(absPath);
11481
+ const canonical = path35.resolve(absPath);
11439
11482
  return crypto5.createHash("sha256").update(canonical).digest("hex").slice(0, 16);
11440
11483
  }
11441
11484
 
@@ -11443,7 +11486,7 @@ function hashProjectRoot(absPath) {
11443
11486
  function createProjectState(projectRoot, opts = {}) {
11444
11487
  return {
11445
11488
  projectRoot,
11446
- dbPath: path35.join(projectRoot, ".ctxloom", "vectors.lancedb"),
11489
+ dbPath: path36.join(projectRoot, ".ctxloom", "vectors.lancedb"),
11447
11490
  pinned: opts.pinned ?? false,
11448
11491
  lastTouchedAt: Date.now(),
11449
11492
  vectorsInitialized: false,
@@ -11454,6 +11497,7 @@ function createProjectState(projectRoot, opts = {}) {
11454
11497
  skeletonizerPromise: null,
11455
11498
  ruleManager: null,
11456
11499
  overlay: null,
11500
+ overlayPromise: null,
11457
11501
  watcher: null,
11458
11502
  pathValidator: null
11459
11503
  };
@@ -11494,6 +11538,7 @@ async function disposeProjectState(state) {
11494
11538
  state.skeletonizerPromise = null;
11495
11539
  state.ruleManager = null;
11496
11540
  state.overlay = null;
11541
+ state.overlayPromise = null;
11497
11542
  state.pathValidator = null;
11498
11543
  state.graphInitialized = false;
11499
11544
  state.vectorsInitialized = false;
@@ -11600,8 +11645,8 @@ var ProjectStateManager = class {
11600
11645
  };
11601
11646
 
11602
11647
  // packages/core/src/server/resolveProjectRoot.ts
11603
- import fs28 from "fs";
11604
- import path36 from "path";
11648
+ import fs29 from "fs";
11649
+ import path37 from "path";
11605
11650
  var PATH_SEPARATOR_PATTERN = /[/\\~]|^[A-Za-z]:/;
11606
11651
  function looksLikePath(value) {
11607
11652
  return PATH_SEPARATOR_PATTERN.test(value);
@@ -11626,13 +11671,13 @@ function resolvePathSafely(p, cwd) {
11626
11671
  let expanded = p;
11627
11672
  if (p === "~" || p.startsWith("~/")) {
11628
11673
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
11629
- expanded = p === "~" ? home : path36.join(home, p.slice(2));
11674
+ expanded = p === "~" ? home : path37.join(home, p.slice(2));
11630
11675
  }
11631
- return path36.isAbsolute(expanded) ? path36.resolve(expanded) : path36.resolve(cwd, expanded);
11676
+ return path37.isAbsolute(expanded) ? path37.resolve(expanded) : path37.resolve(cwd, expanded);
11632
11677
  }
11633
11678
  function realpathOrSame(p) {
11634
11679
  try {
11635
- return fs28.realpathSync(p);
11680
+ return fs29.realpathSync(p);
11636
11681
  } catch {
11637
11682
  return p;
11638
11683
  }
@@ -11657,7 +11702,7 @@ function resolveProjectRoot(input) {
11657
11702
  };
11658
11703
  }
11659
11704
  const resolved2 = resolvePathSafely(arg, cwd);
11660
- if (!fs28.existsSync(resolved2)) {
11705
+ if (!fs29.existsSync(resolved2)) {
11661
11706
  return {
11662
11707
  kind: "project_root_not_found",
11663
11708
  attemptedPath: resolved2,
@@ -11668,7 +11713,7 @@ function resolveProjectRoot(input) {
11668
11713
  }
11669
11714
  if (env !== void 0 && env !== "") {
11670
11715
  const resolved2 = resolvePathSafely(env, cwd);
11671
- if (!fs28.existsSync(resolved2)) {
11716
+ if (!fs29.existsSync(resolved2)) {
11672
11717
  return {
11673
11718
  kind: "project_root_not_found",
11674
11719
  attemptedPath: resolved2,
@@ -11695,12 +11740,12 @@ var FILESYSTEM_ROOTS = /* @__PURE__ */ new Set(["/", "C:\\", "D:\\", "E:\\", "F:
11695
11740
  function validateDefaultRoot(candidate) {
11696
11741
  if (FILESYSTEM_ROOTS.has(candidate)) return false;
11697
11742
  try {
11698
- const stat = fs28.statSync(candidate);
11743
+ const stat = fs29.statSync(candidate);
11699
11744
  if (!stat.isDirectory()) return false;
11700
11745
  } catch {
11701
11746
  return false;
11702
11747
  }
11703
- return PROJECT_MARKERS.some((m) => fs28.existsSync(path36.join(candidate, m)));
11748
+ return PROJECT_MARKERS.some((m) => fs29.existsSync(path37.join(candidate, m)));
11704
11749
  }
11705
11750
 
11706
11751
  // packages/core/src/server/structuredErrors.ts
@@ -11772,8 +11817,8 @@ var EmittedOnceTracker = class {
11772
11817
  };
11773
11818
 
11774
11819
  // packages/core/src/install/installer.ts
11775
- import fs29 from "fs";
11776
- import path37 from "path";
11820
+ import fs30 from "fs";
11821
+ import path38 from "path";
11777
11822
 
11778
11823
  // packages/core/src/install/templates.ts
11779
11824
  var RULES_BLOCK_NAME = "CTXLOOM-RULES";
@@ -12478,8 +12523,8 @@ function skillFilePath(name) {
12478
12523
  // packages/core/src/install/installer.ts
12479
12524
  function installHarness(opts = {}) {
12480
12525
  const cwd = opts.cwd ?? process.cwd();
12481
- const projectRoot = path37.resolve(cwd);
12482
- const stat = fs29.statSync(projectRoot);
12526
+ const projectRoot = path38.resolve(cwd);
12527
+ const stat = fs30.statSync(projectRoot);
12483
12528
  if (!stat.isDirectory()) {
12484
12529
  throw new Error(`installHarness: ${projectRoot} is not a directory`);
12485
12530
  }
@@ -12527,20 +12572,20 @@ function resolveExtraHosts(ids, warnings) {
12527
12572
  return HOST_ADAPTERS.filter((a) => requested.has(a.id));
12528
12573
  }
12529
12574
  function safeJoin(root, name) {
12530
- const target = path37.resolve(root, name);
12531
- const rootResolved = path37.resolve(root);
12575
+ const target = path38.resolve(root, name);
12576
+ const rootResolved = path38.resolve(root);
12532
12577
  const caseFold = process.platform === "darwin" || process.platform === "win32";
12533
12578
  const t = caseFold ? target.toLowerCase() : target;
12534
12579
  const r = caseFold ? rootResolved.toLowerCase() : rootResolved;
12535
- if (!t.startsWith(r + path37.sep) && t !== r) {
12580
+ if (!t.startsWith(r + path38.sep) && t !== r) {
12536
12581
  throw new Error(`installHarness: refusing to write outside project root: ${target}`);
12537
12582
  }
12538
12583
  return target;
12539
12584
  }
12540
12585
  function writeRulesBlock(projectRoot, filename, opts) {
12541
12586
  const filePath = safeJoin(projectRoot, filename);
12542
- const existed = fs29.existsSync(filePath);
12543
- const existing = existed ? fs29.readFileSync(filePath, "utf-8") : "";
12587
+ const existed = fs30.existsSync(filePath);
12588
+ const existing = existed ? fs30.readFileSync(filePath, "utf-8") : "";
12544
12589
  if (existed) {
12545
12590
  const block = extractBlock(existing, RULES_BLOCK_NAME);
12546
12591
  if (block) {
@@ -12558,7 +12603,7 @@ function writeRulesBlock(projectRoot, filename, opts) {
12558
12603
  }
12559
12604
  const next = upsertBlock(existing, RULES_BLOCK_NAME, RULES_BLOCK_CONTENT);
12560
12605
  if (!opts.dryRun) {
12561
- fs29.writeFileSync(filePath, next, "utf-8");
12606
+ fs30.writeFileSync(filePath, next, "utf-8");
12562
12607
  }
12563
12608
  return {
12564
12609
  path: filePath,
@@ -12571,15 +12616,15 @@ function writeRulesBlock(projectRoot, filename, opts) {
12571
12616
  function writeHooksJson(projectRoot, opts) {
12572
12617
  const dir = safeJoin(projectRoot, ".claude");
12573
12618
  const filePath = safeJoin(projectRoot, ".claude/hooks.json");
12574
- const existed = fs29.existsSync(filePath);
12619
+ const existed = fs30.existsSync(filePath);
12575
12620
  let current = {};
12576
12621
  if (existed) {
12577
12622
  try {
12578
- const text = fs29.readFileSync(filePath, "utf-8");
12623
+ const text = fs30.readFileSync(filePath, "utf-8");
12579
12624
  current = JSON.parse(text);
12580
12625
  } catch (err) {
12581
12626
  opts.warnings.push(
12582
- `Could not parse existing ${path37.relative(projectRoot, filePath)}; treating as empty. (${err instanceof Error ? err.message : String(err)})`
12627
+ `Could not parse existing ${path38.relative(projectRoot, filePath)}; treating as empty. (${err instanceof Error ? err.message : String(err)})`
12583
12628
  );
12584
12629
  current = {};
12585
12630
  }
@@ -12596,12 +12641,12 @@ function writeHooksJson(projectRoot, opts) {
12596
12641
  const nextJson = JSON.stringify(merged, null, 2) + "\n";
12597
12642
  let alreadyCorrect = false;
12598
12643
  if (existed) {
12599
- const currentText = fs29.readFileSync(filePath, "utf-8");
12644
+ const currentText = fs30.readFileSync(filePath, "utf-8");
12600
12645
  if (currentText === nextJson) alreadyCorrect = true;
12601
12646
  }
12602
12647
  if (!opts.dryRun && !alreadyCorrect) {
12603
- fs29.mkdirSync(dir, { recursive: true });
12604
- fs29.writeFileSync(filePath, nextJson, "utf-8");
12648
+ fs30.mkdirSync(dir, { recursive: true });
12649
+ fs30.writeFileSync(filePath, nextJson, "utf-8");
12605
12650
  }
12606
12651
  return {
12607
12652
  path: filePath,
@@ -12623,19 +12668,19 @@ function isCtxloomEntry(entry, expectedMatcher) {
12623
12668
  }
12624
12669
  function writeHostAdapter(projectRoot, adapter, opts) {
12625
12670
  const filePath = safeJoin(projectRoot, adapter.path);
12626
- const dir = path37.dirname(filePath);
12627
- const existed = fs29.existsSync(filePath);
12671
+ const dir = path38.dirname(filePath);
12672
+ const existed = fs30.existsSync(filePath);
12628
12673
  const rendered = adapter.render();
12629
12674
  let alreadyCorrect = false;
12630
12675
  if (existed) {
12631
- const current = fs29.readFileSync(filePath, "utf-8");
12676
+ const current = fs30.readFileSync(filePath, "utf-8");
12632
12677
  if (adapter.isCanonical(current)) {
12633
12678
  alreadyCorrect = true;
12634
12679
  }
12635
12680
  }
12636
12681
  if (!opts.dryRun && !alreadyCorrect) {
12637
- fs29.mkdirSync(dir, { recursive: true });
12638
- fs29.writeFileSync(filePath, rendered, "utf-8");
12682
+ fs30.mkdirSync(dir, { recursive: true });
12683
+ fs30.writeFileSync(filePath, rendered, "utf-8");
12639
12684
  }
12640
12685
  return {
12641
12686
  path: filePath,
@@ -12648,16 +12693,16 @@ function writeHostAdapter(projectRoot, adapter, opts) {
12648
12693
  function writeSkill(projectRoot, skill, opts) {
12649
12694
  const dir = safeJoin(projectRoot, `.claude/skills/${skill.name}`);
12650
12695
  const filePath = safeJoin(projectRoot, skillFilePath(skill.name));
12651
- const existed = fs29.existsSync(filePath);
12696
+ const existed = fs30.existsSync(filePath);
12652
12697
  let alreadyCorrect = false;
12653
12698
  if (existed) {
12654
- if (fs29.readFileSync(filePath, "utf-8") === skill.content) {
12699
+ if (fs30.readFileSync(filePath, "utf-8") === skill.content) {
12655
12700
  alreadyCorrect = true;
12656
12701
  }
12657
12702
  }
12658
12703
  if (!opts.dryRun && !alreadyCorrect) {
12659
- fs29.mkdirSync(dir, { recursive: true });
12660
- fs29.writeFileSync(filePath, skill.content, "utf-8");
12704
+ fs30.mkdirSync(dir, { recursive: true });
12705
+ fs30.writeFileSync(filePath, skill.content, "utf-8");
12661
12706
  }
12662
12707
  return {
12663
12708
  path: filePath,
@@ -12670,17 +12715,17 @@ function writeSkill(projectRoot, skill, opts) {
12670
12715
  function writeSessionStartScript(projectRoot, opts) {
12671
12716
  const dir = safeJoin(projectRoot, ".claude/hooks");
12672
12717
  const filePath = safeJoin(projectRoot, ".claude/hooks/session-start.sh");
12673
- const existed = fs29.existsSync(filePath);
12718
+ const existed = fs30.existsSync(filePath);
12674
12719
  let alreadyCorrect = false;
12675
12720
  if (existed) {
12676
- const current = fs29.readFileSync(filePath, "utf-8");
12721
+ const current = fs30.readFileSync(filePath, "utf-8");
12677
12722
  if (current === SESSION_START_FULL) alreadyCorrect = true;
12678
12723
  }
12679
12724
  if (!opts.dryRun && !alreadyCorrect) {
12680
- fs29.mkdirSync(dir, { recursive: true });
12681
- fs29.writeFileSync(filePath, SESSION_START_FULL, "utf-8");
12725
+ fs30.mkdirSync(dir, { recursive: true });
12726
+ fs30.writeFileSync(filePath, SESSION_START_FULL, "utf-8");
12682
12727
  try {
12683
- fs29.chmodSync(filePath, 493);
12728
+ fs30.chmodSync(filePath, 493);
12684
12729
  } catch {
12685
12730
  }
12686
12731
  }
@@ -12828,4 +12873,4 @@ export {
12828
12873
  skillFilePath,
12829
12874
  installHarness
12830
12875
  };
12831
- //# sourceMappingURL=chunk-7WLMI4AD.js.map
12876
+ //# sourceMappingURL=chunk-J3NVYR6J.js.map