code-context-engine 0.4.21__py3-none-any.whl → 0.4.22__py3-none-any.whl
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.
- {code_context_engine-0.4.21.dist-info → code_context_engine-0.4.22.dist-info}/METADATA +22 -16
- {code_context_engine-0.4.21.dist-info → code_context_engine-0.4.22.dist-info}/RECORD +12 -12
- context_engine/cli.py +46 -22
- context_engine/compression/output_rules.py +54 -0
- context_engine/editors.py +20 -4
- context_engine/indexer/embedder.py +58 -8
- context_engine/memory/db.py +8 -0
- context_engine/storage/vector_store.py +17 -3
- {code_context_engine-0.4.21.dist-info → code_context_engine-0.4.22.dist-info}/WHEEL +0 -0
- {code_context_engine-0.4.21.dist-info → code_context_engine-0.4.22.dist-info}/entry_points.txt +0 -0
- {code_context_engine-0.4.21.dist-info → code_context_engine-0.4.22.dist-info}/licenses/LICENSE +0 -0
- {code_context_engine-0.4.21.dist-info → code_context_engine-0.4.22.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-context-engine
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.22
|
|
4
4
|
Summary: Save 94% on Claude Code tokens. Index your codebase locally, AI agents search instead of reading files. Reduce Claude API costs, save tokens on Cursor, VS Code, Gemini CLI. Free, open source MCP server.
|
|
5
5
|
Author-email: Fazle Elahee <felahee@gmail.com>, Raj <rajkumar.sakti@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -115,15 +115,17 @@ Dynamic: license-file
|
|
|
115
115
|
|
|
116
116
|
---
|
|
117
117
|
|
|
118
|
-
## Quick start
|
|
118
|
+
## Quick start
|
|
119
119
|
|
|
120
120
|
```bash
|
|
121
|
-
uv tool install code-context-engine
|
|
121
|
+
uv tool install "code-context-engine[local]" # or: pipx install "code-context-engine[local]"
|
|
122
122
|
cd /path/to/your/project
|
|
123
|
-
cce init
|
|
123
|
+
cce init # or: cce init --agent all
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
That's it. Your AI coding agent now searches your index instead of reading entire files.
|
|
126
|
+
That's it. Your AI coding agent now searches your index instead of reading entire files.
|
|
127
|
+
|
|
128
|
+
> **Already have Ollama?** You can skip `[local]` and use `uv tool install code-context-engine` instead. CCE auto-detects Ollama at localhost:11434 and uses `nomic-embed-text`.
|
|
127
129
|
|
|
128
130
|
---
|
|
129
131
|
|
|
@@ -143,16 +145,18 @@ Tested on all three platforms in CI (macOS, Linux, Windows × Python 3.11/3.12/3
|
|
|
143
145
|
|
|
144
146
|
## Install and see savings in 60 seconds
|
|
145
147
|
|
|
146
|
-
|
|
147
|
-
uv tool install code-context-engine # or: pipx install code-context-engine
|
|
148
|
-
cd /path/to/your/project
|
|
149
|
-
cce init # index, install hooks, register MCP server
|
|
150
|
-
```
|
|
148
|
+
You need an embedding backend to index code. Pick one:
|
|
151
149
|
|
|
152
|
-
|
|
150
|
+
| Option | Install command | Size | Requires |
|
|
151
|
+
|--------|----------------|------|----------|
|
|
152
|
+
| **Local (recommended)** | `uv tool install "code-context-engine[local]"` | +60 MB | Nothing else |
|
|
153
|
+
| **Ollama** | `uv tool install code-context-engine` | Core only | Ollama running + `nomic-embed-text` pulled |
|
|
154
|
+
|
|
155
|
+
Then:
|
|
153
156
|
|
|
154
157
|
```bash
|
|
155
|
-
|
|
158
|
+
cd /path/to/your/project
|
|
159
|
+
cce init # index, install hooks, register MCP server
|
|
156
160
|
```
|
|
157
161
|
|
|
158
162
|
Restart your editor. Done. Every question now hits the index instead of re-reading files.
|
|
@@ -500,16 +504,18 @@ No. Quality stays the same or slightly improves.
|
|
|
500
504
|
|
|
501
505
|
CCE replaces "dump the entire file" with "search for the relevant function." The model still gets the code it needs (0.90 Recall@10 in benchmarks). Less irrelevant context means less noise competing for attention, which can improve the model's focus on your actual question.
|
|
502
506
|
|
|
503
|
-
### How
|
|
507
|
+
### How does output token savings work?
|
|
508
|
+
|
|
509
|
+
CCE writes output compression rules directly into your agent's instruction files (`CLAUDE.md`, `AGENTS.md`, `.cursorrules`, etc.) during `cce init`. These rules apply to the **entire session**, not just CCE tool responses, so every reply from the agent follows them.
|
|
504
510
|
|
|
505
|
-
Set the
|
|
511
|
+
Set the level in `cce.yaml`:
|
|
506
512
|
|
|
507
513
|
```yaml
|
|
508
514
|
compression:
|
|
509
515
|
output: max # off | lite | standard | max
|
|
510
516
|
```
|
|
511
517
|
|
|
512
|
-
|
|
518
|
+
Then re-run `cce init` to update instruction files. Or change at runtime:
|
|
513
519
|
|
|
514
520
|
```
|
|
515
521
|
set_output_level output_level=max
|
|
@@ -522,7 +528,7 @@ set_output_level output_level=max
|
|
|
522
528
|
| `standard` | ~70% | Drops articles, fragments, short synonyms + diff-only for code |
|
|
523
529
|
| `max` | ~80% | Telegraphic style + diff-only for code |
|
|
524
530
|
|
|
525
|
-
Default is `standard`. All levels include **code output rules** that
|
|
531
|
+
Default is `standard`. All levels include **code output rules** that tell the model to show only changed lines (not full file rewrites), which is where most output tokens go in coding sessions. The `max` level produces very terse prose (similar to "caveman mode"). Code blocks, paths, and commands are never compressed regardless of level.
|
|
526
532
|
|
|
527
533
|
### Where do the savings come from?
|
|
528
534
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
code_context_engine-0.4.
|
|
1
|
+
code_context_engine-0.4.22.dist-info/licenses/LICENSE,sha256=vLbw0GGCVJSIRppMus7Oq0PyMDhDXz-dfvz2rPpWtjQ,1069
|
|
2
2
|
context_engine/__init__.py,sha256=qThGxB7xfZi5M9jDpUno0MKBp7KKrEOdH1hG4wHMuLc,193
|
|
3
|
-
context_engine/cli.py,sha256=
|
|
3
|
+
context_engine/cli.py,sha256=iZbxwA0O4zFD_WRVgPnh1WdhsmZpu6Me-9lJTeT28DE,130226
|
|
4
4
|
context_engine/cli_style.py,sha256=a3l3Smq1gIN2asbNalFUz0i_5x7Tmkp_wEhyGMoo8a4,2460
|
|
5
5
|
context_engine/config.py,sha256=UGbVuc8_wTMflzGh80AotMZXZHzzUpLI3QjMnCxTzRo,8370
|
|
6
|
-
context_engine/editors.py,sha256=
|
|
6
|
+
context_engine/editors.py,sha256=k9jrqzU5gvYkR5kMu3VcVKHdjxEODZNmxBIEhQUOszE,23986
|
|
7
7
|
context_engine/event_bus.py,sha256=7Jgw_2YvGQFrnYewXk6T6FJcvRHz0LVEMDgZym9YBCE,760
|
|
8
8
|
context_engine/models.py,sha256=XBbM0CUqNDQ5MOp6F3STST2qLqy2Zk0m050ZtWdXkrk,2048
|
|
9
9
|
context_engine/pricing.py,sha256=aT1bsQuZXPlCdTgtwesJLwlKc2tzh8rxL67sZlMbz4E,4684
|
|
@@ -14,7 +14,7 @@ context_engine/utils.py,sha256=rytymcEY0tjG4uknJU3DXKz1_ZGjUjJRV3PhkjXoC8A,3192
|
|
|
14
14
|
context_engine/compression/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
15
|
context_engine/compression/compressor.py,sha256=JlNxZeM6-tXISWVOGiJAcLoixqAxwfEGcYtE0dj8FPw,6680
|
|
16
16
|
context_engine/compression/ollama_client.py,sha256=MKF1gii2BXMU-wxBRPyMCjo8t72v3dZ06Kv2JNfILgQ,1265
|
|
17
|
-
context_engine/compression/output_rules.py,sha256=
|
|
17
|
+
context_engine/compression/output_rules.py,sha256=kpLZ6r6Ng6PyAvA22wed5ecm8YTxHwwKI57PgsnX6ls,6655
|
|
18
18
|
context_engine/compression/prompts.py,sha256=jZnpqhr77uI9R3S0vm3Dj17JYy03AXq24E6HQTPXy-A,711
|
|
19
19
|
context_engine/compression/quality.py,sha256=F6fyxDdWjq-Hgtw4xFIaE4BqPoJw1W1EQSn3RXDgdHc,1676
|
|
20
20
|
context_engine/dashboard/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -22,7 +22,7 @@ context_engine/dashboard/_page.py,sha256=2LOz6GxVFHdNyd6iGV-u6sbwCnTrw2p_cVUY-Ly
|
|
|
22
22
|
context_engine/dashboard/server.py,sha256=N-QVaDCUL1h70QUgKrIy6QhQIedasf0KYHcV5LACZ0U,17437
|
|
23
23
|
context_engine/indexer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
24
|
context_engine/indexer/chunker.py,sha256=f0n7gJughdHP1fmMd1sbHAxLmVlCnIq6scHOeGFmBS8,6503
|
|
25
|
-
context_engine/indexer/embedder.py,sha256=
|
|
25
|
+
context_engine/indexer/embedder.py,sha256=xznLoW8A9KfDRZWO2MYzCk6o_Kj5YLIMuQ2J-MIbo3g,22717
|
|
26
26
|
context_engine/indexer/embedding_cache.py,sha256=yp7zvjjbhDei1tEczdo25GB_a5SJt3XfO4TVGujjSA0,6454
|
|
27
27
|
context_engine/indexer/git_hooks.py,sha256=GjncsmFu2TZx_3TNQNSBSp15uDwOJ3AtUJxuePQCP24,3258
|
|
28
28
|
context_engine/indexer/git_indexer.py,sha256=3IbAHYKa-XzpEX4zUfdvU0EHj-qjyn8muK6yPuxy9kw,4154
|
|
@@ -38,7 +38,7 @@ context_engine/integration/mcp_server.py,sha256=hIvap8fnpbeAOjJ0oy0GZdgjnUln6b-D
|
|
|
38
38
|
context_engine/integration/session_capture.py,sha256=azc0I2PoQQ-0gsmTFy254na_Ez3ADHJ5IdOKU5oFIEU,12440
|
|
39
39
|
context_engine/memory/__init__.py,sha256=-mzH2HLbjF6mlyzlt0IZoezDPLHBTJmIXFlsn8cjeQA,299
|
|
40
40
|
context_engine/memory/compressor.py,sha256=TiHxFHRPS3TQxo2_YnnXv8QaQXwxehmH2iwe-azuxpw,15763
|
|
41
|
-
context_engine/memory/db.py,sha256=
|
|
41
|
+
context_engine/memory/db.py,sha256=C700MhsdzT8NhpTz_8q-XV4kO6i-Rp4h4GTRoDa8OC4,34936
|
|
42
42
|
context_engine/memory/decision_extractor.py,sha256=tAFcKVaX5Y1qax71MAR03eq6uyCBIfiEDlbsgiodHUw,3508
|
|
43
43
|
context_engine/memory/extractive.py,sha256=VJFBG8P6Wku0OaKBQmOr3eTk5XRS2ed3q-TYb432GLc,3227
|
|
44
44
|
context_engine/memory/grammar.py,sha256=1yrMky1MlmT9m4-_XW3Rq8ZAEE6fBp4miFiWNEcH8ao,16776
|
|
@@ -56,9 +56,9 @@ context_engine/storage/fts_store.py,sha256=GzsF-xUPInqovcK72ULgpYAtMAymx4BRrYmps
|
|
|
56
56
|
context_engine/storage/graph_store.py,sha256=EAJaDK1OzSabm6HY4h7ZdZcykzlqtdFosNTypW5VNpc,8991
|
|
57
57
|
context_engine/storage/local_backend.py,sha256=5MVoAn6Jkiltho-9BjClisLkyXMkSZZc2Z_h3N7Vfcg,4200
|
|
58
58
|
context_engine/storage/remote_backend.py,sha256=6AwEI9YQnmP1w0a7S0ei3YrU2h3z7wbrwv34k7g5YOU,5483
|
|
59
|
-
context_engine/storage/vector_store.py,sha256=
|
|
60
|
-
code_context_engine-0.4.
|
|
61
|
-
code_context_engine-0.4.
|
|
62
|
-
code_context_engine-0.4.
|
|
63
|
-
code_context_engine-0.4.
|
|
64
|
-
code_context_engine-0.4.
|
|
59
|
+
context_engine/storage/vector_store.py,sha256=GyXSTlcKpByjr2C9JUF_cUCvMbGAc1UVV8Apx5X82kw,15772
|
|
60
|
+
code_context_engine-0.4.22.dist-info/METADATA,sha256=UUastWJFLBpuSBE0fr-bWL857Jp06tyCq_5V1bj00CI,25756
|
|
61
|
+
code_context_engine-0.4.22.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
62
|
+
code_context_engine-0.4.22.dist-info/entry_points.txt,sha256=DQuRWUuVFM7nPcXtDmJzlem7QA0IboD_4N8AnTtDD9Q,144
|
|
63
|
+
code_context_engine-0.4.22.dist-info/top_level.txt,sha256=X1-RUqb61WXBjy3JjsW2oXwfvqk2ydXKDNidxmw4CZ4,15
|
|
64
|
+
code_context_engine-0.4.22.dist-info/RECORD,,
|
context_engine/cli.py
CHANGED
|
@@ -182,12 +182,12 @@ _CCE_CLAUDE_MD_MARKER = "## Context Engine (CCE)"
|
|
|
182
182
|
# Version stamp embedded as an HTML comment so it doesn't render in the final
|
|
183
183
|
# Markdown but lets `_ensure_claude_md` detect when the installed block is
|
|
184
184
|
# stale and needs replacing. Bump whenever _CCE_CLAUDE_MD_BLOCK changes.
|
|
185
|
-
_CCE_CLAUDE_MD_VERSION = "
|
|
185
|
+
_CCE_CLAUDE_MD_VERSION = "4"
|
|
186
186
|
_CCE_CLAUDE_MD_VERSION_TAG = f"<!-- cce-block-version: {_CCE_CLAUDE_MD_VERSION} -->"
|
|
187
187
|
_CCE_CLAUDE_MD_VERSION_PREFIX = "<!-- cce-block-version: "
|
|
188
188
|
_CCE_CLAUDE_MD_END_MARKER = "<!-- /cce-block -->"
|
|
189
189
|
|
|
190
|
-
|
|
190
|
+
_CCE_CLAUDE_MD_BLOCK_TEMPLATE = f"""\
|
|
191
191
|
{_CCE_CLAUDE_MD_VERSION_TAG}
|
|
192
192
|
## Context Engine (CCE)
|
|
193
193
|
|
|
@@ -268,18 +268,22 @@ the goal is durable signal, not an event log.
|
|
|
268
268
|
Both are read-only and cheap. Prefer them over re-running tool calls or
|
|
269
269
|
asking the user to re-paste context.
|
|
270
270
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
Be concise. Lead with the answer or action, not reasoning. Skip filler words,
|
|
274
|
-
preamble, and phrases like "I'll help you with that" or "Certainly!". Prefer
|
|
275
|
-
fragments over full sentences in explanations. No trailing summaries of what
|
|
276
|
-
you just did. One sentence if it fits.
|
|
277
|
-
|
|
278
|
-
Code blocks, file paths, commands, and error messages are always written in full.
|
|
271
|
+
{{output_style}}
|
|
279
272
|
{_CCE_CLAUDE_MD_END_MARKER}
|
|
280
273
|
"""
|
|
281
274
|
|
|
282
275
|
|
|
276
|
+
def _build_claude_md_block(output_level: str = "standard") -> str:
|
|
277
|
+
"""Generate the CLAUDE.md CCE block with the configured output style."""
|
|
278
|
+
from context_engine.compression.output_rules import get_instruction_output_block
|
|
279
|
+
block = get_instruction_output_block(output_level)
|
|
280
|
+
return _CCE_CLAUDE_MD_BLOCK_TEMPLATE.replace("{output_style}", block)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
# Default block for backward compat
|
|
284
|
+
_CCE_CLAUDE_MD_BLOCK = _build_claude_md_block("standard")
|
|
285
|
+
|
|
286
|
+
|
|
283
287
|
def _resolve_cce_cmd() -> str:
|
|
284
288
|
"""Find the globally installed cce binary path."""
|
|
285
289
|
from context_engine.utils import resolve_cce_binary
|
|
@@ -623,6 +627,22 @@ def _preflight_check(config) -> None:
|
|
|
623
627
|
one was picked, and surfaces Ollama status for the separate compression
|
|
624
628
|
path so users know what compression level they will get.
|
|
625
629
|
"""
|
|
630
|
+
# --- SQLite extension support ---
|
|
631
|
+
import sqlite3 as _sqlite3
|
|
632
|
+
_test_conn = _sqlite3.connect(":memory:")
|
|
633
|
+
if not hasattr(_test_conn, "enable_load_extension"):
|
|
634
|
+
_test_conn.close()
|
|
635
|
+
raise click.ClickException(
|
|
636
|
+
"Your Python was compiled without SQLite extension support "
|
|
637
|
+
"(enable_load_extension is missing).\n"
|
|
638
|
+
"This is common with python.org installers on macOS.\n\n"
|
|
639
|
+
"Fix: reinstall CCE under a Python that has extension support:\n\n"
|
|
640
|
+
" brew install python3\n"
|
|
641
|
+
" uv tool install --python /opt/homebrew/bin/python3 "
|
|
642
|
+
"--force code-context-engine\n"
|
|
643
|
+
)
|
|
644
|
+
_test_conn.close()
|
|
645
|
+
|
|
626
646
|
# --- Embedding backend ---
|
|
627
647
|
click.echo(_dim(" Detecting embedding backend") + "...", nl=False)
|
|
628
648
|
from context_engine.config import resolve_ollama_url
|
|
@@ -646,13 +666,15 @@ def _preflight_check(config) -> None:
|
|
|
646
666
|
fg="green",
|
|
647
667
|
)
|
|
648
668
|
)
|
|
649
|
-
except Exception
|
|
669
|
+
except Exception:
|
|
650
670
|
click.echo("")
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
"
|
|
654
|
-
|
|
655
|
-
|
|
671
|
+
raise click.ClickException(
|
|
672
|
+
"No embedding backend available.\n\n"
|
|
673
|
+
"Fix (pick one):\n"
|
|
674
|
+
" 1. Install local embeddings:\n"
|
|
675
|
+
" uv tool install 'code-context-engine[local]'\n\n"
|
|
676
|
+
f" 2. Start Ollama and pull the embedding model:\n"
|
|
677
|
+
f" ollama pull {ollama_model}\n"
|
|
656
678
|
)
|
|
657
679
|
|
|
658
680
|
# --- Ollama for LLM compression (independent of the embedding path) ---
|
|
@@ -678,7 +700,7 @@ def _preflight_check(config) -> None:
|
|
|
678
700
|
click.echo(_dim(" Tip: ollama pull phi3:mini for LLM summarization"))
|
|
679
701
|
|
|
680
702
|
|
|
681
|
-
def _ensure_claude_md(project_dir: Path) -> None:
|
|
703
|
+
def _ensure_claude_md(project_dir: Path, output_level: str = "standard") -> None:
|
|
682
704
|
"""Add or upgrade the CCE instructions block in CLAUDE.md.
|
|
683
705
|
|
|
684
706
|
Three states the file can be in:
|
|
@@ -693,9 +715,10 @@ def _ensure_claude_md(project_dir: Path) -> None:
|
|
|
693
715
|
"""
|
|
694
716
|
from context_engine.utils import atomic_write_text
|
|
695
717
|
|
|
718
|
+
block = _build_claude_md_block(output_level)
|
|
696
719
|
claude_md = project_dir / "CLAUDE.md"
|
|
697
720
|
if not claude_md.exists():
|
|
698
|
-
atomic_write_text(claude_md,
|
|
721
|
+
atomic_write_text(claude_md, block)
|
|
699
722
|
_ok("CLAUDE.md created with CCE instructions")
|
|
700
723
|
return
|
|
701
724
|
|
|
@@ -710,13 +733,13 @@ def _ensure_claude_md(project_dir: Path) -> None:
|
|
|
710
733
|
# survives the upgrade.
|
|
711
734
|
old_block = _extract_existing_cce_block(existing)
|
|
712
735
|
if old_block is not None:
|
|
713
|
-
new_content = existing.replace(old_block,
|
|
736
|
+
new_content = existing.replace(old_block, block.rstrip(), 1)
|
|
714
737
|
atomic_write_text(claude_md, new_content)
|
|
715
738
|
_ok("CLAUDE.md upgraded to current CCE instructions")
|
|
716
739
|
return
|
|
717
740
|
|
|
718
741
|
# No CCE block detected — append.
|
|
719
|
-
new_content = existing.rstrip() + "\n\n" +
|
|
742
|
+
new_content = existing.rstrip() + "\n\n" + block
|
|
720
743
|
atomic_write_text(claude_md, new_content)
|
|
721
744
|
_ok("CLAUDE.md updated with CCE instructions")
|
|
722
745
|
|
|
@@ -905,14 +928,15 @@ def init(ctx: click.Context, agent: str) -> None:
|
|
|
905
928
|
for file_key, info in INSTRUCTION_FILES.items():
|
|
906
929
|
if any((project_dir / marker).exists() for marker in info["detect"]):
|
|
907
930
|
instruction_targets.add(file_key)
|
|
931
|
+
output_level = getattr(config, "output_compression", "standard")
|
|
908
932
|
for file_key in sorted(instruction_targets):
|
|
909
933
|
info = INSTRUCTION_FILES[file_key]
|
|
910
|
-
if write_instruction_file(project_dir, file_key):
|
|
934
|
+
if write_instruction_file(project_dir, file_key, output_level=output_level):
|
|
911
935
|
_ok(f"CCE instructions added to {info['name']}")
|
|
912
936
|
|
|
913
937
|
# 5. CLAUDE.md + session hook + memory lifecycle hooks
|
|
914
938
|
if "claude" in editor_targets:
|
|
915
|
-
_ensure_claude_md(project_dir)
|
|
939
|
+
_ensure_claude_md(project_dir, output_level=output_level)
|
|
916
940
|
_ensure_session_hook(project_dir)
|
|
917
941
|
_install_memory_hooks(project_dir)
|
|
918
942
|
_check_memory_capture_reachable(config, project_dir)
|
|
@@ -90,3 +90,57 @@ def get_level_description(level: str) -> str:
|
|
|
90
90
|
"max": "Telegraphic style with abbreviations and symbols. Diff-only for code. ~80% savings.",
|
|
91
91
|
}
|
|
92
92
|
return descriptions.get(level, "Unknown level")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ── Instruction-file blocks ──────────────────────────────────────────
|
|
96
|
+
# These go into CLAUDE.md, AGENTS.md, .cursorrules, etc. so they apply
|
|
97
|
+
# to the entire session, not just CCE tool responses.
|
|
98
|
+
|
|
99
|
+
_INSTRUCTION_OUTPUT_STYLES = {
|
|
100
|
+
"lite": """\
|
|
101
|
+
### Output style
|
|
102
|
+
|
|
103
|
+
Respond concisely. Remove filler words (just, really, basically, actually,
|
|
104
|
+
simply), hedging (I think, it seems, perhaps), and pleasantries (Sure!,
|
|
105
|
+
Happy to help, Great question). No trailing summaries. Keep full grammar.
|
|
106
|
+
|
|
107
|
+
When suggesting code changes, show only the changed lines with 3 lines of
|
|
108
|
+
context. Never rewrite entire files. For new files, show the full file.
|
|
109
|
+
For edits, show only what changes.""",
|
|
110
|
+
|
|
111
|
+
"standard": """\
|
|
112
|
+
### Output style
|
|
113
|
+
|
|
114
|
+
Respond in compressed style. Drop articles (a, an, the) in prose. Use
|
|
115
|
+
sentence fragments over full sentences. Use short synonyms (fix not resolve,
|
|
116
|
+
check not investigate). Pattern: [thing] [action] [reason]. [next step].
|
|
117
|
+
No filler, hedging, pleasantries, trailing summaries, or restating what
|
|
118
|
+
the user said. One sentence if one sentence is enough.
|
|
119
|
+
|
|
120
|
+
When suggesting code changes, show only the changed lines with 3 lines of
|
|
121
|
+
context. Never rewrite entire files. Multiple changes in one file: show each
|
|
122
|
+
change separately. Never echo back unchanged code the user already has.
|
|
123
|
+
|
|
124
|
+
Code blocks, file paths, commands, error messages: always written in full.
|
|
125
|
+
Security warnings and destructive action confirmations: use full clarity.""",
|
|
126
|
+
|
|
127
|
+
"max": """\
|
|
128
|
+
### Output style
|
|
129
|
+
|
|
130
|
+
Respond in telegraphic style. Drop articles, pronouns, conjunctions where
|
|
131
|
+
meaning survives. Abbreviate common terms: DB, auth, config, fn, dep, impl,
|
|
132
|
+
req, resp, init. Use arrows for causality: X → Y. Use symbols: + (add),
|
|
133
|
+
- (remove), ~ (change), ! (warning). Max 1-2 sentences per explanation.
|
|
134
|
+
Pattern: [thing] → [action]. [reason].
|
|
135
|
+
|
|
136
|
+
When suggesting code changes, show only changed lines. Never rewrite files.
|
|
137
|
+
Never echo back unchanged code.
|
|
138
|
+
|
|
139
|
+
Code blocks, paths, commands, errors: always full.
|
|
140
|
+
Security warnings and destructive actions: full clarity, drop compression.""",
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def get_instruction_output_block(level: str) -> str:
|
|
145
|
+
"""Return the output style block for instruction files, or empty if off."""
|
|
146
|
+
return _INSTRUCTION_OUTPUT_STYLES.get(level, "")
|
context_engine/editors.py
CHANGED
|
@@ -92,7 +92,7 @@ EDITORS: dict[str, dict] = {
|
|
|
92
92
|
# ── Instruction file definitions ──────────────────────────────────────
|
|
93
93
|
|
|
94
94
|
# Editor-agnostic CCE instructions (no "Claude Code" references)
|
|
95
|
-
|
|
95
|
+
_CCE_INSTRUCTIONS_BASE = """\
|
|
96
96
|
## Context Engine (CCE)
|
|
97
97
|
|
|
98
98
|
This project uses Code Context Engine for intelligent code retrieval and
|
|
@@ -122,6 +122,19 @@ Call `record_decision(decision="...", reason="...")` after making choices.
|
|
|
122
122
|
Call `record_code_area(file_path="...", description="...")` after meaningful work.
|
|
123
123
|
"""
|
|
124
124
|
|
|
125
|
+
|
|
126
|
+
def _build_instructions(output_level: str = "standard") -> str:
|
|
127
|
+
"""Build CCE instructions with the configured output style."""
|
|
128
|
+
from context_engine.compression.output_rules import get_instruction_output_block
|
|
129
|
+
block = get_instruction_output_block(output_level)
|
|
130
|
+
if block:
|
|
131
|
+
return _CCE_INSTRUCTIONS_BASE + "\n" + block + "\n"
|
|
132
|
+
return _CCE_INSTRUCTIONS_BASE
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# Default instructions (standard output compression)
|
|
136
|
+
_CCE_INSTRUCTIONS = _build_instructions("standard")
|
|
137
|
+
|
|
125
138
|
INSTRUCTION_FILES: dict[str, dict] = {
|
|
126
139
|
"agents": {
|
|
127
140
|
"name": "AGENTS.md",
|
|
@@ -568,21 +581,24 @@ def _remove_toml(config_path: Path, display_path: str, *, section: str) -> str |
|
|
|
568
581
|
return None
|
|
569
582
|
|
|
570
583
|
|
|
571
|
-
def write_instruction_file(
|
|
584
|
+
def write_instruction_file(
|
|
585
|
+
project_dir: Path, file_key: str, output_level: str = "standard",
|
|
586
|
+
) -> bool:
|
|
572
587
|
"""Write CCE instructions to an editor's instruction file. Returns True if written."""
|
|
573
588
|
info = INSTRUCTION_FILES[file_key]
|
|
574
589
|
path = project_dir / info["path"]
|
|
575
590
|
marker = "## Context Engine (CCE)"
|
|
576
591
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
592
|
+
instructions = _build_instructions(output_level)
|
|
577
593
|
|
|
578
594
|
if path.exists():
|
|
579
595
|
content = path.read_text()
|
|
580
596
|
if marker in content:
|
|
581
597
|
return False # already has CCE block
|
|
582
598
|
# Append
|
|
583
|
-
path.write_text(content.rstrip() + "\n\n" +
|
|
599
|
+
path.write_text(content.rstrip() + "\n\n" + instructions)
|
|
584
600
|
else:
|
|
585
|
-
path.write_text(
|
|
601
|
+
path.write_text(instructions)
|
|
586
602
|
return True
|
|
587
603
|
|
|
588
604
|
|
|
@@ -319,16 +319,66 @@ class OllamaBackend:
|
|
|
319
319
|
for _ in resp.iter_lines():
|
|
320
320
|
pass
|
|
321
321
|
|
|
322
|
+
# nomic-embed-text has an 8192-token context. Dense-tokenizing content
|
|
323
|
+
# (YAML with ${{ }}, Python separator comments) can hit ~1 char/token,
|
|
324
|
+
# so 3000 chars is a safe ceiling that works for all content types.
|
|
325
|
+
_MAX_EMBED_CHARS = 3000
|
|
326
|
+
|
|
322
327
|
def _embed_batch(self, texts: list[str]) -> list[list[float]]:
|
|
323
328
|
import httpx
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
329
|
+
# Truncate oversized texts and skip empty ones
|
|
330
|
+
safe_texts = []
|
|
331
|
+
original_indices = []
|
|
332
|
+
for i, t in enumerate(texts):
|
|
333
|
+
if not t or not t.strip():
|
|
334
|
+
continue
|
|
335
|
+
safe_texts.append(t[:self._MAX_EMBED_CHARS])
|
|
336
|
+
original_indices.append(i)
|
|
337
|
+
|
|
338
|
+
if not safe_texts:
|
|
339
|
+
return [[] for _ in texts]
|
|
340
|
+
|
|
341
|
+
try:
|
|
342
|
+
resp = httpx.post(
|
|
343
|
+
f"{self.base_url}/api/embed",
|
|
344
|
+
json={"model": self.model_name, "input": safe_texts},
|
|
345
|
+
timeout=self._timeout,
|
|
346
|
+
)
|
|
347
|
+
resp.raise_for_status()
|
|
348
|
+
embeddings = resp.json().get("embeddings", [])
|
|
349
|
+
except httpx.HTTPStatusError as exc:
|
|
350
|
+
if exc.response.status_code != 400:
|
|
351
|
+
raise
|
|
352
|
+
# Batch failed (possibly one text still too large after truncation).
|
|
353
|
+
# Fall back to one-at-a-time with halving retry.
|
|
354
|
+
log.warning("Ollama batch embed failed, retrying one-at-a-time")
|
|
355
|
+
embeddings = []
|
|
356
|
+
for text in safe_texts:
|
|
357
|
+
vec = self._embed_single_with_retry(text)
|
|
358
|
+
embeddings.append(vec)
|
|
359
|
+
|
|
360
|
+
# Map embeddings back to original positions (empty texts get empty vecs)
|
|
361
|
+
result: list[list[float]] = [[] for _ in texts]
|
|
362
|
+
for idx, emb in zip(original_indices, embeddings):
|
|
363
|
+
result[idx] = emb
|
|
364
|
+
return result
|
|
365
|
+
|
|
366
|
+
def _embed_single_with_retry(self, text: str) -> list[float]:
|
|
367
|
+
"""Embed a single text, halving on context-length errors."""
|
|
368
|
+
import httpx
|
|
369
|
+
while text:
|
|
370
|
+
resp = httpx.post(
|
|
371
|
+
f"{self.base_url}/api/embed",
|
|
372
|
+
json={"model": self.model_name, "input": [text]},
|
|
373
|
+
timeout=self._timeout,
|
|
374
|
+
)
|
|
375
|
+
if resp.status_code == 400 and "context length" in resp.text:
|
|
376
|
+
text = text[:len(text) // 2]
|
|
377
|
+
continue
|
|
378
|
+
resp.raise_for_status()
|
|
379
|
+
vecs = resp.json().get("embeddings", [[]])
|
|
380
|
+
return vecs[0] if vecs else []
|
|
381
|
+
return []
|
|
332
382
|
|
|
333
383
|
def embed_texts(self, texts: list[str], batch_size: int = 64) -> list[list[float]]:
|
|
334
384
|
out: list[list[float]] = []
|
context_engine/memory/db.py
CHANGED
|
@@ -281,6 +281,14 @@ def _try_load_vec(conn: sqlite3.Connection) -> bool:
|
|
|
281
281
|
sqlite_vec.load(conn)
|
|
282
282
|
conn.enable_load_extension(False)
|
|
283
283
|
return True
|
|
284
|
+
except AttributeError:
|
|
285
|
+
log.warning(
|
|
286
|
+
"sqlite-vec load failed; semantic recall disabled. "
|
|
287
|
+
"Python was compiled without SQLite extension support. "
|
|
288
|
+
"Reinstall CCE with Homebrew Python: "
|
|
289
|
+
"uv tool install --python /opt/homebrew/bin/python3 --force code-context-engine"
|
|
290
|
+
)
|
|
291
|
+
return False
|
|
284
292
|
except Exception as exc:
|
|
285
293
|
log.warning("sqlite-vec load failed; semantic recall disabled: %s", exc)
|
|
286
294
|
return False
|
|
@@ -46,9 +46,23 @@ class VectorStore:
|
|
|
46
46
|
def _connect(self) -> sqlite3.Connection:
|
|
47
47
|
import sqlite_vec
|
|
48
48
|
conn = sqlite3.connect(self._db_file, check_same_thread=False)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
try:
|
|
50
|
+
conn.enable_load_extension(True)
|
|
51
|
+
sqlite_vec.load(conn)
|
|
52
|
+
conn.enable_load_extension(False)
|
|
53
|
+
except AttributeError:
|
|
54
|
+
raise RuntimeError(
|
|
55
|
+
"Your Python was compiled without SQLite extension support "
|
|
56
|
+
"(enable_load_extension is missing). This is common with "
|
|
57
|
+
"python.org installers on macOS.\n\n"
|
|
58
|
+
"Fix: reinstall CCE under a Python that has extension support:\n"
|
|
59
|
+
" uv tool install --python $(brew --prefix python3)/bin/python3 "
|
|
60
|
+
"--force code-context-engine\n\n"
|
|
61
|
+
"Or use Homebrew Python directly:\n"
|
|
62
|
+
" brew install python3\n"
|
|
63
|
+
" uv tool install --python /opt/homebrew/bin/python3 "
|
|
64
|
+
"--force code-context-engine"
|
|
65
|
+
) from None
|
|
52
66
|
conn.execute("PRAGMA journal_mode=WAL")
|
|
53
67
|
conn.execute("PRAGMA synchronous=NORMAL")
|
|
54
68
|
return conn
|
|
File without changes
|
{code_context_engine-0.4.21.dist-info → code_context_engine-0.4.22.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{code_context_engine-0.4.21.dist-info → code_context_engine-0.4.22.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|