ctxloom-pro 1.0.26 → 1.0.28
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.
|
@@ -168,7 +168,7 @@ var init_VectorStore = __esm({
|
|
|
168
168
|
import express from "express";
|
|
169
169
|
import cors from "cors";
|
|
170
170
|
import path38 from "path";
|
|
171
|
-
import
|
|
171
|
+
import fs31 from "fs";
|
|
172
172
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
173
173
|
|
|
174
174
|
// server/loader.ts
|
|
@@ -8307,6 +8307,18 @@ var Schema9 = external_exports.object({
|
|
|
8307
8307
|
var Schema10 = external_exports.object({
|
|
8308
8308
|
show_files: external_exports.boolean().optional().default(false).describe(
|
|
8309
8309
|
"Include member file paths in output (default: false for compact output)"
|
|
8310
|
+
),
|
|
8311
|
+
limit: external_exports.number().int().min(1).max(200).optional().default(50).describe(
|
|
8312
|
+
"Maximum number of communities to return per call (default: 50, max: 200)"
|
|
8313
|
+
),
|
|
8314
|
+
offset: external_exports.number().int().min(0).optional().default(0).describe(
|
|
8315
|
+
"Number of communities to skip before returning results \u2014 for paging (default: 0)"
|
|
8316
|
+
),
|
|
8317
|
+
min_size: external_exports.number().int().min(1).optional().default(2).describe(
|
|
8318
|
+
"Skip communities smaller than this (default: 2 \u2014 single-file communities are usually noise)"
|
|
8319
|
+
),
|
|
8320
|
+
detail_level: external_exports.enum(["standard", "minimal"]).default("standard").describe(
|
|
8321
|
+
'"standard" (default) returns paged community list. "minimal" returns counts only \u2014 useful for a quick size check before paging.'
|
|
8310
8322
|
)
|
|
8311
8323
|
});
|
|
8312
8324
|
|
|
@@ -8348,9 +8360,13 @@ var Schema13 = external_exports.object({
|
|
|
8348
8360
|
});
|
|
8349
8361
|
|
|
8350
8362
|
// ../../packages/core/src/tools/wiki-generate.ts
|
|
8363
|
+
import fs16 from "fs";
|
|
8351
8364
|
var Schema14 = external_exports.object({
|
|
8352
8365
|
force: external_exports.boolean().optional().default(false).describe(
|
|
8353
8366
|
"Regenerate all pages even if content unchanged (default: false)"
|
|
8367
|
+
),
|
|
8368
|
+
detail_level: external_exports.enum(["standard", "minimal"]).default("standard").describe(
|
|
8369
|
+
'"standard" (default) lists each written page with size. "minimal" returns counts only.'
|
|
8354
8370
|
)
|
|
8355
8371
|
});
|
|
8356
8372
|
|
|
@@ -8381,7 +8397,7 @@ var Schema16 = external_exports.object({
|
|
|
8381
8397
|
});
|
|
8382
8398
|
|
|
8383
8399
|
// ../../packages/core/src/tools/refactor-preview.ts
|
|
8384
|
-
import
|
|
8400
|
+
import fs17 from "fs";
|
|
8385
8401
|
import path19 from "path";
|
|
8386
8402
|
var Schema17 = external_exports.object({
|
|
8387
8403
|
symbol: external_exports.string().min(1).describe("Symbol name to rename (exact match, case-sensitive)"),
|
|
@@ -8407,7 +8423,7 @@ var Schema18 = external_exports.object({
|
|
|
8407
8423
|
init_embedder();
|
|
8408
8424
|
init_VectorStore();
|
|
8409
8425
|
init_logger();
|
|
8410
|
-
import
|
|
8426
|
+
import fs18 from "fs";
|
|
8411
8427
|
import path20 from "path";
|
|
8412
8428
|
var Schema19 = external_exports.object({
|
|
8413
8429
|
query: external_exports.string().min(1).describe("Search query \u2014 natural language or code fragment"),
|
|
@@ -8420,7 +8436,7 @@ var Schema19 = external_exports.object({
|
|
|
8420
8436
|
});
|
|
8421
8437
|
|
|
8422
8438
|
// ../../packages/core/src/tools/apply-refactor.ts
|
|
8423
|
-
import
|
|
8439
|
+
import fs19 from "fs";
|
|
8424
8440
|
import path21 from "path";
|
|
8425
8441
|
var Schema20 = external_exports.object({
|
|
8426
8442
|
symbol: external_exports.string().min(1).describe("Symbol name to rename (exact, case-sensitive)"),
|
|
@@ -8448,7 +8464,7 @@ var Schema21 = external_exports.object({
|
|
|
8448
8464
|
});
|
|
8449
8465
|
|
|
8450
8466
|
// ../../packages/core/src/tools/full-text-search.ts
|
|
8451
|
-
import
|
|
8467
|
+
import fs20 from "fs";
|
|
8452
8468
|
import path22 from "path";
|
|
8453
8469
|
var Schema22 = external_exports.object({
|
|
8454
8470
|
query: external_exports.string().min(1).describe("Search term \u2014 literal or /regex/"),
|
|
@@ -8474,7 +8490,7 @@ var Schema24 = external_exports.object({
|
|
|
8474
8490
|
});
|
|
8475
8491
|
|
|
8476
8492
|
// ../../packages/core/src/tools/graph-snapshot.ts
|
|
8477
|
-
import
|
|
8493
|
+
import fs21 from "fs";
|
|
8478
8494
|
import path23 from "path";
|
|
8479
8495
|
var schema = external_exports.object({
|
|
8480
8496
|
name: external_exports.string().min(1).max(64).regex(/^[\w.-]+$/, "Name may only contain letters, digits, dots, underscores, hyphens").describe(
|
|
@@ -8486,7 +8502,7 @@ var schema = external_exports.object({
|
|
|
8486
8502
|
});
|
|
8487
8503
|
|
|
8488
8504
|
// ../../packages/core/src/tools/graph-diff.ts
|
|
8489
|
-
import
|
|
8505
|
+
import fs22 from "fs";
|
|
8490
8506
|
import path24 from "path";
|
|
8491
8507
|
var schema2 = external_exports.object({
|
|
8492
8508
|
baseline: external_exports.string().min(1).describe('Name of the baseline snapshot (the "before" state).'),
|
|
@@ -8520,7 +8536,7 @@ var Schema26 = external_exports.object({
|
|
|
8520
8536
|
});
|
|
8521
8537
|
|
|
8522
8538
|
// ../../packages/core/src/rules/loadConfig.ts
|
|
8523
|
-
import
|
|
8539
|
+
import fs23 from "fs/promises";
|
|
8524
8540
|
import path25 from "path";
|
|
8525
8541
|
|
|
8526
8542
|
// ../../node_modules/js-yaml/dist/js-yaml.mjs
|
|
@@ -11146,24 +11162,24 @@ var Schema27 = external_exports.object({
|
|
|
11146
11162
|
});
|
|
11147
11163
|
|
|
11148
11164
|
// ../../packages/core/src/tools/ruleManager.ts
|
|
11149
|
-
import
|
|
11165
|
+
import fs24 from "fs";
|
|
11150
11166
|
import path26 from "path";
|
|
11151
11167
|
|
|
11152
11168
|
// ../../packages/core/src/review/AuthorResolver.ts
|
|
11153
|
-
import
|
|
11169
|
+
import fs25 from "fs/promises";
|
|
11154
11170
|
import path27 from "path";
|
|
11155
11171
|
|
|
11156
11172
|
// ../../packages/core/src/review/CodeownersWriter.ts
|
|
11157
|
-
import
|
|
11173
|
+
import fs26 from "fs/promises";
|
|
11158
11174
|
import path28 from "path";
|
|
11159
11175
|
|
|
11160
11176
|
// ../../packages/core/src/review/loadConfig.ts
|
|
11161
|
-
import
|
|
11177
|
+
import fs27 from "fs/promises";
|
|
11162
11178
|
import path29 from "path";
|
|
11163
11179
|
|
|
11164
11180
|
// ../../packages/core/src/security/PathValidator.ts
|
|
11165
11181
|
import path30 from "path";
|
|
11166
|
-
import
|
|
11182
|
+
import fs28 from "fs";
|
|
11167
11183
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
11168
11184
|
|
|
11169
11185
|
// ../../packages/core/src/index.ts
|
|
@@ -11463,7 +11479,7 @@ function buildOwnershipRouter(ctx) {
|
|
|
11463
11479
|
|
|
11464
11480
|
// server/routes/file.ts
|
|
11465
11481
|
import { Router as Router7 } from "express";
|
|
11466
|
-
import
|
|
11482
|
+
import fs29 from "fs/promises";
|
|
11467
11483
|
import path33 from "path";
|
|
11468
11484
|
function buildFileRouter(ctx) {
|
|
11469
11485
|
const router = Router7();
|
|
@@ -11476,7 +11492,7 @@ function buildFileRouter(ctx) {
|
|
|
11476
11492
|
return res.status(403).json({ error: "forbidden" });
|
|
11477
11493
|
}
|
|
11478
11494
|
try {
|
|
11479
|
-
const content = await
|
|
11495
|
+
const content = await fs29.readFile(abs, "utf-8");
|
|
11480
11496
|
const ext = path33.extname(abs).slice(1);
|
|
11481
11497
|
res.json({ content, lines: content.split("\n").length, ext });
|
|
11482
11498
|
} catch {
|
|
@@ -11514,7 +11530,7 @@ function buildOpenRouter(ctx) {
|
|
|
11514
11530
|
// server/routes/tokens.ts
|
|
11515
11531
|
import { Router as Router9 } from "express";
|
|
11516
11532
|
import path35 from "path";
|
|
11517
|
-
import
|
|
11533
|
+
import fs30 from "fs";
|
|
11518
11534
|
var CHARS_PER_TOKEN = 4;
|
|
11519
11535
|
var cache = null;
|
|
11520
11536
|
function buildTokensRouter(ctx) {
|
|
@@ -11531,7 +11547,7 @@ function buildTokensRouter(ctx) {
|
|
|
11531
11547
|
for (const file of files) {
|
|
11532
11548
|
const absPath = path35.join(ctx.root, file);
|
|
11533
11549
|
try {
|
|
11534
|
-
const content =
|
|
11550
|
+
const content = fs30.readFileSync(absPath, "utf-8");
|
|
11535
11551
|
fullChars += content.length;
|
|
11536
11552
|
const skeleton = await skeletonizer.skeletonize(absPath);
|
|
11537
11553
|
skeletonChars += skeleton.length;
|
|
@@ -11797,7 +11813,7 @@ async function startDashboard(options) {
|
|
|
11797
11813
|
}
|
|
11798
11814
|
const snapshotDir = path38.join(targetRoot, ".ctxloom");
|
|
11799
11815
|
try {
|
|
11800
|
-
activeWatcher =
|
|
11816
|
+
activeWatcher = fs31.watch(snapshotDir, (_event, filename) => {
|
|
11801
11817
|
if (!filename || !filename.includes("snapshot")) return;
|
|
11802
11818
|
if (debounce) clearTimeout(debounce);
|
|
11803
11819
|
debounce = setTimeout(async () => {
|
|
@@ -11826,7 +11842,7 @@ async function startDashboard(options) {
|
|
|
11826
11842
|
}
|
|
11827
11843
|
}));
|
|
11828
11844
|
const clientDist = path38.join(__dirname2, "../dashboard/client");
|
|
11829
|
-
const clientDistExists =
|
|
11845
|
+
const clientDistExists = fs31.existsSync(path38.join(clientDist, "index.html"));
|
|
11830
11846
|
if (clientDistExists) {
|
|
11831
11847
|
app.use(express.static(clientDist, { dotfiles: "allow" }));
|
|
11832
11848
|
app.get(/.*/, (_req, res) => {
|
|
@@ -5269,6 +5269,18 @@ import { z as z10 } from "zod";
|
|
|
5269
5269
|
var Schema10 = z10.object({
|
|
5270
5270
|
show_files: z10.boolean().optional().default(false).describe(
|
|
5271
5271
|
"Include member file paths in output (default: false for compact output)"
|
|
5272
|
+
),
|
|
5273
|
+
limit: z10.number().int().min(1).max(200).optional().default(50).describe(
|
|
5274
|
+
"Maximum number of communities to return per call (default: 50, max: 200)"
|
|
5275
|
+
),
|
|
5276
|
+
offset: z10.number().int().min(0).optional().default(0).describe(
|
|
5277
|
+
"Number of communities to skip before returning results \u2014 for paging (default: 0)"
|
|
5278
|
+
),
|
|
5279
|
+
min_size: z10.number().int().min(1).optional().default(2).describe(
|
|
5280
|
+
"Skip communities smaller than this (default: 2 \u2014 single-file communities are usually noise)"
|
|
5281
|
+
),
|
|
5282
|
+
detail_level: z10.enum(["standard", "minimal"]).default("standard").describe(
|
|
5283
|
+
'"standard" (default) returns paged community list. "minimal" returns counts only \u2014 useful for a quick size check before paging.'
|
|
5272
5284
|
)
|
|
5273
5285
|
});
|
|
5274
5286
|
function escapeXML10(text) {
|
|
@@ -5279,30 +5291,53 @@ function registerCommunityListTool(registry, ctx) {
|
|
|
5279
5291
|
"ctx_community_list",
|
|
5280
5292
|
{
|
|
5281
5293
|
name: "ctx_community_list",
|
|
5282
|
-
description: "Return
|
|
5294
|
+
description: "Return architectural communities detected via Louvain clustering of the import graph. Each community is a cluster of tightly-coupled files (a feature area, module, or layer). Paged by default (limit=50, min_size=2). Use this to understand high-level codebase structure before diving into details; raise `limit` or page via `offset` for full coverage.",
|
|
5283
5295
|
inputSchema: {
|
|
5284
5296
|
type: "object",
|
|
5285
5297
|
properties: {
|
|
5286
5298
|
show_files: {
|
|
5287
5299
|
type: "boolean",
|
|
5288
5300
|
description: "Include member file paths in output (default: false)"
|
|
5301
|
+
},
|
|
5302
|
+
limit: {
|
|
5303
|
+
type: "number",
|
|
5304
|
+
description: "Maximum communities to return (default: 50, max: 200)"
|
|
5305
|
+
},
|
|
5306
|
+
offset: {
|
|
5307
|
+
type: "number",
|
|
5308
|
+
description: "Number of communities to skip for paging (default: 0)"
|
|
5309
|
+
},
|
|
5310
|
+
min_size: {
|
|
5311
|
+
type: "number",
|
|
5312
|
+
description: "Skip communities smaller than this (default: 2)"
|
|
5313
|
+
},
|
|
5314
|
+
detail_level: {
|
|
5315
|
+
type: "string",
|
|
5316
|
+
enum: ["standard", "minimal"],
|
|
5317
|
+
description: '"standard" returns the paged list. "minimal" returns counts only.'
|
|
5289
5318
|
}
|
|
5290
5319
|
}
|
|
5291
5320
|
}
|
|
5292
5321
|
},
|
|
5293
5322
|
async (args) => {
|
|
5294
|
-
const { show_files } = Schema10.parse(args);
|
|
5323
|
+
const { show_files, limit, offset, min_size, detail_level } = Schema10.parse(args);
|
|
5295
5324
|
const graph = await ctx.getGraph();
|
|
5296
5325
|
const files = graph.allFiles();
|
|
5297
5326
|
if (files.length === 0) {
|
|
5298
5327
|
return '<communities total="0" edge_count="0" />';
|
|
5299
5328
|
}
|
|
5300
5329
|
const detector = new CommunityDetector(graph);
|
|
5301
|
-
const
|
|
5330
|
+
const allCommunities = detector.detect();
|
|
5331
|
+
const filtered = allCommunities.filter((c) => c.files.length >= min_size).sort((a, b) => b.files.length - a.files.length);
|
|
5332
|
+
if (detail_level === "minimal") {
|
|
5333
|
+
return `<communities detail_level="minimal" total="${allCommunities.length}" filtered_total="${filtered.length}" edge_count="${graph.edgeCount()}" total_files="${files.length}" />`;
|
|
5334
|
+
}
|
|
5335
|
+
const page = filtered.slice(offset, offset + limit);
|
|
5336
|
+
const hasMore = offset + page.length < filtered.length;
|
|
5302
5337
|
const lines = [
|
|
5303
|
-
`<communities total="${
|
|
5338
|
+
`<communities total="${allCommunities.length}" filtered_total="${filtered.length}" showing="${page.length}" offset="${offset}" limit="${limit}" min_size="${min_size}" has_more="${hasMore}" edge_count="${graph.edgeCount()}" total_files="${files.length}">`
|
|
5304
5339
|
];
|
|
5305
|
-
for (const c of
|
|
5340
|
+
for (const c of page) {
|
|
5306
5341
|
if (show_files) {
|
|
5307
5342
|
lines.push(` <community id="${c.id}" name="${escapeXML10(c.name)}" size="${c.files.length}">`);
|
|
5308
5343
|
for (const f of c.files.sort()) {
|
|
@@ -5668,43 +5703,62 @@ function registerSurprisingConnectionsTool(registry, ctx) {
|
|
|
5668
5703
|
|
|
5669
5704
|
// packages/core/src/tools/wiki-generate.ts
|
|
5670
5705
|
import { z as z14 } from "zod";
|
|
5706
|
+
import fs14 from "fs";
|
|
5671
5707
|
var Schema14 = z14.object({
|
|
5672
5708
|
force: z14.boolean().optional().default(false).describe(
|
|
5673
5709
|
"Regenerate all pages even if content unchanged (default: false)"
|
|
5710
|
+
),
|
|
5711
|
+
detail_level: z14.enum(["standard", "minimal"]).default("standard").describe(
|
|
5712
|
+
'"standard" (default) lists each written page with size. "minimal" returns counts only.'
|
|
5674
5713
|
)
|
|
5675
5714
|
});
|
|
5676
5715
|
function escapeXML14(text) {
|
|
5677
5716
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
5678
5717
|
}
|
|
5718
|
+
function safeFileSize(filePath) {
|
|
5719
|
+
try {
|
|
5720
|
+
return fs14.statSync(filePath).size;
|
|
5721
|
+
} catch {
|
|
5722
|
+
return 0;
|
|
5723
|
+
}
|
|
5724
|
+
}
|
|
5679
5725
|
function registerWikiGenerateTool(registry, ctx) {
|
|
5680
5726
|
registry.register(
|
|
5681
5727
|
"ctx_wiki_generate",
|
|
5682
5728
|
{
|
|
5683
5729
|
name: "ctx_wiki_generate",
|
|
5684
|
-
description: "Generate structural Markdown wiki pages for each Louvain community. Writes to .ctxloom/wiki/: one page per community with its files, public API, dependency map, and hub file skeleton. Pages are hash-cached \u2014 only updated when content changes.
|
|
5730
|
+
description: "Generate structural Markdown wiki pages for each Louvain community. Writes to .ctxloom/wiki/: one page per community with its files, public API, dependency map, and hub file skeleton. Pages are hash-cached \u2014 only updated when content changes. Returns per-page metadata (path, size) for written pages and a count for skipped pages; read the listed paths directly when the body is needed.",
|
|
5685
5731
|
inputSchema: {
|
|
5686
5732
|
type: "object",
|
|
5687
5733
|
properties: {
|
|
5688
5734
|
force: {
|
|
5689
5735
|
type: "boolean",
|
|
5690
5736
|
description: "Regenerate all pages even if content is unchanged (default: false)"
|
|
5737
|
+
},
|
|
5738
|
+
detail_level: {
|
|
5739
|
+
type: "string",
|
|
5740
|
+
enum: ["standard", "minimal"],
|
|
5741
|
+
description: '"standard" lists written pages with size. "minimal" returns counts only.'
|
|
5691
5742
|
}
|
|
5692
5743
|
}
|
|
5693
5744
|
}
|
|
5694
5745
|
},
|
|
5695
5746
|
async (args) => {
|
|
5696
|
-
const { force } = Schema14.parse(args);
|
|
5747
|
+
const { force, detail_level } = Schema14.parse(args);
|
|
5697
5748
|
const [graph, skeletonizer] = await Promise.all([ctx.getGraph(), ctx.getSkeletonizer()]);
|
|
5698
5749
|
const generator = new WikiGenerator(graph, ctx.projectRoot, skeletonizer);
|
|
5699
5750
|
const result = await generator.generate(force);
|
|
5751
|
+
if (detail_level === "minimal") {
|
|
5752
|
+
return `<wiki_generate detail_level="minimal" wiki_dir="${escapeXML14(result.wikiDir)}" written="${result.written.length}" skipped="${result.skipped.length}" />`;
|
|
5753
|
+
}
|
|
5700
5754
|
const lines = [
|
|
5701
5755
|
`<wiki_generate wiki_dir="${escapeXML14(result.wikiDir)}" written="${result.written.length}" skipped="${result.skipped.length}">`
|
|
5702
5756
|
];
|
|
5703
5757
|
for (const p of result.written) {
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5758
|
+
const size = safeFileSize(p.filePath);
|
|
5759
|
+
lines.push(
|
|
5760
|
+
` <page community="${escapeXML14(p.communityName)}" file="${escapeXML14(p.filePath)}" size="${size}" status="written" />`
|
|
5761
|
+
);
|
|
5708
5762
|
}
|
|
5709
5763
|
lines.push("</wiki_generate>");
|
|
5710
5764
|
return lines.join("\n");
|
|
@@ -5907,7 +5961,7 @@ function registerGitDiffReviewTool(registry, ctx) {
|
|
|
5907
5961
|
|
|
5908
5962
|
// packages/core/src/tools/refactor-preview.ts
|
|
5909
5963
|
import { z as z17 } from "zod";
|
|
5910
|
-
import
|
|
5964
|
+
import fs15 from "fs";
|
|
5911
5965
|
import path17 from "path";
|
|
5912
5966
|
var Schema17 = z17.object({
|
|
5913
5967
|
symbol: z17.string().min(1).describe("Symbol name to rename (exact match, case-sensitive)"),
|
|
@@ -5922,7 +5976,7 @@ function escapeXML17(text) {
|
|
|
5922
5976
|
function scanFile(filePath, symbol, newName) {
|
|
5923
5977
|
let content;
|
|
5924
5978
|
try {
|
|
5925
|
-
content =
|
|
5979
|
+
content = fs15.readFileSync(filePath, "utf-8");
|
|
5926
5980
|
} catch {
|
|
5927
5981
|
return [];
|
|
5928
5982
|
}
|
|
@@ -6152,7 +6206,7 @@ function registerExecutionFlowTool(registry, ctx) {
|
|
|
6152
6206
|
|
|
6153
6207
|
// packages/core/src/tools/cross-repo-search.ts
|
|
6154
6208
|
import { z as z19 } from "zod";
|
|
6155
|
-
import
|
|
6209
|
+
import fs16 from "fs";
|
|
6156
6210
|
import path18 from "path";
|
|
6157
6211
|
var RepoRegistry = class {
|
|
6158
6212
|
filePath;
|
|
@@ -6163,16 +6217,16 @@ var RepoRegistry = class {
|
|
|
6163
6217
|
}
|
|
6164
6218
|
load() {
|
|
6165
6219
|
try {
|
|
6166
|
-
if (!
|
|
6167
|
-
return JSON.parse(
|
|
6220
|
+
if (!fs16.existsSync(this.filePath)) return [];
|
|
6221
|
+
return JSON.parse(fs16.readFileSync(this.filePath, "utf-8"));
|
|
6168
6222
|
} catch {
|
|
6169
6223
|
return [];
|
|
6170
6224
|
}
|
|
6171
6225
|
}
|
|
6172
6226
|
save() {
|
|
6173
6227
|
const dir = path18.dirname(this.filePath);
|
|
6174
|
-
if (!
|
|
6175
|
-
|
|
6228
|
+
if (!fs16.existsSync(dir)) fs16.mkdirSync(dir, { recursive: true });
|
|
6229
|
+
fs16.writeFileSync(this.filePath, JSON.stringify(this.repos, null, 2), "utf-8");
|
|
6176
6230
|
}
|
|
6177
6231
|
list() {
|
|
6178
6232
|
return [...this.repos];
|
|
@@ -6313,7 +6367,7 @@ function registerCrossRepoSearchTool(registry, _ctx, registryFilePath) {
|
|
|
6313
6367
|
|
|
6314
6368
|
// packages/core/src/tools/apply-refactor.ts
|
|
6315
6369
|
import { z as z20 } from "zod";
|
|
6316
|
-
import
|
|
6370
|
+
import fs17 from "fs";
|
|
6317
6371
|
import path19 from "path";
|
|
6318
6372
|
var Schema20 = z20.object({
|
|
6319
6373
|
symbol: z20.string().min(1).describe("Symbol name to rename (exact, case-sensitive)"),
|
|
@@ -6331,7 +6385,7 @@ function escapeXML20(text) {
|
|
|
6331
6385
|
function applyToFile(absPath, symbol, newName, dryRun) {
|
|
6332
6386
|
let content;
|
|
6333
6387
|
try {
|
|
6334
|
-
content =
|
|
6388
|
+
content = fs17.readFileSync(absPath, "utf-8");
|
|
6335
6389
|
} catch {
|
|
6336
6390
|
return 0;
|
|
6337
6391
|
}
|
|
@@ -6340,7 +6394,7 @@ function applyToFile(absPath, symbol, newName, dryRun) {
|
|
|
6340
6394
|
const occurrences = (content.match(regex) ?? []).length;
|
|
6341
6395
|
if (occurrences === 0) return 0;
|
|
6342
6396
|
if (!dryRun) {
|
|
6343
|
-
|
|
6397
|
+
fs17.writeFileSync(absPath, content.replace(regex, newName), "utf-8");
|
|
6344
6398
|
}
|
|
6345
6399
|
return occurrences;
|
|
6346
6400
|
}
|
|
@@ -6513,7 +6567,7 @@ function registerDetectChangesTool(registry, ctx) {
|
|
|
6513
6567
|
|
|
6514
6568
|
// packages/core/src/tools/full-text-search.ts
|
|
6515
6569
|
import { z as z22 } from "zod";
|
|
6516
|
-
import
|
|
6570
|
+
import fs18 from "fs";
|
|
6517
6571
|
import path20 from "path";
|
|
6518
6572
|
var Schema22 = z22.object({
|
|
6519
6573
|
query: z22.string().min(1).describe("Search term \u2014 literal or /regex/"),
|
|
@@ -6539,7 +6593,7 @@ function buildPattern(query, caseSensitive) {
|
|
|
6539
6593
|
function scanFile2(absPath, pattern, contextLines) {
|
|
6540
6594
|
let content;
|
|
6541
6595
|
try {
|
|
6542
|
-
content =
|
|
6596
|
+
content = fs18.readFileSync(absPath, "utf-8");
|
|
6543
6597
|
} catch {
|
|
6544
6598
|
return null;
|
|
6545
6599
|
}
|
|
@@ -6825,7 +6879,7 @@ function registerGetWorkflowTool(registry, _ctx) {
|
|
|
6825
6879
|
}
|
|
6826
6880
|
|
|
6827
6881
|
// packages/core/src/tools/graph-snapshot.ts
|
|
6828
|
-
import
|
|
6882
|
+
import fs19 from "fs";
|
|
6829
6883
|
import path21 from "path";
|
|
6830
6884
|
import { z as z25 } from "zod";
|
|
6831
6885
|
var schema = z25.object({
|
|
@@ -6838,12 +6892,12 @@ var schema = z25.object({
|
|
|
6838
6892
|
});
|
|
6839
6893
|
function saveNamedSnapshot(graph, name, rootDir, overwrite = false) {
|
|
6840
6894
|
const snapshotsDir = path21.resolve(rootDir, ".ctxloom", "snapshots");
|
|
6841
|
-
|
|
6895
|
+
fs19.mkdirSync(snapshotsDir, { recursive: true });
|
|
6842
6896
|
const snapshotPath = path21.resolve(snapshotsDir, `${name}.json`);
|
|
6843
6897
|
if (!snapshotPath.startsWith(snapshotsDir + path21.sep)) {
|
|
6844
6898
|
throw new Error(`Invalid snapshot name: "${name}"`);
|
|
6845
6899
|
}
|
|
6846
|
-
if (
|
|
6900
|
+
if (fs19.existsSync(snapshotPath) && !overwrite) {
|
|
6847
6901
|
throw new Error(`Snapshot "${name}" already exists. Pass overwrite: true to replace it.`);
|
|
6848
6902
|
}
|
|
6849
6903
|
const files = graph.allFiles();
|
|
@@ -6859,13 +6913,13 @@ function saveNamedSnapshot(graph, name, rootDir, overwrite = false) {
|
|
|
6859
6913
|
forwardEdges
|
|
6860
6914
|
};
|
|
6861
6915
|
const tmp = snapshotPath + ".tmp";
|
|
6862
|
-
|
|
6863
|
-
|
|
6916
|
+
fs19.writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
|
|
6917
|
+
fs19.renameSync(tmp, snapshotPath);
|
|
6864
6918
|
}
|
|
6865
6919
|
function listNamedSnapshots(rootDir) {
|
|
6866
6920
|
const snapshotsDir = path21.join(rootDir, ".ctxloom", "snapshots");
|
|
6867
|
-
if (!
|
|
6868
|
-
return
|
|
6921
|
+
if (!fs19.existsSync(snapshotsDir)) return [];
|
|
6922
|
+
return fs19.readdirSync(snapshotsDir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5)).sort();
|
|
6869
6923
|
}
|
|
6870
6924
|
function registerGraphSnapshotTool(registry, ctx) {
|
|
6871
6925
|
registry.register(
|
|
@@ -6913,7 +6967,7 @@ function registerGraphSnapshotTool(registry, ctx) {
|
|
|
6913
6967
|
}
|
|
6914
6968
|
|
|
6915
6969
|
// packages/core/src/tools/graph-diff.ts
|
|
6916
|
-
import
|
|
6970
|
+
import fs20 from "fs";
|
|
6917
6971
|
import path22 from "path";
|
|
6918
6972
|
import { z as z26 } from "zod";
|
|
6919
6973
|
var schema2 = z26.object({
|
|
@@ -6926,11 +6980,11 @@ function loadSnapshot(name, rootDir) {
|
|
|
6926
6980
|
if (!snapshotPath.startsWith(snapshotsDir + path22.sep)) {
|
|
6927
6981
|
throw new Error(`Invalid snapshot name: "${name}"`);
|
|
6928
6982
|
}
|
|
6929
|
-
if (!
|
|
6983
|
+
if (!fs20.existsSync(snapshotPath)) {
|
|
6930
6984
|
throw new Error(`Snapshot "${name}" not found. Run ctx_graph_snapshot first.`);
|
|
6931
6985
|
}
|
|
6932
6986
|
try {
|
|
6933
|
-
return JSON.parse(
|
|
6987
|
+
return JSON.parse(fs20.readFileSync(snapshotPath, "utf-8"));
|
|
6934
6988
|
} catch (e) {
|
|
6935
6989
|
throw new Error(`Snapshot "${name}" is corrupted: ${e instanceof Error ? e.message : String(e)}`);
|
|
6936
6990
|
}
|
|
@@ -7295,7 +7349,7 @@ var RulesConfigError = class extends Error {
|
|
|
7295
7349
|
};
|
|
7296
7350
|
|
|
7297
7351
|
// packages/core/src/rules/loadConfig.ts
|
|
7298
|
-
import
|
|
7352
|
+
import fs21 from "fs/promises";
|
|
7299
7353
|
import path23 from "path";
|
|
7300
7354
|
import yaml from "js-yaml";
|
|
7301
7355
|
import { z as z30 } from "zod";
|
|
@@ -7314,7 +7368,7 @@ async function loadRulesConfig(rootDir) {
|
|
|
7314
7368
|
const filePath = path23.join(rootDir, ".ctxloom", "rules.yml");
|
|
7315
7369
|
let raw;
|
|
7316
7370
|
try {
|
|
7317
|
-
raw = await
|
|
7371
|
+
raw = await fs21.readFile(filePath, "utf-8");
|
|
7318
7372
|
} catch (err) {
|
|
7319
7373
|
if (err.code === "ENOENT") return null;
|
|
7320
7374
|
throw new RulesConfigError(`Failed to read rules config: ${String(err)}`);
|
|
@@ -7679,7 +7733,7 @@ function createToolRegistry(ctx) {
|
|
|
7679
7733
|
}
|
|
7680
7734
|
|
|
7681
7735
|
// packages/core/src/tools/ruleManager.ts
|
|
7682
|
-
import
|
|
7736
|
+
import fs22 from "fs";
|
|
7683
7737
|
import path24 from "path";
|
|
7684
7738
|
var RULE_FILES = [
|
|
7685
7739
|
".cursorrules",
|
|
@@ -7707,17 +7761,17 @@ var RuleManager = class {
|
|
|
7707
7761
|
const fullPath = path24.join(this.projectRoot, ruleFile);
|
|
7708
7762
|
try {
|
|
7709
7763
|
this.pathValidator.validate(fullPath);
|
|
7710
|
-
if (
|
|
7711
|
-
const stat =
|
|
7764
|
+
if (fs22.existsSync(fullPath)) {
|
|
7765
|
+
const stat = fs22.statSync(fullPath);
|
|
7712
7766
|
if (stat.isFile()) {
|
|
7713
|
-
const content =
|
|
7767
|
+
const content = fs22.readFileSync(fullPath, "utf-8");
|
|
7714
7768
|
rules.push({
|
|
7715
7769
|
name: ruleFile,
|
|
7716
7770
|
path: ruleFile,
|
|
7717
7771
|
content
|
|
7718
7772
|
});
|
|
7719
7773
|
} else if (stat.isDirectory()) {
|
|
7720
|
-
const dirEntries =
|
|
7774
|
+
const dirEntries = fs22.readdirSync(fullPath);
|
|
7721
7775
|
for (const entry of dirEntries) {
|
|
7722
7776
|
const entryPath = path24.join(fullPath, entry);
|
|
7723
7777
|
try {
|
|
@@ -7725,9 +7779,9 @@ var RuleManager = class {
|
|
|
7725
7779
|
} catch {
|
|
7726
7780
|
continue;
|
|
7727
7781
|
}
|
|
7728
|
-
const entryStat =
|
|
7782
|
+
const entryStat = fs22.statSync(entryPath);
|
|
7729
7783
|
if (entryStat.isFile()) {
|
|
7730
|
-
const content =
|
|
7784
|
+
const content = fs22.readFileSync(entryPath, "utf-8");
|
|
7731
7785
|
rules.push({
|
|
7732
7786
|
name: `${ruleFile}/${entry}`,
|
|
7733
7787
|
path: `${ruleFile}/${entry}`,
|
|
@@ -7770,7 +7824,7 @@ var RuleManager = class {
|
|
|
7770
7824
|
};
|
|
7771
7825
|
|
|
7772
7826
|
// packages/core/src/review/AuthorResolver.ts
|
|
7773
|
-
import
|
|
7827
|
+
import fs23 from "fs/promises";
|
|
7774
7828
|
import path25 from "path";
|
|
7775
7829
|
import yaml2 from "js-yaml";
|
|
7776
7830
|
var AuthorResolver = class {
|
|
@@ -7796,7 +7850,7 @@ var AuthorResolver = class {
|
|
|
7796
7850
|
/** Write a new mapping to the cache file. */
|
|
7797
7851
|
async writeCache(email, handle) {
|
|
7798
7852
|
this.cache = { ...this.cache, [email]: handle };
|
|
7799
|
-
await
|
|
7853
|
+
await fs23.writeFile(
|
|
7800
7854
|
path25.join(this.ctxloomDir, "authors-cache.json"),
|
|
7801
7855
|
JSON.stringify(this.cache, null, 2)
|
|
7802
7856
|
);
|
|
@@ -7808,7 +7862,7 @@ var AuthorResolver = class {
|
|
|
7808
7862
|
async loadYml() {
|
|
7809
7863
|
const file = path25.join(this.ctxloomDir, "authors.yml");
|
|
7810
7864
|
try {
|
|
7811
|
-
const raw = await
|
|
7865
|
+
const raw = await fs23.readFile(file, "utf8");
|
|
7812
7866
|
const parsed = yaml2.load(raw);
|
|
7813
7867
|
if (!parsed) return;
|
|
7814
7868
|
this.mappings = parsed.mappings ?? {};
|
|
@@ -7819,7 +7873,7 @@ var AuthorResolver = class {
|
|
|
7819
7873
|
async loadCache() {
|
|
7820
7874
|
const file = path25.join(this.ctxloomDir, "authors-cache.json");
|
|
7821
7875
|
try {
|
|
7822
|
-
const raw = await
|
|
7876
|
+
const raw = await fs23.readFile(file, "utf8");
|
|
7823
7877
|
this.cache = JSON.parse(raw);
|
|
7824
7878
|
} catch {
|
|
7825
7879
|
}
|
|
@@ -7844,7 +7898,7 @@ async function resolveViaGitHubApi(email, owner, repo, token) {
|
|
|
7844
7898
|
}
|
|
7845
7899
|
|
|
7846
7900
|
// packages/core/src/review/CodeownersWriter.ts
|
|
7847
|
-
import
|
|
7901
|
+
import fs24 from "fs/promises";
|
|
7848
7902
|
import path26 from "path";
|
|
7849
7903
|
var MARKER_START = "# <ctxloom:start> \u2014 managed by ctxloom review-suggest; do not edit between markers";
|
|
7850
7904
|
var MARKER_START_DETECT = "# <ctxloom:start>";
|
|
@@ -7877,15 +7931,15 @@ ${block}
|
|
|
7877
7931
|
async function generateCODEOWNERS(codeownersPath, rules) {
|
|
7878
7932
|
let existing = "";
|
|
7879
7933
|
try {
|
|
7880
|
-
existing = await
|
|
7934
|
+
existing = await fs24.readFile(codeownersPath, "utf8");
|
|
7881
7935
|
} catch {
|
|
7882
7936
|
}
|
|
7883
7937
|
const block = buildCodeownersBlock(rules);
|
|
7884
7938
|
return mergeIntoFile(existing, block);
|
|
7885
7939
|
}
|
|
7886
7940
|
async function writeCODEOWNERS(codeownersPath, content) {
|
|
7887
|
-
await
|
|
7888
|
-
await
|
|
7941
|
+
await fs24.mkdir(path26.dirname(codeownersPath), { recursive: true });
|
|
7942
|
+
await fs24.writeFile(codeownersPath, content, "utf8");
|
|
7889
7943
|
}
|
|
7890
7944
|
|
|
7891
7945
|
// packages/core/src/review/types.ts
|
|
@@ -8070,7 +8124,7 @@ function matchGlob(pattern, value) {
|
|
|
8070
8124
|
}
|
|
8071
8125
|
|
|
8072
8126
|
// packages/core/src/review/loadConfig.ts
|
|
8073
|
-
import
|
|
8127
|
+
import fs25 from "fs/promises";
|
|
8074
8128
|
import path27 from "path";
|
|
8075
8129
|
import yaml3 from "js-yaml";
|
|
8076
8130
|
function freshDefaults() {
|
|
@@ -8084,7 +8138,7 @@ function freshDefaults() {
|
|
|
8084
8138
|
async function loadReviewConfig(root) {
|
|
8085
8139
|
const file = path27.join(root, ".ctxloom", "review.yml");
|
|
8086
8140
|
try {
|
|
8087
|
-
const raw = await
|
|
8141
|
+
const raw = await fs25.readFile(file, "utf8");
|
|
8088
8142
|
const parsed = yaml3.load(raw);
|
|
8089
8143
|
if (!parsed) return freshDefaults();
|
|
8090
8144
|
return {
|
|
@@ -8100,12 +8154,12 @@ async function loadReviewConfig(root) {
|
|
|
8100
8154
|
|
|
8101
8155
|
// packages/core/src/security/PathValidator.ts
|
|
8102
8156
|
import path28 from "path";
|
|
8103
|
-
import
|
|
8157
|
+
import fs26 from "fs";
|
|
8104
8158
|
var MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
8105
8159
|
var PathValidator = class {
|
|
8106
8160
|
canonicalRoot;
|
|
8107
8161
|
constructor(projectRoot) {
|
|
8108
|
-
this.canonicalRoot =
|
|
8162
|
+
this.canonicalRoot = fs26.realpathSync(path28.resolve(projectRoot));
|
|
8109
8163
|
}
|
|
8110
8164
|
/**
|
|
8111
8165
|
* Validates that the given input path resolves within the project root.
|
|
@@ -8118,7 +8172,7 @@ var PathValidator = class {
|
|
|
8118
8172
|
const resolved = path28.resolve(this.canonicalRoot, inputPath);
|
|
8119
8173
|
let canonical;
|
|
8120
8174
|
try {
|
|
8121
|
-
canonical =
|
|
8175
|
+
canonical = fs26.realpathSync(resolved);
|
|
8122
8176
|
} catch {
|
|
8123
8177
|
canonical = resolved;
|
|
8124
8178
|
}
|
|
@@ -8147,11 +8201,11 @@ var PathValidator = class {
|
|
|
8147
8201
|
*/
|
|
8148
8202
|
readFile(inputPath) {
|
|
8149
8203
|
const absPath = this.validate(inputPath);
|
|
8150
|
-
const stat =
|
|
8204
|
+
const stat = fs26.statSync(absPath);
|
|
8151
8205
|
if (stat.size > MAX_FILE_SIZE) {
|
|
8152
8206
|
throw new Error(`File too large: ${inputPath} (${Math.round(stat.size / 1024)}KB, max ${MAX_FILE_SIZE / 1024 / 1024}MB)`);
|
|
8153
8207
|
}
|
|
8154
|
-
return
|
|
8208
|
+
return fs26.readFileSync(absPath, "utf-8");
|
|
8155
8209
|
}
|
|
8156
8210
|
/**
|
|
8157
8211
|
* Checks if a path exists and is within the project root.
|
|
@@ -8741,4 +8795,4 @@ export {
|
|
|
8741
8795
|
track,
|
|
8742
8796
|
captureError
|
|
8743
8797
|
};
|
|
8744
|
-
//# sourceMappingURL=chunk-
|
|
8798
|
+
//# sourceMappingURL=chunk-3AF7Z7DD.js.map
|
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
startTrial,
|
|
35
35
|
track,
|
|
36
36
|
writeCODEOWNERS
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-3AF7Z7DD.js";
|
|
38
38
|
import {
|
|
39
39
|
VectorStore
|
|
40
40
|
} from "./chunk-NEHYSE2Y.js";
|
|
@@ -125,7 +125,10 @@ function buildContext() {
|
|
|
125
125
|
if (!_ruleManager) _ruleManager = new RuleManager(PROJECT_ROOT, ctx.getPathValidator());
|
|
126
126
|
return _ruleManager;
|
|
127
127
|
},
|
|
128
|
-
isStoreInitialized: () =>
|
|
128
|
+
isStoreInitialized: () => {
|
|
129
|
+
if (_storePromise !== null) return true;
|
|
130
|
+
return fs.existsSync(path.join(DB_PATH, "code_embeddings.lance"));
|
|
131
|
+
},
|
|
129
132
|
isGraphInitialized: () => _graphPromise !== null,
|
|
130
133
|
isParserInitialized: () => _parserPromise !== null
|
|
131
134
|
};
|
|
@@ -613,7 +616,7 @@ try {
|
|
|
613
616
|
} catch {
|
|
614
617
|
}
|
|
615
618
|
var args = process.argv.slice(2);
|
|
616
|
-
var ctxloomVersion = "1.0.
|
|
619
|
+
var ctxloomVersion = "1.0.28".length > 0 ? "1.0.28" : "dev";
|
|
617
620
|
if (args.includes("--version") || args.includes("-v")) {
|
|
618
621
|
process.stdout.write(`ctxloom ${ctxloomVersion}
|
|
619
622
|
`);
|
|
@@ -686,7 +689,7 @@ async function checkLicense() {
|
|
|
686
689
|
if (command !== void 0 && LICENSE_GATE_BYPASS_COMMANDS.has(command)) return;
|
|
687
690
|
const ciKey = process.env["CTXLOOM_LICENSE_KEY"];
|
|
688
691
|
if (ciKey) {
|
|
689
|
-
const { ApiClient } = await import("./src-
|
|
692
|
+
const { ApiClient } = await import("./src-DZ5Z7KVU.js");
|
|
690
693
|
const client = new ApiClient(process.env["CTXLOOM_API_BASE"]);
|
|
691
694
|
try {
|
|
692
695
|
const result = await client.validate(ciKey, "ci-ephemeral");
|
|
@@ -924,11 +927,12 @@ async function main() {
|
|
|
924
927
|
`);
|
|
925
928
|
const indexStart = Date.now();
|
|
926
929
|
const result = await indexDirectory(root, (file, i, total) => {
|
|
930
|
+
if (!isTTY) return;
|
|
927
931
|
const trimmed = file.length > 60 ? "\u2026" + file.slice(-59) : file;
|
|
928
932
|
process.stdout.write(`\r ${style.dim(`[${i}/${total}]`)} ${style.dim(trimmed)}\x1B[K`);
|
|
929
933
|
});
|
|
930
934
|
const indexMs = Date.now() - indexStart;
|
|
931
|
-
process.stdout.write("\r\x1B[K");
|
|
935
|
+
if (isTTY) process.stdout.write("\r\x1B[K");
|
|
932
936
|
const errLabel = result.errors === 0 ? style.dim("0 errors") : style.warn(`${result.errors} error${result.errors === 1 ? "" : "s"}`);
|
|
933
937
|
process.stdout.write(` ${success(`Indexed ${style.bold(String(result.indexed))} files`)} ${style.dim("\xB7")} ${errLabel} ${style.dim(`\xB7 ${(indexMs / 1e3).toFixed(1)}s`)}
|
|
934
938
|
|
|
@@ -1247,7 +1251,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1247
1251
|
process.stderr.write("[ctxloom] --limit must be a non-negative integer (0 for unlimited)\n");
|
|
1248
1252
|
process.exit(2);
|
|
1249
1253
|
}
|
|
1250
|
-
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-
|
|
1254
|
+
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-DZ5Z7KVU.js");
|
|
1251
1255
|
let config;
|
|
1252
1256
|
try {
|
|
1253
1257
|
config = await loadRulesConfig(root);
|
|
@@ -1271,7 +1275,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1271
1275
|
}
|
|
1272
1276
|
let graph;
|
|
1273
1277
|
if (useSnapshot) {
|
|
1274
|
-
const { DependencyGraph: DG } = await import("./src-
|
|
1278
|
+
const { DependencyGraph: DG } = await import("./src-DZ5Z7KVU.js");
|
|
1275
1279
|
graph = new DG();
|
|
1276
1280
|
const loaded = await graph.loadSnapshotOnly(root);
|
|
1277
1281
|
if (!loaded) {
|
|
@@ -1280,7 +1284,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1280
1284
|
}
|
|
1281
1285
|
} else {
|
|
1282
1286
|
process.stderr.write("[ctxloom] Building dependency graph...\n");
|
|
1283
|
-
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-
|
|
1287
|
+
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-DZ5Z7KVU.js");
|
|
1284
1288
|
let parser;
|
|
1285
1289
|
try {
|
|
1286
1290
|
parser = new ASTParser2();
|
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
startTrial,
|
|
81
81
|
track,
|
|
82
82
|
writeCODEOWNERS
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-3AF7Z7DD.js";
|
|
84
84
|
import {
|
|
85
85
|
VectorStore
|
|
86
86
|
} from "./chunk-NEHYSE2Y.js";
|
|
@@ -182,4 +182,4 @@ export {
|
|
|
182
182
|
track,
|
|
183
183
|
writeCODEOWNERS
|
|
184
184
|
};
|
|
185
|
-
//# sourceMappingURL=src-
|
|
185
|
+
//# sourceMappingURL=src-DZ5Z7KVU.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ctxloom-pro",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.28",
|
|
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",
|