ctxloom-pro 1.2.5 → 1.2.7
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/apps/dashboard/dist/server/index.js +189 -85
- package/dist/{chunk-6S3ZF2YS.js → chunk-RY3JAC2Q.js} +974 -366
- package/dist/{chunk-NMXQC5CG.js → chunk-UVR65QBJ.js} +1 -1
- package/dist/{embedder-5LMEYY4M.js → embedder-R4KCXSGO.js} +2 -2
- package/dist/index.js +16 -8
- package/dist/{src-JZWAESJU.js → src-3ZB6BHFW.js} +7 -3
- package/dist/workers/indexerWorker.js +1 -1
- package/package.json +1 -1
|
@@ -166,12 +166,12 @@ var init_VectorStore = __esm({
|
|
|
166
166
|
// server/index.ts
|
|
167
167
|
import express from "express";
|
|
168
168
|
import cors from "cors";
|
|
169
|
-
import
|
|
169
|
+
import path44 from "path";
|
|
170
170
|
import fs32 from "fs";
|
|
171
171
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
172
172
|
|
|
173
173
|
// server/loader.ts
|
|
174
|
-
import
|
|
174
|
+
import path38 from "path";
|
|
175
175
|
|
|
176
176
|
// ../../packages/core/src/graph/DependencyGraph.ts
|
|
177
177
|
import fs7 from "fs";
|
|
@@ -327,7 +327,7 @@ var GrammarLoader = class {
|
|
|
327
327
|
const url = entry.downloadUrl?.trim() ? entry.downloadUrl : `${this.cdn}/${entry.npmPackage}@${entry.version}/${entry.wasmFile}`;
|
|
328
328
|
const dest = path.join(this.cacheDir, entry.wasmFile);
|
|
329
329
|
logger.info("Downloading grammar", { language, url, source: entry.downloadUrl?.trim() ? "custom" : "cdn" });
|
|
330
|
-
fs.mkdirSync(
|
|
330
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
331
331
|
await this.download(url, dest);
|
|
332
332
|
if (entry.sha256 && !this.skipVerify) {
|
|
333
333
|
await this.verifyHash(dest, entry.sha256, language);
|
|
@@ -341,6 +341,12 @@ var GrammarLoader = class {
|
|
|
341
341
|
return new Promise((resolve, reject) => {
|
|
342
342
|
const tmp = dest + ".tmp";
|
|
343
343
|
const file = fs.createWriteStream(tmp);
|
|
344
|
+
const onFileError = (err) => {
|
|
345
|
+
file.destroy();
|
|
346
|
+
fs.rmSync(tmp, { force: true });
|
|
347
|
+
reject(err);
|
|
348
|
+
};
|
|
349
|
+
file.on("error", onFileError);
|
|
344
350
|
const request = https.get(url, (response) => {
|
|
345
351
|
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
346
352
|
const location = response.headers.location;
|
|
@@ -370,11 +376,6 @@ var GrammarLoader = class {
|
|
|
370
376
|
fs.rmSync(tmp, { force: true });
|
|
371
377
|
reject(err);
|
|
372
378
|
});
|
|
373
|
-
file.on("error", (err) => {
|
|
374
|
-
file.destroy();
|
|
375
|
-
fs.rmSync(tmp, { force: true });
|
|
376
|
-
reject(err);
|
|
377
|
-
});
|
|
378
379
|
file.on("finish", () => {
|
|
379
380
|
fs.renameSync(tmp, dest);
|
|
380
381
|
resolve();
|
|
@@ -1001,15 +1002,14 @@ var ASTParser = class {
|
|
|
1001
1002
|
return;
|
|
1002
1003
|
}
|
|
1003
1004
|
case "import_declaration": {
|
|
1005
|
+
const specs = [];
|
|
1004
1006
|
const walkImport = (n) => {
|
|
1005
1007
|
if (n.type === "import_spec") {
|
|
1006
1008
|
const pathNode = n.childForFieldName?.("path");
|
|
1007
1009
|
if (pathNode) {
|
|
1008
1010
|
const spec = pathNode.text.replace(/^"|"$/g, "");
|
|
1009
|
-
|
|
1010
|
-
type: "import",
|
|
1011
|
+
specs.push({
|
|
1011
1012
|
name: spec,
|
|
1012
|
-
source: spec,
|
|
1013
1013
|
startLine: n.startPosition.row + 1,
|
|
1014
1014
|
endLine: n.endPosition.row + 1
|
|
1015
1015
|
});
|
|
@@ -1020,6 +1020,25 @@ var ASTParser = class {
|
|
|
1020
1020
|
}
|
|
1021
1021
|
};
|
|
1022
1022
|
walkImport(node);
|
|
1023
|
+
if (specs.length > 0) {
|
|
1024
|
+
const firstSpec = specs[0];
|
|
1025
|
+
nodes.push({
|
|
1026
|
+
type: "import",
|
|
1027
|
+
name: firstSpec.name,
|
|
1028
|
+
source: firstSpec.name,
|
|
1029
|
+
startLine: node.startPosition.row + 1,
|
|
1030
|
+
endLine: node.endPosition.row + 1
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
for (const spec of specs) {
|
|
1034
|
+
nodes.push({
|
|
1035
|
+
type: "import",
|
|
1036
|
+
name: spec.name,
|
|
1037
|
+
source: spec.name,
|
|
1038
|
+
startLine: spec.startLine,
|
|
1039
|
+
endLine: spec.endLine
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1023
1042
|
return;
|
|
1024
1043
|
}
|
|
1025
1044
|
}
|
|
@@ -1341,6 +1360,24 @@ var ASTParser = class {
|
|
|
1341
1360
|
const lines = source.split("\n");
|
|
1342
1361
|
const walk = (node) => {
|
|
1343
1362
|
switch (node.type) {
|
|
1363
|
+
case "call": {
|
|
1364
|
+
const methodNode = node.childForFieldName?.("method") ?? node.children.find((c) => c?.type === "identifier");
|
|
1365
|
+
const name = methodNode?.text ?? "";
|
|
1366
|
+
if (name === "require" || name === "require_relative" || name === "load" || name === "autoload") {
|
|
1367
|
+
const argsNode = node.childForFieldName?.("arguments") ?? node.children.find((c) => c?.type === "argument_list");
|
|
1368
|
+
const firstStringArg = argsNode?.children.find((c) => c?.type === "string" || c?.type === "simple_symbol");
|
|
1369
|
+
const spec = firstStringArg?.text.replace(/^['":]+|['"]+$/g, "") ?? "";
|
|
1370
|
+
nodes.push({
|
|
1371
|
+
type: "import",
|
|
1372
|
+
name: spec,
|
|
1373
|
+
source: spec,
|
|
1374
|
+
startLine: node.startPosition.row + 1,
|
|
1375
|
+
endLine: node.endPosition.row + 1
|
|
1376
|
+
});
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
break;
|
|
1380
|
+
}
|
|
1344
1381
|
case "method":
|
|
1345
1382
|
case "singleton_method": {
|
|
1346
1383
|
const nameNode = node.childForFieldName?.("name") ?? node.children.find((c) => c?.type === "identifier");
|
|
@@ -1628,9 +1665,18 @@ var ASTParser = class {
|
|
|
1628
1665
|
const walk = (node) => {
|
|
1629
1666
|
switch (node.type) {
|
|
1630
1667
|
case "import_or_export": {
|
|
1631
|
-
const
|
|
1668
|
+
const findUri = (n) => {
|
|
1669
|
+
if (n.type === "uri") return n;
|
|
1670
|
+
for (const c of n.children) {
|
|
1671
|
+
if (!c) continue;
|
|
1672
|
+
const hit = findUri(c);
|
|
1673
|
+
if (hit) return hit;
|
|
1674
|
+
}
|
|
1675
|
+
return void 0;
|
|
1676
|
+
};
|
|
1677
|
+
const uriNode = findUri(node);
|
|
1632
1678
|
const uri = uriNode?.text?.replace(/['"]/g, "") ?? "";
|
|
1633
|
-
if (uri
|
|
1679
|
+
if (uri) {
|
|
1634
1680
|
nodes.push({
|
|
1635
1681
|
type: "import",
|
|
1636
1682
|
name: uri,
|
|
@@ -3135,8 +3181,8 @@ var CoChangeIndex = class _CoChangeIndex {
|
|
|
3135
3181
|
if (event.isBulk || event.isMerge) return;
|
|
3136
3182
|
const paths = event.files.map((f) => f.path);
|
|
3137
3183
|
if (paths.length === 0) return;
|
|
3138
|
-
for (const
|
|
3139
|
-
this.nodeCounts.set(
|
|
3184
|
+
for (const path45 of paths) {
|
|
3185
|
+
this.nodeCounts.set(path45, (this.nodeCounts.get(path45) ?? 0) + 1);
|
|
3140
3186
|
}
|
|
3141
3187
|
for (let i = 0; i < paths.length; i++) {
|
|
3142
3188
|
for (let j = i + 1; j < paths.length; j++) {
|
|
@@ -3283,8 +3329,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3283
3329
|
*/
|
|
3284
3330
|
snapshot() {
|
|
3285
3331
|
const nodes = {};
|
|
3286
|
-
for (const [
|
|
3287
|
-
nodes[
|
|
3332
|
+
for (const [path45, raw] of this.nodes) {
|
|
3333
|
+
nodes[path45] = {
|
|
3288
3334
|
commits: raw.commits,
|
|
3289
3335
|
churnLines: raw.churnLines,
|
|
3290
3336
|
bugCommits: raw.bugCommits,
|
|
@@ -3299,8 +3345,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3299
3345
|
*/
|
|
3300
3346
|
static load(s) {
|
|
3301
3347
|
const idx = new _ChurnIndex();
|
|
3302
|
-
for (const [
|
|
3303
|
-
idx.nodes.set(
|
|
3348
|
+
for (const [path45, raw] of Object.entries(s.nodes)) {
|
|
3349
|
+
idx.nodes.set(path45, {
|
|
3304
3350
|
commits: raw.commits,
|
|
3305
3351
|
churnLines: raw.churnLines,
|
|
3306
3352
|
bugCommits: raw.bugCommits,
|
|
@@ -3313,8 +3359,8 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3313
3359
|
// -------------------------------------------------------------------------
|
|
3314
3360
|
// Private helpers
|
|
3315
3361
|
// -------------------------------------------------------------------------
|
|
3316
|
-
getOrCreate(
|
|
3317
|
-
const existing = this.nodes.get(
|
|
3362
|
+
getOrCreate(path45) {
|
|
3363
|
+
const existing = this.nodes.get(path45);
|
|
3318
3364
|
if (existing !== void 0) return existing;
|
|
3319
3365
|
const fresh = {
|
|
3320
3366
|
commits: 0,
|
|
@@ -3323,7 +3369,7 @@ var ChurnIndex = class _ChurnIndex {
|
|
|
3323
3369
|
authorCounts: {},
|
|
3324
3370
|
lastTouch: 0
|
|
3325
3371
|
};
|
|
3326
|
-
this.nodes.set(
|
|
3372
|
+
this.nodes.set(path45, fresh);
|
|
3327
3373
|
return fresh;
|
|
3328
3374
|
}
|
|
3329
3375
|
};
|
|
@@ -3404,12 +3450,12 @@ var OwnershipIndex = class _OwnershipIndex {
|
|
|
3404
3450
|
*/
|
|
3405
3451
|
snapshot() {
|
|
3406
3452
|
const nodes = {};
|
|
3407
|
-
for (const [
|
|
3453
|
+
for (const [path45, raw] of this.nodes) {
|
|
3408
3454
|
const authorWeights = {};
|
|
3409
3455
|
for (const [email, entry] of Object.entries(raw.authorWeights)) {
|
|
3410
3456
|
authorWeights[email] = { ...entry };
|
|
3411
3457
|
}
|
|
3412
|
-
nodes[
|
|
3458
|
+
nodes[path45] = { authorWeights, lastTouch: raw.lastTouch };
|
|
3413
3459
|
}
|
|
3414
3460
|
return { version: 1, nodes };
|
|
3415
3461
|
}
|
|
@@ -3418,23 +3464,23 @@ var OwnershipIndex = class _OwnershipIndex {
|
|
|
3418
3464
|
*/
|
|
3419
3465
|
static load(s) {
|
|
3420
3466
|
const idx = new _OwnershipIndex();
|
|
3421
|
-
for (const [
|
|
3467
|
+
for (const [path45, raw] of Object.entries(s.nodes)) {
|
|
3422
3468
|
const authorWeights = {};
|
|
3423
3469
|
for (const [email, entry] of Object.entries(raw.authorWeights)) {
|
|
3424
3470
|
authorWeights[email] = { ...entry };
|
|
3425
3471
|
}
|
|
3426
|
-
idx.nodes.set(
|
|
3472
|
+
idx.nodes.set(path45, { authorWeights, lastTouch: raw.lastTouch });
|
|
3427
3473
|
}
|
|
3428
3474
|
return idx;
|
|
3429
3475
|
}
|
|
3430
3476
|
// -------------------------------------------------------------------------
|
|
3431
3477
|
// Private helpers
|
|
3432
3478
|
// -------------------------------------------------------------------------
|
|
3433
|
-
getOrCreate(
|
|
3434
|
-
const existing = this.nodes.get(
|
|
3479
|
+
getOrCreate(path45) {
|
|
3480
|
+
const existing = this.nodes.get(path45);
|
|
3435
3481
|
if (existing !== void 0) return existing;
|
|
3436
3482
|
const fresh = { authorWeights: {}, lastTouch: 0 };
|
|
3437
|
-
this.nodes.set(
|
|
3483
|
+
this.nodes.set(path45, fresh);
|
|
3438
3484
|
return fresh;
|
|
3439
3485
|
}
|
|
3440
3486
|
};
|
|
@@ -4672,8 +4718,8 @@ function getErrorMap() {
|
|
|
4672
4718
|
|
|
4673
4719
|
// ../../node_modules/zod/v3/helpers/parseUtil.js
|
|
4674
4720
|
var makeIssue = (params) => {
|
|
4675
|
-
const { data, path:
|
|
4676
|
-
const fullPath = [...
|
|
4721
|
+
const { data, path: path45, errorMaps, issueData } = params;
|
|
4722
|
+
const fullPath = [...path45, ...issueData.path || []];
|
|
4677
4723
|
const fullIssue = {
|
|
4678
4724
|
...issueData,
|
|
4679
4725
|
path: fullPath
|
|
@@ -4789,11 +4835,11 @@ var errorUtil;
|
|
|
4789
4835
|
|
|
4790
4836
|
// ../../node_modules/zod/v3/types.js
|
|
4791
4837
|
var ParseInputLazyPath = class {
|
|
4792
|
-
constructor(parent, value,
|
|
4838
|
+
constructor(parent, value, path45, key) {
|
|
4793
4839
|
this._cachedPath = [];
|
|
4794
4840
|
this.parent = parent;
|
|
4795
4841
|
this.data = value;
|
|
4796
|
-
this._path =
|
|
4842
|
+
this._path = path45;
|
|
4797
4843
|
this._key = key;
|
|
4798
4844
|
}
|
|
4799
4845
|
get path() {
|
|
@@ -8244,16 +8290,29 @@ init_logger();
|
|
|
8244
8290
|
|
|
8245
8291
|
// ../../packages/core/src/tools/search.ts
|
|
8246
8292
|
init_embedder();
|
|
8293
|
+
|
|
8294
|
+
// ../../packages/core/src/budget/budget.ts
|
|
8295
|
+
init_logger();
|
|
8296
|
+
|
|
8297
|
+
// ../../packages/core/src/tools/search.ts
|
|
8247
8298
|
var Schema = external_exports.object({
|
|
8248
8299
|
query: external_exports.string().describe("Search query \u2014 natural language or code fragment"),
|
|
8249
8300
|
limit: external_exports.number().max(100).optional().default(10).describe("Maximum results to return"),
|
|
8250
|
-
project_root: ProjectRootField
|
|
8301
|
+
project_root: ProjectRootField,
|
|
8302
|
+
// ─── Phase B2 budget surface ──
|
|
8303
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 4000 (when budget surface is opted into). Over-budget rebuilds the result list without the content snippets (paths + scores only)."),
|
|
8304
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) drops snippets; 'truncate' slices the raw XML; 'error' throws."),
|
|
8305
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the path-and-score-only view; 'full'/'auto' lets the budget decide.")
|
|
8251
8306
|
});
|
|
8252
8307
|
|
|
8253
8308
|
// ../../packages/core/src/tools/file.ts
|
|
8254
8309
|
var Schema2 = external_exports.object({
|
|
8255
8310
|
path: external_exports.string().describe("Relative path to the file"),
|
|
8256
|
-
project_root: ProjectRootField
|
|
8311
|
+
project_root: ProjectRootField,
|
|
8312
|
+
// ─── Phase B2 budget surface (all optional; back-compat preserved) ──
|
|
8313
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget in tokens. Falls back to a skeleton when exceeded. Default: 8000 (when budget surface is opted into)."),
|
|
8314
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when the response would exceed max_response_tokens. 'skeleton' (default) substitutes a Skeletonizer signature view; 'truncate' slices the raw text; 'error' throws a structured error with token counts so the caller can re-ask."),
|
|
8315
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces a Skeletonizer view regardless of budget; 'full'/'auto' lets the budget decide.")
|
|
8257
8316
|
});
|
|
8258
8317
|
|
|
8259
8318
|
// ../../packages/core/src/tools/context-packet.ts
|
|
@@ -8261,7 +8320,11 @@ import path16 from "path";
|
|
|
8261
8320
|
var Schema3 = external_exports.object({
|
|
8262
8321
|
target_file: external_exports.string().describe("Relative path to the primary file"),
|
|
8263
8322
|
mode: external_exports.enum(["edit", "read"]).optional().default("edit").describe("Context mode"),
|
|
8264
|
-
project_root: ProjectRootField
|
|
8323
|
+
project_root: ProjectRootField,
|
|
8324
|
+
// ─── Phase B2 budget surface ──
|
|
8325
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 6000 (when budget surface is opted into). Over-budget rebuilds the packet with the primary file replaced by its skeleton."),
|
|
8326
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) re-renders the packet with the primary file skeletonized; 'truncate' slices the raw envelope; 'error' throws."),
|
|
8327
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the skeletonized-primary packet; 'full'/'auto' lets the budget decide.")
|
|
8265
8328
|
});
|
|
8266
8329
|
|
|
8267
8330
|
// ../../packages/core/src/tools/findCallers.ts
|
|
@@ -8279,7 +8342,11 @@ var Schema4 = external_exports.object({
|
|
|
8279
8342
|
// ../../packages/core/src/tools/definition.ts
|
|
8280
8343
|
var Schema5 = external_exports.object({
|
|
8281
8344
|
symbol: external_exports.string().describe("Symbol name to look up"),
|
|
8282
|
-
project_root: ProjectRootField
|
|
8345
|
+
project_root: ProjectRootField,
|
|
8346
|
+
// ─── Phase B2 budget surface (all optional; back-compat preserved) ──
|
|
8347
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget in tokens. Default: 2000 (when budget surface is opted into). No skeleton fallback for this tool \u2014 the response is structural metadata; over-budget falls back to truncation."),
|
|
8348
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton'/'truncate' both slice the XML (no file context to skeletonize from); 'error' throws."),
|
|
8349
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'full'/'auto' default. 'skeleton' is accepted for consistency but produces the same output as 'full' here \u2014 the response is already a compact symbol list.")
|
|
8283
8350
|
});
|
|
8284
8351
|
|
|
8285
8352
|
// ../../packages/core/src/tools/rules.ts
|
|
@@ -8399,7 +8466,11 @@ var Schema15 = external_exports.object({
|
|
|
8399
8466
|
detail_level: external_exports.enum(["standard", "minimal"]).default("standard").describe(
|
|
8400
8467
|
'"standard" (default) lists each written page with size. "minimal" returns counts only.'
|
|
8401
8468
|
),
|
|
8402
|
-
project_root: ProjectRootField
|
|
8469
|
+
project_root: ProjectRootField,
|
|
8470
|
+
// ─── Phase B2 budget surface ──
|
|
8471
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 12000 (when opted in). Over-budget re-renders at detail_level=minimal (counts only) \u2014 the wiki files themselves are unaffected."),
|
|
8472
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) downgrades to minimal output; 'truncate' slices; 'error' throws."),
|
|
8473
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces minimal output regardless of budget; 'full'/'auto' lets the budget decide.")
|
|
8403
8474
|
});
|
|
8404
8475
|
|
|
8405
8476
|
// ../../packages/core/src/tools/graph-export.ts
|
|
@@ -8427,7 +8498,11 @@ var Schema17 = external_exports.object({
|
|
|
8427
8498
|
max_diff_lines: external_exports.number().min(10).max(2e3).optional().default(300).describe(
|
|
8428
8499
|
"Max diff lines to include per file (default: 300)"
|
|
8429
8500
|
),
|
|
8430
|
-
project_root: ProjectRootField
|
|
8501
|
+
project_root: ProjectRootField,
|
|
8502
|
+
// ─── Phase B2 budget surface ──
|
|
8503
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 8000 (when opted in). Over-budget re-renders without <skeleton> blocks and without the transitive_importers section \u2014 keeps diffs, direct importers, and call sites."),
|
|
8504
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) drops skeletons + transitive importers; 'truncate' slices; 'error' throws."),
|
|
8505
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the lighter view; 'full'/'auto' lets the budget decide.")
|
|
8431
8506
|
});
|
|
8432
8507
|
|
|
8433
8508
|
// ../../packages/core/src/tools/refactor-preview.ts
|
|
@@ -8439,7 +8514,11 @@ var Schema18 = external_exports.object({
|
|
|
8439
8514
|
max_files: external_exports.number().min(1).max(200).optional().default(50).describe(
|
|
8440
8515
|
"Maximum number of files to scan for occurrences (default: 50)"
|
|
8441
8516
|
),
|
|
8442
|
-
project_root: ProjectRootField
|
|
8517
|
+
project_root: ProjectRootField,
|
|
8518
|
+
// ─── Phase B2 budget surface ──
|
|
8519
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 4000 (when opted in). Over-budget drops the per-change before/after lines; keeps the file+occurrence summary so callers can decide which files to drill into."),
|
|
8520
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) drops change details; 'truncate' slices; 'error' throws."),
|
|
8521
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the summary-only view; 'full'/'auto' lets the budget decide.")
|
|
8443
8522
|
});
|
|
8444
8523
|
|
|
8445
8524
|
// ../../packages/core/src/tools/execution-flow.ts
|
|
@@ -8452,7 +8531,11 @@ var Schema19 = external_exports.object({
|
|
|
8452
8531
|
max_nodes: external_exports.number().min(1).max(200).optional().default(50).describe(
|
|
8453
8532
|
"Max total steps to include in output (default: 50)"
|
|
8454
8533
|
),
|
|
8455
|
-
project_root: ProjectRootField
|
|
8534
|
+
project_root: ProjectRootField,
|
|
8535
|
+
// ─── Phase B2 budget surface ──
|
|
8536
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 4000 (when opted in). No skeleton fallback \u2014 response is already a bounded step list; over-budget falls through to truncation."),
|
|
8537
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton'/'truncate' both slice; 'error' throws."),
|
|
8538
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'full'/'auto' default; 'skeleton' same output.")
|
|
8456
8539
|
});
|
|
8457
8540
|
|
|
8458
8541
|
// ../../packages/core/src/tools/cross-repo-search.ts
|
|
@@ -8469,7 +8552,11 @@ var Schema20 = external_exports.object({
|
|
|
8469
8552
|
repos: external_exports.array(external_exports.string()).optional().describe(
|
|
8470
8553
|
"Specific repo root paths to search. Omit to search all registered repos."
|
|
8471
8554
|
),
|
|
8472
|
-
project_root: ProjectRootField
|
|
8555
|
+
project_root: ProjectRootField,
|
|
8556
|
+
// ─── Phase B2 budget surface ──
|
|
8557
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 4000 (when opted in). Over-budget drops content snippets (repo + path + score only)."),
|
|
8558
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) drops content snippets; 'truncate' slices; 'error' throws."),
|
|
8559
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the path-and-score-only view; 'full'/'auto' lets the budget decide.")
|
|
8473
8560
|
});
|
|
8474
8561
|
|
|
8475
8562
|
// ../../packages/core/src/tools/apply-refactor.ts
|
|
@@ -8484,7 +8571,11 @@ var Schema21 = external_exports.object({
|
|
|
8484
8571
|
max_files: external_exports.number().min(1).max(200).optional().default(50).describe(
|
|
8485
8572
|
"Maximum candidate files to process (default: 50)"
|
|
8486
8573
|
),
|
|
8487
|
-
project_root: ProjectRootField
|
|
8574
|
+
project_root: ProjectRootField,
|
|
8575
|
+
// ─── Phase B2 budget surface ──
|
|
8576
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 2000 (when opted in). No skeleton fallback \u2014 response is already compact; over-budget falls through to truncation."),
|
|
8577
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton'/'truncate' both slice the XML; 'error' throws."),
|
|
8578
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'full'/'auto' default; 'skeleton' produces the same output.")
|
|
8488
8579
|
});
|
|
8489
8580
|
|
|
8490
8581
|
// ../../packages/core/src/tools/detect-changes.ts
|
|
@@ -8511,7 +8602,11 @@ var Schema23 = external_exports.object({
|
|
|
8511
8602
|
case_sensitive: external_exports.boolean().optional().default(false),
|
|
8512
8603
|
limit: external_exports.number().min(1).max(100).optional().default(20),
|
|
8513
8604
|
context_lines: external_exports.number().min(0).max(5).optional().default(1),
|
|
8514
|
-
project_root: ProjectRootField
|
|
8605
|
+
project_root: ProjectRootField,
|
|
8606
|
+
// ─── Phase B2 budget surface ──
|
|
8607
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 4000 (when budget surface is opted into). Over-budget rebuilds the result list without match snippets (paths + match counts only)."),
|
|
8608
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton' (default) drops snippets; 'truncate' slices the raw XML; 'error' throws."),
|
|
8609
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'skeleton' forces the path-and-count-only view; 'full'/'auto' lets the budget decide.")
|
|
8515
8610
|
});
|
|
8516
8611
|
|
|
8517
8612
|
// ../../packages/core/src/tools/suggested-questions.ts
|
|
@@ -8564,7 +8659,11 @@ var schema3 = external_exports.object({
|
|
|
8564
8659
|
limit: external_exports.number().int().min(1).max(200).default(30).describe(
|
|
8565
8660
|
"Maximum results to return (default: 30)."
|
|
8566
8661
|
),
|
|
8567
|
-
project_root: ProjectRootField
|
|
8662
|
+
project_root: ProjectRootField,
|
|
8663
|
+
// ─── Phase B2 budget surface ──
|
|
8664
|
+
max_response_tokens: external_exports.number().int().positive().optional().describe("Soft response budget. Default: 2000 (when opted in). No skeleton fallback \u2014 response is already structural; over-budget falls through to truncation."),
|
|
8665
|
+
on_budget_exceeded: external_exports.enum(["skeleton", "truncate", "error"]).optional().describe("Behavior when over budget. 'skeleton'/'truncate' both slice the XML; 'error' throws."),
|
|
8666
|
+
response_format: external_exports.enum(["full", "skeleton", "auto"]).optional().describe("'full'/'auto' default; 'skeleton' produces the same output (response is already compact).")
|
|
8568
8667
|
});
|
|
8569
8668
|
|
|
8570
8669
|
// ../../packages/core/src/tools/git-coupling.ts
|
|
@@ -11268,15 +11367,15 @@ import os2 from "os";
|
|
|
11268
11367
|
import { readFileSync as readFileSync2 } from "fs";
|
|
11269
11368
|
|
|
11270
11369
|
// ../../packages/core/src/license/index.ts
|
|
11271
|
-
import
|
|
11370
|
+
import os5 from "os";
|
|
11272
11371
|
|
|
11273
11372
|
// ../../packages/core/src/license/DistinctIdStore.ts
|
|
11274
11373
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
|
|
11275
11374
|
import path32 from "path";
|
|
11276
|
-
import
|
|
11375
|
+
import os3 from "os";
|
|
11277
11376
|
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;
|
|
11278
11377
|
function distinctIdPath(home) {
|
|
11279
|
-
return path32.join(home ??
|
|
11378
|
+
return path32.join(home ?? os3.homedir(), ".ctxloom", "distinct_id");
|
|
11280
11379
|
}
|
|
11281
11380
|
function isValidV4(id) {
|
|
11282
11381
|
return typeof id === "string" && UUID_V4_REGEX.test(id);
|
|
@@ -11295,7 +11394,7 @@ function getOrCreateDistinctId(home) {
|
|
|
11295
11394
|
}
|
|
11296
11395
|
const record = {
|
|
11297
11396
|
id: crypto.randomUUID(),
|
|
11298
|
-
alias_pending:
|
|
11397
|
+
alias_pending: os3.hostname()
|
|
11299
11398
|
};
|
|
11300
11399
|
mkdirSync2(path32.dirname(filePath), { recursive: true });
|
|
11301
11400
|
writeFileSync2(filePath, JSON.stringify(record), { mode: 384 });
|
|
@@ -11323,7 +11422,7 @@ function resolveTelemetryLevel() {
|
|
|
11323
11422
|
}
|
|
11324
11423
|
var TELEMETRY_LEVEL = resolveTelemetryLevel();
|
|
11325
11424
|
var TELEMETRY_DISABLED = TELEMETRY_LEVEL === "off";
|
|
11326
|
-
var CTXLOOM_VERSION = "1.2.
|
|
11425
|
+
var CTXLOOM_VERSION = "1.2.7".length > 0 ? "1.2.7" : "dev";
|
|
11327
11426
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
11328
11427
|
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
|
|
11329
11428
|
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
|
|
@@ -11454,28 +11553,33 @@ function parseStack(stack) {
|
|
|
11454
11553
|
}).filter((f) => f !== null).slice(0, 20);
|
|
11455
11554
|
}
|
|
11456
11555
|
|
|
11457
|
-
// ../../packages/core/src/license/
|
|
11556
|
+
// ../../packages/core/src/license/FunnelMilestones.ts
|
|
11458
11557
|
import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
11459
11558
|
import path33 from "path";
|
|
11460
|
-
import
|
|
11559
|
+
import os4 from "os";
|
|
11560
|
+
|
|
11561
|
+
// ../../packages/core/src/license/TelemetryNotice.ts
|
|
11562
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
11563
|
+
import path34 from "path";
|
|
11564
|
+
import os6 from "os";
|
|
11461
11565
|
|
|
11462
11566
|
// ../../packages/core/src/server/ProjectState.ts
|
|
11463
|
-
import
|
|
11567
|
+
import path36 from "path";
|
|
11464
11568
|
|
|
11465
11569
|
// ../../packages/core/src/server/projectId.ts
|
|
11466
11570
|
import crypto5 from "crypto";
|
|
11467
|
-
import
|
|
11571
|
+
import path35 from "path";
|
|
11468
11572
|
|
|
11469
11573
|
// ../../packages/core/src/server/ProjectStateManager.ts
|
|
11470
11574
|
init_logger();
|
|
11471
11575
|
|
|
11472
11576
|
// ../../packages/core/src/server/resolveProjectRoot.ts
|
|
11473
11577
|
import fs29 from "fs";
|
|
11474
|
-
import
|
|
11578
|
+
import path37 from "path";
|
|
11475
11579
|
|
|
11476
11580
|
// server/loader.ts
|
|
11477
11581
|
async function loadContext(root) {
|
|
11478
|
-
const absRoot =
|
|
11582
|
+
const absRoot = path38.resolve(root);
|
|
11479
11583
|
const overlay = new GitOverlayStore(absRoot);
|
|
11480
11584
|
const gitEnabled = await overlay.loadSnapshot();
|
|
11481
11585
|
const graph = new DependencyGraph();
|
|
@@ -11729,20 +11833,20 @@ function buildOwnershipRouter(ctx) {
|
|
|
11729
11833
|
// server/routes/file.ts
|
|
11730
11834
|
import { Router as Router7 } from "express";
|
|
11731
11835
|
import fs30 from "fs/promises";
|
|
11732
|
-
import
|
|
11836
|
+
import path39 from "path";
|
|
11733
11837
|
function buildFileRouter(ctx) {
|
|
11734
11838
|
const router = Router7();
|
|
11735
11839
|
router.get("/", async (req, res) => {
|
|
11736
11840
|
const rel = req.query.path;
|
|
11737
11841
|
if (!rel) return res.status(400).json({ error: "missing path" });
|
|
11738
|
-
const abs =
|
|
11739
|
-
const rootBoundary = ctx.root.endsWith(
|
|
11842
|
+
const abs = path39.resolve(ctx.root, rel);
|
|
11843
|
+
const rootBoundary = ctx.root.endsWith(path39.sep) ? ctx.root : ctx.root + path39.sep;
|
|
11740
11844
|
if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
|
|
11741
11845
|
return res.status(403).json({ error: "forbidden" });
|
|
11742
11846
|
}
|
|
11743
11847
|
try {
|
|
11744
11848
|
const content = await fs30.readFile(abs, "utf-8");
|
|
11745
|
-
const ext =
|
|
11849
|
+
const ext = path39.extname(abs).slice(1);
|
|
11746
11850
|
res.json({ content, lines: content.split("\n").length, ext });
|
|
11747
11851
|
} catch {
|
|
11748
11852
|
res.status(404).json({ error: "not found" });
|
|
@@ -11754,7 +11858,7 @@ function buildFileRouter(ctx) {
|
|
|
11754
11858
|
// server/routes/open.ts
|
|
11755
11859
|
import { Router as Router8 } from "express";
|
|
11756
11860
|
import { execFile as execFile2 } from "child_process";
|
|
11757
|
-
import
|
|
11861
|
+
import path40 from "path";
|
|
11758
11862
|
function tryOpen(bin, abs) {
|
|
11759
11863
|
return new Promise((resolve) => {
|
|
11760
11864
|
execFile2(bin, [abs], { timeout: 5e3 }, (err) => resolve(!err));
|
|
@@ -11765,8 +11869,8 @@ function buildOpenRouter(ctx) {
|
|
|
11765
11869
|
router.post("/", async (req, res) => {
|
|
11766
11870
|
const rel = req.body?.path;
|
|
11767
11871
|
if (!rel || typeof rel !== "string") return res.status(400).json({ error: "missing path" });
|
|
11768
|
-
const abs =
|
|
11769
|
-
const rootBoundary = ctx.root.endsWith(
|
|
11872
|
+
const abs = path40.resolve(ctx.root, rel);
|
|
11873
|
+
const rootBoundary = ctx.root.endsWith(path40.sep) ? ctx.root : ctx.root + path40.sep;
|
|
11770
11874
|
if (abs !== ctx.root && !abs.startsWith(rootBoundary)) {
|
|
11771
11875
|
return res.status(403).json({ error: "forbidden" });
|
|
11772
11876
|
}
|
|
@@ -11778,7 +11882,7 @@ function buildOpenRouter(ctx) {
|
|
|
11778
11882
|
|
|
11779
11883
|
// server/routes/tokens.ts
|
|
11780
11884
|
import { Router as Router9 } from "express";
|
|
11781
|
-
import
|
|
11885
|
+
import path41 from "path";
|
|
11782
11886
|
import fs31 from "fs";
|
|
11783
11887
|
var CHARS_PER_TOKEN = 4;
|
|
11784
11888
|
var cache = null;
|
|
@@ -11794,7 +11898,7 @@ function buildTokensRouter(ctx) {
|
|
|
11794
11898
|
let fullChars = 0;
|
|
11795
11899
|
let skeletonChars = 0;
|
|
11796
11900
|
for (const file of files) {
|
|
11797
|
-
const absPath =
|
|
11901
|
+
const absPath = path41.join(ctx.root, file);
|
|
11798
11902
|
try {
|
|
11799
11903
|
const content = fs31.readFileSync(absPath, "utf-8");
|
|
11800
11904
|
fullChars += content.length;
|
|
@@ -11897,21 +12001,21 @@ function buildFileTrendsRouter(ctx) {
|
|
|
11897
12001
|
|
|
11898
12002
|
// server/routes/projects.ts
|
|
11899
12003
|
import { Router as Router12 } from "express";
|
|
11900
|
-
import
|
|
12004
|
+
import path43 from "path";
|
|
11901
12005
|
|
|
11902
12006
|
// server/projects.ts
|
|
11903
|
-
import { existsSync as
|
|
11904
|
-
import
|
|
11905
|
-
import
|
|
12007
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
12008
|
+
import os7 from "os";
|
|
12009
|
+
import path42 from "path";
|
|
11906
12010
|
import crypto6 from "crypto";
|
|
11907
|
-
var HOME =
|
|
11908
|
-
var REGISTRY_PATH =
|
|
12011
|
+
var HOME = os7.homedir();
|
|
12012
|
+
var REGISTRY_PATH = path42.join(HOME, ".ctxloom", "repos.json");
|
|
11909
12013
|
function slugFor(root) {
|
|
11910
|
-
const abs =
|
|
12014
|
+
const abs = path42.resolve(root);
|
|
11911
12015
|
return crypto6.createHash("sha1").update(abs).digest("hex").slice(0, 12);
|
|
11912
12016
|
}
|
|
11913
12017
|
function readRegistry() {
|
|
11914
|
-
if (!
|
|
12018
|
+
if (!existsSync5(REGISTRY_PATH)) return [];
|
|
11915
12019
|
try {
|
|
11916
12020
|
const raw = readFileSync4(REGISTRY_PATH, "utf-8");
|
|
11917
12021
|
const parsed = JSON.parse(raw);
|
|
@@ -11922,27 +12026,27 @@ function readRegistry() {
|
|
|
11922
12026
|
}
|
|
11923
12027
|
}
|
|
11924
12028
|
function listProjects(defaultRoot) {
|
|
11925
|
-
const absDefault =
|
|
12029
|
+
const absDefault = path42.resolve(defaultRoot);
|
|
11926
12030
|
const out = [
|
|
11927
12031
|
{
|
|
11928
12032
|
slug: slugFor(absDefault),
|
|
11929
|
-
name:
|
|
12033
|
+
name: path42.basename(absDefault) || absDefault,
|
|
11930
12034
|
root: absDefault,
|
|
11931
12035
|
isDefault: true,
|
|
11932
|
-
hasSnapshot:
|
|
12036
|
+
hasSnapshot: existsSync5(path42.join(absDefault, ".ctxloom"))
|
|
11933
12037
|
}
|
|
11934
12038
|
];
|
|
11935
12039
|
const seen = /* @__PURE__ */ new Set([absDefault]);
|
|
11936
12040
|
for (const entry of readRegistry()) {
|
|
11937
|
-
const abs =
|
|
12041
|
+
const abs = path42.resolve(entry.root);
|
|
11938
12042
|
if (seen.has(abs)) continue;
|
|
11939
12043
|
seen.add(abs);
|
|
11940
12044
|
const item = {
|
|
11941
12045
|
slug: slugFor(abs),
|
|
11942
|
-
name: entry.name ?? (
|
|
12046
|
+
name: entry.name ?? (path42.basename(abs) || abs),
|
|
11943
12047
|
root: abs,
|
|
11944
12048
|
isDefault: false,
|
|
11945
|
-
hasSnapshot:
|
|
12049
|
+
hasSnapshot: existsSync5(path42.join(abs, ".ctxloom"))
|
|
11946
12050
|
};
|
|
11947
12051
|
if (entry.alias !== void 0) item.alias = entry.alias;
|
|
11948
12052
|
out.push(item);
|
|
@@ -11995,7 +12099,7 @@ function buildProjectsRouter(deps) {
|
|
|
11995
12099
|
} catch (err) {
|
|
11996
12100
|
const detail = err instanceof Error ? err.message : String(err);
|
|
11997
12101
|
res.status(500).json({
|
|
11998
|
-
error: `failed to switch to ${
|
|
12102
|
+
error: `failed to switch to ${path43.basename(target.root)}: ${detail}`
|
|
11999
12103
|
});
|
|
12000
12104
|
}
|
|
12001
12105
|
});
|
|
@@ -12052,7 +12156,7 @@ function buildTelemetryRouter() {
|
|
|
12052
12156
|
}
|
|
12053
12157
|
|
|
12054
12158
|
// server/index.ts
|
|
12055
|
-
var __dirname2 =
|
|
12159
|
+
var __dirname2 = path44.dirname(fileURLToPath2(import.meta.url));
|
|
12056
12160
|
async function startDashboard(options) {
|
|
12057
12161
|
const { root, port, open } = options;
|
|
12058
12162
|
console.log(`ctxloom dashboard \u2014 loading context from ${root}...`);
|
|
@@ -12111,7 +12215,7 @@ async function startDashboard(options) {
|
|
|
12111
12215
|
}
|
|
12112
12216
|
activeWatcher = null;
|
|
12113
12217
|
}
|
|
12114
|
-
const snapshotDir =
|
|
12218
|
+
const snapshotDir = path44.join(targetRoot, ".ctxloom");
|
|
12115
12219
|
try {
|
|
12116
12220
|
activeWatcher = fs32.watch(snapshotDir, (_event, filename) => {
|
|
12117
12221
|
if (!filename || !filename.includes("snapshot")) return;
|
|
@@ -12142,12 +12246,12 @@ async function startDashboard(options) {
|
|
|
12142
12246
|
attachSnapshotWatcher(newRoot);
|
|
12143
12247
|
}
|
|
12144
12248
|
}));
|
|
12145
|
-
const clientDist =
|
|
12146
|
-
const clientDistExists = fs32.existsSync(
|
|
12249
|
+
const clientDist = path44.join(__dirname2, "../dashboard/client");
|
|
12250
|
+
const clientDistExists = fs32.existsSync(path44.join(clientDist, "index.html"));
|
|
12147
12251
|
if (clientDistExists) {
|
|
12148
12252
|
app.use(express.static(clientDist, { dotfiles: "allow" }));
|
|
12149
12253
|
app.get(/.*/, (_req, res) => {
|
|
12150
|
-
res.sendFile(
|
|
12254
|
+
res.sendFile(path44.join(clientDist, "index.html"), { dotfiles: "allow" });
|
|
12151
12255
|
});
|
|
12152
12256
|
} else {
|
|
12153
12257
|
app.get(/^\/(?!api\/).*/, (_req, res) => {
|