ctxloom-pro 1.0.4
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/LICENSE +208 -0
- package/README.md +619 -0
- package/dist/ASTParser-3QJ637L6.js +8 -0
- package/dist/DependencyGraph-FGTNORTW.js +10 -0
- package/dist/VectorStore-UQNBYPBV.js +8 -0
- package/dist/chunk-5CJIMX6D.js +1599 -0
- package/dist/chunk-IHXVD5SO.js +35 -0
- package/dist/chunk-II2DPYRJ.js +352 -0
- package/dist/chunk-PSLPRDPL.js +139 -0
- package/dist/chunk-RE2V3FRF.js +1052 -0
- package/dist/chunk-XNKTZGDX.js +125 -0
- package/dist/chunk-ZYDVY7VZ.js +140 -0
- package/dist/dashboard-LVSRXGZN.js +13 -0
- package/dist/embedder-RECRKXTB.js +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5617 -0
- package/dist/logger-2FVN3AGZ.js +7 -0
- package/dist/rules-OMDOX7IJ.js +15 -0
- package/dist/setup/postinstall.d.ts +1 -0
- package/dist/setup/postinstall.js +42 -0
- package/dist/wasm/tree-sitter-tsx.wasm +0 -0
- package/dist/wasm/tree-sitter-typescript.wasm +0 -0
- package/dist/wasm/tree-sitter.wasm +0 -0
- package/dist/workers/indexerWorker.d.ts +2 -0
- package/dist/workers/indexerWorker.js +29 -0
- package/package.json +98 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// src/utils/logger.js
|
|
2
|
+
var LEVELS = {
|
|
3
|
+
debug: 0,
|
|
4
|
+
info: 1,
|
|
5
|
+
warn: 2,
|
|
6
|
+
error: 3
|
|
7
|
+
};
|
|
8
|
+
function getConfiguredLevel() {
|
|
9
|
+
const raw = (process.env.LOG_LEVEL ?? "info").toLowerCase();
|
|
10
|
+
return raw in LEVELS ? raw : "info";
|
|
11
|
+
}
|
|
12
|
+
function write(level, msg, extra) {
|
|
13
|
+
if (LEVELS[level] < LEVELS[getConfiguredLevel()])
|
|
14
|
+
return;
|
|
15
|
+
const entry = {
|
|
16
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17
|
+
level,
|
|
18
|
+
msg
|
|
19
|
+
};
|
|
20
|
+
if (extra) {
|
|
21
|
+
Object.assign(entry, extra);
|
|
22
|
+
}
|
|
23
|
+
process.stderr.write(JSON.stringify(entry) + "\n");
|
|
24
|
+
}
|
|
25
|
+
var logger = {
|
|
26
|
+
debug: (msg, extra) => write("debug", msg, extra),
|
|
27
|
+
info: (msg, extra) => write("info", msg, extra),
|
|
28
|
+
warn: (msg, extra) => write("warn", msg, extra),
|
|
29
|
+
error: (msg, extra) => write("error", msg, extra)
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export {
|
|
33
|
+
logger
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=chunk-IHXVD5SO.js.map
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
// src/setup/clients.ts
|
|
2
|
+
import os from "os";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import { execFileSync } from "child_process";
|
|
6
|
+
var HOME = os.homedir();
|
|
7
|
+
var PLATFORM = process.platform;
|
|
8
|
+
function xdgConfig() {
|
|
9
|
+
if (PLATFORM === "darwin") return path.join(HOME, "Library", "Application Support");
|
|
10
|
+
if (PLATFORM === "win32") return process.env.APPDATA ?? path.join(HOME, "AppData", "Roaming");
|
|
11
|
+
return process.env.XDG_CONFIG_HOME ?? path.join(HOME, ".config");
|
|
12
|
+
}
|
|
13
|
+
var CTXLOOM_SERVER = {
|
|
14
|
+
command: "npx",
|
|
15
|
+
args: ["-y", "ctxloom"],
|
|
16
|
+
env: {}
|
|
17
|
+
};
|
|
18
|
+
var _serverEntry;
|
|
19
|
+
function getServerEntry() {
|
|
20
|
+
if (_serverEntry) return _serverEntry;
|
|
21
|
+
_serverEntry = commandExists("ctxloom") ? { command: "ctxloom", args: [], env: {} } : CTXLOOM_SERVER;
|
|
22
|
+
return _serverEntry;
|
|
23
|
+
}
|
|
24
|
+
var MCP_CLIENTS = [
|
|
25
|
+
// ─── Claude Desktop ───────────────────────────────────────
|
|
26
|
+
{
|
|
27
|
+
id: "claude-desktop",
|
|
28
|
+
name: "Claude Desktop",
|
|
29
|
+
description: "Anthropic Claude desktop application",
|
|
30
|
+
configPaths: [
|
|
31
|
+
path.join(HOME, ".claude", "claude_desktop_config.json"),
|
|
32
|
+
path.join(xdgConfig(), "Claude", "claude_desktop_config.json")
|
|
33
|
+
],
|
|
34
|
+
cliBinaries: ["claude-desktop"],
|
|
35
|
+
appBundles: ["com.anthropic.claudedesktop"],
|
|
36
|
+
usesMcpServersFormat: true,
|
|
37
|
+
serversPath: "mcpServers"
|
|
38
|
+
},
|
|
39
|
+
// ─── Claude Code (CLI) ───────────────────────────────────
|
|
40
|
+
{
|
|
41
|
+
id: "claude-code",
|
|
42
|
+
name: "Claude Code",
|
|
43
|
+
description: "Anthropic Claude Code CLI agent",
|
|
44
|
+
configPaths: [
|
|
45
|
+
path.join(HOME, ".claude", "mcp.json"),
|
|
46
|
+
path.join(HOME, ".claude.json")
|
|
47
|
+
],
|
|
48
|
+
cliBinaries: ["claude"],
|
|
49
|
+
appBundles: [],
|
|
50
|
+
usesMcpServersFormat: true,
|
|
51
|
+
serversPath: "mcpServers"
|
|
52
|
+
},
|
|
53
|
+
// ─── Cursor ─────────────────────────────────────────────
|
|
54
|
+
{
|
|
55
|
+
id: "cursor",
|
|
56
|
+
name: "Cursor",
|
|
57
|
+
description: "Cursor AI code editor",
|
|
58
|
+
configPaths: [
|
|
59
|
+
path.join(xdgConfig(), "Cursor", "User", "globalStorage", "cursor-mcp", "mcp.json"),
|
|
60
|
+
path.join(HOME, ".cursor", "mcp.json"),
|
|
61
|
+
path.join(xdgConfig(), "Cursor", "mcp.json")
|
|
62
|
+
],
|
|
63
|
+
cliBinaries: ["cursor"],
|
|
64
|
+
appBundles: ["com.todesktop.230313mzl4w4u92"],
|
|
65
|
+
usesMcpServersFormat: true,
|
|
66
|
+
serversPath: "mcpServers"
|
|
67
|
+
},
|
|
68
|
+
// ─── VS Code (GitHub Copilot MCP) ───────────────────────
|
|
69
|
+
{
|
|
70
|
+
id: "vscode",
|
|
71
|
+
name: "VS Code",
|
|
72
|
+
description: "Visual Studio Code with MCP support",
|
|
73
|
+
configPaths: [
|
|
74
|
+
path.join(HOME, ".vscode", "mcp.json"),
|
|
75
|
+
path.join(xdgConfig(), "Code", "User", "globalStorage", "mcp.json"),
|
|
76
|
+
path.join(xdgConfig(), "Code - Insiders", "User", "globalStorage", "mcp.json")
|
|
77
|
+
],
|
|
78
|
+
cliBinaries: ["code"],
|
|
79
|
+
appBundles: ["com.microsoft.VSCode", "com.microsoft.VSCodeInsiders"],
|
|
80
|
+
usesMcpServersFormat: true,
|
|
81
|
+
serversPath: "servers"
|
|
82
|
+
},
|
|
83
|
+
// ─── Windsurf (Codeium) ──────────────────────────────────
|
|
84
|
+
{
|
|
85
|
+
id: "windsurf",
|
|
86
|
+
name: "Windsurf",
|
|
87
|
+
description: "Windsurf AI code editor by Codeium",
|
|
88
|
+
configPaths: [
|
|
89
|
+
path.join(xdgConfig(), "Windsurf", "User", "globalStorage", "windsurf-mcp", "mcp.json"),
|
|
90
|
+
path.join(HOME, ".windsurf", "mcp.json")
|
|
91
|
+
],
|
|
92
|
+
cliBinaries: ["windsurf"],
|
|
93
|
+
appBundles: ["com.codeium.windsurf"],
|
|
94
|
+
usesMcpServersFormat: true,
|
|
95
|
+
serversPath: "mcpServers"
|
|
96
|
+
},
|
|
97
|
+
// ─── Augment Code ───────────────────────────────────────
|
|
98
|
+
{
|
|
99
|
+
id: "augment",
|
|
100
|
+
name: "Augment Code",
|
|
101
|
+
description: "Augment Code AI assistant",
|
|
102
|
+
configPaths: [
|
|
103
|
+
path.join(xdgConfig(), "Augment", "mcp.json"),
|
|
104
|
+
path.join(HOME, ".augment", "mcp.json"),
|
|
105
|
+
path.join(xdgConfig(), "Augment Code", "mcp.json")
|
|
106
|
+
],
|
|
107
|
+
cliBinaries: ["augment"],
|
|
108
|
+
appBundles: [],
|
|
109
|
+
usesMcpServersFormat: true,
|
|
110
|
+
serversPath: "mcpServers"
|
|
111
|
+
},
|
|
112
|
+
// ─── Kilo Code ──────────────────────────────────────────
|
|
113
|
+
{
|
|
114
|
+
id: "kilo-code",
|
|
115
|
+
name: "Kilo Code",
|
|
116
|
+
description: "Kilo Code AI coding assistant",
|
|
117
|
+
configPaths: [
|
|
118
|
+
path.join(xdgConfig(), "KiloCode", "mcp.json"),
|
|
119
|
+
path.join(HOME, ".kilocode", "mcp.json")
|
|
120
|
+
],
|
|
121
|
+
cliBinaries: ["kilo-code", "kilocode"],
|
|
122
|
+
appBundles: [],
|
|
123
|
+
usesMcpServersFormat: true,
|
|
124
|
+
serversPath: "mcpServers"
|
|
125
|
+
},
|
|
126
|
+
// ─── Continue.dev ───────────────────────────────────────
|
|
127
|
+
{
|
|
128
|
+
id: "continue",
|
|
129
|
+
name: "Continue.dev",
|
|
130
|
+
description: "Continue open-source AI code assistant",
|
|
131
|
+
configPaths: [
|
|
132
|
+
path.join(HOME, ".continue", "config.json"),
|
|
133
|
+
path.join(xdgConfig(), "continue", "config.json")
|
|
134
|
+
],
|
|
135
|
+
cliBinaries: ["continue"],
|
|
136
|
+
appBundles: [],
|
|
137
|
+
usesMcpServersFormat: false,
|
|
138
|
+
serversPath: "experimental.mcpServers",
|
|
139
|
+
formatConfig: (entry) => ({
|
|
140
|
+
command: entry.command,
|
|
141
|
+
args: entry.args,
|
|
142
|
+
transport: "stdio"
|
|
143
|
+
})
|
|
144
|
+
},
|
|
145
|
+
// ─── Aider ──────────────────────────────────────────────
|
|
146
|
+
{
|
|
147
|
+
id: "aider",
|
|
148
|
+
name: "Aider",
|
|
149
|
+
description: "Aider AI pair programming CLI",
|
|
150
|
+
configPaths: [
|
|
151
|
+
path.join(HOME, ".aider", "mcp.json"),
|
|
152
|
+
path.join(xdgConfig(), "aider", "mcp.json")
|
|
153
|
+
],
|
|
154
|
+
cliBinaries: ["aider"],
|
|
155
|
+
appBundles: [],
|
|
156
|
+
usesMcpServersFormat: true,
|
|
157
|
+
serversPath: "mcpServers"
|
|
158
|
+
},
|
|
159
|
+
// ─── Codex CLI (OpenAI) ─────────────────────────────────
|
|
160
|
+
{
|
|
161
|
+
id: "codex",
|
|
162
|
+
name: "Codex CLI",
|
|
163
|
+
description: "OpenAI Codex CLI agent",
|
|
164
|
+
configPaths: [
|
|
165
|
+
path.join(HOME, ".codex", "mcp.json"),
|
|
166
|
+
path.join(xdgConfig(), "codex", "mcp.json")
|
|
167
|
+
],
|
|
168
|
+
cliBinaries: ["codex"],
|
|
169
|
+
appBundles: [],
|
|
170
|
+
usesMcpServersFormat: true,
|
|
171
|
+
serversPath: "mcpServers"
|
|
172
|
+
},
|
|
173
|
+
// ─── Kimi ───────────────────────────────────────────────
|
|
174
|
+
{
|
|
175
|
+
id: "kimi",
|
|
176
|
+
name: "Kimi",
|
|
177
|
+
description: "Moonshot AI Kimi coding assistant",
|
|
178
|
+
configPaths: [
|
|
179
|
+
path.join(HOME, ".kimi", "mcp.json"),
|
|
180
|
+
path.join(xdgConfig(), "kimi", "mcp.json")
|
|
181
|
+
],
|
|
182
|
+
cliBinaries: ["kimi"],
|
|
183
|
+
appBundles: [],
|
|
184
|
+
usesMcpServersFormat: true,
|
|
185
|
+
serversPath: "mcpServers"
|
|
186
|
+
},
|
|
187
|
+
// ─── Qwen (Alibaba) ─────────────────────────────────────
|
|
188
|
+
{
|
|
189
|
+
id: "qwen",
|
|
190
|
+
name: "Qwen Code",
|
|
191
|
+
description: "Alibaba Qwen coding assistant",
|
|
192
|
+
configPaths: [
|
|
193
|
+
path.join(HOME, ".qwen", "mcp.json"),
|
|
194
|
+
path.join(xdgConfig(), "qwen", "mcp.json")
|
|
195
|
+
],
|
|
196
|
+
cliBinaries: ["qwen-code", "qwen"],
|
|
197
|
+
appBundles: [],
|
|
198
|
+
usesMcpServersFormat: true,
|
|
199
|
+
serversPath: "mcpServers"
|
|
200
|
+
},
|
|
201
|
+
// ─── JetBrains AI ───────────────────────────────────────
|
|
202
|
+
{
|
|
203
|
+
id: "jetbrains",
|
|
204
|
+
name: "JetBrains AI",
|
|
205
|
+
description: "JetBrains IDE with AI assistant",
|
|
206
|
+
configPaths: [
|
|
207
|
+
path.join(xdgConfig(), "JetBrains", "ai-mcp.json")
|
|
208
|
+
],
|
|
209
|
+
cliBinaries: [],
|
|
210
|
+
appBundles: ["com.jetbrains.intellij"],
|
|
211
|
+
usesMcpServersFormat: true,
|
|
212
|
+
serversPath: "mcpServers"
|
|
213
|
+
}
|
|
214
|
+
];
|
|
215
|
+
function detectInstalledClients() {
|
|
216
|
+
const results = [];
|
|
217
|
+
for (const client of MCP_CLIENTS) {
|
|
218
|
+
let detected = false;
|
|
219
|
+
let configPath = "";
|
|
220
|
+
let configExists = false;
|
|
221
|
+
let alreadyConfigured = false;
|
|
222
|
+
for (const cp of client.configPaths) {
|
|
223
|
+
if (fs.existsSync(cp)) {
|
|
224
|
+
detected = true;
|
|
225
|
+
configPath = cp;
|
|
226
|
+
configExists = true;
|
|
227
|
+
try {
|
|
228
|
+
const content = fs.readFileSync(cp, "utf-8");
|
|
229
|
+
const config = JSON.parse(content);
|
|
230
|
+
const servers = getNestedValue(config, client.serversPath);
|
|
231
|
+
if (servers && servers["ctxloom"]) {
|
|
232
|
+
alreadyConfigured = true;
|
|
233
|
+
}
|
|
234
|
+
} catch {
|
|
235
|
+
}
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (!detected) {
|
|
240
|
+
for (const bin of client.cliBinaries) {
|
|
241
|
+
if (commandExists(bin)) {
|
|
242
|
+
detected = true;
|
|
243
|
+
configPath = client.configPaths[0];
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (!detected && PLATFORM === "darwin") {
|
|
249
|
+
for (const bundle of client.appBundles) {
|
|
250
|
+
const appLocations = [
|
|
251
|
+
`/Applications/${bundle.split(".").pop()}.app`,
|
|
252
|
+
path.join(HOME, "Applications", `${bundle.split(".").pop()}.app`)
|
|
253
|
+
];
|
|
254
|
+
for (const loc of appLocations) {
|
|
255
|
+
if (fs.existsSync(loc)) {
|
|
256
|
+
detected = true;
|
|
257
|
+
configPath = client.configPaths[0];
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (detected) break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (detected) {
|
|
265
|
+
results.push({
|
|
266
|
+
client,
|
|
267
|
+
configPath,
|
|
268
|
+
configExists,
|
|
269
|
+
alreadyConfigured
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return results;
|
|
274
|
+
}
|
|
275
|
+
function getNestedValue(obj, path2) {
|
|
276
|
+
const parts = path2.split(".");
|
|
277
|
+
let current = obj;
|
|
278
|
+
for (const part of parts) {
|
|
279
|
+
if (current && typeof current === "object" && part in current) {
|
|
280
|
+
current = current[part];
|
|
281
|
+
} else {
|
|
282
|
+
return void 0;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return current;
|
|
286
|
+
}
|
|
287
|
+
function setNestedValue(obj, path2, value) {
|
|
288
|
+
const parts = path2.split(".");
|
|
289
|
+
let current = obj;
|
|
290
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
291
|
+
if (!(parts[i] in current) || typeof current[parts[i]] !== "object") {
|
|
292
|
+
current[parts[i]] = {};
|
|
293
|
+
}
|
|
294
|
+
current = current[parts[i]];
|
|
295
|
+
}
|
|
296
|
+
current[parts[parts.length - 1]] = value;
|
|
297
|
+
}
|
|
298
|
+
function commandExists(cmd) {
|
|
299
|
+
const isWin = PLATFORM === "win32";
|
|
300
|
+
const checkCmd = isWin ? "where" : "which";
|
|
301
|
+
try {
|
|
302
|
+
const result = execFileSync(checkCmd, [cmd], {
|
|
303
|
+
encoding: "utf-8",
|
|
304
|
+
timeout: 500,
|
|
305
|
+
stdio: "pipe"
|
|
306
|
+
});
|
|
307
|
+
return result.trim().length > 0;
|
|
308
|
+
} catch {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function addCtxloomToConfig(detected) {
|
|
313
|
+
const { client, configPath, configExists, alreadyConfigured } = detected;
|
|
314
|
+
if (alreadyConfigured) {
|
|
315
|
+
return { success: true, message: `ctxloom is already configured in ${client.name}` };
|
|
316
|
+
}
|
|
317
|
+
let config;
|
|
318
|
+
if (configExists) {
|
|
319
|
+
try {
|
|
320
|
+
const content = fs.readFileSync(configPath, "utf-8");
|
|
321
|
+
config = JSON.parse(content);
|
|
322
|
+
} catch {
|
|
323
|
+
return { success: false, message: `Failed to parse existing config at ${configPath}` };
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
config = {};
|
|
327
|
+
}
|
|
328
|
+
const serverEntry = getServerEntry();
|
|
329
|
+
const entryValue = client.formatConfig ? client.formatConfig(serverEntry) : { command: serverEntry.command, args: serverEntry.args, ...serverEntry.env && Object.keys(serverEntry.env).length > 0 ? { env: serverEntry.env } : {} };
|
|
330
|
+
let servers = getNestedValue(config, client.serversPath);
|
|
331
|
+
if (!servers || typeof servers !== "object") {
|
|
332
|
+
setNestedValue(config, client.serversPath, {});
|
|
333
|
+
servers = getNestedValue(config, client.serversPath);
|
|
334
|
+
}
|
|
335
|
+
servers["ctxloom"] = entryValue;
|
|
336
|
+
const dir = path.dirname(configPath);
|
|
337
|
+
if (!fs.existsSync(dir)) {
|
|
338
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
342
|
+
return { success: true, message: `Added ctxloom to ${client.name} (${configPath})` };
|
|
343
|
+
} catch (err) {
|
|
344
|
+
return { success: false, message: `Failed to write config at ${configPath}: ${err}` };
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export {
|
|
349
|
+
detectInstalledClients,
|
|
350
|
+
addCtxloomToConfig
|
|
351
|
+
};
|
|
352
|
+
//# sourceMappingURL=chunk-II2DPYRJ.js.map
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// src/rules/types.ts
|
|
2
|
+
var RulesConfigError = class extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "RulesConfigError";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
// src/rules/loadConfig.ts
|
|
10
|
+
import fs from "fs/promises";
|
|
11
|
+
import path from "path";
|
|
12
|
+
import yaml from "js-yaml";
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
var RuleSchema = z.object({
|
|
15
|
+
name: z.string(),
|
|
16
|
+
type: z.literal("no-import"),
|
|
17
|
+
from: z.string(),
|
|
18
|
+
to: z.string(),
|
|
19
|
+
severity: z.enum(["error", "warn"]).optional()
|
|
20
|
+
});
|
|
21
|
+
var RulesConfigSchema = z.object({
|
|
22
|
+
version: z.literal(1),
|
|
23
|
+
rules: z.array(RuleSchema).default([])
|
|
24
|
+
});
|
|
25
|
+
async function loadRulesConfig(rootDir) {
|
|
26
|
+
const filePath = path.join(rootDir, ".ctxloom", "rules.yml");
|
|
27
|
+
let raw;
|
|
28
|
+
try {
|
|
29
|
+
raw = await fs.readFile(filePath, "utf-8");
|
|
30
|
+
} catch (err) {
|
|
31
|
+
if (err.code === "ENOENT") return null;
|
|
32
|
+
throw new RulesConfigError(`Failed to read rules config: ${String(err)}`);
|
|
33
|
+
}
|
|
34
|
+
let parsed;
|
|
35
|
+
try {
|
|
36
|
+
parsed = yaml.load(raw);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
throw new RulesConfigError(`Invalid YAML in .ctxloom/rules.yml: ${String(err)}`);
|
|
39
|
+
}
|
|
40
|
+
const result = RulesConfigSchema.safeParse(parsed);
|
|
41
|
+
if (!result.success) {
|
|
42
|
+
const messages = result.error.errors.map((e) => ` ${e.path.join(".")}: ${e.message}`).join("\n");
|
|
43
|
+
throw new RulesConfigError(`Invalid .ctxloom/rules.yml schema:
|
|
44
|
+
${messages}`);
|
|
45
|
+
}
|
|
46
|
+
return result.data;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/rules/RulesChecker.ts
|
|
50
|
+
import picomatch from "picomatch";
|
|
51
|
+
var RulesChecker = class {
|
|
52
|
+
constructor(graph, config) {
|
|
53
|
+
this.graph = graph;
|
|
54
|
+
this.config = config;
|
|
55
|
+
}
|
|
56
|
+
graph;
|
|
57
|
+
config;
|
|
58
|
+
check() {
|
|
59
|
+
const start = Date.now();
|
|
60
|
+
const violations = [];
|
|
61
|
+
const warnings = [];
|
|
62
|
+
const allFiles = this.graph.allFiles();
|
|
63
|
+
for (const rule of this.config.rules) {
|
|
64
|
+
const fromMatcher = picomatch(rule.from, { dot: true });
|
|
65
|
+
const toMatcher = picomatch(rule.to, { dot: true });
|
|
66
|
+
const fromFiles = allFiles.filter((f) => fromMatcher(f));
|
|
67
|
+
const toFiles = new Set(allFiles.filter((f) => toMatcher(f)));
|
|
68
|
+
if (fromFiles.length === 0 || toFiles.size === 0) {
|
|
69
|
+
warnings.push(`rule "${rule.name}" matched 0 files on from/to \u2014 check glob`);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const severity = rule.severity ?? "error";
|
|
73
|
+
for (const fromFile of fromFiles) {
|
|
74
|
+
for (const importedFile of this.graph.getImports(fromFile)) {
|
|
75
|
+
if (toFiles.has(importedFile)) {
|
|
76
|
+
violations.push({
|
|
77
|
+
rule: rule.name,
|
|
78
|
+
severity,
|
|
79
|
+
fromFile,
|
|
80
|
+
toFile: importedFile,
|
|
81
|
+
message: `${fromFile} must not import ${importedFile} [${rule.name}]`
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
violations,
|
|
89
|
+
warnings,
|
|
90
|
+
rulesChecked: this.config.rules.length,
|
|
91
|
+
filesChecked: allFiles.length,
|
|
92
|
+
durationMs: Date.now() - start
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// src/rules/reporter.ts
|
|
98
|
+
function formatText(result, limit = 50) {
|
|
99
|
+
const lines = [];
|
|
100
|
+
if (result.warnings.length > 0) {
|
|
101
|
+
for (const w of result.warnings) {
|
|
102
|
+
lines.push(` \u26A0 ${w}`);
|
|
103
|
+
}
|
|
104
|
+
lines.push("");
|
|
105
|
+
}
|
|
106
|
+
const toShow = limit === 0 ? result.violations : result.violations.slice(0, limit);
|
|
107
|
+
const hidden = result.violations.length - toShow.length;
|
|
108
|
+
for (const v of toShow) {
|
|
109
|
+
const tag = v.severity === "warn" ? "WARN" : "ERROR";
|
|
110
|
+
lines.push(` [${tag}] ${v.message}`);
|
|
111
|
+
}
|
|
112
|
+
if (hidden > 0) {
|
|
113
|
+
lines.push(`
|
|
114
|
+
... and ${hidden} more. Run with --json for full output.`);
|
|
115
|
+
}
|
|
116
|
+
if (result.violations.length === 0) {
|
|
117
|
+
lines.push(
|
|
118
|
+
`\u2713 ${result.rulesChecked} rules checked, 0 violations. (${result.filesChecked} files, ${result.durationMs}ms)`
|
|
119
|
+
);
|
|
120
|
+
} else {
|
|
121
|
+
lines.push(
|
|
122
|
+
`
|
|
123
|
+
${result.violations.length} violation(s) found. (${result.filesChecked} files, ${result.rulesChecked} rules, ${result.durationMs}ms)`
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return lines.join("\n");
|
|
127
|
+
}
|
|
128
|
+
function formatJson(result) {
|
|
129
|
+
return JSON.stringify({ schemaVersion: 1, ...result }, null, 2);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export {
|
|
133
|
+
RulesConfigError,
|
|
134
|
+
loadRulesConfig,
|
|
135
|
+
RulesChecker,
|
|
136
|
+
formatText,
|
|
137
|
+
formatJson
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=chunk-PSLPRDPL.js.map
|