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.
- package/README.md +2 -2
- package/apps/dashboard/dist/server/index.js +86 -79
- package/dist/VectorStore-ZA4EEL6C.js +11 -0
- package/dist/{chunk-7WLMI4AD.js → chunk-J3NVYR6J.js} +185 -140
- package/dist/{chunk-Z3NQHCWG.js → chunk-UWQKNIZO.js} +37 -5
- package/dist/{chunk-R32CUQAL.js → chunk-XJJHX227.js} +2 -2
- package/dist/{embedder-E2GDBGOH.js → embedder-WQMRK5T7.js} +2 -2
- package/dist/index.js +43 -14
- package/dist/{src-C5NS2JAW.js → src-7ETGK3MB.js} +7 -5
- package/dist/workers/indexerWorker.js +2 -2
- package/package.json +1 -1
- package/dist/VectorStore-IHNQ3NOD.js +0 -9
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
VectorStore
|
|
3
|
-
|
|
2
|
+
VectorStore,
|
|
3
|
+
isCorruptionError
|
|
4
|
+
} from "./chunk-UWQKNIZO.js";
|
|
4
5
|
import {
|
|
5
6
|
collectFiles,
|
|
6
7
|
generateEmbedding,
|
|
7
8
|
isIgnoredDir
|
|
8
|
-
} from "./chunk-
|
|
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.
|
|
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
|
|
4222
|
-
this.nodeCounts.set(
|
|
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 [
|
|
4370
|
-
nodes[
|
|
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 [
|
|
4386
|
-
idx.nodes.set(
|
|
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(
|
|
4400
|
-
const existing = this.nodes.get(
|
|
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(
|
|
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 [
|
|
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[
|
|
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 [
|
|
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(
|
|
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(
|
|
4517
|
-
const existing = this.nodes.get(
|
|
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(
|
|
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
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
|
-
|
|
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 && 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
|
|
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
|
|
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 =
|
|
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-
|
|
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-
|
|
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
|
|
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
|
-
|
|
9477
|
-
|
|
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 =
|
|
9519
|
+
const coChange = overlay.coChange;
|
|
9480
9520
|
if (coChange.size().pairs === 0) {
|
|
9481
|
-
return JSON.stringify(
|
|
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
|
-
|
|
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:
|
|
9604
|
+
note: await overlayUnavailableNote(ctx, project_root)
|
|
9562
9605
|
};
|
|
9563
9606
|
return JSON.stringify(response2);
|
|
9564
9607
|
}
|
|
9565
|
-
const { churn, ownership, coChange } =
|
|
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
|
|
9631
|
-
import
|
|
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 =
|
|
9689
|
+
const filePath = path25.join(rootDir, ".ctxloom", "rules.yml");
|
|
9647
9690
|
let raw;
|
|
9648
9691
|
try {
|
|
9649
|
-
raw = await
|
|
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
|
|
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:
|
|
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
|
|
10294
|
-
import
|
|
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 =
|
|
10361
|
+
const fullPath = path26.join(this.projectRoot, ruleFile);
|
|
10319
10362
|
try {
|
|
10320
10363
|
this.pathValidator.validate(fullPath);
|
|
10321
|
-
if (
|
|
10322
|
-
const stat =
|
|
10364
|
+
if (fs24.existsSync(fullPath)) {
|
|
10365
|
+
const stat = fs24.statSync(fullPath);
|
|
10323
10366
|
if (stat.isFile()) {
|
|
10324
|
-
const content =
|
|
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 =
|
|
10374
|
+
const dirEntries = fs24.readdirSync(fullPath);
|
|
10332
10375
|
for (const entry of dirEntries) {
|
|
10333
|
-
const entryPath =
|
|
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 =
|
|
10382
|
+
const entryStat = fs24.statSync(entryPath);
|
|
10340
10383
|
if (entryStat.isFile()) {
|
|
10341
|
-
const content =
|
|
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
|
|
10385
|
-
import
|
|
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
|
|
10411
|
-
|
|
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 =
|
|
10463
|
+
const file = path27.join(this.ctxloomDir, "authors.yml");
|
|
10421
10464
|
try {
|
|
10422
|
-
const raw = await
|
|
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 =
|
|
10474
|
+
const file = path27.join(this.ctxloomDir, "authors-cache.json");
|
|
10432
10475
|
try {
|
|
10433
|
-
const raw = await
|
|
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
|
|
10459
|
-
import
|
|
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
|
|
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
|
|
10499
|
-
await
|
|
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
|
|
10685
|
-
import
|
|
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 =
|
|
10739
|
+
const file = path29.join(root, ".ctxloom", "review.yml");
|
|
10697
10740
|
try {
|
|
10698
|
-
const raw = await
|
|
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
|
|
10714
|
-
import
|
|
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 =
|
|
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 =
|
|
10772
|
+
const resolved = path30.resolve(this.canonicalRoot, inputPath);
|
|
10730
10773
|
let canonical;
|
|
10731
10774
|
try {
|
|
10732
|
-
canonical =
|
|
10775
|
+
canonical = fs28.realpathSync(resolved);
|
|
10733
10776
|
} catch {
|
|
10734
10777
|
canonical = resolved;
|
|
10735
10778
|
}
|
|
10736
|
-
if (!canonical.startsWith(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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
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.
|
|
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
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
11458
|
+
import path34 from "path";
|
|
11416
11459
|
import os6 from "os";
|
|
11417
11460
|
function noticePath(home) {
|
|
11418
|
-
return
|
|
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(
|
|
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
|
|
11475
|
+
import path36 from "path";
|
|
11433
11476
|
|
|
11434
11477
|
// packages/core/src/server/projectId.ts
|
|
11435
11478
|
import crypto5 from "crypto";
|
|
11436
|
-
import
|
|
11479
|
+
import path35 from "path";
|
|
11437
11480
|
function hashProjectRoot(absPath) {
|
|
11438
|
-
const canonical =
|
|
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:
|
|
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
|
|
11604
|
-
import
|
|
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 :
|
|
11674
|
+
expanded = p === "~" ? home : path37.join(home, p.slice(2));
|
|
11630
11675
|
}
|
|
11631
|
-
return
|
|
11676
|
+
return path37.isAbsolute(expanded) ? path37.resolve(expanded) : path37.resolve(cwd, expanded);
|
|
11632
11677
|
}
|
|
11633
11678
|
function realpathOrSame(p) {
|
|
11634
11679
|
try {
|
|
11635
|
-
return
|
|
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 (!
|
|
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 (!
|
|
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 =
|
|
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) =>
|
|
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
|
|
11776
|
-
import
|
|
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 =
|
|
12482
|
-
const stat =
|
|
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 =
|
|
12531
|
-
const rootResolved =
|
|
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 +
|
|
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 =
|
|
12543
|
-
const existing = existed ?
|
|
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
|
-
|
|
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 =
|
|
12619
|
+
const existed = fs30.existsSync(filePath);
|
|
12575
12620
|
let current = {};
|
|
12576
12621
|
if (existed) {
|
|
12577
12622
|
try {
|
|
12578
|
-
const text =
|
|
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 ${
|
|
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 =
|
|
12644
|
+
const currentText = fs30.readFileSync(filePath, "utf-8");
|
|
12600
12645
|
if (currentText === nextJson) alreadyCorrect = true;
|
|
12601
12646
|
}
|
|
12602
12647
|
if (!opts.dryRun && !alreadyCorrect) {
|
|
12603
|
-
|
|
12604
|
-
|
|
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 =
|
|
12627
|
-
const existed =
|
|
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 =
|
|
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
|
-
|
|
12638
|
-
|
|
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 =
|
|
12696
|
+
const existed = fs30.existsSync(filePath);
|
|
12652
12697
|
let alreadyCorrect = false;
|
|
12653
12698
|
if (existed) {
|
|
12654
|
-
if (
|
|
12699
|
+
if (fs30.readFileSync(filePath, "utf-8") === skill.content) {
|
|
12655
12700
|
alreadyCorrect = true;
|
|
12656
12701
|
}
|
|
12657
12702
|
}
|
|
12658
12703
|
if (!opts.dryRun && !alreadyCorrect) {
|
|
12659
|
-
|
|
12660
|
-
|
|
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 =
|
|
12718
|
+
const existed = fs30.existsSync(filePath);
|
|
12674
12719
|
let alreadyCorrect = false;
|
|
12675
12720
|
if (existed) {
|
|
12676
|
-
const current =
|
|
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
|
-
|
|
12681
|
-
|
|
12725
|
+
fs30.mkdirSync(dir, { recursive: true });
|
|
12726
|
+
fs30.writeFileSync(filePath, SESSION_START_FULL, "utf-8");
|
|
12682
12727
|
try {
|
|
12683
|
-
|
|
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-
|
|
12876
|
+
//# sourceMappingURL=chunk-J3NVYR6J.js.map
|