bgts-context-engine 0.2.3__tar.gz → 0.2.5__tar.gz
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.
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/CHANGELOG.md +77 -1
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/MANIFEST.in +1 -0
- {bgts_context_engine-0.2.3/src/bgts_context_engine.egg-info → bgts_context_engine-0.2.5}/PKG-INFO +31 -8
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/README.md +30 -7
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/README.tr.md +30 -5
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/pyproject.toml +1 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/__init__.py +1 -1
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/mcp/server.py +5 -2
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/mcp/tools.py +33 -8
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/schemas.py +7 -3
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/cli.py +130 -3
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/config.py +14 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/assembler/assembler.py +16 -11
- bgts_context_engine-0.2.5/src/bce/core/defaults.py +19 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/embedder/encoder.py +14 -5
- bgts_context_engine-0.2.5/src/bce/integrations/__init__.py +10 -0
- bgts_context_engine-0.2.5/src/bce/integrations/agents.py +271 -0
- bgts_context_engine-0.2.5/src/bce/integrations/precontext.py +168 -0
- bgts_context_engine-0.2.5/src/bce/integrations/templates/claude_section.md +16 -0
- bgts_context_engine-0.2.5/src/bce/integrations/templates/cursor_rule.mdc +20 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer3/__init__.py +3 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer3/orchestration.py +34 -8
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5/src/bgts_context_engine.egg-info}/PKG-INFO +31 -8
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bgts_context_engine.egg-info/SOURCES.txt +6 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/LICENSE +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/setup.cfg +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/mcp/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/app.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/deps.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/routes.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/static/assets/index-Cc4pUpSE.js +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/static/assets/index-DrLRaTMU.css +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/static/favicon.svg +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/static/index.html +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/ui/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/ui/queries.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/ui/router.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/ui/schemas.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/api/rest/ui/trace.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/bench/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/bench/pr_ablation.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/bench/runner.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/assembler/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/auth/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/auth/scope.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/coverage/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/coverage/confidence.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/i18n/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/i18n/catalogs/en.json +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/i18n/catalogs/tr.json +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/i18n/locale.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/i18n/translator.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/logging.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/anchors.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/bulk.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/expand.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/orchestrator.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/profile.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/orchestrator/text.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/scoring/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/core/scoring/engine.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/domain/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/domain/enums.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/domain/models.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/churn.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/embedder/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/embedder/embedder.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/extractor/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/extractor/bridges.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/extractor/designnote.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/extractor/extractor.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/extractor/routes.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/gitsync/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/gitsync/bitbucket.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/gitsync/local.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/gitsync/remote.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/indexer.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/linker/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/linker/linker.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/_treesitter.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/base.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/csharp_provider.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/go_provider.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/java_provider.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/jsts_provider.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/languages/python_provider.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/registry.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/scip/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/scip/resolver.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/parser/symbol_id.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/upserter/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/indexing/upserter/upserter.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/jobs/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/jobs/store.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/jobs/worker.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/graph/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/graph/client.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/graph/repository.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/db.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0001_extensions_graph.sql +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0002_relational.sql +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0003_vector.sql +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0004_rls.sql +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0005_embeddings_dim.sql +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0006_fts.sql +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0007_jobs.sql +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0008_graph_label_indexes.sql +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0009_fts_identifier_parts.sql +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrations/0010_file_churn.sql +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/migrator.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/relational/queries.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/vector/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/storage/vector/store.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer1/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer1/primitives.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer2/__init__.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bce/tools/layer2/search.py +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bgts_context_engine.egg-info/dependency_links.txt +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bgts_context_engine.egg-info/entry_points.txt +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bgts_context_engine.egg-info/requires.txt +0 -0
- {bgts_context_engine-0.2.3 → bgts_context_engine-0.2.5}/src/bgts_context_engine.egg-info/top_level.txt +0 -0
|
@@ -7,6 +7,80 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.5] - 2026-09-24
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- The English and Turkish READMEs link to the project website
|
|
15
|
+
([bgts-ai-org.github.io/bce-microsite](https://bgts-ai-org.github.io/bce-microsite/))
|
|
16
|
+
from the top navigation.
|
|
17
|
+
|
|
18
|
+
## [0.2.4] - 2026-09-22
|
|
19
|
+
|
|
20
|
+
This release turns the agent benchmark (a Cursor CLI agent on 14 tasks against a
|
|
21
|
+
React/TypeScript codebase, plain vs. with the engine) into product behaviour. The MCP server alone made a Cursor agent *worse*: it asked the graph late (after
|
|
22
|
+
a round of grep) or eight times per task, and the answer it got back had no file or line.
|
|
23
|
+
With one advertised tool, a rule that says "call it first and treat `file:line` as a
|
|
24
|
+
verified location", the answer handed over before the first turn, and a shorter context
|
|
25
|
+
budget, the same agent on the same 14 tasks, model and machine used 20 % fewer tokens,
|
|
26
|
+
produced 46 % less grep/glob output and 13 % fewer model turns, passed 73/73 checks against
|
|
27
|
+
72/73, and changed 23 % fewer lines. Everything below is what that took.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- `bce cursor-init [--project DIR] [--repo-id ID]... [--env-file PATH] [--bce-command X]`:
|
|
32
|
+
writes a project's `.cursor/mcp.json` (merged into an existing one; other servers are
|
|
33
|
+
kept) with the absolute path of the running `bce`, `serve-mcp --env-file …` and
|
|
34
|
+
`PYTHONUTF8=1`, plus the `alwaysApply` rule `.cursor/rules/bgts-context-engine.mdc`. The
|
|
35
|
+
rule ships in the wheel as a template, so every install gets the same instruction: call
|
|
36
|
+
`get_context_for_task` first with the full task text, open the returned `file:line`
|
|
37
|
+
entries directly instead of grepping for them, treat the list as places to inspect rather
|
|
38
|
+
than files to edit, never repeat a call with the same text. Idempotent.
|
|
39
|
+
- `bce claude-init …[--no-hook]`: the Claude Code counterpart — `.mcp.json` (merged), a
|
|
40
|
+
section in `CLAUDE.md` between `<!-- bgts-context-engine:begin/end -->` markers (the rest
|
|
41
|
+
of the file is kept), and by default a `UserPromptSubmit` hook in `.claude/settings.json`
|
|
42
|
+
(other hooks kept) that runs `bce precontext` before every prompt. Cursor gets no hook: its
|
|
43
|
+
`beforeSubmitPrompt` event can only allow or block a prompt, so there the rule carries the
|
|
44
|
+
instruction and the agent makes the one call itself.
|
|
45
|
+
- `bce precontext [--task TEXT] [--repo-id ID]...`: the compact "Code context for this
|
|
46
|
+
task" block an agent prompt carries — a file list in rank order, then one line per symbol
|
|
47
|
+
(`path:line kind name distance`) with a snippet of at most 12 lines where the snippet says
|
|
48
|
+
more than the name; about 5 k characters at the default budget. Without `--task` it is the
|
|
49
|
+
Claude Code hook: reads the hook JSON on stdin, skips prompts under 20 characters and slash
|
|
50
|
+
commands, prints `hookSpecificOutput.additionalContext` capped at Claude Code's
|
|
51
|
+
10 000-character limit, and fails open (an unreachable database is a prompt without
|
|
52
|
+
context, never a blocked prompt). `bce.integrations.precontext.render_context_markdown`
|
|
53
|
+
is the renderer, for other hosts.
|
|
54
|
+
- `BCE_MCP_TOOLS`: comma-separated names the MCP server advertises (default: all read
|
|
55
|
+
tools). Agents fetch a tool's schema before first use and chain small calls when many
|
|
56
|
+
tools are on offer; the benchmark ran with `get_context_for_task` alone. Unknown names
|
|
57
|
+
fail at startup, and a tool not advertised cannot be dispatched either.
|
|
58
|
+
- `BCE_EMBEDDING_QUERY_TIMEOUT` (10 s, two attempts): the wait for embedding a single
|
|
59
|
+
*query* at search time, separate from the indexing timeout (600 s). When it expires,
|
|
60
|
+
`get_context_for_task` and `suggest_change_sites` answer from lexical and structural
|
|
61
|
+
anchors alone — visible as lower `coverage.confidence` — instead of failing after having
|
|
62
|
+
waited past an agent's MCP timeout (Cursor: 60 s). Motivated by a RunPod TCP proxy that
|
|
63
|
+
stalled connects for 19–67 s during the benchmark.
|
|
64
|
+
- Context items carry `name`, `kind`, `file_id` and `line` next to `symbol_id`. Without
|
|
65
|
+
them an agent had to search the tree for the file behind every symbol id (the module path
|
|
66
|
+
does not say `.ts` vs `.tsx`, nor the line) — one tool call, one model turn, per item.
|
|
67
|
+
|
|
68
|
+
### Changed
|
|
69
|
+
|
|
70
|
+
- **Defaults: `max_candidates` 8 → 20, `max_tokens` 4000 → 1500**, on every surface (CLI
|
|
71
|
+
`bce context`, REST, MCP schema defaults, and the `bce.core.defaults` module they all
|
|
72
|
+
read). K=20 is where recall@K flattened in the PR replay for both fitted profiles and
|
|
73
|
+
what the agent benchmark ran with. The budget is shorter because an agent re-sends the
|
|
74
|
+
context on every turn: at 1500 it travelled as ~1.3 k tokens per turn and still halved
|
|
75
|
+
the agent's search output; 4000 travelled as ~4 k per turn for no further gain. Callers
|
|
76
|
+
wanting longer snippets pass `max_tokens` per request; the assembler still degrades items
|
|
77
|
+
to reference level rather than dropping them.
|
|
78
|
+
- `.env.example` shows `voyage-code-4` (the model the voyage profile was fitted on) as the
|
|
79
|
+
Voyage example and documents `BCE_EMBEDDING_QUERY_TIMEOUT` and `BCE_MCP_TOOLS`. The code
|
|
80
|
+
default stays `voyage-code-3` so indexes built with it keep matching.
|
|
81
|
+
- `server.json` describes the catalog as eight read-only tools plus two write tools rather
|
|
82
|
+
than "14 tools".
|
|
83
|
+
|
|
10
84
|
## [0.2.3] - 2026-09-21
|
|
11
85
|
|
|
12
86
|
### Added
|
|
@@ -304,7 +378,9 @@ First public release.
|
|
|
304
378
|
- Docker Compose deployment with PostgreSQL, Apache AGE and pgvector preconfigured.
|
|
305
379
|
- `server.json` manifest describing the stdio server for the official MCP registry.
|
|
306
380
|
|
|
307
|
-
[Unreleased]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.
|
|
381
|
+
[Unreleased]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.5...HEAD
|
|
382
|
+
[0.2.5]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.4...v0.2.5
|
|
383
|
+
[0.2.4]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.3...v0.2.4
|
|
308
384
|
[0.2.3]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.2...v0.2.3
|
|
309
385
|
[0.2.2]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.1...v0.2.2
|
|
310
386
|
[0.2.1]: https://github.com/bgts-ai-org/bgts-context-engine/compare/v0.2.0...v0.2.1
|
|
@@ -4,6 +4,7 @@ include README.tr.md
|
|
|
4
4
|
include CHANGELOG.md
|
|
5
5
|
recursive-include src/bce/core/i18n/catalogs *.json
|
|
6
6
|
recursive-include src/bce/storage/relational/migrations *.sql
|
|
7
|
+
recursive-include src/bce/integrations/templates *.mdc *.md
|
|
7
8
|
|
|
8
9
|
# Compiled frontend, vendored by scripts/build_ui.py before a release build.
|
|
9
10
|
recursive-include src/bce/api/rest/static *
|
{bgts_context_engine-0.2.3/src/bgts_context_engine.egg-info → bgts_context_engine-0.2.5}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: bgts-context-engine
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: Deterministic code-graph context engine for AI coding agents - PostgreSQL, Apache AGE and pgvector, over MCP and REST
|
|
5
5
|
Author: Oğuz Öztürk, Enes İyidil
|
|
6
6
|
Maintainer: BGTS
|
|
@@ -69,7 +69,7 @@ that actually answer it — ranked, budgeted, and reproducible.
|
|
|
69
69
|
[](docs/mcp.md)
|
|
70
70
|
[](https://github.com/bgts-ai-org/bgts-context-engine/stargazers)
|
|
71
71
|
|
|
72
|
-
[Quick start](#quick-start) · [Use it from your agent](#use-it-from-your-agent) · [How it works](#how-it-works) · [Supported models](#supported-models) · [Documentation](#documentation) · [Türkçe](README.tr.md)
|
|
72
|
+
[Quick start](#quick-start) · [Use it from your agent](#use-it-from-your-agent) · [How it works](#how-it-works) · [Supported models](#supported-models) · [Documentation](#documentation) · [Website](https://bgts-ai-org.github.io/bce-microsite/) · [Türkçe](README.tr.md)
|
|
73
73
|
|
|
74
74
|
</div>
|
|
75
75
|
|
|
@@ -150,9 +150,28 @@ Every MCP client uses the same stdio command plus the database in the environmen
|
|
|
150
150
|
leave `bce serve-mcp` running in a terminal for the editor: stdout is the protocol, so the
|
|
151
151
|
process stays silent, and the IDE starts its own copy.
|
|
152
152
|
|
|
153
|
+
**One command per editor.** Run it in the project the agent works on (the repository you
|
|
154
|
+
indexed), pointing at the engine's `.env`:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
bce --env-file /path/to/engine/.env cursor-init --repo-id my-service # Cursor
|
|
158
|
+
bce --env-file /path/to/engine/.env claude-init --repo-id my-service # Claude Code
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`cursor-init` writes `.cursor/mcp.json` (merged into an existing one) and the rule
|
|
162
|
+
`.cursor/rules/bgts-context-engine.mdc`, which tells the agent to call
|
|
163
|
+
`get_context_for_task` *first*, treat its `file:line` entries as verified locations instead
|
|
164
|
+
of grepping for them, and not to edit a file just because it was listed. `claude-init`
|
|
165
|
+
writes `.mcp.json`, a marked section in `CLAUDE.md`, and a `UserPromptSubmit` hook
|
|
166
|
+
(`bce precontext`) that runs the graph once per prompt and hands the agent the answer
|
|
167
|
+
before its first turn — the flow the agent benchmark measured (−20 % tokens, half the search
|
|
168
|
+
output, same or better checks; 14 tasks on a React/TypeScript codebase, same model and machine).
|
|
169
|
+
Cursor's prompt hook cannot add context, so there the rule does that job. `--no-hook`,
|
|
170
|
+
`--repo-id` (repeatable) and `--bce-command` adjust the files; both commands are safe to rerun.
|
|
171
|
+
|
|
153
172
|
After you add or change the MCP config, **restart Cursor or VS Code** (or Command Palette
|
|
154
173
|
→ “Developer: Reload Window”). The server should then show as enabled with eight tools (ten with indexing enabled).
|
|
155
|
-
Setup detail: [docs/mcp.md](docs/mcp.md).
|
|
174
|
+
Setup detail: [docs/mcp.md](docs/mcp.md). The manual equivalents:
|
|
156
175
|
|
|
157
176
|
**Cursor** — user config `~/.cursor/mcp.json` (applies to every project), or a project
|
|
158
177
|
`.cursor/mcp.json` that stays local (the directory is gitignored):
|
|
@@ -217,12 +236,14 @@ Not a list of file paths. A ranked pack, with the reasoning attached:
|
|
|
217
236
|
},
|
|
218
237
|
"context": {
|
|
219
238
|
"items": [
|
|
220
|
-
{ "symbol_id": "...refresh_session#88c2", "
|
|
239
|
+
{ "symbol_id": "...refresh_session#88c2", "name": "refresh_session", "kind": "function",
|
|
240
|
+
"file_id": "my-service:src/auth/session.py", "line": 41, "detail_level": "full",
|
|
221
241
|
"graph_distance": 0, "score": 11.42, "tokens": 214, "content": "def refresh_session(...)" },
|
|
222
|
-
{ "symbol_id": "...SESSION_TTL#4b0d",
|
|
242
|
+
{ "symbol_id": "...SESSION_TTL#4b0d", "name": "SESSION_TTL", "kind": "constant",
|
|
243
|
+
"file_id": "my-service:src/auth/config.py", "line": 12, "detail_level": "signature",
|
|
223
244
|
"graph_distance": 2, "score": 6.10, "tokens": 31, "content": "SESSION_TTL: int" }
|
|
224
245
|
],
|
|
225
|
-
"used_tokens":
|
|
246
|
+
"used_tokens": 1388, "budget": 1500, "included": 20, "skipped": 0
|
|
226
247
|
},
|
|
227
248
|
"coverage": {
|
|
228
249
|
"anchor_source_count": 3, "connected_component_ratio": 0.875,
|
|
@@ -243,8 +264,10 @@ could not corroborate it — the moment for an agent to ask a follow-up question
|
|
|
243
264
|
editing. `commit_mismatch` means the index is behind your working tree.
|
|
244
265
|
|
|
245
266
|
**`detail_level`** falls off with graph distance: the symbol you are changing arrives in
|
|
246
|
-
full, its neighbours as signatures, the outer ring as `name @ file:line`. That is how
|
|
247
|
-
genuinely relevant symbols fit in
|
|
267
|
+
full, its neighbours as signatures, the outer ring as `name @ file:line`. That is how twenty
|
|
268
|
+
genuinely relevant symbols fit in 1500 tokens — short enough for an agent to carry on every
|
|
269
|
+
turn. Every item also names its `file_id` and `line`, so the agent opens the file instead of
|
|
270
|
+
searching for the symbol.
|
|
248
271
|
|
|
249
272
|
## How it works
|
|
250
273
|
|
|
@@ -16,7 +16,7 @@ that actually answer it — ranked, budgeted, and reproducible.
|
|
|
16
16
|
[](docs/mcp.md)
|
|
17
17
|
[](https://github.com/bgts-ai-org/bgts-context-engine/stargazers)
|
|
18
18
|
|
|
19
|
-
[Quick start](#quick-start) · [Use it from your agent](#use-it-from-your-agent) · [How it works](#how-it-works) · [Supported models](#supported-models) · [Documentation](#documentation) · [Türkçe](README.tr.md)
|
|
19
|
+
[Quick start](#quick-start) · [Use it from your agent](#use-it-from-your-agent) · [How it works](#how-it-works) · [Supported models](#supported-models) · [Documentation](#documentation) · [Website](https://bgts-ai-org.github.io/bce-microsite/) · [Türkçe](README.tr.md)
|
|
20
20
|
|
|
21
21
|
</div>
|
|
22
22
|
|
|
@@ -97,9 +97,28 @@ Every MCP client uses the same stdio command plus the database in the environmen
|
|
|
97
97
|
leave `bce serve-mcp` running in a terminal for the editor: stdout is the protocol, so the
|
|
98
98
|
process stays silent, and the IDE starts its own copy.
|
|
99
99
|
|
|
100
|
+
**One command per editor.** Run it in the project the agent works on (the repository you
|
|
101
|
+
indexed), pointing at the engine's `.env`:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
bce --env-file /path/to/engine/.env cursor-init --repo-id my-service # Cursor
|
|
105
|
+
bce --env-file /path/to/engine/.env claude-init --repo-id my-service # Claude Code
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`cursor-init` writes `.cursor/mcp.json` (merged into an existing one) and the rule
|
|
109
|
+
`.cursor/rules/bgts-context-engine.mdc`, which tells the agent to call
|
|
110
|
+
`get_context_for_task` *first*, treat its `file:line` entries as verified locations instead
|
|
111
|
+
of grepping for them, and not to edit a file just because it was listed. `claude-init`
|
|
112
|
+
writes `.mcp.json`, a marked section in `CLAUDE.md`, and a `UserPromptSubmit` hook
|
|
113
|
+
(`bce precontext`) that runs the graph once per prompt and hands the agent the answer
|
|
114
|
+
before its first turn — the flow the agent benchmark measured (−20 % tokens, half the search
|
|
115
|
+
output, same or better checks; 14 tasks on a React/TypeScript codebase, same model and machine).
|
|
116
|
+
Cursor's prompt hook cannot add context, so there the rule does that job. `--no-hook`,
|
|
117
|
+
`--repo-id` (repeatable) and `--bce-command` adjust the files; both commands are safe to rerun.
|
|
118
|
+
|
|
100
119
|
After you add or change the MCP config, **restart Cursor or VS Code** (or Command Palette
|
|
101
120
|
→ “Developer: Reload Window”). The server should then show as enabled with eight tools (ten with indexing enabled).
|
|
102
|
-
Setup detail: [docs/mcp.md](docs/mcp.md).
|
|
121
|
+
Setup detail: [docs/mcp.md](docs/mcp.md). The manual equivalents:
|
|
103
122
|
|
|
104
123
|
**Cursor** — user config `~/.cursor/mcp.json` (applies to every project), or a project
|
|
105
124
|
`.cursor/mcp.json` that stays local (the directory is gitignored):
|
|
@@ -164,12 +183,14 @@ Not a list of file paths. A ranked pack, with the reasoning attached:
|
|
|
164
183
|
},
|
|
165
184
|
"context": {
|
|
166
185
|
"items": [
|
|
167
|
-
{ "symbol_id": "...refresh_session#88c2", "
|
|
186
|
+
{ "symbol_id": "...refresh_session#88c2", "name": "refresh_session", "kind": "function",
|
|
187
|
+
"file_id": "my-service:src/auth/session.py", "line": 41, "detail_level": "full",
|
|
168
188
|
"graph_distance": 0, "score": 11.42, "tokens": 214, "content": "def refresh_session(...)" },
|
|
169
|
-
{ "symbol_id": "...SESSION_TTL#4b0d",
|
|
189
|
+
{ "symbol_id": "...SESSION_TTL#4b0d", "name": "SESSION_TTL", "kind": "constant",
|
|
190
|
+
"file_id": "my-service:src/auth/config.py", "line": 12, "detail_level": "signature",
|
|
170
191
|
"graph_distance": 2, "score": 6.10, "tokens": 31, "content": "SESSION_TTL: int" }
|
|
171
192
|
],
|
|
172
|
-
"used_tokens":
|
|
193
|
+
"used_tokens": 1388, "budget": 1500, "included": 20, "skipped": 0
|
|
173
194
|
},
|
|
174
195
|
"coverage": {
|
|
175
196
|
"anchor_source_count": 3, "connected_component_ratio": 0.875,
|
|
@@ -190,8 +211,10 @@ could not corroborate it — the moment for an agent to ask a follow-up question
|
|
|
190
211
|
editing. `commit_mismatch` means the index is behind your working tree.
|
|
191
212
|
|
|
192
213
|
**`detail_level`** falls off with graph distance: the symbol you are changing arrives in
|
|
193
|
-
full, its neighbours as signatures, the outer ring as `name @ file:line`. That is how
|
|
194
|
-
genuinely relevant symbols fit in
|
|
214
|
+
full, its neighbours as signatures, the outer ring as `name @ file:line`. That is how twenty
|
|
215
|
+
genuinely relevant symbols fit in 1500 tokens — short enough for an agent to carry on every
|
|
216
|
+
turn. Every item also names its `file_id` and `line`, so the agent opens the file instead of
|
|
217
|
+
searching for the symbol.
|
|
195
218
|
|
|
196
219
|
## How it works
|
|
197
220
|
|
|
@@ -14,7 +14,7 @@ veren sekiz sembolü sıralanmış, bütçelenmiş ve yeniden üretilebilir şek
|
|
|
14
14
|
[](docs/mcp.md)
|
|
15
15
|
[](https://github.com/bgts-ai-org/bgts-context-engine/stargazers)
|
|
16
16
|
|
|
17
|
-
[Hızlı başlangıç](#hızlı-başlangıç) · [Ajanınızdan kullanma](#ajanınızdan-kullanma) · [Nasıl çalışır](#nasıl-çalışır) · [Desteklenen modeller](#desteklenen-modeller) · [Dokümantasyon](#dokümantasyon) · [English](README.md)
|
|
17
|
+
[Hızlı başlangıç](#hızlı-başlangıç) · [Ajanınızdan kullanma](#ajanınızdan-kullanma) · [Nasıl çalışır](#nasıl-çalışır) · [Desteklenen modeller](#desteklenen-modeller) · [Dokümantasyon](#dokümantasyon) · [Site](https://bgts-ai-org.github.io/bce-microsite/) · [English](README.md)
|
|
18
18
|
|
|
19
19
|
</div>
|
|
20
20
|
|
|
@@ -97,9 +97,31 @@ Her MCP istemcisi aynı stdio komutunu ve ortamındaki veritabanı bağlantısı
|
|
|
97
97
|
Editör için terminalde `bce serve-mcp` açık bırakmayın: stdout protokoldür, bu yüzden
|
|
98
98
|
süreç sessiz kalır; IDE kendi kopyasını başlatır.
|
|
99
99
|
|
|
100
|
+
**Editör başına tek komut.** Ajanın çalıştığı projede (indekslediğiniz depoda), motorun
|
|
101
|
+
`.env` dosyasını göstererek çalıştırın:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
bce --env-file /path/to/engine/.env cursor-init --repo-id my-service # Cursor
|
|
105
|
+
bce --env-file /path/to/engine/.env claude-init --repo-id my-service # Claude Code
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`cursor-init`, `.cursor/mcp.json` dosyasını (varsa üzerine birleştirerek) ve
|
|
109
|
+
`.cursor/rules/bgts-context-engine.mdc` kuralını yazar. Kural ajana şunları söyler:
|
|
110
|
+
`get_context_for_task` aracını *ilk iş olarak* çağır, döndürdüğü `dosya:satır`
|
|
111
|
+
girdilerini doğrulanmış konum say ve onları grep ile arama, listelendi diye bir dosyayı
|
|
112
|
+
düzenleme. `claude-init` ise `.mcp.json`, `CLAUDE.md` içinde işaretli bir bölüm ve
|
|
113
|
+
`bce precontext` komutunu çalıştıran bir `UserPromptSubmit` kancası yazar: kanca her
|
|
114
|
+
istemde grafı bir kez sorgular ve yanıtı ajana ilk turundan önce verir. Ajan
|
|
115
|
+
benchmark'ında ölçülen akış budur (−%20 token, yarıya inen arama çıktısı, eşit veya daha
|
|
116
|
+
iyi kontroller; React/TypeScript bir kod tabanında 14 görev, aynı model ve makine).
|
|
117
|
+
Cursor'ın istem kancası bağlam ekleyemediği için orada bu işi kural yapar. `--no-hook`,
|
|
118
|
+
`--repo-id` (tekrarlanabilir) ve `--bce-command` dosyaları ayarlar; iki komut da tekrar
|
|
119
|
+
çalıştırılmaya uygundur.
|
|
120
|
+
|
|
100
121
|
MCP yapılandırmasını ekledikten veya değiştirdikten sonra **Cursor veya VS Code'u
|
|
101
122
|
yeniden başlatın** (veya Komut Paleti → “Developer: Reload Window”). Sunucu sekiz araçla
|
|
102
123
|
(indeksleme açıkken on araçla) etkin görünmelidir. Ayrıntı: [docs/mcp.md](docs/mcp.md).
|
|
124
|
+
Elle yapmanın karşılıkları:
|
|
103
125
|
|
|
104
126
|
**Cursor** — her proje için kullanıcı yapılandırması `~/.cursor/mcp.json`, ya da yerelde
|
|
105
127
|
kalan bir proje `.cursor/mcp.json` (dizin gitignore'dadır):
|
|
@@ -165,12 +187,14 @@ Dosya yolu listesi değil. Gerekçesi ekli, sıralanmış bir paket:
|
|
|
165
187
|
},
|
|
166
188
|
"context": {
|
|
167
189
|
"items": [
|
|
168
|
-
{ "symbol_id": "...refresh_session#88c2", "
|
|
190
|
+
{ "symbol_id": "...refresh_session#88c2", "name": "refresh_session", "kind": "function",
|
|
191
|
+
"file_id": "my-service:src/auth/session.py", "line": 41, "detail_level": "full",
|
|
169
192
|
"graph_distance": 0, "score": 11.42, "tokens": 214, "content": "def refresh_session(...)" },
|
|
170
|
-
{ "symbol_id": "...SESSION_TTL#4b0d",
|
|
193
|
+
{ "symbol_id": "...SESSION_TTL#4b0d", "name": "SESSION_TTL", "kind": "constant",
|
|
194
|
+
"file_id": "my-service:src/auth/config.py", "line": 12, "detail_level": "signature",
|
|
171
195
|
"graph_distance": 2, "score": 6.10, "tokens": 31, "content": "SESSION_TTL: int" }
|
|
172
196
|
],
|
|
173
|
-
"used_tokens":
|
|
197
|
+
"used_tokens": 1388, "budget": 1500, "included": 20, "skipped": 0
|
|
174
198
|
},
|
|
175
199
|
"coverage": {
|
|
176
200
|
"anchor_source_count": 3, "connected_component_ratio": 0.875,
|
|
@@ -192,7 +216,8 @@ gereken an. `commit_mismatch` ise indeksin çalışma ağacınızın gerisinde k
|
|
|
192
216
|
|
|
193
217
|
**`detail_level`** graf mesafesiyle azalır: değiştirdiğiniz sembol tam gövdesiyle,
|
|
194
218
|
komşuları imza olarak, dış halka `name @ dosya:satır` biçiminde gelir. Gerçekten ilgili
|
|
195
|
-
|
|
219
|
+
yirmi sembolün 1500 token'a sığması bu sayede olur — bir ajanın her turda taşıyabileceği
|
|
220
|
+
kadar kısa. Her öge `file_id` ve `line` da taşır; ajan sembolü aramak yerine dosyayı açar.
|
|
196
221
|
|
|
197
222
|
## Nasıl çalışır
|
|
198
223
|
|
|
@@ -106,6 +106,7 @@ include = ["bce*"]
|
|
|
106
106
|
bce = [
|
|
107
107
|
"core/i18n/catalogs/*.json",
|
|
108
108
|
"storage/relational/migrations/*.sql",
|
|
109
|
+
"integrations/templates/*",
|
|
109
110
|
# Compiled frontend served at /ui; vendored by scripts/build_ui.py before a release build.
|
|
110
111
|
"api/rest/static/**/*",
|
|
111
112
|
]
|
|
@@ -44,8 +44,9 @@ def build_server(name: str = "bgts-context-engine") -> Any:
|
|
|
44
44
|
from bce.config import get_settings
|
|
45
45
|
from bce.storage.relational.db import connection
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
settings = get_settings()
|
|
48
|
+
allow_write = settings.mcp_allow_write
|
|
49
|
+
specs = available_tool_specs(allow_write=allow_write, allowlist=settings.mcp_tool_allowlist)
|
|
49
50
|
server = Server(name)
|
|
50
51
|
|
|
51
52
|
@server.list_tools()
|
|
@@ -56,6 +57,8 @@ def build_server(name: str = "bgts-context-engine") -> Any:
|
|
|
56
57
|
]
|
|
57
58
|
|
|
58
59
|
def _dispatch_blocking(name: str, arguments: dict[str, Any]) -> dict[str, Any]:
|
|
60
|
+
if name not in specs:
|
|
61
|
+
raise KeyError(f"tool '{name}' is not advertised by this server (BCE_MCP_TOOLS)")
|
|
59
62
|
with connection() as conn:
|
|
60
63
|
try:
|
|
61
64
|
result = dispatch_tool(conn, name, arguments, allow_write=allow_write)
|
|
@@ -26,6 +26,7 @@ from typing import Any
|
|
|
26
26
|
import psycopg
|
|
27
27
|
|
|
28
28
|
from bce.core.auth.scope import Principal, ScopeFilter
|
|
29
|
+
from bce.core.defaults import DEFAULT_MAX_CANDIDATES, DEFAULT_MAX_TOKENS
|
|
29
30
|
from bce.core.i18n import get_translator
|
|
30
31
|
from bce.jobs.store import enqueue_job, get_job, list_jobs
|
|
31
32
|
from bce.storage.graph.client import GraphClient
|
|
@@ -57,10 +58,21 @@ TOOL_SPECS: dict[str, dict[str, Any]] = {
|
|
|
57
58
|
),
|
|
58
59
|
"schema": _schema(
|
|
59
60
|
{
|
|
60
|
-
"task_text":
|
|
61
|
+
"task_text": {
|
|
62
|
+
**_STR,
|
|
63
|
+
"description": "The task in prose; pass the full request text, once.",
|
|
64
|
+
},
|
|
61
65
|
"task_id": _STR,
|
|
62
|
-
"max_tokens":
|
|
63
|
-
|
|
66
|
+
"max_tokens": {
|
|
67
|
+
**_INT,
|
|
68
|
+
"default": DEFAULT_MAX_TOKENS,
|
|
69
|
+
"description": "Token budget of the assembled context.",
|
|
70
|
+
},
|
|
71
|
+
"max_candidates": {
|
|
72
|
+
**_INT,
|
|
73
|
+
"default": DEFAULT_MAX_CANDIDATES,
|
|
74
|
+
"description": "How many symbols to return (K).",
|
|
75
|
+
},
|
|
64
76
|
"commit": _STR,
|
|
65
77
|
"repo_ids": _STR_LIST,
|
|
66
78
|
"explicit_symbols": _STR_LIST,
|
|
@@ -137,11 +149,24 @@ TOOL_SPECS: dict[str, dict[str, Any]] = {
|
|
|
137
149
|
}
|
|
138
150
|
|
|
139
151
|
|
|
140
|
-
def available_tool_specs(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
def available_tool_specs(
|
|
153
|
+
*, allow_write: bool, allowlist: frozenset[str] | None = None
|
|
154
|
+
) -> dict[str, dict[str, Any]]:
|
|
155
|
+
"""The catalog to advertise: write tools are hidden unless ``allow_write`` is set, and an
|
|
156
|
+
``allowlist`` (``BCE_MCP_TOOLS``) narrows it further. Unknown names in the allowlist raise, so a
|
|
157
|
+
typo does not silently publish nothing."""
|
|
158
|
+
if allowlist is not None:
|
|
159
|
+
unknown = sorted(allowlist - set(TOOL_SPECS))
|
|
160
|
+
if unknown:
|
|
161
|
+
raise ValueError(
|
|
162
|
+
f"BCE_MCP_TOOLS names unknown tools {unknown}; known: {sorted(TOOL_SPECS)}"
|
|
163
|
+
)
|
|
164
|
+
specs = {
|
|
165
|
+
name: spec
|
|
166
|
+
for name, spec in TOOL_SPECS.items()
|
|
167
|
+
if (allow_write or name not in WRITE_TOOLS) and (allowlist is None or name in allowlist)
|
|
168
|
+
}
|
|
169
|
+
return specs
|
|
145
170
|
|
|
146
171
|
|
|
147
172
|
def dispatch_tool(
|
|
@@ -11,6 +11,8 @@ from typing import Any
|
|
|
11
11
|
|
|
12
12
|
from pydantic import BaseModel, Field
|
|
13
13
|
|
|
14
|
+
from bce.core.defaults import DEFAULT_MAX_CANDIDATES, DEFAULT_MAX_TOKENS
|
|
15
|
+
|
|
14
16
|
|
|
15
17
|
class IndexRequest(BaseModel):
|
|
16
18
|
repo_path: str = Field(..., description="Path to a local repository to index.")
|
|
@@ -64,9 +66,11 @@ class ContextForTaskRequest(BaseModel):
|
|
|
64
66
|
task_id: str | None = Field(
|
|
65
67
|
default=None, description="Optional Jira task id (audit + history)."
|
|
66
68
|
)
|
|
67
|
-
max_tokens: int = Field(
|
|
69
|
+
max_tokens: int = Field(
|
|
70
|
+
default=DEFAULT_MAX_TOKENS, ge=1, le=200000, description="Token budget for assembly."
|
|
71
|
+
)
|
|
68
72
|
max_candidates: int = Field(
|
|
69
|
-
default=
|
|
73
|
+
default=DEFAULT_MAX_CANDIDATES, ge=1, le=100, description="Narrow to at most N candidates."
|
|
70
74
|
)
|
|
71
75
|
commit: str | None = Field(default=None, description="Pinned commit sha (stage 0).")
|
|
72
76
|
repo_ids: list[str] | None = Field(
|
|
@@ -108,7 +112,7 @@ class AssembleContextRequest(BaseModel):
|
|
|
108
112
|
symbol_ids: list[str] = Field(
|
|
109
113
|
..., description="Symbols to deduplicate and fit into the budget."
|
|
110
114
|
)
|
|
111
|
-
max_tokens: int = Field(default=
|
|
115
|
+
max_tokens: int = Field(default=DEFAULT_MAX_TOKENS, ge=1, le=200000)
|
|
112
116
|
|
|
113
117
|
|
|
114
118
|
class ToolResponse(BaseModel):
|
|
@@ -9,10 +9,16 @@ Commands:
|
|
|
9
9
|
- ``bench-prs`` PR replay ablation: Voyage alone vs Voyage + BCE (``prepare`` / ``run``)
|
|
10
10
|
- ``resolve-symbol`` Layer-1: resolve a symbol by name
|
|
11
11
|
- ``find-references`` Layer-1: find references to a symbol_id
|
|
12
|
+
- ``context`` Layer-3: assemble the context package for a task
|
|
13
|
+
- ``precontext`` the compact context block an agent prompt carries (also a Claude Code hook)
|
|
14
|
+
- ``cursor-init`` write .cursor/mcp.json + the agent rule into a project
|
|
15
|
+
- ``claude-init`` write .mcp.json, a CLAUDE.md section and the pre-context hook into a project
|
|
12
16
|
- ``languages`` list supported languages/extensions (no database needed)
|
|
13
17
|
- ``serve`` run the REST API (FastAPI/uvicorn)
|
|
18
|
+
- ``serve-mcp`` run the MCP server over stdio
|
|
14
19
|
|
|
15
|
-
Read/write commands need a running PostgreSQL (Apache AGE + pgvector); ``languages
|
|
20
|
+
Read/write commands need a running PostgreSQL (Apache AGE + pgvector); ``languages``,
|
|
21
|
+
``cursor-init`` and ``claude-init`` do not.
|
|
16
22
|
"""
|
|
17
23
|
|
|
18
24
|
from __future__ import annotations
|
|
@@ -23,6 +29,8 @@ from pathlib import Path
|
|
|
23
29
|
|
|
24
30
|
import typer
|
|
25
31
|
|
|
32
|
+
from bce.core.defaults import DEFAULT_MAX_CANDIDATES, DEFAULT_MAX_TOKENS
|
|
33
|
+
|
|
26
34
|
app = typer.Typer(add_completion=False, help="BGTS Context Engine CLI")
|
|
27
35
|
|
|
28
36
|
|
|
@@ -382,9 +390,11 @@ def find_references_cmd(
|
|
|
382
390
|
@app.command()
|
|
383
391
|
def context(
|
|
384
392
|
task: str = typer.Option(..., "--task", help="Task title + description text"),
|
|
385
|
-
max_tokens: int = typer.Option(
|
|
393
|
+
max_tokens: int = typer.Option(
|
|
394
|
+
DEFAULT_MAX_TOKENS, "--max-tokens", help="Token budget for assembly"
|
|
395
|
+
),
|
|
386
396
|
max_candidates: int = typer.Option(
|
|
387
|
-
|
|
397
|
+
DEFAULT_MAX_CANDIDATES, "--max-candidates", help="Narrow to at most N candidates (K)"
|
|
388
398
|
),
|
|
389
399
|
commit: str | None = typer.Option(None, "--commit", help="Pinned commit sha (stage 0)"),
|
|
390
400
|
locale: str | None = typer.Option(None, "--locale", help="Message locale (en/tr)"),
|
|
@@ -412,6 +422,123 @@ def context(
|
|
|
412
422
|
_echo_json(result["payload"])
|
|
413
423
|
|
|
414
424
|
|
|
425
|
+
@app.command()
|
|
426
|
+
def precontext(
|
|
427
|
+
task: str | None = typer.Option(
|
|
428
|
+
None,
|
|
429
|
+
"--task",
|
|
430
|
+
help="Task text. Omit it to read a Claude Code UserPromptSubmit hook payload from stdin.",
|
|
431
|
+
),
|
|
432
|
+
repo_id: list[str] = typer.Option(
|
|
433
|
+
[], "--repo-id", help="Restrict to these repository ids (repeatable)"
|
|
434
|
+
),
|
|
435
|
+
max_candidates: int = typer.Option(DEFAULT_MAX_CANDIDATES, "--max-candidates", help="K"),
|
|
436
|
+
max_tokens: int = typer.Option(DEFAULT_MAX_TOKENS, "--max-tokens", help="Token budget"),
|
|
437
|
+
) -> None:
|
|
438
|
+
"""The compact "Code context for this task" block an agent starts with (markdown).
|
|
439
|
+
|
|
440
|
+
With ``--task`` it prints the block. Without it, it acts as a Claude Code ``UserPromptSubmit``
|
|
441
|
+
hook: reads the hook JSON on stdin and prints the ``additionalContext`` response, or nothing
|
|
442
|
+
when the prompt is not a task. It never fails the prompt: errors go to stderr, exit code 0.
|
|
443
|
+
"""
|
|
444
|
+
from bce.integrations.precontext import claude_hook_response, precontext_for_task
|
|
445
|
+
|
|
446
|
+
if task is not None:
|
|
447
|
+
pre = precontext_for_task(
|
|
448
|
+
task, repo_ids=repo_id or None, max_candidates=max_candidates, max_tokens=max_tokens
|
|
449
|
+
)
|
|
450
|
+
typer.echo(pre["text"], nl=False)
|
|
451
|
+
return
|
|
452
|
+
try:
|
|
453
|
+
payload = json.loads(sys.stdin.read() or "{}")
|
|
454
|
+
except json.JSONDecodeError as exc:
|
|
455
|
+
typer.echo(f"bce precontext: stdin is not JSON ({exc})", err=True)
|
|
456
|
+
return
|
|
457
|
+
response = claude_hook_response(payload, repo_ids=repo_id or None)
|
|
458
|
+
if response is not None:
|
|
459
|
+
typer.echo(json.dumps(response, ensure_ascii=False))
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _agent_init_common(
|
|
463
|
+
project: Path, repo_id: list[str], env_file: Path | None, bce_command: str | None
|
|
464
|
+
) -> tuple[Path, list[str], str, Path | None]:
|
|
465
|
+
from bce.integrations.agents import resolve_bce_command, resolve_env_file
|
|
466
|
+
|
|
467
|
+
project = project.resolve()
|
|
468
|
+
if not project.is_dir():
|
|
469
|
+
raise typer.BadParameter(f"not a directory: {project}", param_hint="--project")
|
|
470
|
+
repo_ids = [r.strip() for r in repo_id if r.strip()] or [project.name]
|
|
471
|
+
try:
|
|
472
|
+
resolved_env = resolve_env_file(env_file, project)
|
|
473
|
+
except FileNotFoundError as exc:
|
|
474
|
+
raise typer.BadParameter(str(exc), param_hint="--env-file") from None
|
|
475
|
+
return project, repo_ids, resolve_bce_command(bce_command), resolved_env
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def _report_setup(result, env_file: Path | None) -> None:
|
|
479
|
+
for p in result.written:
|
|
480
|
+
typer.echo(f"wrote {p}")
|
|
481
|
+
if env_file is None:
|
|
482
|
+
typer.secho(
|
|
483
|
+
"No .env found: the MCP server will read BCE_* from the editor's environment. Pass "
|
|
484
|
+
"--env-file PATH (or run `bce --env-file PATH ...`) to point it at your engine "
|
|
485
|
+
"configuration.",
|
|
486
|
+
err=True,
|
|
487
|
+
fg=typer.colors.YELLOW,
|
|
488
|
+
)
|
|
489
|
+
for n in result.notes:
|
|
490
|
+
typer.echo(f"- {n}")
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
_INIT_PROJECT_HELP = "Project directory to configure (default: current directory)"
|
|
494
|
+
_INIT_REPO_HELP = "Repository id as indexed in the engine (repeatable; default: the directory name)"
|
|
495
|
+
_INIT_ENV_HELP = "The engine .env the MCP server loads (default: --env-file/BCE_ENV_FILE, then <project>/.env, then ./.env)"
|
|
496
|
+
_INIT_CMD_HELP = "Executable the editor spawns (default: this bce)"
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
@app.command(name="cursor-init")
|
|
500
|
+
def cursor_init(
|
|
501
|
+
project: Path = typer.Option(Path("."), "--project", help=_INIT_PROJECT_HELP),
|
|
502
|
+
repo_id: list[str] = typer.Option([], "--repo-id", help=_INIT_REPO_HELP),
|
|
503
|
+
env_file: Path | None = typer.Option(None, "--env-file", help=_INIT_ENV_HELP),
|
|
504
|
+
bce_command: str | None = typer.Option(None, "--bce-command", help=_INIT_CMD_HELP),
|
|
505
|
+
) -> None:
|
|
506
|
+
"""Connect a project to the engine for Cursor: .cursor/mcp.json + the agent rule.
|
|
507
|
+
|
|
508
|
+
Merges into an existing mcp.json (other servers are kept) and is safe to rerun.
|
|
509
|
+
"""
|
|
510
|
+
from bce.integrations.agents import setup_cursor
|
|
511
|
+
|
|
512
|
+
project, repo_ids, cmd, env = _agent_init_common(project, repo_id, env_file, bce_command)
|
|
513
|
+
_report_setup(setup_cursor(project, repo_ids=repo_ids, bce_cmd=cmd, env_file=env), env)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
@app.command(name="claude-init")
|
|
517
|
+
def claude_init(
|
|
518
|
+
project: Path = typer.Option(Path("."), "--project", help=_INIT_PROJECT_HELP),
|
|
519
|
+
repo_id: list[str] = typer.Option([], "--repo-id", help=_INIT_REPO_HELP),
|
|
520
|
+
env_file: Path | None = typer.Option(None, "--env-file", help=_INIT_ENV_HELP),
|
|
521
|
+
bce_command: str | None = typer.Option(None, "--bce-command", help=_INIT_CMD_HELP),
|
|
522
|
+
hook: bool = typer.Option(
|
|
523
|
+
True,
|
|
524
|
+
"--hook/--no-hook",
|
|
525
|
+
help="Install the UserPromptSubmit hook that adds the graph's answer to every prompt",
|
|
526
|
+
),
|
|
527
|
+
) -> None:
|
|
528
|
+
"""Connect a project to the engine for Claude Code: .mcp.json, a CLAUDE.md section and, by
|
|
529
|
+
default, the pre-computed context hook in .claude/settings.json.
|
|
530
|
+
|
|
531
|
+
Merges into existing files (other servers, hooks and CLAUDE.md content are kept) and is safe
|
|
532
|
+
to rerun.
|
|
533
|
+
"""
|
|
534
|
+
from bce.integrations.agents import setup_claude
|
|
535
|
+
|
|
536
|
+
project, repo_ids, cmd, env = _agent_init_common(project, repo_id, env_file, bce_command)
|
|
537
|
+
_report_setup(
|
|
538
|
+
setup_claude(project, repo_ids=repo_ids, bce_cmd=cmd, env_file=env, hook=hook), env
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
|
|
415
542
|
@app.command()
|
|
416
543
|
def bench(
|
|
417
544
|
cases: Path = typer.Option(..., "--cases", help="Path to a JSON file of benchmark cases"),
|
|
@@ -100,6 +100,11 @@ class Settings(BaseSettings):
|
|
|
100
100
|
)
|
|
101
101
|
# Seconds to wait for one embedding request (a local model on a laptop can need minutes).
|
|
102
102
|
embedding_timeout: float = 600.0
|
|
103
|
+
# Seconds to wait when embedding a single *query* at search time (openai provider). Queries are
|
|
104
|
+
# one short text, so a healthy server answers in well under a second; a longer wait only means
|
|
105
|
+
# the network stalled. Kept short so a tool call returns (possibly without its semantic anchor)
|
|
106
|
+
# before an agent's own MCP timeout (Cursor: 60 s) kills it. Each query gets two attempts.
|
|
107
|
+
embedding_query_timeout: float = 10.0
|
|
103
108
|
|
|
104
109
|
# --- Retrieval profile (engine constants that depend on the embedding model) ---
|
|
105
110
|
# "auto" (default) picks the profile fitted for ``embedding_model`` - "voyage" for voyage-*,
|
|
@@ -128,6 +133,15 @@ class Settings(BaseSettings):
|
|
|
128
133
|
# only read-only tools and runs no job workers. Turning it on also starts a worker pool inside
|
|
129
134
|
# the MCP process, so queued indexing runs without a separate ``bce serve``.
|
|
130
135
|
mcp_allow_write: bool = False
|
|
136
|
+
# Which tools the MCP server advertises (comma-separated names; empty = all). Agents fetch a
|
|
137
|
+
# tool's schema before using it and tend to chain small calls when many tools are on offer, so a
|
|
138
|
+
# narrow catalog (e.g. "get_context_for_task,find_references") costs fewer model turns.
|
|
139
|
+
mcp_tools: str = ""
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def mcp_tool_allowlist(self) -> frozenset[str] | None:
|
|
143
|
+
names = frozenset(n.strip() for n in self.mcp_tools.split(",") if n.strip())
|
|
144
|
+
return names or None
|
|
131
145
|
|
|
132
146
|
@property
|
|
133
147
|
def dsn(self) -> str:
|