ctxloom-pro 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -47,7 +47,7 @@ The full first-run flow is **one install + one trial + one init per project.** E
|
|
|
47
47
|
npm install -g ctxloom-pro
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
> **For local trial / dev use the unpinned command above is fine.** For unattended CI usage, pin to the exact version (`ctxloom-pro@1.
|
|
50
|
+
> **For local trial / dev use the unpinned command above is fine.** For unattended CI usage, pin to the exact version (`ctxloom-pro@1.5.0`) so future CLI releases don't silently desync your agent-spec coverage — see the workflow example below.
|
|
51
51
|
|
|
52
52
|
### 2 — Start your free trial (once per email)
|
|
53
53
|
|
|
@@ -346,7 +346,7 @@ jobs:
|
|
|
346
346
|
# Exact pin (not `@^1`) so future CLI releases that add/remove MCP
|
|
347
347
|
# tools don't silently desync your reviewer-agent specs. Bump on
|
|
348
348
|
# every release; see CHANGELOG.md for the live version table.
|
|
349
|
-
- run: npm install -g ctxloom-pro@1.
|
|
349
|
+
- run: npm install -g ctxloom-pro@1.5.0
|
|
350
350
|
- run: ctxloom index
|
|
351
351
|
- run: ctxloom rules check --json
|
|
352
352
|
```
|
|
@@ -8288,9 +8288,6 @@ var ProjectRootField = external_exports.string().optional().describe(PROJECT_ROO
|
|
|
8288
8288
|
// ../../packages/core/src/tools/registry.ts
|
|
8289
8289
|
init_logger();
|
|
8290
8290
|
|
|
8291
|
-
// ../../packages/core/src/tools/search.ts
|
|
8292
|
-
init_embedder();
|
|
8293
|
-
|
|
8294
8291
|
// ../../packages/core/src/budget/budget.ts
|
|
8295
8292
|
init_logger();
|
|
8296
8293
|
|
|
@@ -8301,7 +8298,21 @@ import os2 from "os";
|
|
|
8301
8298
|
import path16 from "path";
|
|
8302
8299
|
var DEFAULT_TELEMETRY_DIR = path16.join(os2.homedir(), ".ctxloom", "telemetry");
|
|
8303
8300
|
|
|
8301
|
+
// ../../packages/core/src/budget/learnedSuggestions.ts
|
|
8302
|
+
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
8303
|
+
|
|
8304
|
+
// ../../packages/core/src/budget/taskBudget.ts
|
|
8305
|
+
var OVER_BUDGET_ARG_OVERRIDES = Object.freeze({
|
|
8306
|
+
// Budget-surface tools (the 12 source-returning ones).
|
|
8307
|
+
max_response_tokens: 200,
|
|
8308
|
+
response_format: "skeleton",
|
|
8309
|
+
on_budget_exceeded: "skeleton",
|
|
8310
|
+
// Tools with detail_level (hub_nodes, bridge_nodes, etc).
|
|
8311
|
+
detail_level: "minimal"
|
|
8312
|
+
});
|
|
8313
|
+
|
|
8304
8314
|
// ../../packages/core/src/tools/search.ts
|
|
8315
|
+
init_embedder();
|
|
8305
8316
|
var Schema = external_exports.object({
|
|
8306
8317
|
query: external_exports.string().describe("Search query \u2014 natural language or code fragment"),
|
|
8307
8318
|
limit: external_exports.number().max(100).optional().default(10).describe("Maximum results to return"),
|
|
@@ -11441,7 +11452,7 @@ function resolveTelemetryLevel() {
|
|
|
11441
11452
|
}
|
|
11442
11453
|
var TELEMETRY_LEVEL = resolveTelemetryLevel();
|
|
11443
11454
|
var TELEMETRY_DISABLED = TELEMETRY_LEVEL === "off";
|
|
11444
|
-
var CTXLOOM_VERSION = "1.
|
|
11455
|
+
var CTXLOOM_VERSION = "1.5.0".length > 0 ? "1.5.0" : "dev";
|
|
11445
11456
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
11446
11457
|
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
|
|
11447
11458
|
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
|
|
@@ -11601,6 +11612,72 @@ import fs31 from "fs";
|
|
|
11601
11612
|
import path39 from "path";
|
|
11602
11613
|
|
|
11603
11614
|
// ../../packages/core/src/install/templates.ts
|
|
11615
|
+
var RULES_BLOCK_CONTENT = `## MCP Tools: ctxloom
|
|
11616
|
+
|
|
11617
|
+
**IMPORTANT: This project has a knowledge graph. ALWAYS use the
|
|
11618
|
+
ctxloom MCP tools BEFORE Grep/Glob/Read to explore the codebase.**
|
|
11619
|
+
The graph is faster, cheaper (fewer tokens), and gives you
|
|
11620
|
+
structural context (callers, dependents, test coverage) that file
|
|
11621
|
+
scanning cannot.
|
|
11622
|
+
|
|
11623
|
+
### Start every workflow with \`ctx_get_minimal_context\`
|
|
11624
|
+
|
|
11625
|
+
The first MCP call into ctxloom should always be
|
|
11626
|
+
\`ctx_get_minimal_context(task="<what you're about to do>")\`. It
|
|
11627
|
+
returns ~150 tokens of orientation plus a task-aware
|
|
11628
|
+
\`suggested_first_tool\` you should call next instead of guessing.
|
|
11629
|
+
|
|
11630
|
+
### When to use graph tools FIRST
|
|
11631
|
+
|
|
11632
|
+
- **Code review**: \`ctx_detect_changes\` + \`ctx_get_review_context\`
|
|
11633
|
+
instead of reading whole files
|
|
11634
|
+
- **Understanding impact**: \`ctx_blast_radius\` + \`ctx_get_affected_flows\`
|
|
11635
|
+
instead of manually tracing imports
|
|
11636
|
+
- **Refactor planning**: \`ctx_get_call_graph(direction: 'callers')\`
|
|
11637
|
+
+ \`ctx_refactor_preview\` before any rename
|
|
11638
|
+
- **Architecture questions**: \`ctx_architecture_overview\`,
|
|
11639
|
+
\`ctx_community_list\`, \`ctx_hub_nodes\`
|
|
11640
|
+
- **Finding code**: \`ctx_search\` or \`ctx_full_text_search\` instead
|
|
11641
|
+
of \`Grep\`
|
|
11642
|
+
|
|
11643
|
+
Fall back to Grep/Glob/Read **only** when the graph doesn't cover
|
|
11644
|
+
what you need.
|
|
11645
|
+
|
|
11646
|
+
### Follow the \`next_tool_suggestions\` in every response
|
|
11647
|
+
|
|
11648
|
+
Every budget-wrapped ctxloom response includes
|
|
11649
|
+
\`meta.next_tool_suggestions\` \u2014 author-curated follow-ups with
|
|
11650
|
+
\`why\` reasoning and \`estimated_tokens\` per entry. Pick from
|
|
11651
|
+
those instead of guessing.
|
|
11652
|
+
|
|
11653
|
+
### Token-budget protocol
|
|
11654
|
+
|
|
11655
|
+
- Target: \u22648 tool calls per task, \u22642000 total tokens of graph context
|
|
11656
|
+
- Pass \`max_response_tokens\` on calls that might return large
|
|
11657
|
+
responses; the budget surface returns skeletons instead of dumping
|
|
11658
|
+
- Use \`response_format: 'skeleton'\` when you know you only need
|
|
11659
|
+
signatures, not bodies
|
|
11660
|
+
|
|
11661
|
+
### Key tools at a glance
|
|
11662
|
+
|
|
11663
|
+
| Tool | Use when |
|
|
11664
|
+
|------|----------|
|
|
11665
|
+
| \`ctx_get_minimal_context\` | START HERE \u2014 orientation anchor |
|
|
11666
|
+
| \`ctx_detect_changes\` | Reviewing code changes; risk-scored |
|
|
11667
|
+
| \`ctx_get_review_context\` | Token-efficient review snippets |
|
|
11668
|
+
| \`ctx_blast_radius\` | Blast radius of a change |
|
|
11669
|
+
| \`ctx_get_affected_flows\` | Execution paths impacted |
|
|
11670
|
+
| \`ctx_get_call_graph\` | Callers / callees of a symbol |
|
|
11671
|
+
| \`ctx_search\` / \`ctx_full_text_search\` | Find code |
|
|
11672
|
+
| \`ctx_architecture_overview\` | High-level codebase map |
|
|
11673
|
+
| \`ctx_refactor_preview\` / \`ctx_apply_refactor\` | Plan a rename |
|
|
11674
|
+
|
|
11675
|
+
### Hooks keep the graph fresh
|
|
11676
|
+
|
|
11677
|
+
\`ctxloom init\` installed a PostToolUse hook on \`Write|Edit\` that
|
|
11678
|
+
runs \`ctxloom update --incremental --quiet\` \u2014 so the graph is
|
|
11679
|
+
always up to date when you query it. No "did the index update yet?"
|
|
11680
|
+
guessing.`;
|
|
11604
11681
|
var SESSION_START_HEADER = `#!/usr/bin/env bash
|
|
11605
11682
|
# ctxloom \u2014 agent-harness session-start hook
|
|
11606
11683
|
# Generated by \`ctxloom init\`. Re-run \`ctxloom init\` to update.
|
|
@@ -11640,6 +11717,69 @@ EOF
|
|
|
11640
11717
|
fi
|
|
11641
11718
|
`;
|
|
11642
11719
|
var SESSION_START_FULL = SESSION_START_HEADER + "\n" + SESSION_START_BODY;
|
|
11720
|
+
function plainBody() {
|
|
11721
|
+
return RULES_BLOCK_CONTENT.replace(/^##\s+/gm, "").replace(/^###\s+/gm, "").replace(/^####\s+/gm, "").replace(/\*\*(.*?)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1");
|
|
11722
|
+
}
|
|
11723
|
+
var CURSOR_HEADER = `# ctxloom \u2014 Cursor agent rules
|
|
11724
|
+
# Generated by \`ctxloom init --host=cursor\`. Re-run to update.
|
|
11725
|
+
|
|
11726
|
+
`;
|
|
11727
|
+
var AIDER_HEADER = `# Project Conventions
|
|
11728
|
+
|
|
11729
|
+
Generated by \`ctxloom init --host=aider\`. Re-run to update.
|
|
11730
|
+
|
|
11731
|
+
`;
|
|
11732
|
+
var COPILOT_HEADER = `# ctxloom \u2014 GitHub Copilot instructions
|
|
11733
|
+
|
|
11734
|
+
Generated by \`ctxloom init --host=copilot\`. Re-run to update.
|
|
11735
|
+
|
|
11736
|
+
`;
|
|
11737
|
+
var WINDSURF_HEADER = `# ctxloom \u2014 Windsurf agent rules
|
|
11738
|
+
# Generated by \`ctxloom init --host=windsurf\`. Re-run to update.
|
|
11739
|
+
|
|
11740
|
+
`;
|
|
11741
|
+
var HOST_ADAPTERS = [
|
|
11742
|
+
// Note: claude / agents / gemini are NOT defined as HostAdapters in
|
|
11743
|
+
// this list — they predate the matrix and live in their own writeRulesBlock
|
|
11744
|
+
// path with HMAC-wrapped Markdown blocks. Future refactor may unify.
|
|
11745
|
+
{
|
|
11746
|
+
id: "cursor",
|
|
11747
|
+
path: ".cursorrules",
|
|
11748
|
+
defaultEnabled: false,
|
|
11749
|
+
render: () => CURSOR_HEADER + plainBody() + "\n",
|
|
11750
|
+
isCanonical(current) {
|
|
11751
|
+
return current === this.render();
|
|
11752
|
+
}
|
|
11753
|
+
},
|
|
11754
|
+
{
|
|
11755
|
+
id: "aider",
|
|
11756
|
+
path: "CONVENTIONS.md",
|
|
11757
|
+
defaultEnabled: false,
|
|
11758
|
+
render: () => AIDER_HEADER + RULES_BLOCK_CONTENT + "\n",
|
|
11759
|
+
isCanonical(current) {
|
|
11760
|
+
return current === this.render();
|
|
11761
|
+
}
|
|
11762
|
+
},
|
|
11763
|
+
{
|
|
11764
|
+
id: "copilot",
|
|
11765
|
+
path: ".github/copilot-instructions.md",
|
|
11766
|
+
defaultEnabled: false,
|
|
11767
|
+
render: () => COPILOT_HEADER + RULES_BLOCK_CONTENT + "\n",
|
|
11768
|
+
isCanonical(current) {
|
|
11769
|
+
return current === this.render();
|
|
11770
|
+
}
|
|
11771
|
+
},
|
|
11772
|
+
{
|
|
11773
|
+
id: "windsurf",
|
|
11774
|
+
path: ".windsurfrules",
|
|
11775
|
+
defaultEnabled: false,
|
|
11776
|
+
render: () => WINDSURF_HEADER + plainBody() + "\n",
|
|
11777
|
+
isCanonical(current) {
|
|
11778
|
+
return current === this.render();
|
|
11779
|
+
}
|
|
11780
|
+
}
|
|
11781
|
+
];
|
|
11782
|
+
var SUPPORTED_HOST_IDS = HOST_ADAPTERS.map((h) => h.id);
|
|
11643
11783
|
|
|
11644
11784
|
// ../../packages/core/src/install/hmacBlock.ts
|
|
11645
11785
|
import crypto6 from "crypto";
|
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
generateEmbedding
|
|
7
7
|
} from "./chunk-UVR65QBJ.js";
|
|
8
8
|
import {
|
|
9
|
-
diskSink
|
|
9
|
+
diskSink,
|
|
10
|
+
readEvents
|
|
10
11
|
} from "./chunk-5I6CJITG.js";
|
|
11
12
|
import {
|
|
12
13
|
logger
|
|
@@ -4590,29 +4591,110 @@ function registerStatusTool(registry, ctx) {
|
|
|
4590
4591
|
);
|
|
4591
4592
|
}
|
|
4592
4593
|
|
|
4593
|
-
// packages/core/src/
|
|
4594
|
-
var
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4594
|
+
// packages/core/src/budget/learnedSuggestions.ts
|
|
4595
|
+
var DEFAULT_WINDOW_DAYS2 = 14;
|
|
4596
|
+
var SESSION_GAP_MS = 9e4;
|
|
4597
|
+
var MIN_SAMPLES_PER_PAIR = 3;
|
|
4598
|
+
var TOP_K = 3;
|
|
4599
|
+
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
4600
|
+
var _cache = null;
|
|
4601
|
+
function __resetLearnedSuggestionsCacheForTests() {
|
|
4602
|
+
_cache = null;
|
|
4603
|
+
}
|
|
4604
|
+
function clampTokens(n) {
|
|
4605
|
+
if (!Number.isFinite(n)) return 0;
|
|
4606
|
+
return Math.max(0, Math.min(1e5, Math.round(n)));
|
|
4607
|
+
}
|
|
4608
|
+
function learnSuggestionsFromTelemetry(opts = {}) {
|
|
4609
|
+
const windowDays = opts.windowDays ?? DEFAULT_WINDOW_DAYS2;
|
|
4610
|
+
const sessionGapMs = opts.sessionGapMs ?? SESSION_GAP_MS;
|
|
4611
|
+
const minSamples = opts.minSamples ?? MIN_SAMPLES_PER_PAIR;
|
|
4612
|
+
const allowlist = opts.registeredTools;
|
|
4613
|
+
const events = opts.events ?? safeReadEvents(windowDays);
|
|
4614
|
+
if (events.length === 0) return {};
|
|
4615
|
+
const sorted = events.map((e) => ({ ts: Date.parse(e.ts), tool: e.tool })).filter((e) => Number.isFinite(e.ts) && typeof e.tool === "string" && e.tool.length > 0).sort((a, b) => a.ts - b.ts);
|
|
4616
|
+
const transitions = /* @__PURE__ */ new Map();
|
|
4617
|
+
const tokenSums = /* @__PURE__ */ new Map();
|
|
4618
|
+
let prevTool = null;
|
|
4619
|
+
let prevTs = -Infinity;
|
|
4620
|
+
for (const e of sorted) {
|
|
4621
|
+
if (prevTool && e.tool !== prevTool && e.ts - prevTs <= sessionGapMs) {
|
|
4622
|
+
let row = transitions.get(prevTool);
|
|
4623
|
+
if (!row) {
|
|
4624
|
+
row = /* @__PURE__ */ new Map();
|
|
4625
|
+
transitions.set(prevTool, row);
|
|
4626
|
+
}
|
|
4627
|
+
row.set(e.tool, (row.get(e.tool) ?? 0) + 1);
|
|
4628
|
+
}
|
|
4629
|
+
prevTool = e.tool;
|
|
4630
|
+
prevTs = e.ts;
|
|
4631
|
+
}
|
|
4632
|
+
for (const raw of events) {
|
|
4633
|
+
const tok = raw.original_tokens;
|
|
4634
|
+
if (typeof tok === "number" && Number.isFinite(tok)) {
|
|
4635
|
+
const acc = tokenSums.get(raw.tool) ?? { sum: 0, n: 0 };
|
|
4636
|
+
acc.sum += tok;
|
|
4637
|
+
acc.n += 1;
|
|
4638
|
+
tokenSums.set(raw.tool, acc);
|
|
4639
|
+
}
|
|
4598
4640
|
}
|
|
4599
|
-
|
|
4600
|
-
|
|
4641
|
+
const out = {};
|
|
4642
|
+
for (const [from, row] of transitions) {
|
|
4643
|
+
if (allowlist && !allowlist.has(from)) continue;
|
|
4644
|
+
const candidates = [];
|
|
4645
|
+
for (const [to, count] of row) {
|
|
4646
|
+
if (count < minSamples) continue;
|
|
4647
|
+
if (allowlist && !allowlist.has(to)) continue;
|
|
4648
|
+
const tokenAcc = tokenSums.get(to);
|
|
4649
|
+
const avgTokens = tokenAcc && tokenAcc.n > 0 ? tokenAcc.sum / tokenAcc.n : 0;
|
|
4650
|
+
candidates.push({
|
|
4651
|
+
tool: to,
|
|
4652
|
+
why: `Learned from telemetry: ${count} agents followed ${from} with ${to}.`,
|
|
4653
|
+
estimated_tokens: clampTokens(avgTokens)
|
|
4654
|
+
});
|
|
4655
|
+
}
|
|
4656
|
+
if (candidates.length === 0) continue;
|
|
4657
|
+
candidates.sort((a, b) => {
|
|
4658
|
+
const matchA = a.why.match(/(\d+) agents/);
|
|
4659
|
+
const matchB = b.why.match(/(\d+) agents/);
|
|
4660
|
+
const ca = matchA ? parseInt(matchA[1], 10) : 0;
|
|
4661
|
+
const cb = matchB ? parseInt(matchB[1], 10) : 0;
|
|
4662
|
+
return cb - ca;
|
|
4663
|
+
});
|
|
4664
|
+
out[from] = candidates.slice(0, TOP_K);
|
|
4601
4665
|
}
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4666
|
+
return out;
|
|
4667
|
+
}
|
|
4668
|
+
function safeReadEvents(windowDays) {
|
|
4669
|
+
try {
|
|
4670
|
+
const until = /* @__PURE__ */ new Date();
|
|
4671
|
+
const since = new Date(until.getTime() - windowDays * 24 * 60 * 60 * 1e3);
|
|
4672
|
+
return readEvents({ since, until });
|
|
4673
|
+
} catch {
|
|
4674
|
+
return [];
|
|
4608
4675
|
}
|
|
4609
|
-
|
|
4610
|
-
|
|
4676
|
+
}
|
|
4677
|
+
function getLearnedRules(opts = {}) {
|
|
4678
|
+
if (_cache && _cache.expiresAt > Date.now()) {
|
|
4679
|
+
return filterRulesByAllowlist(_cache.rules, opts.registeredTools);
|
|
4611
4680
|
}
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4681
|
+
const unfilteredRules = learnSuggestionsFromTelemetry({
|
|
4682
|
+
...opts,
|
|
4683
|
+
registeredTools: void 0
|
|
4684
|
+
});
|
|
4685
|
+
_cache = { rules: unfilteredRules, expiresAt: Date.now() + CACHE_TTL_MS };
|
|
4686
|
+
return filterRulesByAllowlist(unfilteredRules, opts.registeredTools);
|
|
4687
|
+
}
|
|
4688
|
+
function filterRulesByAllowlist(rules, allowlist) {
|
|
4689
|
+
if (!allowlist) return rules;
|
|
4690
|
+
const out = {};
|
|
4691
|
+
for (const [from, suggestions] of Object.entries(rules)) {
|
|
4692
|
+
if (!allowlist.has(from)) continue;
|
|
4693
|
+
const kept = suggestions.filter((s) => allowlist.has(s.tool));
|
|
4694
|
+
if (kept.length > 0) out[from] = kept;
|
|
4695
|
+
}
|
|
4696
|
+
return out;
|
|
4697
|
+
}
|
|
4616
4698
|
|
|
4617
4699
|
// packages/core/src/budget/nextToolSuggestions.ts
|
|
4618
4700
|
var STATIC_RULES = {
|
|
@@ -4923,6 +5005,17 @@ function clampEstimate(n) {
|
|
|
4923
5005
|
return Math.max(TOKEN_ESTIMATE_MIN, Math.min(TOKEN_ESTIMATE_MAX, Math.round(n)));
|
|
4924
5006
|
}
|
|
4925
5007
|
function suggestNext(fromTool, registeredTools) {
|
|
5008
|
+
if (process.env.CTXLOOM_LEARNED_SUGGESTIONS === "1") {
|
|
5009
|
+
const learned = getLearnedRules({ registeredTools })[fromTool];
|
|
5010
|
+
if (learned && learned.length > 0) {
|
|
5011
|
+
return learned.slice(0, 3).map((s) => ({
|
|
5012
|
+
tool: s.tool,
|
|
5013
|
+
args: s.args,
|
|
5014
|
+
why: s.why,
|
|
5015
|
+
estimated_tokens: clampEstimate(s.estimated_tokens)
|
|
5016
|
+
}));
|
|
5017
|
+
}
|
|
5018
|
+
}
|
|
4926
5019
|
const raw = STATIC_RULES[fromTool] ?? [];
|
|
4927
5020
|
const filtered = registeredTools ? raw.filter((s) => registeredTools.has(s.tool)) : raw;
|
|
4928
5021
|
return filtered.slice(0, 3).map((s) => ({
|
|
@@ -5141,7 +5234,159 @@ function wrapResponse(result) {
|
|
|
5141
5234
|
return JSON.stringify(envelope);
|
|
5142
5235
|
}
|
|
5143
5236
|
|
|
5237
|
+
// packages/core/src/budget/taskBudget.ts
|
|
5238
|
+
var DEFAULT_MAX_CALLS = 8;
|
|
5239
|
+
var DEFAULT_RESET_GAP_MS = 9e4;
|
|
5240
|
+
var ENV_VAR = "CTXLOOM_TASK_TOOL_BUDGET";
|
|
5241
|
+
function parseEnvBudget() {
|
|
5242
|
+
const raw = process.env[ENV_VAR];
|
|
5243
|
+
if (!raw) return null;
|
|
5244
|
+
const n = parseInt(raw, 10);
|
|
5245
|
+
if (!Number.isFinite(n) || n <= 0) return null;
|
|
5246
|
+
return n;
|
|
5247
|
+
}
|
|
5248
|
+
var TaskBudgetTracker = class {
|
|
5249
|
+
state = /* @__PURE__ */ new Map();
|
|
5250
|
+
maxCalls;
|
|
5251
|
+
resetGapMs;
|
|
5252
|
+
constructor(opts = {}) {
|
|
5253
|
+
this.maxCalls = opts.maxCalls ?? parseEnvBudget() ?? DEFAULT_MAX_CALLS;
|
|
5254
|
+
this.resetGapMs = opts.resetGapMs ?? DEFAULT_RESET_GAP_MS;
|
|
5255
|
+
}
|
|
5256
|
+
/**
|
|
5257
|
+
* Record a tool call against the budget. Returns the enforcement
|
|
5258
|
+
* decision the dispatch layer should act on.
|
|
5259
|
+
*
|
|
5260
|
+
* @param sessionId — opaque session identifier. Currently a
|
|
5261
|
+
* single global key ('process'); reserved for future multi-
|
|
5262
|
+
* session enforcement.
|
|
5263
|
+
* @param now — milliseconds since epoch. Test hook; defaults to
|
|
5264
|
+
* `Date.now()`.
|
|
5265
|
+
*/
|
|
5266
|
+
recordCall(sessionId = "process", now = Date.now()) {
|
|
5267
|
+
if (isBudgetDisabled()) {
|
|
5268
|
+
return {
|
|
5269
|
+
overBudget: false,
|
|
5270
|
+
callCount: 0,
|
|
5271
|
+
maxCalls: this.maxCalls,
|
|
5272
|
+
firstBreach: false
|
|
5273
|
+
};
|
|
5274
|
+
}
|
|
5275
|
+
const existing = this.state.get(sessionId);
|
|
5276
|
+
if (existing && now - existing.lastCallTs > this.resetGapMs) {
|
|
5277
|
+
const fresh = { count: 1, lastCallTs: now, breachEmitted: false };
|
|
5278
|
+
this.state.set(sessionId, fresh);
|
|
5279
|
+
return {
|
|
5280
|
+
overBudget: false,
|
|
5281
|
+
callCount: 1,
|
|
5282
|
+
maxCalls: this.maxCalls,
|
|
5283
|
+
firstBreach: false
|
|
5284
|
+
};
|
|
5285
|
+
}
|
|
5286
|
+
const next = (existing?.count ?? 0) + 1;
|
|
5287
|
+
const wasBreached = existing?.breachEmitted ?? false;
|
|
5288
|
+
const overBudget = next > this.maxCalls;
|
|
5289
|
+
const firstBreach = overBudget && !wasBreached;
|
|
5290
|
+
this.state.set(sessionId, {
|
|
5291
|
+
count: next,
|
|
5292
|
+
lastCallTs: now,
|
|
5293
|
+
breachEmitted: wasBreached || firstBreach
|
|
5294
|
+
});
|
|
5295
|
+
return { overBudget, callCount: next, maxCalls: this.maxCalls, firstBreach };
|
|
5296
|
+
}
|
|
5297
|
+
/**
|
|
5298
|
+
* Test-only: drop all state. Lets tests run in isolation without
|
|
5299
|
+
* depending on order.
|
|
5300
|
+
*
|
|
5301
|
+
* @internal
|
|
5302
|
+
*/
|
|
5303
|
+
reset() {
|
|
5304
|
+
this.state.clear();
|
|
5305
|
+
}
|
|
5306
|
+
/**
|
|
5307
|
+
* Test/diagnostic — current call count for the default session.
|
|
5308
|
+
* @internal
|
|
5309
|
+
*/
|
|
5310
|
+
__getCount(sessionId = "process") {
|
|
5311
|
+
return this.state.get(sessionId)?.count ?? 0;
|
|
5312
|
+
}
|
|
5313
|
+
};
|
|
5314
|
+
var _singleton = null;
|
|
5315
|
+
function getTaskBudgetTracker() {
|
|
5316
|
+
if (!_singleton) _singleton = new TaskBudgetTracker();
|
|
5317
|
+
return _singleton;
|
|
5318
|
+
}
|
|
5319
|
+
function __resetTaskBudgetTrackerForTests() {
|
|
5320
|
+
_singleton = null;
|
|
5321
|
+
}
|
|
5322
|
+
var OVER_BUDGET_ARG_OVERRIDES = Object.freeze({
|
|
5323
|
+
// Budget-surface tools (the 12 source-returning ones).
|
|
5324
|
+
max_response_tokens: 200,
|
|
5325
|
+
response_format: "skeleton",
|
|
5326
|
+
on_budget_exceeded: "skeleton",
|
|
5327
|
+
// Tools with detail_level (hub_nodes, bridge_nodes, etc).
|
|
5328
|
+
detail_level: "minimal"
|
|
5329
|
+
});
|
|
5330
|
+
function applyOverBudgetOverrides(args) {
|
|
5331
|
+
if (!args || typeof args !== "object") {
|
|
5332
|
+
return { ...OVER_BUDGET_ARG_OVERRIDES };
|
|
5333
|
+
}
|
|
5334
|
+
return { ...args, ...OVER_BUDGET_ARG_OVERRIDES };
|
|
5335
|
+
}
|
|
5336
|
+
function emitTaskBudgetBreached(toolName, callCount, maxCalls) {
|
|
5337
|
+
emitTelemetry({
|
|
5338
|
+
event: "mcp.task_budget.exceeded",
|
|
5339
|
+
tool: toolName,
|
|
5340
|
+
calls: callCount,
|
|
5341
|
+
budget: maxCalls
|
|
5342
|
+
});
|
|
5343
|
+
}
|
|
5344
|
+
|
|
5345
|
+
// packages/core/src/tools/registry.ts
|
|
5346
|
+
var TASK_BUDGET_EXEMPT = /* @__PURE__ */ new Set([
|
|
5347
|
+
"ctx_get_minimal_context",
|
|
5348
|
+
"ctx_status",
|
|
5349
|
+
"ctx_get_workflow",
|
|
5350
|
+
"ctx_get_rules",
|
|
5351
|
+
"ctx_suggested_questions"
|
|
5352
|
+
]);
|
|
5353
|
+
var ToolRegistry = class {
|
|
5354
|
+
tools = /* @__PURE__ */ new Map();
|
|
5355
|
+
register(name, schema4, handler) {
|
|
5356
|
+
this.tools.set(name, { schema: schema4, handler });
|
|
5357
|
+
}
|
|
5358
|
+
list() {
|
|
5359
|
+
return Array.from(this.tools.values()).map((t) => t.schema);
|
|
5360
|
+
}
|
|
5361
|
+
async dispatch(name, args) {
|
|
5362
|
+
const def = this.tools.get(name);
|
|
5363
|
+
if (!def) throw new Error(`Unknown tool: ${name}`);
|
|
5364
|
+
const projectRoot = args && typeof args === "object" && "project_root" in args ? args.project_root : void 0;
|
|
5365
|
+
logger.debug("tool.dispatch", { tool: name, project_root: projectRoot });
|
|
5366
|
+
let dispatchArgs = args;
|
|
5367
|
+
if (!TASK_BUDGET_EXEMPT.has(name)) {
|
|
5368
|
+
const decision = getTaskBudgetTracker().recordCall();
|
|
5369
|
+
if (decision.overBudget) {
|
|
5370
|
+
dispatchArgs = applyOverBudgetOverrides(args);
|
|
5371
|
+
if (decision.firstBreach) {
|
|
5372
|
+
logger.warn("task tool budget exceeded \u2014 auto-throttling responses to skeleton/minimal", {
|
|
5373
|
+
tool: name,
|
|
5374
|
+
calls: decision.callCount,
|
|
5375
|
+
budget: decision.maxCalls
|
|
5376
|
+
});
|
|
5377
|
+
emitTaskBudgetBreached(name, decision.callCount, decision.maxCalls);
|
|
5378
|
+
}
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
return def.handler(dispatchArgs);
|
|
5382
|
+
}
|
|
5383
|
+
has(name) {
|
|
5384
|
+
return this.tools.has(name);
|
|
5385
|
+
}
|
|
5386
|
+
};
|
|
5387
|
+
|
|
5144
5388
|
// packages/core/src/tools/search.ts
|
|
5389
|
+
import { z as z3 } from "zod";
|
|
5145
5390
|
var DEFAULT_MAX_RESPONSE_TOKENS = 4e3;
|
|
5146
5391
|
var Schema = z3.object({
|
|
5147
5392
|
query: z3.string().describe("Search query \u2014 natural language or code fragment"),
|
|
@@ -8897,7 +9142,7 @@ function computeTopHubs(graph) {
|
|
|
8897
9142
|
reason: s.inDeg > s.outDeg ? "fan_in" : s.outDeg > s.inDeg ? "fan_out" : "bridge"
|
|
8898
9143
|
}));
|
|
8899
9144
|
}
|
|
8900
|
-
var
|
|
9145
|
+
var CACHE_TTL_MS2 = 1e4;
|
|
8901
9146
|
var responseCache = /* @__PURE__ */ new Map();
|
|
8902
9147
|
function cacheKey(projectRoot, task) {
|
|
8903
9148
|
return `${projectRoot}|${task ?? ""}`;
|
|
@@ -8912,7 +9157,7 @@ function cacheGet(key) {
|
|
|
8912
9157
|
return e.body;
|
|
8913
9158
|
}
|
|
8914
9159
|
function cachePut(key, body) {
|
|
8915
|
-
responseCache.set(key, { expiresAt: Date.now() +
|
|
9160
|
+
responseCache.set(key, { expiresAt: Date.now() + CACHE_TTL_MS2, body });
|
|
8916
9161
|
}
|
|
8917
9162
|
function sanitizeTask(raw) {
|
|
8918
9163
|
if (raw == null) return void 0;
|
|
@@ -9960,7 +10205,7 @@ var TELEMETRY_DISABLED = TELEMETRY_LEVEL === "off";
|
|
|
9960
10205
|
function getTelemetryLevel() {
|
|
9961
10206
|
return TELEMETRY_LEVEL;
|
|
9962
10207
|
}
|
|
9963
|
-
var CTXLOOM_VERSION = "1.
|
|
10208
|
+
var CTXLOOM_VERSION = "1.5.0".length > 0 ? "1.5.0" : "dev";
|
|
9964
10209
|
var POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
9965
10210
|
var POSTHOG_KEY = process.env["POSTHOG_API_KEY"] ?? (true ? "phc_CiDkmFLcZ2K6uCpcoSUQLmFrnnUvsyXGhSxopX5TVKE6" : "");
|
|
9966
10211
|
var SENTRY_DSN = process.env["SENTRY_DSN"] ?? (true ? "https://81c94a0f04a8e242dee493ac1e17f733@o4508531702497280.ingest.de.sentry.io/4511256875368528" : "");
|
|
@@ -10711,6 +10956,72 @@ var CTXLOOM_HOOK_ENTRIES = {
|
|
|
10711
10956
|
]
|
|
10712
10957
|
}
|
|
10713
10958
|
};
|
|
10959
|
+
function plainBody() {
|
|
10960
|
+
return RULES_BLOCK_CONTENT.replace(/^##\s+/gm, "").replace(/^###\s+/gm, "").replace(/^####\s+/gm, "").replace(/\*\*(.*?)\*\*/g, "$1").replace(/`([^`]+)`/g, "$1");
|
|
10961
|
+
}
|
|
10962
|
+
var CURSOR_HEADER = `# ctxloom \u2014 Cursor agent rules
|
|
10963
|
+
# Generated by \`ctxloom init --host=cursor\`. Re-run to update.
|
|
10964
|
+
|
|
10965
|
+
`;
|
|
10966
|
+
var AIDER_HEADER = `# Project Conventions
|
|
10967
|
+
|
|
10968
|
+
Generated by \`ctxloom init --host=aider\`. Re-run to update.
|
|
10969
|
+
|
|
10970
|
+
`;
|
|
10971
|
+
var COPILOT_HEADER = `# ctxloom \u2014 GitHub Copilot instructions
|
|
10972
|
+
|
|
10973
|
+
Generated by \`ctxloom init --host=copilot\`. Re-run to update.
|
|
10974
|
+
|
|
10975
|
+
`;
|
|
10976
|
+
var WINDSURF_HEADER = `# ctxloom \u2014 Windsurf agent rules
|
|
10977
|
+
# Generated by \`ctxloom init --host=windsurf\`. Re-run to update.
|
|
10978
|
+
|
|
10979
|
+
`;
|
|
10980
|
+
var HOST_ADAPTERS = [
|
|
10981
|
+
// Note: claude / agents / gemini are NOT defined as HostAdapters in
|
|
10982
|
+
// this list — they predate the matrix and live in their own writeRulesBlock
|
|
10983
|
+
// path with HMAC-wrapped Markdown blocks. Future refactor may unify.
|
|
10984
|
+
{
|
|
10985
|
+
id: "cursor",
|
|
10986
|
+
path: ".cursorrules",
|
|
10987
|
+
defaultEnabled: false,
|
|
10988
|
+
render: () => CURSOR_HEADER + plainBody() + "\n",
|
|
10989
|
+
isCanonical(current) {
|
|
10990
|
+
return current === this.render();
|
|
10991
|
+
}
|
|
10992
|
+
},
|
|
10993
|
+
{
|
|
10994
|
+
id: "aider",
|
|
10995
|
+
path: "CONVENTIONS.md",
|
|
10996
|
+
defaultEnabled: false,
|
|
10997
|
+
render: () => AIDER_HEADER + RULES_BLOCK_CONTENT + "\n",
|
|
10998
|
+
isCanonical(current) {
|
|
10999
|
+
return current === this.render();
|
|
11000
|
+
}
|
|
11001
|
+
},
|
|
11002
|
+
{
|
|
11003
|
+
id: "copilot",
|
|
11004
|
+
path: ".github/copilot-instructions.md",
|
|
11005
|
+
defaultEnabled: false,
|
|
11006
|
+
render: () => COPILOT_HEADER + RULES_BLOCK_CONTENT + "\n",
|
|
11007
|
+
isCanonical(current) {
|
|
11008
|
+
return current === this.render();
|
|
11009
|
+
}
|
|
11010
|
+
},
|
|
11011
|
+
{
|
|
11012
|
+
id: "windsurf",
|
|
11013
|
+
path: ".windsurfrules",
|
|
11014
|
+
defaultEnabled: false,
|
|
11015
|
+
render: () => WINDSURF_HEADER + plainBody() + "\n",
|
|
11016
|
+
isCanonical(current) {
|
|
11017
|
+
return current === this.render();
|
|
11018
|
+
}
|
|
11019
|
+
}
|
|
11020
|
+
];
|
|
11021
|
+
function getHostAdapter(id) {
|
|
11022
|
+
return HOST_ADAPTERS.find((h) => h.id === id);
|
|
11023
|
+
}
|
|
11024
|
+
var SUPPORTED_HOST_IDS = HOST_ADAPTERS.map((h) => h.id);
|
|
10714
11025
|
|
|
10715
11026
|
// packages/core/src/install/hmacBlock.ts
|
|
10716
11027
|
import crypto6 from "crypto";
|
|
@@ -11159,6 +11470,9 @@ function installHarness(opts = {}) {
|
|
|
11159
11470
|
const hooksJson = writeHooksJson(projectRoot, { dryRun, warnings });
|
|
11160
11471
|
const sessionStartSh = writeSessionStartScript(projectRoot, { dryRun });
|
|
11161
11472
|
const skills = CTXLOOM_SKILLS.map((s) => writeSkill(projectRoot, s, { dryRun }));
|
|
11473
|
+
const extraHosts = resolveExtraHosts(opts.extraHosts ?? [], warnings).map(
|
|
11474
|
+
(adapter) => Object.assign(writeHostAdapter(projectRoot, adapter, { dryRun }), { hostId: adapter.id })
|
|
11475
|
+
);
|
|
11162
11476
|
return {
|
|
11163
11477
|
projectRoot,
|
|
11164
11478
|
claudeMd,
|
|
@@ -11167,9 +11481,29 @@ function installHarness(opts = {}) {
|
|
|
11167
11481
|
hooksJson,
|
|
11168
11482
|
sessionStartSh,
|
|
11169
11483
|
skills,
|
|
11484
|
+
extraHosts,
|
|
11170
11485
|
warnings
|
|
11171
11486
|
};
|
|
11172
11487
|
}
|
|
11488
|
+
function resolveExtraHosts(ids, warnings) {
|
|
11489
|
+
const requested = /* @__PURE__ */ new Set();
|
|
11490
|
+
for (const raw of ids) {
|
|
11491
|
+
const id = raw.trim().toLowerCase();
|
|
11492
|
+
if (id === "") continue;
|
|
11493
|
+
if (id === "all") {
|
|
11494
|
+
for (const a of HOST_ADAPTERS) requested.add(a.id);
|
|
11495
|
+
continue;
|
|
11496
|
+
}
|
|
11497
|
+
if (!getHostAdapter(id)) {
|
|
11498
|
+
warnings.push(
|
|
11499
|
+
`Unknown --host id "${id}" \u2014 ignored. Supported: ${HOST_ADAPTERS.map((a) => a.id).join(", ")}, or "all".`
|
|
11500
|
+
);
|
|
11501
|
+
continue;
|
|
11502
|
+
}
|
|
11503
|
+
requested.add(id);
|
|
11504
|
+
}
|
|
11505
|
+
return HOST_ADAPTERS.filter((a) => requested.has(a.id));
|
|
11506
|
+
}
|
|
11173
11507
|
function safeJoin(root, name) {
|
|
11174
11508
|
const target = path36.resolve(root, name);
|
|
11175
11509
|
const rootResolved = path36.resolve(root);
|
|
@@ -11262,6 +11596,30 @@ function isCtxloomEntry(entry, expectedMatcher) {
|
|
|
11262
11596
|
return cmd.includes("ctxloom") || cmd.includes(".claude/hooks/session-start.sh");
|
|
11263
11597
|
});
|
|
11264
11598
|
}
|
|
11599
|
+
function writeHostAdapter(projectRoot, adapter, opts) {
|
|
11600
|
+
const filePath = safeJoin(projectRoot, adapter.path);
|
|
11601
|
+
const dir = path36.dirname(filePath);
|
|
11602
|
+
const existed = fs28.existsSync(filePath);
|
|
11603
|
+
const rendered = adapter.render();
|
|
11604
|
+
let alreadyCorrect = false;
|
|
11605
|
+
if (existed) {
|
|
11606
|
+
const current = fs28.readFileSync(filePath, "utf-8");
|
|
11607
|
+
if (adapter.isCanonical(current)) {
|
|
11608
|
+
alreadyCorrect = true;
|
|
11609
|
+
}
|
|
11610
|
+
}
|
|
11611
|
+
if (!opts.dryRun && !alreadyCorrect) {
|
|
11612
|
+
fs28.mkdirSync(dir, { recursive: true });
|
|
11613
|
+
fs28.writeFileSync(filePath, rendered, "utf-8");
|
|
11614
|
+
}
|
|
11615
|
+
return {
|
|
11616
|
+
path: filePath,
|
|
11617
|
+
created: !existed,
|
|
11618
|
+
updated: existed && !alreadyCorrect,
|
|
11619
|
+
alreadyCorrect,
|
|
11620
|
+
dryRun: opts.dryRun
|
|
11621
|
+
};
|
|
11622
|
+
}
|
|
11265
11623
|
function writeSkill(projectRoot, skill, opts) {
|
|
11266
11624
|
const dir = safeJoin(projectRoot, `.claude/skills/${skill.name}`);
|
|
11267
11625
|
const filePath = safeJoin(projectRoot, skillFilePath(skill.name));
|
|
@@ -11347,6 +11705,15 @@ export {
|
|
|
11347
11705
|
loadFileRiskHistory,
|
|
11348
11706
|
Skeletonizer,
|
|
11349
11707
|
renderStatusXml,
|
|
11708
|
+
__resetLearnedSuggestionsCacheForTests,
|
|
11709
|
+
learnSuggestionsFromTelemetry,
|
|
11710
|
+
getLearnedRules,
|
|
11711
|
+
TaskBudgetTracker,
|
|
11712
|
+
getTaskBudgetTracker,
|
|
11713
|
+
__resetTaskBudgetTrackerForTests,
|
|
11714
|
+
OVER_BUDGET_ARG_OVERRIDES,
|
|
11715
|
+
applyOverBudgetOverrides,
|
|
11716
|
+
emitTaskBudgetBreached,
|
|
11350
11717
|
ToolRegistry,
|
|
11351
11718
|
detectChanges,
|
|
11352
11719
|
getImpactRadius,
|
|
@@ -11419,6 +11786,9 @@ export {
|
|
|
11419
11786
|
RULES_BLOCK_CONTENT,
|
|
11420
11787
|
SESSION_START_FULL,
|
|
11421
11788
|
CTXLOOM_HOOK_ENTRIES,
|
|
11789
|
+
HOST_ADAPTERS,
|
|
11790
|
+
getHostAdapter,
|
|
11791
|
+
SUPPORTED_HOST_IDS,
|
|
11422
11792
|
DEFAULT_HMAC_KEY,
|
|
11423
11793
|
resolveHmacKey,
|
|
11424
11794
|
computeBlockHmac,
|
|
@@ -11430,4 +11800,4 @@ export {
|
|
|
11430
11800
|
skillFilePath,
|
|
11431
11801
|
installHarness
|
|
11432
11802
|
};
|
|
11433
|
-
//# sourceMappingURL=chunk-
|
|
11803
|
+
//# sourceMappingURL=chunk-J2NLNQ4I.js.map
|
package/dist/index.js
CHANGED
|
@@ -49,7 +49,7 @@ import {
|
|
|
49
49
|
validateDefaultRoot,
|
|
50
50
|
wrapWithIndexingEnvelope,
|
|
51
51
|
writeCODEOWNERS
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-J2NLNQ4I.js";
|
|
53
53
|
import {
|
|
54
54
|
VectorStore
|
|
55
55
|
} from "./chunk-DVI2RWJR.js";
|
|
@@ -1019,7 +1019,7 @@ try {
|
|
|
1019
1019
|
} catch {
|
|
1020
1020
|
}
|
|
1021
1021
|
var args = process.argv.slice(2);
|
|
1022
|
-
var ctxloomVersion = "1.
|
|
1022
|
+
var ctxloomVersion = "1.5.0".length > 0 ? "1.5.0" : "dev";
|
|
1023
1023
|
if (args.includes("--version") || args.includes("-v")) {
|
|
1024
1024
|
process.stdout.write(`ctxloom ${ctxloomVersion}
|
|
1025
1025
|
`);
|
|
@@ -1092,7 +1092,7 @@ async function checkLicense() {
|
|
|
1092
1092
|
if (command !== void 0 && LICENSE_GATE_BYPASS_COMMANDS.has(command)) return;
|
|
1093
1093
|
const ciKey = process.env["CTXLOOM_LICENSE_KEY"];
|
|
1094
1094
|
if (ciKey) {
|
|
1095
|
-
const { ApiClient } = await import("./src-
|
|
1095
|
+
const { ApiClient } = await import("./src-PVBWVQMM.js");
|
|
1096
1096
|
const client = new ApiClient(process.env["CTXLOOM_API_BASE"]);
|
|
1097
1097
|
try {
|
|
1098
1098
|
const result = await client.validate(ciKey, "ci-ephemeral");
|
|
@@ -1447,6 +1447,15 @@ async function main() {
|
|
|
1447
1447
|
const skipHarness = process.argv.includes("--skip-harness");
|
|
1448
1448
|
const dryRun = process.argv.includes("--dry-run");
|
|
1449
1449
|
const force = process.argv.includes("--force");
|
|
1450
|
+
const extraHosts = [];
|
|
1451
|
+
for (let i = 0; i < process.argv.length; i++) {
|
|
1452
|
+
const arg = process.argv[i];
|
|
1453
|
+
if (arg.startsWith("--host=")) {
|
|
1454
|
+
extraHosts.push(...arg.slice("--host=".length).split(",").map((s) => s.trim()));
|
|
1455
|
+
} else if (arg === "--host" && i + 1 < process.argv.length) {
|
|
1456
|
+
extraHosts.push(...process.argv[i + 1].split(",").map((s) => s.trim()));
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1450
1459
|
try {
|
|
1451
1460
|
const result = runInit(initRoot);
|
|
1452
1461
|
const mcpLabel = result.mcpJson.created ? `${style.bold("Created")} ${result.mcpJson.path}` : result.mcpJson.merged ? `${style.bold("Merged ctxloom entry into")} ${result.mcpJson.path}` : `${style.dim("Already up to date:")} ${result.mcpJson.path}`;
|
|
@@ -1461,15 +1470,16 @@ async function main() {
|
|
|
1461
1470
|
}
|
|
1462
1471
|
if (!skipHarness) {
|
|
1463
1472
|
process.stdout.write("\n");
|
|
1464
|
-
const { installHarness } = await import("./src-
|
|
1465
|
-
const h = installHarness({ cwd: initRoot, dryRun, force });
|
|
1473
|
+
const { installHarness } = await import("./src-PVBWVQMM.js");
|
|
1474
|
+
const h = installHarness({ cwd: initRoot, dryRun, force, extraHosts });
|
|
1466
1475
|
const harnessFiles = [
|
|
1467
1476
|
h.claudeMd,
|
|
1468
1477
|
h.agentsMd,
|
|
1469
1478
|
h.geminiMd,
|
|
1470
1479
|
h.hooksJson,
|
|
1471
1480
|
h.sessionStartSh,
|
|
1472
|
-
...h.skills
|
|
1481
|
+
...h.skills,
|
|
1482
|
+
...h.extraHosts
|
|
1473
1483
|
];
|
|
1474
1484
|
for (const fr of harnessFiles) {
|
|
1475
1485
|
const rel = path4.relative(initRoot, fr.path);
|
|
@@ -1523,7 +1533,7 @@ async function main() {
|
|
|
1523
1533
|
process.exit(1);
|
|
1524
1534
|
}
|
|
1525
1535
|
if (alias !== void 0) {
|
|
1526
|
-
const { validateAlias } = await import("./src-
|
|
1536
|
+
const { validateAlias } = await import("./src-PVBWVQMM.js");
|
|
1527
1537
|
const v = validateAlias(alias);
|
|
1528
1538
|
if (!v.ok) {
|
|
1529
1539
|
console.error(`[ctxloom] Invalid alias: ${v.reason}`);
|
|
@@ -1787,7 +1797,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1787
1797
|
process.stderr.write("[ctxloom] --limit must be a non-negative integer (0 for unlimited)\n");
|
|
1788
1798
|
process.exit(2);
|
|
1789
1799
|
}
|
|
1790
|
-
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-
|
|
1800
|
+
const { loadRulesConfig, RulesChecker, formatText, formatJson, RulesConfigError } = await import("./src-PVBWVQMM.js");
|
|
1791
1801
|
let config;
|
|
1792
1802
|
try {
|
|
1793
1803
|
config = await loadRulesConfig(root);
|
|
@@ -1811,7 +1821,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1811
1821
|
}
|
|
1812
1822
|
let graph;
|
|
1813
1823
|
if (useSnapshot) {
|
|
1814
|
-
const { DependencyGraph: DG } = await import("./src-
|
|
1824
|
+
const { DependencyGraph: DG } = await import("./src-PVBWVQMM.js");
|
|
1815
1825
|
graph = new DG();
|
|
1816
1826
|
const loaded = await graph.loadSnapshotOnly(root);
|
|
1817
1827
|
if (!loaded) {
|
|
@@ -1820,7 +1830,7 @@ Suggested reviewers for ${files.length} file(s):`);
|
|
|
1820
1830
|
}
|
|
1821
1831
|
} else {
|
|
1822
1832
|
process.stderr.write("[ctxloom] Building dependency graph...\n");
|
|
1823
|
-
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-
|
|
1833
|
+
const { ASTParser: ASTParser2, DependencyGraph: DependencyGraph2 } = await import("./src-PVBWVQMM.js");
|
|
1824
1834
|
let parser;
|
|
1825
1835
|
try {
|
|
1826
1836
|
parser = new ASTParser2();
|
|
@@ -24,11 +24,13 @@ import {
|
|
|
24
24
|
GoModuleResolver,
|
|
25
25
|
GrammarLoader,
|
|
26
26
|
GraphExporter,
|
|
27
|
+
HOST_ADAPTERS,
|
|
27
28
|
InvalidKeyError,
|
|
28
29
|
LicenseRequiredError,
|
|
29
30
|
LicenseRevokedError,
|
|
30
31
|
LicenseStore,
|
|
31
32
|
NetworkError,
|
|
33
|
+
OVER_BUDGET_ARG_OVERRIDES,
|
|
32
34
|
OwnershipIndex,
|
|
33
35
|
PathValidator,
|
|
34
36
|
ProjectStateManager,
|
|
@@ -42,14 +44,19 @@ import {
|
|
|
42
44
|
SCORE_FLOOR,
|
|
43
45
|
SESSION_START_FULL,
|
|
44
46
|
SILO_BUS_FACTOR,
|
|
47
|
+
SUPPORTED_HOST_IDS,
|
|
45
48
|
SeatLimitError,
|
|
46
49
|
Skeletonizer,
|
|
50
|
+
TaskBudgetTracker,
|
|
47
51
|
ToolRegistry,
|
|
48
52
|
TrialUnavailableError,
|
|
49
53
|
TsConfigPathsResolver,
|
|
50
54
|
WikiGenerator,
|
|
55
|
+
__resetLearnedSuggestionsCacheForTests,
|
|
56
|
+
__resetTaskBudgetTrackerForTests,
|
|
51
57
|
activateLicense,
|
|
52
58
|
aliasNotFoundError,
|
|
59
|
+
applyOverBudgetOverrides,
|
|
53
60
|
assignLabelsByPercentile,
|
|
54
61
|
buildBlastRadiusXml,
|
|
55
62
|
buildCodeownersBlock,
|
|
@@ -62,6 +69,7 @@ import {
|
|
|
62
69
|
deactivateLicense,
|
|
63
70
|
detectChanges,
|
|
64
71
|
disposeProjectState,
|
|
72
|
+
emitTaskBudgetBreached,
|
|
65
73
|
ensureVectorsInitialized,
|
|
66
74
|
extractBlock,
|
|
67
75
|
extractImports,
|
|
@@ -73,14 +81,18 @@ import {
|
|
|
73
81
|
formatJson,
|
|
74
82
|
formatText,
|
|
75
83
|
generateCODEOWNERS,
|
|
84
|
+
getHostAdapter,
|
|
76
85
|
getImpactRadius,
|
|
86
|
+
getLearnedRules,
|
|
77
87
|
getLicenseInfo,
|
|
78
88
|
getOrCreateDistinctId,
|
|
89
|
+
getTaskBudgetTracker,
|
|
79
90
|
getTelemetryLevel,
|
|
80
91
|
hashProjectRoot,
|
|
81
92
|
installHarness,
|
|
82
93
|
isActive,
|
|
83
94
|
isSiloed,
|
|
95
|
+
learnSuggestionsFromTelemetry,
|
|
84
96
|
listNamedSnapshots,
|
|
85
97
|
loadFileRiskHistory,
|
|
86
98
|
loadReviewConfig,
|
|
@@ -117,7 +129,7 @@ import {
|
|
|
117
129
|
wrapBlock,
|
|
118
130
|
wrapWithIndexingEnvelope,
|
|
119
131
|
writeCODEOWNERS
|
|
120
|
-
} from "./chunk-
|
|
132
|
+
} from "./chunk-J2NLNQ4I.js";
|
|
121
133
|
import {
|
|
122
134
|
VectorStore
|
|
123
135
|
} from "./chunk-DVI2RWJR.js";
|
|
@@ -158,11 +170,13 @@ export {
|
|
|
158
170
|
GoModuleResolver,
|
|
159
171
|
GrammarLoader,
|
|
160
172
|
GraphExporter,
|
|
173
|
+
HOST_ADAPTERS,
|
|
161
174
|
InvalidKeyError,
|
|
162
175
|
LicenseRequiredError,
|
|
163
176
|
LicenseRevokedError,
|
|
164
177
|
LicenseStore,
|
|
165
178
|
NetworkError,
|
|
179
|
+
OVER_BUDGET_ARG_OVERRIDES,
|
|
166
180
|
OwnershipIndex,
|
|
167
181
|
PathValidator,
|
|
168
182
|
ProjectStateManager,
|
|
@@ -176,15 +190,20 @@ export {
|
|
|
176
190
|
SCORE_FLOOR,
|
|
177
191
|
SESSION_START_FULL,
|
|
178
192
|
SILO_BUS_FACTOR,
|
|
193
|
+
SUPPORTED_HOST_IDS,
|
|
179
194
|
SeatLimitError,
|
|
180
195
|
Skeletonizer,
|
|
196
|
+
TaskBudgetTracker,
|
|
181
197
|
ToolRegistry,
|
|
182
198
|
TrialUnavailableError,
|
|
183
199
|
TsConfigPathsResolver,
|
|
184
200
|
VectorStore,
|
|
185
201
|
WikiGenerator,
|
|
202
|
+
__resetLearnedSuggestionsCacheForTests,
|
|
203
|
+
__resetTaskBudgetTrackerForTests,
|
|
186
204
|
activateLicense,
|
|
187
205
|
aliasNotFoundError,
|
|
206
|
+
applyOverBudgetOverrides,
|
|
188
207
|
assignLabelsByPercentile,
|
|
189
208
|
buildBlastRadiusXml,
|
|
190
209
|
buildCodeownersBlock,
|
|
@@ -198,6 +217,7 @@ export {
|
|
|
198
217
|
deactivateLicense,
|
|
199
218
|
detectChanges,
|
|
200
219
|
disposeProjectState,
|
|
220
|
+
emitTaskBudgetBreached,
|
|
201
221
|
ensureVectorsInitialized,
|
|
202
222
|
extractBlock,
|
|
203
223
|
extractImports,
|
|
@@ -210,15 +230,19 @@ export {
|
|
|
210
230
|
formatText,
|
|
211
231
|
generateCODEOWNERS,
|
|
212
232
|
generateEmbedding,
|
|
233
|
+
getHostAdapter,
|
|
213
234
|
getImpactRadius,
|
|
235
|
+
getLearnedRules,
|
|
214
236
|
getLicenseInfo,
|
|
215
237
|
getOrCreateDistinctId,
|
|
238
|
+
getTaskBudgetTracker,
|
|
216
239
|
getTelemetryLevel,
|
|
217
240
|
hashProjectRoot,
|
|
218
241
|
indexDirectory,
|
|
219
242
|
installHarness,
|
|
220
243
|
isActive,
|
|
221
244
|
isSiloed,
|
|
245
|
+
learnSuggestionsFromTelemetry,
|
|
222
246
|
listNamedSnapshots,
|
|
223
247
|
loadFileRiskHistory,
|
|
224
248
|
loadReviewConfig,
|
|
@@ -257,4 +281,4 @@ export {
|
|
|
257
281
|
wrapWithIndexingEnvelope,
|
|
258
282
|
writeCODEOWNERS
|
|
259
283
|
};
|
|
260
|
-
//# sourceMappingURL=src-
|
|
284
|
+
//# sourceMappingURL=src-PVBWVQMM.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ctxloom-pro",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
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",
|