code-context-control 2.28.2__py3-none-any.whl → 2.29.0__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.
- cli/c3.py +6 -1
- cli/hub.html +128 -3
- cli/hub_server.py +23 -0
- code_context_control-2.29.0.dist-info/METADATA +331 -0
- {code_context_control-2.28.2.dist-info → code_context_control-2.29.0.dist-info}/RECORD +10 -10
- services/project_manager.py +244 -0
- code_context_control-2.28.2.dist-info/METADATA +0 -249
- {code_context_control-2.28.2.dist-info → code_context_control-2.29.0.dist-info}/WHEEL +0 -0
- {code_context_control-2.28.2.dist-info → code_context_control-2.29.0.dist-info}/entry_points.txt +0 -0
- {code_context_control-2.28.2.dist-info → code_context_control-2.29.0.dist-info}/licenses/LICENSE +0 -0
- {code_context_control-2.28.2.dist-info → code_context_control-2.29.0.dist-info}/top_level.txt +0 -0
cli/c3.py
CHANGED
|
@@ -85,7 +85,7 @@ console = Console() if HAS_RICH else None
|
|
|
85
85
|
# Config
|
|
86
86
|
CONFIG_DIR = ".c3"
|
|
87
87
|
CONFIG_FILE = ".c3/config.json"
|
|
88
|
-
__version__ = "2.28.
|
|
88
|
+
__version__ = "2.28.3"
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
def _command_deps() -> CommandDeps:
|
|
@@ -827,6 +827,11 @@ def cmd_init(args):
|
|
|
827
827
|
if not c3_dir.exists() or not (c3_dir / "config.json").exists():
|
|
828
828
|
print_header(f"Initializing C3 for: {project_path}")
|
|
829
829
|
_do_init(project_path, ide_name=requested_ide)
|
|
830
|
+
try:
|
|
831
|
+
from services.project_manager import ProjectManager
|
|
832
|
+
ProjectManager().add_project(project_path)
|
|
833
|
+
except Exception as _e:
|
|
834
|
+
print(f" [warn] Could not register project with hub: {_e}")
|
|
830
835
|
if getattr(args, "force", False):
|
|
831
836
|
if git_requested:
|
|
832
837
|
_init_local_git_repo(project_path)
|
cli/hub.html
CHANGED
|
@@ -995,6 +995,49 @@
|
|
|
995
995
|
</div>
|
|
996
996
|
</div>
|
|
997
997
|
|
|
998
|
+
<!-- Merge Modal -->
|
|
999
|
+
<div class="modal-backdrop hidden" id="merge-modal">
|
|
1000
|
+
<div class="modal">
|
|
1001
|
+
<div class="modal-header">
|
|
1002
|
+
<h3>Merge Projects</h3>
|
|
1003
|
+
<button class="modal-close" onclick="closeModal('merge-modal')">×</button>
|
|
1004
|
+
</div>
|
|
1005
|
+
<div class="modal-body">
|
|
1006
|
+
<div class="form-group">
|
|
1007
|
+
<span class="form-label">Merging from</span>
|
|
1008
|
+
<div class="form-path" id="merge-modal-source-path"></div>
|
|
1009
|
+
</div>
|
|
1010
|
+
<div class="form-group">
|
|
1011
|
+
<label class="form-label" for="merge-target-select">Merge into</label>
|
|
1012
|
+
<select class="form-input" id="merge-target-select"></select>
|
|
1013
|
+
</div>
|
|
1014
|
+
<div class="form-group">
|
|
1015
|
+
<span class="form-label">After merge</span>
|
|
1016
|
+
<div style="display:flex;flex-direction:column;gap:.4rem;margin-top:.3rem;">
|
|
1017
|
+
<label style="display:flex;align-items:center;gap:.5rem;font-weight:normal;cursor:pointer;">
|
|
1018
|
+
<input type="radio" name="merge-cleanup" value="keep" checked onchange="updateMergeWarning()">
|
|
1019
|
+
<span>Keep source project intact</span>
|
|
1020
|
+
</label>
|
|
1021
|
+
<label style="display:flex;align-items:center;gap:.5rem;font-weight:normal;cursor:pointer;">
|
|
1022
|
+
<input type="radio" name="merge-cleanup" value="clear" onchange="updateMergeWarning()">
|
|
1023
|
+
<span>Clear source (wipe .c3/, MCP configs & instruction docs)</span>
|
|
1024
|
+
</label>
|
|
1025
|
+
</div>
|
|
1026
|
+
</div>
|
|
1027
|
+
<div id="merge-warning" style="display:none;border:1px solid #b13c3c;background:rgba(177,60,60,.1);padding:.6rem .75rem;border-radius:4px;margin-top:.5rem;color:var(--text);font-size:.85rem;">
|
|
1028
|
+
⚠ The source project's <code>.c3/</code> directory, MCP configs (<code>.mcp.json</code>, <code>.claude/settings.local.json</code>, <code>.codex/</code>) and instruction docs (CLAUDE.md, GEMINI.md, AGENTS.md) will be deleted. The source directory itself stays in place.
|
|
1029
|
+
</div>
|
|
1030
|
+
<p style="font-size:.8rem;color:var(--text-muted);margin-top:.5rem;">
|
|
1031
|
+
Combines memory facts, conversation sessions, and edit-ledger entries into the target. File-memory and indices are not merged.
|
|
1032
|
+
</p>
|
|
1033
|
+
</div>
|
|
1034
|
+
<div class="modal-footer">
|
|
1035
|
+
<button class="btn btn-ghost" onclick="closeModal('merge-modal')">Cancel</button>
|
|
1036
|
+
<button class="btn btn-primary" id="merge-save-btn" onclick="saveMerge()">Merge</button>
|
|
1037
|
+
</div>
|
|
1038
|
+
</div>
|
|
1039
|
+
</div>
|
|
1040
|
+
|
|
998
1041
|
<!-- Settings Modal -->
|
|
999
1042
|
<div class="modal-backdrop hidden" id="settings-modal">
|
|
1000
1043
|
<div class="modal">
|
|
@@ -1186,6 +1229,8 @@ let sessionPollTimer = null;
|
|
|
1186
1229
|
let modalPath = null;
|
|
1187
1230
|
let editPath = null;
|
|
1188
1231
|
let transferPath = null;
|
|
1232
|
+
let mergeSourcePath = null;
|
|
1233
|
+
let mergeSourceName = '';
|
|
1189
1234
|
let idePath = null;
|
|
1190
1235
|
let ideSelected = null;
|
|
1191
1236
|
let mcpModalPath = null;
|
|
@@ -1261,7 +1306,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
1261
1306
|
);
|
|
1262
1307
|
document.addEventListener('keydown', e => {
|
|
1263
1308
|
if (e.key === 'Escape') {
|
|
1264
|
-
['mcp-modal','edit-modal','transfer-modal','settings-modal','update-modal','batch-update-modal','ide-modal','c3-setup-modal'].forEach(id => {
|
|
1309
|
+
['mcp-modal','edit-modal','transfer-modal','merge-modal','settings-modal','update-modal','batch-update-modal','ide-modal','c3-setup-modal'].forEach(id => {
|
|
1265
1310
|
if (!document.getElementById(id).classList.contains('hidden')) closeModal(id);
|
|
1266
1311
|
});
|
|
1267
1312
|
}
|
|
@@ -1712,6 +1757,7 @@ function projectCard(p) {
|
|
|
1712
1757
|
const editBtn = `<button class="btn btn-xs btn-ghost" onclick="openEditModal('${jsq(p.path)}','${jsq(p.name)}','${jsq((p.tags||[]).join(','))}')">✏ Edit</button>`;
|
|
1713
1758
|
const ideBtn = `<button class="btn btn-xs btn-primary" onclick="openIdeModal('${jsq(p.path)}','${jsq(p.ide)}')">▶ IDE</button>`;
|
|
1714
1759
|
const transferBtn = !active ? `<button class="btn btn-xs btn-ghost" onclick="openTransferModal('${jsq(p.path)}','${jsq(p.name)}')">Transfer</button>` : '';
|
|
1760
|
+
const mergeBtn = !active ? `<button class="btn btn-xs btn-ghost" onclick="openMergeModal('${jsq(p.path)}','${jsq(p.name)}')" title="Merge memory, sessions and edit ledger into another project">⇄ Merge</button>` : '';
|
|
1715
1761
|
const removeBtn = !active ? `<button class="btn btn-xs btn-danger" onclick="removeProject('${jsq(p.path)}','${jsq(p.name)}')">✕ Remove</button>` : '';
|
|
1716
1762
|
|
|
1717
1763
|
const isLaunching = !active && launchingPaths.has(p.path);
|
|
@@ -1741,7 +1787,7 @@ function projectCard(p) {
|
|
|
1741
1787
|
${startBtn}${startUiBtn}${openBtn}${restartBtn}${stopBtn}${autostartBtn}${folderBtn}${logBtn}${ledgerBtn}${setupBtn}${ideBtn}
|
|
1742
1788
|
</div>
|
|
1743
1789
|
<div class="card-actions-secondary">
|
|
1744
|
-
${clearNotifBtn}${editBtn}${transferBtn}${removeBtn}
|
|
1790
|
+
${clearNotifBtn}${editBtn}${transferBtn}${mergeBtn}${removeBtn}
|
|
1745
1791
|
</div>
|
|
1746
1792
|
</div>
|
|
1747
1793
|
</div>`;
|
|
@@ -2977,6 +3023,84 @@ async function saveTransfer() {
|
|
|
2977
3023
|
}
|
|
2978
3024
|
}
|
|
2979
3025
|
|
|
3026
|
+
// ── Merge Modal ───────────────────────────────────────────────────────────
|
|
3027
|
+
|
|
3028
|
+
function openMergeModal(path, name) {
|
|
3029
|
+
mergeSourcePath = path;
|
|
3030
|
+
mergeSourceName = name;
|
|
3031
|
+
document.getElementById('merge-modal-source-path').textContent = `${name} (${path})`;
|
|
3032
|
+
// Populate target dropdown with idle projects (exclude source and active sessions).
|
|
3033
|
+
const sel = document.getElementById('merge-target-select');
|
|
3034
|
+
sel.innerHTML = '';
|
|
3035
|
+
const candidates = (allProjects || []).filter(p => p.path !== path && !p.session_active && p.port == null);
|
|
3036
|
+
if (!candidates.length) {
|
|
3037
|
+
const opt = document.createElement('option');
|
|
3038
|
+
opt.value = '';
|
|
3039
|
+
opt.textContent = '— No eligible target projects —';
|
|
3040
|
+
sel.appendChild(opt);
|
|
3041
|
+
document.getElementById('merge-save-btn').disabled = true;
|
|
3042
|
+
} else {
|
|
3043
|
+
candidates.forEach(p => {
|
|
3044
|
+
const opt = document.createElement('option');
|
|
3045
|
+
opt.value = p.path;
|
|
3046
|
+
opt.textContent = `${p.name} (${p.path})`;
|
|
3047
|
+
sel.appendChild(opt);
|
|
3048
|
+
});
|
|
3049
|
+
document.getElementById('merge-save-btn').disabled = false;
|
|
3050
|
+
}
|
|
3051
|
+
// Reset cleanup radio + warning
|
|
3052
|
+
document.querySelectorAll('input[name="merge-cleanup"]').forEach(r => { r.checked = (r.value === 'keep'); });
|
|
3053
|
+
updateMergeWarning();
|
|
3054
|
+
openModal('merge-modal');
|
|
3055
|
+
}
|
|
3056
|
+
|
|
3057
|
+
function updateMergeWarning() {
|
|
3058
|
+
const cleanup = (document.querySelector('input[name="merge-cleanup"]:checked') || {}).value || 'keep';
|
|
3059
|
+
document.getElementById('merge-warning').style.display = cleanup === 'clear' ? 'block' : 'none';
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
async function saveMerge() {
|
|
3063
|
+
if (!mergeSourcePath) return;
|
|
3064
|
+
const target = document.getElementById('merge-target-select').value;
|
|
3065
|
+
if (!target) { toast('Pick a target project', 'err'); return; }
|
|
3066
|
+
const cleanup = (document.querySelector('input[name="merge-cleanup"]:checked') || {}).value || 'keep';
|
|
3067
|
+
if (cleanup === 'clear') {
|
|
3068
|
+
const ok = await confirmDialog({
|
|
3069
|
+
title: 'Clear source after merge?',
|
|
3070
|
+
message: `This will permanently delete .c3/, MCP configs and instruction docs from "${mergeSourceName}". The merged data will live on inside the target. Continue?`,
|
|
3071
|
+
confirmText: 'Merge & clear',
|
|
3072
|
+
danger: true,
|
|
3073
|
+
});
|
|
3074
|
+
if (!ok) return;
|
|
3075
|
+
}
|
|
3076
|
+
const btn = document.getElementById('merge-save-btn');
|
|
3077
|
+
btn.disabled = true;
|
|
3078
|
+
try {
|
|
3079
|
+
const r = await fetch('/api/projects/merge', {
|
|
3080
|
+
method: 'POST',
|
|
3081
|
+
headers: {'Content-Type':'application/json'},
|
|
3082
|
+
body: JSON.stringify({source_path: mergeSourcePath, target_path: target, cleanup}),
|
|
3083
|
+
});
|
|
3084
|
+
const d = await r.json();
|
|
3085
|
+
if (d.error) throw new Error(d.error);
|
|
3086
|
+
if (!d.merged) throw new Error('Merge did not complete');
|
|
3087
|
+
const s = d.stats || {};
|
|
3088
|
+
const summary = `Merged ${s.facts||0} facts, ${s.sessions||0} sessions, ${s.ledger_entries||0} ledger entries`;
|
|
3089
|
+
toast(summary + (cleanup === 'clear' ? ' — source cleared' : ''), 'ok');
|
|
3090
|
+
if (d.warnings && d.warnings.length) {
|
|
3091
|
+
console.warn('merge warnings:', d.warnings);
|
|
3092
|
+
}
|
|
3093
|
+
closeModal('merge-modal');
|
|
3094
|
+
delete detailsCache[mergeSourcePath];
|
|
3095
|
+
delete detailsCache[target];
|
|
3096
|
+
loadProjects();
|
|
3097
|
+
} catch(e) {
|
|
3098
|
+
toast('Merge error: ' + e.message, 'err');
|
|
3099
|
+
} finally {
|
|
3100
|
+
btn.disabled = false;
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
|
|
2980
3104
|
// ── Settings Modal ─────────────────────────────────────────────────────────
|
|
2981
3105
|
async function openSettings() {
|
|
2982
3106
|
try {
|
|
@@ -3166,11 +3290,12 @@ function closeModal(id) {
|
|
|
3166
3290
|
if (id === 'update-modal') { modalPath = null; }
|
|
3167
3291
|
if (id === 'edit-modal') { editPath = null; }
|
|
3168
3292
|
if (id === 'transfer-modal') { transferPath = null; }
|
|
3293
|
+
if (id === 'merge-modal') { mergeSourcePath = null; mergeSourceName = ''; }
|
|
3169
3294
|
if (id === 'c3-setup-modal') { c3SetupPath = null; }
|
|
3170
3295
|
}
|
|
3171
3296
|
|
|
3172
3297
|
// Close modals on backdrop click
|
|
3173
|
-
['mcp-modal','edit-modal','transfer-modal','settings-modal','update-modal','batch-update-modal','ide-modal','c3-setup-modal'].forEach(id => {
|
|
3298
|
+
['mcp-modal','edit-modal','transfer-modal','merge-modal','settings-modal','update-modal','batch-update-modal','ide-modal','c3-setup-modal'].forEach(id => {
|
|
3174
3299
|
document.getElementById(id).addEventListener('click', e => {
|
|
3175
3300
|
if (e.target === e.currentTarget) closeModal(id);
|
|
3176
3301
|
});
|
cli/hub_server.py
CHANGED
|
@@ -699,6 +699,29 @@ def api_projects_transfer():
|
|
|
699
699
|
return jsonify({"error": str(e)}), 500
|
|
700
700
|
|
|
701
701
|
|
|
702
|
+
@app.route("/api/projects/merge", methods=["POST"])
|
|
703
|
+
def api_projects_merge():
|
|
704
|
+
"""Merge source project's memory/sessions/ledger into target.
|
|
705
|
+
|
|
706
|
+
Body: {source_path, target_path, cleanup: 'keep'|'clear'}
|
|
707
|
+
"""
|
|
708
|
+
data = request.get_json(force=True) or {}
|
|
709
|
+
src = (data.get("source_path") or "").strip()
|
|
710
|
+
tgt = (data.get("target_path") or "").strip()
|
|
711
|
+
cleanup = (data.get("cleanup") or "keep").strip().lower()
|
|
712
|
+
if not src or not tgt:
|
|
713
|
+
return jsonify({"error": "source_path and target_path are required"}), 400
|
|
714
|
+
if cleanup not in ("keep", "clear"):
|
|
715
|
+
return jsonify({"error": "cleanup must be 'keep' or 'clear'"}), 400
|
|
716
|
+
try:
|
|
717
|
+
result = _pm().merge_projects(src, tgt, cleanup=cleanup)
|
|
718
|
+
if result.get("error"):
|
|
719
|
+
return jsonify(result), 400
|
|
720
|
+
return jsonify(result)
|
|
721
|
+
except Exception as e:
|
|
722
|
+
return jsonify({"error": str(e)}), 500
|
|
723
|
+
|
|
724
|
+
|
|
702
725
|
@app.route("/api/projects/details", methods=["POST"])
|
|
703
726
|
def api_projects_details():
|
|
704
727
|
data = request.get_json(force=True) or {}
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: code-context-control
|
|
3
|
+
Version: 2.29.0
|
|
4
|
+
Summary: Local code-intelligence layer for AI coding tools (Claude Code, Codex, Gemini, Copilot). Retrieve less, read less, edit safer.
|
|
5
|
+
Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/drknowhow/code-context-control
|
|
8
|
+
Project-URL: Documentation, https://github.com/drknowhow/code-context-control#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/drknowhow/code-context-control
|
|
10
|
+
Project-URL: Changelog, https://github.com/drknowhow/code-context-control/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: Issues, https://github.com/drknowhow/code-context-control/issues
|
|
12
|
+
Keywords: claude,claude-code,mcp,ai,code-intelligence,code-context,developer-tools,llm-tools
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development
|
|
21
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: tiktoken>=0.7.0
|
|
27
|
+
Requires-Dist: fastmcp>=2.0.0
|
|
28
|
+
Requires-Dist: watchdog>=4.0.0
|
|
29
|
+
Requires-Dist: tree-sitter>=0.22.0
|
|
30
|
+
Requires-Dist: tree-sitter-python>=0.21.0
|
|
31
|
+
Requires-Dist: tree-sitter-javascript>=0.21.0
|
|
32
|
+
Requires-Dist: tree-sitter-typescript>=0.21.0
|
|
33
|
+
Requires-Dist: tree-sitter-html>=0.21.0
|
|
34
|
+
Requires-Dist: tree-sitter-markdown>=0.3.2
|
|
35
|
+
Requires-Dist: tree-sitter-css>=0.21.0
|
|
36
|
+
Requires-Dist: tree-sitter-go>=0.21.0
|
|
37
|
+
Requires-Dist: tree-sitter-rust>=0.21.0
|
|
38
|
+
Requires-Dist: tree-sitter-json>=0.21.0
|
|
39
|
+
Requires-Dist: tree-sitter-yaml>=0.6.0
|
|
40
|
+
Requires-Dist: flask>=3.0.0
|
|
41
|
+
Requires-Dist: rich>=13.0.0
|
|
42
|
+
Requires-Dist: click>=8.0.0
|
|
43
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
44
|
+
Provides-Extra: vector
|
|
45
|
+
Requires-Dist: scikit-learn>=1.3.0; extra == "vector"
|
|
46
|
+
Requires-Dist: numpy>=1.24.0; extra == "vector"
|
|
47
|
+
Requires-Dist: chromadb>=0.4.24; extra == "vector"
|
|
48
|
+
Provides-Extra: tui
|
|
49
|
+
Requires-Dist: textual>=0.50.0; extra == "tui"
|
|
50
|
+
Provides-Extra: telemetry
|
|
51
|
+
Requires-Dist: sentry-sdk>=2.0.0; extra == "telemetry"
|
|
52
|
+
Provides-Extra: dev
|
|
53
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
54
|
+
Requires-Dist: pyright>=1.1.350; extra == "dev"
|
|
55
|
+
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
56
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
57
|
+
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
58
|
+
Requires-Dist: numpy>=1.24.0; extra == "dev"
|
|
59
|
+
Requires-Dist: scikit-learn>=1.3.0; extra == "dev"
|
|
60
|
+
Dynamic: license-file
|
|
61
|
+
|
|
62
|
+
<h1 align="center">Code Context Control</h1>
|
|
63
|
+
|
|
64
|
+
<p align="center">
|
|
65
|
+
<strong>The local code-intelligence layer for AI coding tools.</strong><br>
|
|
66
|
+
Stop burning tokens on whole-file reads, blind greps, and unbounded log dumps.<br>
|
|
67
|
+
Works with Claude Code, Codex, Gemini CLI, Copilot, Cursor, and Antigravity.
|
|
68
|
+
</p>
|
|
69
|
+
|
|
70
|
+
<p align="center">
|
|
71
|
+
<a href="https://pypi.org/project/code-context-control/"><img alt="PyPI" src="https://img.shields.io/pypi/v/code-context-control?color=blue&logo=pypi&logoColor=white"></a>
|
|
72
|
+
<a href="LICENSE"><img alt="License: Apache-2.0" src="https://img.shields.io/badge/license-Apache--2.0-blue.svg"></a>
|
|
73
|
+
<img alt="Python 3.10+" src="https://img.shields.io/badge/python-3.10%2B-blue.svg">
|
|
74
|
+
<img alt="Platforms" src="https://img.shields.io/badge/platform-windows%20%7C%20macos%20%7C%20linux-lightgrey">
|
|
75
|
+
<img alt="Status: Beta" src="https://img.shields.io/badge/status-beta-yellow">
|
|
76
|
+
</p>
|
|
77
|
+
|
|
78
|
+
<p align="center">
|
|
79
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/ui_dashboard.png" alt="C3 per-project dashboard" width="900">
|
|
80
|
+
</p>
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## The problem
|
|
85
|
+
|
|
86
|
+
LLM-driven coding tools have one expensive failure mode: **they read too much.** They `cat` whole files, regex the entire repo, dump 10k-line logs into context, edit-and-pray, and burn through budget before they touch a single line of code. On a half-day session you can spend $20+ on token waste that adds zero value.
|
|
87
|
+
|
|
88
|
+
## What C3 does about it
|
|
89
|
+
|
|
90
|
+
A thin **local** layer that sits between your IDE and your repo. Every AI tool call gets routed through narrow, surgical operations instead of broad, wasteful ones:
|
|
91
|
+
|
|
92
|
+
| Without C3 | With C3 |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `Read` the whole 2,000-line file | `c3_compress` returns a 70%-smaller structural map → `c3_read(symbols=...)` for the exact function |
|
|
95
|
+
| `Grep` the whole repo blindly | `c3_search` returns ranked candidates with TF-IDF + symbol awareness |
|
|
96
|
+
| Dump full `pytest` output into the prompt | `c3_filter` distills 500 lines → 30 actionable ones |
|
|
97
|
+
| Edit, hope it compiled | `c3_edit` writes via a ledger + `c3_validate` runs `pyright`/`tsc` automatically |
|
|
98
|
+
| `Bash` test runs that hang on Windows | `c3_shell` returns structured `{exit_code, stdout, stderr, duration}` with auto-filter |
|
|
99
|
+
| Lose all context on `/clear` | `c3_session(snapshot)` + `c3_memory` persist decisions across sessions |
|
|
100
|
+
| Re-explain the project every session | Auto-synced `CLAUDE.md` / `AGENTS.md` / `GEMINI.md` / `copilot-instructions.md` from a single source of truth |
|
|
101
|
+
|
|
102
|
+
Everything runs **locally**. No source code, prompts, or model output ever leaves your machine unless you explicitly opt into a third-party model API.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Install
|
|
107
|
+
|
|
108
|
+
Requires Python 3.10+.
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pip install code-context-control
|
|
112
|
+
c3 init /path/to/your/project
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The interactive setup walks you through:
|
|
116
|
+
1. **IDE selection** (Claude Code CLI/App, Codex CLI, Gemini CLI, VS Code, Cursor, Antigravity, or Custom)
|
|
117
|
+
2. Optional local `git init`
|
|
118
|
+
3. MCP server registration (auto-wired into your IDE)
|
|
119
|
+
4. (Claude Code only) Permission tier selection
|
|
120
|
+
|
|
121
|
+
Headless / scripted install:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
c3 init /path/to/project --force --ide claude --mcp-mode direct --permissions standard
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Or install from source:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git clone https://github.com/drknowhow/code-context-control.git
|
|
131
|
+
cd code-context-control
|
|
132
|
+
pip install .[tui] # add the optional Textual TUI
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## A tour of the UI
|
|
138
|
+
|
|
139
|
+
C3 ships with two web UIs (no electron, no install — pure Flask + vanilla JS):
|
|
140
|
+
|
|
141
|
+
- **The Hub** (`c3-hub`, port 3330) — manage all your C3 projects from one dashboard.
|
|
142
|
+
- **The per-project UI** (`c3 ui`, per-project port) — deep dive into one project's session, memory, edits, instructions, and settings.
|
|
143
|
+
|
|
144
|
+
### 1. Project Hub — multi-project mission control
|
|
145
|
+
|
|
146
|
+
<p align="center">
|
|
147
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/hub_projects.png" alt="C3 Project Hub - all projects" width="900">
|
|
148
|
+
</p>
|
|
149
|
+
|
|
150
|
+
Every C3-initialized project on your machine appears here. Group them by tag, filter by active/idle, see which IDE each project uses, jump straight into your IDE with one click, and monitor session activity at a glance. Each project card shows live status, version, MCP wiring mode, port, and last activity.
|
|
151
|
+
|
|
152
|
+
Same data, grid layout:
|
|
153
|
+
|
|
154
|
+
<p align="center">
|
|
155
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/hub_projects_grid.png" alt="C3 Project Hub - grid view" width="900">
|
|
156
|
+
</p>
|
|
157
|
+
|
|
158
|
+
**Open in your IDE of choice** — C3 auto-detects which CLIs you have installed and gives you one-click launchers:
|
|
159
|
+
|
|
160
|
+
<p align="center">
|
|
161
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/hub_ide_config.png" alt="C3 Hub IDE picker" width="700">
|
|
162
|
+
</p>
|
|
163
|
+
|
|
164
|
+
Hub runs as a **background Windows service** if you want it to (no terminal, auto-starts on login):
|
|
165
|
+
|
|
166
|
+
<p align="center">
|
|
167
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/hub_settings.png" alt="C3 Hub settings" width="700">
|
|
168
|
+
</p>
|
|
169
|
+
|
|
170
|
+
### 2. Per-project dashboard — at-a-glance health
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
c3 ui # opens http://127.0.0.1:3333
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
<p align="center">
|
|
177
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/ui_dashboard.png" alt="C3 per-project dashboard" width="900">
|
|
178
|
+
</p>
|
|
179
|
+
|
|
180
|
+
Real metrics from a real project: **448K tokens saved** (89.9% rate), 208 files indexed, 20 sessions, codebase breakdown by language, current-session live counters (in/out tokens, cache reads, services online), and a stream of recent tool calls and file changes.
|
|
181
|
+
|
|
182
|
+
### 3. Edit Ledger — every AI-driven edit tracked
|
|
183
|
+
|
|
184
|
+
<p align="center">
|
|
185
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/ui_edits.png" alt="C3 Edit Ledger" width="900">
|
|
186
|
+
</p>
|
|
187
|
+
|
|
188
|
+
A complete audit trail of every file change made via `c3_edit` (or via Bash git commands intercepted by the C3 shell). Each entry shows timestamp, file, version number, change summary, and +/- line counts. Filter by file path, switch to **Stats** view for aggregate trends. Backed by a content-addressable store so any prior version is one click away.
|
|
189
|
+
|
|
190
|
+
### 4. Memory — durable knowledge across sessions
|
|
191
|
+
|
|
192
|
+
<p align="center">
|
|
193
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/ui_memory.png" alt="C3 Memory" width="900">
|
|
194
|
+
</p>
|
|
195
|
+
|
|
196
|
+
A categorized store of facts the AI has learned about your project (architecture decisions, conventions, gotchas, references). Categories, full-text + semantic search, decision tracking, list ↔ graph toggle, and one-click Markdown export. Backed by TF-IDF + optional Ollama embeddings + `chromadb` (when the `[vector]` extra is installed).
|
|
197
|
+
|
|
198
|
+
### 5. Sessions — history, decisions, costs
|
|
199
|
+
|
|
200
|
+
<p align="center">
|
|
201
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/ui_sessions.png" alt="C3 Sessions" width="900">
|
|
202
|
+
</p>
|
|
203
|
+
|
|
204
|
+
Every session you've ever run, with duration, decision count, file count, tool calls, token usage, and cost. Captured automatically via the IDE's Stop hook — nothing to remember to click. Click any row to see the full task list, decisions, and file diffs from that session.
|
|
205
|
+
|
|
206
|
+
### 6. Instructions — sync your project context across IDEs
|
|
207
|
+
|
|
208
|
+
<p align="center">
|
|
209
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/ui_instructions.png" alt="C3 Instructions" width="900">
|
|
210
|
+
</p>
|
|
211
|
+
|
|
212
|
+
Manage `CLAUDE.md`, `AGENTS.md` (Codex), `GEMINI.md`, and `.github/copilot-instructions.md` from one editor. Generate from project state, run a Health Check (drift detection vs the actual codebase), Compact stale sections, or Promote insights captured during sessions. **One source of truth** instead of four out-of-sync files.
|
|
213
|
+
|
|
214
|
+
### 7. Chat — browse prior AI conversations
|
|
215
|
+
|
|
216
|
+
<p align="center">
|
|
217
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/ui_chat.png" alt="C3 Chat" width="900">
|
|
218
|
+
</p>
|
|
219
|
+
|
|
220
|
+
C3 syncs and indexes your IDE's chat transcripts (currently Claude Code; others coming). Filter by source, search, click a row to view the full conversation. Useful for "wait, what did we decide about X last week?".
|
|
221
|
+
|
|
222
|
+
### 8. Settings — feature flags + integrations
|
|
223
|
+
|
|
224
|
+
<p align="center">
|
|
225
|
+
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/ui_settings.png" alt="C3 Settings" width="900">
|
|
226
|
+
</p>
|
|
227
|
+
|
|
228
|
+
Per-project knobs for everything: budget thresholds, feature flag mode, edit ledger, background agents, delegate routing, Codex/Gemini integrations, agent workflows, proxy mode, MCP servers, Claude Code permission tier, and more.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## The MCP tool suite
|
|
233
|
+
|
|
234
|
+
C3 exposes 14 tools as a native MCP server. Your IDE calls them directly:
|
|
235
|
+
|
|
236
|
+
| Tool | What it does |
|
|
237
|
+
|---|---|
|
|
238
|
+
| `c3_search` | TF-IDF / regex / semantic search ranked across the indexed repo |
|
|
239
|
+
| `c3_compress` | AST-based file map (modes: `map`, `dense_map`, `smart`, `diff`, `bug_scan`, `ast`) |
|
|
240
|
+
| `c3_read` | Surgical reads — by symbol name, regex, or line ranges |
|
|
241
|
+
| `c3_edit` | Atomic patch with automatic ledger logging + content-addressable history |
|
|
242
|
+
| `c3_validate` | Type / syntax check (pyright, tsc, ruff, etc. — auto-detected) |
|
|
243
|
+
| `c3_filter` | Distill long terminal/log output via pattern + LLM summarization |
|
|
244
|
+
| `c3_shell` | Run shell commands with structured returns + auto-filtered stdout |
|
|
245
|
+
| `c3_status` | Project health, token budget, notifications, ghost-file detection |
|
|
246
|
+
| `c3_memory` | Persistent fact store with categories, recall, and graph queries |
|
|
247
|
+
| `c3_session` | Snapshot, restore, log decisions, compact session history |
|
|
248
|
+
| `c3_impact` | Blast-radius analysis before edits to shared symbols |
|
|
249
|
+
| `c3_delegate` | Offload heavy work to local Ollama / Codex / Gemini / etc. |
|
|
250
|
+
| `c3_agent` | Multi-step agentic workflows (review, investigate, refactor) |
|
|
251
|
+
| `c3_edits` | Edit-ledger queries + version diffs + restore points |
|
|
252
|
+
|
|
253
|
+
Every tool is **read-only safe in plan mode** (except `c3_edit` and `c3_shell`).
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Tiered local AI (optional)
|
|
258
|
+
|
|
259
|
+
C3 ships with optional Ollama integration so the primary model doesn't have to waste context on grunt work:
|
|
260
|
+
|
|
261
|
+
| Tier | Model class | Used for | Latency target |
|
|
262
|
+
|---|---|---|---|
|
|
263
|
+
| **Nano** | `qwen2:0.5b` | Intent routing, classification | <100 ms |
|
|
264
|
+
| **Micro** | `deepseek-r1:1.5b` | Last-turn Q&A, summarization | <1 s |
|
|
265
|
+
| **Base** | `llama3.2:3b`+ | Code analysis, technical reasoning | <5 s |
|
|
266
|
+
|
|
267
|
+
```text
|
|
268
|
+
c3_delegate(task="summarize this 4k-line stacktrace", backend="ollama")
|
|
269
|
+
c3_delegate(task="rate-limit refactor", backend="auto") # picks the right tier
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Ollama is **fully optional**. C3 works without it.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Permissions (Claude Code)
|
|
277
|
+
|
|
278
|
+
C3 manages `.claude/settings.local.json` for you, with three sensible tiers:
|
|
279
|
+
|
|
280
|
+
| Tier | What it allows |
|
|
281
|
+
|---|---|
|
|
282
|
+
| `read-only` | Exploration only — no file writes, no git writes, no installs |
|
|
283
|
+
| `standard` | Normal dev workflow — edit, build, test, local git **(recommended)** |
|
|
284
|
+
| `permissive` | Full trust — everything except destructive ops |
|
|
285
|
+
|
|
286
|
+
All tiers always allow C3 MCP tools and include a hard deny list (`rm -rf`, `sudo`, `git push --force`, etc.).
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
c3 permissions show
|
|
290
|
+
c3 permissions standard
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
---
|
|
294
|
+
|
|
295
|
+
## Benchmarks
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
c3 benchmark /path/to/project
|
|
299
|
+
c3 bench aider # Aider Polyglot suite
|
|
300
|
+
c3 bench swe # SWE-bench Lite
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Real-world A/B tests: same task, with and without C3 mounted. Reports include token deltas, cost deltas, win rates, tool-usage analysis, and per-task breakdowns. See the **Benchmark Dashboard** under Settings → Background Agents in the Hub.
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
## Security & privacy
|
|
308
|
+
|
|
309
|
+
- **Hub binds to `127.0.0.1` by default.** Setting `host` to a non-loopback interface in `~/.c3/hub_config.json` is opt-in and warned at startup. **Do not expose the Hub to a public network without auth/TLS in front of it.**
|
|
310
|
+
- **No telemetry by default.** The OSS package collects nothing. Opt-in Sentry crash reporting requires the `[telemetry]` extra plus both `SENTRY_DSN` and `C3_TELEMETRY_OPT_IN=1`. Even when enabled, request bodies, local variables, and prompts are stripped before sending.
|
|
311
|
+
- **API keys** for third-party model providers are read from environment variables and never persisted by C3.
|
|
312
|
+
- See [`SECURITY.md`](SECURITY.md) for the full hardening guide and disclosure policy.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## License
|
|
317
|
+
|
|
318
|
+
- **Current OSS license** — Apache License 2.0 ([`LICENSE`](LICENSE)). Free for any use, including commercial. Modify, fork, redistribute — all permitted under Apache-2.0 terms.
|
|
319
|
+
- **Third-party deps** — see [`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md).
|
|
320
|
+
|
|
321
|
+
The author may introduce a paid offering or relicense future major versions; no commitment either way. Releases already published under Apache-2.0 (including all 2.x versions) keep their Apache-2.0 grant — that grant is irrevocable. Background and FAQ in [`LICENSING.md`](LICENSING.md). No warranty or support obligation; see LICENSE Sections 7–8.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## Links
|
|
326
|
+
|
|
327
|
+
- **PyPI:** https://pypi.org/project/code-context-control/
|
|
328
|
+
- **Changelog:** [`CHANGELOG.md`](CHANGELOG.md)
|
|
329
|
+
- **Security policy:** [`SECURITY.md`](SECURITY.md)
|
|
330
|
+
- **Licensing FAQ:** [`LICENSING.md`](LICENSING.md)
|
|
331
|
+
- **Issues:** https://github.com/drknowhow/code-context-control/issues
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
cli/__init__.py,sha256=ec66drCZGNMRU4V6ov0zVhYZph1us12Vn8OvG_LJyRY,22
|
|
2
2
|
cli/_hook_utils.py,sha256=1_hTA-Wz62xB8jnSAH4C5TfCkrwEP0g2kq_-oRfQLm4,3724
|
|
3
|
-
cli/c3.py,sha256=
|
|
3
|
+
cli/c3.py,sha256=2tgG0qDN6DF0xPt298c3y_DiHV4jvg93rAkdI2QjHCQ,279052
|
|
4
4
|
cli/docs.html,sha256=qlkXoSfnh8yz78LpkrhzsjTieZJuStlzdo9bktkyQSg,140621
|
|
5
5
|
cli/edits.html,sha256=UjAhoCmBmQ89cklGvJqzC6eyNP2tc8H6T-e01DVkLvE,43418
|
|
6
6
|
cli/hook_auto_snapshot.py,sha256=amtliVDzKUQr6KBR0pdBA8vXghAV-gKr19jBaJVnP_w,5006
|
|
@@ -14,8 +14,8 @@ cli/hook_pretool_enforce.py,sha256=Mo9b6SyjlCuwPnkbNSX0RDfNkmt5u_YeEfdY9Q79RKc,1
|
|
|
14
14
|
cli/hook_read.py,sha256=M5l_SU899O72tZe3j4YQJJKNb1-xulvKOj8XZjJzwYU,8021
|
|
15
15
|
cli/hook_session_stats.py,sha256=a1OKi9kmiXRI2qieY_Uq14xRxdXQTQu9WVzDTUlI0GQ,1897
|
|
16
16
|
cli/hook_terse_advisor.py,sha256=pD7Bap7OYOKqtYz7cX8nWSRLH7ook-tSD2Ov2MNp_sA,5907
|
|
17
|
-
cli/hub.html,sha256=
|
|
18
|
-
cli/hub_server.py,sha256=
|
|
17
|
+
cli/hub.html,sha256=Hl-XPZGT1mMiKrbX9c5OsEw6mXEumwIB3vp1WlWaplM,183966
|
|
18
|
+
cli/hub_server.py,sha256=gnUJdCgX5ZKZHwLKLYW5ki-P_9HH9Zyi_Hb_yoSKwSI,61979
|
|
19
19
|
cli/mcp_proxy.py,sha256=92htuT-p0j-cDTbyqlIJpGoQ85_Aw7UuB8L_Toi_u20,17511
|
|
20
20
|
cli/mcp_server.py,sha256=DsAPun5WIAmMkn9LISU9HMTCbJJGRaDXCCyRLbI0NP4,28286
|
|
21
21
|
cli/server.py,sha256=Or1emfUbBslscikFqfZ1-w8CRYfTdCSKJJshhBHfD3w,109665
|
|
@@ -54,7 +54,7 @@ cli/ui/components/memory.js,sha256=v5IsHTxLHpXX4xCsUaZ_UPprZEabdgP4jiWc298iV2U,2
|
|
|
54
54
|
cli/ui/components/sessions.js,sha256=FIKtil76B8tCkAmcFV7hlj6GQ_DCJK2jCzvEmdK7NBE,30837
|
|
55
55
|
cli/ui/components/settings.js,sha256=8LVTV2TQl9tcRXhXbtBEJOCBdiyk-x2QASoVYZUAuEA,71442
|
|
56
56
|
cli/ui/components/sidebar.js,sha256=cAY_jwYB-o1X_wWn__VXlG4IegVObuE3NmVsuFWqxtg,7417
|
|
57
|
-
code_context_control-2.
|
|
57
|
+
code_context_control-2.29.0.dist-info/licenses/LICENSE,sha256=l8Kh5QCNWNvR6kIt8L0BUZvc2LAFiHv2c-FnsGnUZf4,11301
|
|
58
58
|
core/__init__.py,sha256=TSDCEcM4V7gcZVM3w2ykJaqEUch4Dkon-rivV17T73s,2501
|
|
59
59
|
core/config.py,sha256=4zrJtooWn78JQMsuMWabfh7mCithCCQzVfRsHI7TMsQ,11768
|
|
60
60
|
core/ide.py,sha256=9LzsDVK2LL8RVpL40l6oNGiasZ3D8OCU_9i9A0gJKBo,6876
|
|
@@ -105,7 +105,7 @@ services/notifications.py,sha256=pyESc6vgV_bBQPppcR4TcjmoHsqIaFTU_J1bvjJbx5Q,865
|
|
|
105
105
|
services/ollama_client.py,sha256=UiC5Abca622-ac-4goCkAjwo7iUye9g9JGFMN6Ea08M,7983
|
|
106
106
|
services/output_filter.py,sha256=097HPxy0BM8fXePktb7sOf-Yfkx4qqHy0HurNRjhNDY,18635
|
|
107
107
|
services/parser.py,sha256=vv3B4Di1q4Sr2QFAxvsk5tsYNVcAEw8oAQMhXjLURDE,54011
|
|
108
|
-
services/project_manager.py,sha256
|
|
108
|
+
services/project_manager.py,sha256=zHT4Ll9fSD09M1v-PpeoVO0G0DueCm2lbtVc3GlxbjA,35304
|
|
109
109
|
services/protocol.py,sha256=gfvk4y-bEwGlXlOwBoyqKuADXe42NAKfdWl3BAzyVJc,11962
|
|
110
110
|
services/proxy_state.py,sha256=u5rd0k6CrOsywZA8FpRu_hMLwhR0TAJhZjy5MdWbCGc,6107
|
|
111
111
|
services/retrieval_broker.py,sha256=9X67VZ_6AkbAzopHuuMFKmP4CGZLnW576kjSKMenBnw,5261
|
|
@@ -143,8 +143,8 @@ tui/screens/search_view.py,sha256=MMHjVdlk3HZSuDBSvq8IGrqv_Mh5Us6YqXQ80bcWSMk,19
|
|
|
143
143
|
tui/screens/session_view.py,sha256=eZ1eDwHTvPOck1wCCviixtOaCxIkBT_95ytNNNriGNA,5991
|
|
144
144
|
tui/screens/stats.py,sha256=p81PjzdaIv7hllb8f45-rlVe4lJZwSdIMqu7e86_u5s,6223
|
|
145
145
|
tui/screens/ui_view.py,sha256=1QJCgLh2YfgWIpvzRG1KOGXYEaOYX6ojN61Azjf2oX0,2125
|
|
146
|
-
code_context_control-2.
|
|
147
|
-
code_context_control-2.
|
|
148
|
-
code_context_control-2.
|
|
149
|
-
code_context_control-2.
|
|
150
|
-
code_context_control-2.
|
|
146
|
+
code_context_control-2.29.0.dist-info/METADATA,sha256=aosiSsHji1aTcG5hPfeRJv0_S6DfyUwFhO-kNIeo53Y,16096
|
|
147
|
+
code_context_control-2.29.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
148
|
+
code_context_control-2.29.0.dist-info/entry_points.txt,sha256=7kX_WUsDCF2hbXzvbNyscyaBb9AeA-DJY5v_5hN0DlU,93
|
|
149
|
+
code_context_control-2.29.0.dist-info/top_level.txt,sha256=wRt41zBybVF3qAiNXHz9BURbkKvUvfhmWWtKMhaw6eE,29
|
|
150
|
+
code_context_control-2.29.0.dist-info/RECORD,,
|
services/project_manager.py
CHANGED
|
@@ -577,3 +577,247 @@ class ProjectManager:
|
|
|
577
577
|
|
|
578
578
|
self._write_projects(projects)
|
|
579
579
|
return {"transferred": True, "old_path": old_path, "new_path": new_path}
|
|
580
|
+
|
|
581
|
+
def merge_projects(self, source_path: str, target_path: str, cleanup: str = "keep") -> dict:
|
|
582
|
+
"""Merge source project's memory/sessions/ledger into target.
|
|
583
|
+
|
|
584
|
+
Combines facts (.c3/facts/facts.json), edit-ledger entries
|
|
585
|
+
(.c3/edit_ledger.jsonl), conversation sessions (.c3/conversations/),
|
|
586
|
+
and unions registry tags + appends notes. Skips file_memory and
|
|
587
|
+
indices because their paths reference source files that don't
|
|
588
|
+
exist in the target.
|
|
589
|
+
|
|
590
|
+
Args:
|
|
591
|
+
source_path: project being merged FROM.
|
|
592
|
+
target_path: project being merged INTO.
|
|
593
|
+
cleanup: "keep" leaves source untouched after the merge;
|
|
594
|
+
"clear" wipes source's .c3/, MCP configs and
|
|
595
|
+
instruction docs (equivalent to ``c3 init --clear``)
|
|
596
|
+
and removes its registry entry.
|
|
597
|
+
|
|
598
|
+
Returns:
|
|
599
|
+
``{"merged": True, "source", "target", "cleanup", "stats": {...}}``
|
|
600
|
+
on success or ``{"merged": False, "error": "..."}`` on validation
|
|
601
|
+
failure.
|
|
602
|
+
"""
|
|
603
|
+
import shutil
|
|
604
|
+
import uuid
|
|
605
|
+
|
|
606
|
+
if cleanup not in ("keep", "clear"):
|
|
607
|
+
return {"merged": False, "error": "cleanup must be 'keep' or 'clear'"}
|
|
608
|
+
|
|
609
|
+
src = Path(source_path).resolve()
|
|
610
|
+
tgt = Path(target_path).resolve()
|
|
611
|
+
|
|
612
|
+
if str(src) == str(tgt):
|
|
613
|
+
return {"merged": False, "error": "Paths are identical"}
|
|
614
|
+
if not src.is_dir():
|
|
615
|
+
return {"merged": False, "error": "Source path does not exist"}
|
|
616
|
+
if not tgt.is_dir():
|
|
617
|
+
return {"merged": False, "error": "Target path does not exist"}
|
|
618
|
+
if not (src / ".c3").is_dir():
|
|
619
|
+
return {"merged": False, "error": "Source has no .c3 directory"}
|
|
620
|
+
if not (tgt / ".c3").is_dir():
|
|
621
|
+
return {"merged": False, "error": "Target has no .c3 directory"}
|
|
622
|
+
|
|
623
|
+
projects = self._read_projects()
|
|
624
|
+
src_entry = next((p for p in projects if p.get("path") == str(src)), None)
|
|
625
|
+
tgt_entry = next((p for p in projects if p.get("path") == str(tgt)), None)
|
|
626
|
+
if src_entry is None:
|
|
627
|
+
return {"merged": False, "error": "Source project not registered"}
|
|
628
|
+
if tgt_entry is None:
|
|
629
|
+
return {"merged": False, "error": "Target project not registered"}
|
|
630
|
+
|
|
631
|
+
src_name = src_entry.get("name") or src.name
|
|
632
|
+
slug = "".join(c if c.isalnum() else "_" for c in src_name.lower())[:32] or "merged"
|
|
633
|
+
merge_tag = f"merged:{slug}"
|
|
634
|
+
merged_at = datetime.utcnow().isoformat() + "Z"
|
|
635
|
+
|
|
636
|
+
stats = {"facts": 0, "ledger_entries": 0, "sessions": 0}
|
|
637
|
+
warnings: list[str] = []
|
|
638
|
+
|
|
639
|
+
# ── Facts ────────────────────────────────────────────────────
|
|
640
|
+
src_facts_file = src / ".c3" / "facts" / "facts.json"
|
|
641
|
+
tgt_facts_dir = tgt / ".c3" / "facts"
|
|
642
|
+
tgt_facts_file = tgt_facts_dir / "facts.json"
|
|
643
|
+
|
|
644
|
+
if src_facts_file.exists():
|
|
645
|
+
try:
|
|
646
|
+
with open(src_facts_file, encoding="utf-8") as f:
|
|
647
|
+
src_facts = json.load(f) or []
|
|
648
|
+
except Exception as e:
|
|
649
|
+
src_facts = []
|
|
650
|
+
warnings.append(f"facts read failed: {e}")
|
|
651
|
+
|
|
652
|
+
if src_facts:
|
|
653
|
+
tgt_facts: list = []
|
|
654
|
+
if tgt_facts_file.exists():
|
|
655
|
+
try:
|
|
656
|
+
with open(tgt_facts_file, encoding="utf-8") as f:
|
|
657
|
+
tgt_facts = json.load(f) or []
|
|
658
|
+
except Exception as e:
|
|
659
|
+
warnings.append(f"target facts read failed: {e}")
|
|
660
|
+
tgt_facts = []
|
|
661
|
+
for fact in src_facts:
|
|
662
|
+
if not isinstance(fact, dict):
|
|
663
|
+
continue
|
|
664
|
+
new_fact = dict(fact)
|
|
665
|
+
new_id = uuid.uuid4().hex[:12]
|
|
666
|
+
new_fact["id"] = new_id
|
|
667
|
+
new_fact["vector_id"] = new_id
|
|
668
|
+
new_fact["merged_from"] = src_name
|
|
669
|
+
new_fact["merged_at"] = merged_at
|
|
670
|
+
tgt_facts.append(new_fact)
|
|
671
|
+
stats["facts"] += 1
|
|
672
|
+
tgt_facts_dir.mkdir(parents=True, exist_ok=True)
|
|
673
|
+
with open(tgt_facts_file, "w", encoding="utf-8") as f:
|
|
674
|
+
json.dump(tgt_facts, f, indent=2)
|
|
675
|
+
|
|
676
|
+
# ── Edit ledger ──────────────────────────────────────────────
|
|
677
|
+
src_ledger = src / ".c3" / "edit_ledger.jsonl"
|
|
678
|
+
tgt_ledger = tgt / ".c3" / "edit_ledger.jsonl"
|
|
679
|
+
if src_ledger.exists():
|
|
680
|
+
tgt_ledger.parent.mkdir(parents=True, exist_ok=True)
|
|
681
|
+
try:
|
|
682
|
+
with open(tgt_ledger, "a", encoding="utf-8") as out:
|
|
683
|
+
for line in src_ledger.read_text(encoding="utf-8").splitlines():
|
|
684
|
+
if not line.strip():
|
|
685
|
+
continue
|
|
686
|
+
try:
|
|
687
|
+
entry = json.loads(line)
|
|
688
|
+
except Exception:
|
|
689
|
+
continue
|
|
690
|
+
summary = entry.get("summary") or ""
|
|
691
|
+
entry["summary"] = f"[merged from {src_name}] {summary}".rstrip()
|
|
692
|
+
tags = list(entry.get("tags") or [])
|
|
693
|
+
if merge_tag not in tags:
|
|
694
|
+
tags.append(merge_tag)
|
|
695
|
+
entry["tags"] = tags
|
|
696
|
+
entry["merged_from"] = src_name
|
|
697
|
+
out.write(json.dumps(entry) + "\n")
|
|
698
|
+
stats["ledger_entries"] += 1
|
|
699
|
+
except Exception as e:
|
|
700
|
+
warnings.append(f"ledger merge failed: {e}")
|
|
701
|
+
|
|
702
|
+
# ── Conversations ────────────────────────────────────────────
|
|
703
|
+
src_conv = src / ".c3" / "conversations"
|
|
704
|
+
tgt_conv = tgt / ".c3" / "conversations"
|
|
705
|
+
if src_conv.is_dir():
|
|
706
|
+
tgt_conv.mkdir(parents=True, exist_ok=True)
|
|
707
|
+
renamed: dict[str, str] = {} # old_id -> new_id
|
|
708
|
+
|
|
709
|
+
# Load target sessions index up-front so we know what IDs collide.
|
|
710
|
+
tgt_sessions: list = []
|
|
711
|
+
tgt_sessions_file = tgt_conv / "sessions.json"
|
|
712
|
+
if tgt_sessions_file.exists():
|
|
713
|
+
try:
|
|
714
|
+
with open(tgt_sessions_file, encoding="utf-8") as f:
|
|
715
|
+
tgt_sessions = json.load(f) or []
|
|
716
|
+
except Exception as e:
|
|
717
|
+
warnings.append(f"target sessions read failed: {e}")
|
|
718
|
+
tgt_sessions = []
|
|
719
|
+
tgt_ids = {s.get("session_id") for s in tgt_sessions if s.get("session_id")}
|
|
720
|
+
|
|
721
|
+
# Copy turn files (rename on collision).
|
|
722
|
+
for entry_path in src_conv.iterdir():
|
|
723
|
+
if not entry_path.is_file():
|
|
724
|
+
continue
|
|
725
|
+
if entry_path.name == "sessions.json":
|
|
726
|
+
continue
|
|
727
|
+
if entry_path.name.endswith(".jsonl.gz"):
|
|
728
|
+
base = entry_path.name[:-len(".jsonl.gz")]
|
|
729
|
+
ext = ".jsonl.gz"
|
|
730
|
+
elif entry_path.suffix == ".jsonl":
|
|
731
|
+
base = entry_path.stem
|
|
732
|
+
ext = ".jsonl"
|
|
733
|
+
else:
|
|
734
|
+
continue
|
|
735
|
+
new_base = base
|
|
736
|
+
if base in tgt_ids or (tgt_conv / (base + ext)).exists():
|
|
737
|
+
new_base = f"{base}_merged_{uuid.uuid4().hex[:6]}"
|
|
738
|
+
renamed[base] = new_base
|
|
739
|
+
try:
|
|
740
|
+
shutil.copy2(entry_path, tgt_conv / (new_base + ext))
|
|
741
|
+
except Exception as e:
|
|
742
|
+
warnings.append(f"session copy {entry_path.name} failed: {e}")
|
|
743
|
+
|
|
744
|
+
# Merge sessions index.
|
|
745
|
+
src_sessions_file = src_conv / "sessions.json"
|
|
746
|
+
if src_sessions_file.exists():
|
|
747
|
+
try:
|
|
748
|
+
with open(src_sessions_file, encoding="utf-8") as f:
|
|
749
|
+
src_sessions = json.load(f) or []
|
|
750
|
+
except Exception as e:
|
|
751
|
+
src_sessions = []
|
|
752
|
+
warnings.append(f"source sessions read failed: {e}")
|
|
753
|
+
for s in src_sessions:
|
|
754
|
+
if not isinstance(s, dict):
|
|
755
|
+
continue
|
|
756
|
+
new_s = dict(s)
|
|
757
|
+
sid = new_s.get("session_id", "")
|
|
758
|
+
if sid in renamed:
|
|
759
|
+
new_s["session_id"] = renamed[sid]
|
|
760
|
+
elif sid and sid in tgt_ids:
|
|
761
|
+
new_s["session_id"] = f"{sid}_merged_{uuid.uuid4().hex[:6]}"
|
|
762
|
+
new_s["merged_from"] = src_name
|
|
763
|
+
new_s["merged_at"] = merged_at
|
|
764
|
+
tgt_sessions.append(new_s)
|
|
765
|
+
stats["sessions"] += 1
|
|
766
|
+
with open(tgt_sessions_file, "w", encoding="utf-8") as f:
|
|
767
|
+
json.dump(tgt_sessions, f, ensure_ascii=False, indent=2)
|
|
768
|
+
else:
|
|
769
|
+
# No index in source — count copied turn files as sessions.
|
|
770
|
+
stats["sessions"] = len(list(tgt_conv.iterdir())) - (1 if tgt_sessions_file.exists() else 0)
|
|
771
|
+
|
|
772
|
+
# ── Registry tags + notes ────────────────────────────────────
|
|
773
|
+
for p in projects:
|
|
774
|
+
if p.get("path") == str(tgt):
|
|
775
|
+
tags = list(p.get("tags") or [])
|
|
776
|
+
for t in (src_entry.get("tags") or []):
|
|
777
|
+
if t and t not in tags:
|
|
778
|
+
tags.append(t)
|
|
779
|
+
p["tags"] = tags
|
|
780
|
+
src_notes = (src_entry.get("notes") or "").strip()
|
|
781
|
+
if src_notes:
|
|
782
|
+
tgt_notes = (p.get("notes") or "").strip()
|
|
783
|
+
sep = f"--- merged from {src_name} ---"
|
|
784
|
+
p["notes"] = f"{tgt_notes}\n\n{sep}\n{src_notes}".strip() if tgt_notes else f"{sep}\n{src_notes}"
|
|
785
|
+
break
|
|
786
|
+
self._write_projects(projects)
|
|
787
|
+
|
|
788
|
+
# ── Cleanup ──────────────────────────────────────────────────
|
|
789
|
+
if cleanup == "clear":
|
|
790
|
+
try:
|
|
791
|
+
# Lazy import to avoid services -> cli circular import at module load.
|
|
792
|
+
from cli.c3 import _instruction_documents_for_project, _uninstall_mcp_all
|
|
793
|
+
try:
|
|
794
|
+
_uninstall_mcp_all(str(src))
|
|
795
|
+
except Exception as e:
|
|
796
|
+
warnings.append(f"uninstall_mcp failed: {e}")
|
|
797
|
+
c3_dir = src / ".c3"
|
|
798
|
+
if c3_dir.exists():
|
|
799
|
+
try:
|
|
800
|
+
shutil.rmtree(c3_dir)
|
|
801
|
+
except Exception as e:
|
|
802
|
+
warnings.append(f"rmtree .c3 failed: {e}")
|
|
803
|
+
for filename, _ in _instruction_documents_for_project():
|
|
804
|
+
doc = src / filename
|
|
805
|
+
if doc.exists():
|
|
806
|
+
try:
|
|
807
|
+
doc.unlink()
|
|
808
|
+
except Exception as e:
|
|
809
|
+
warnings.append(f"delete {filename} failed: {e}")
|
|
810
|
+
except Exception as e:
|
|
811
|
+
warnings.append(f"cleanup helpers unavailable: {e}")
|
|
812
|
+
self.remove_project(str(src))
|
|
813
|
+
|
|
814
|
+
result = {
|
|
815
|
+
"merged": True,
|
|
816
|
+
"source": str(src),
|
|
817
|
+
"target": str(tgt),
|
|
818
|
+
"cleanup": cleanup,
|
|
819
|
+
"stats": stats,
|
|
820
|
+
}
|
|
821
|
+
if warnings:
|
|
822
|
+
result["warnings"] = warnings
|
|
823
|
+
return result
|
|
@@ -1,249 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: code-context-control
|
|
3
|
-
Version: 2.28.2
|
|
4
|
-
Summary: Local code-intelligence layer for AI coding tools (Claude Code, Codex, Gemini, Copilot). Retrieve less, read less, edit safer.
|
|
5
|
-
Author-email: Dimitri Tselenchuk <dtselenc@gmail.com>
|
|
6
|
-
License-Expression: Apache-2.0
|
|
7
|
-
Project-URL: Homepage, https://github.com/drknowhow/code-context-control
|
|
8
|
-
Project-URL: Documentation, https://github.com/drknowhow/code-context-control#readme
|
|
9
|
-
Project-URL: Repository, https://github.com/drknowhow/code-context-control
|
|
10
|
-
Project-URL: Changelog, https://github.com/drknowhow/code-context-control/blob/main/CHANGELOG.md
|
|
11
|
-
Project-URL: Issues, https://github.com/drknowhow/code-context-control/issues
|
|
12
|
-
Keywords: claude,claude-code,mcp,ai,code-intelligence,code-context,developer-tools,llm-tools
|
|
13
|
-
Classifier: Development Status :: 4 - Beta
|
|
14
|
-
Classifier: Intended Audience :: Developers
|
|
15
|
-
Classifier: Operating System :: OS Independent
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
-
Classifier: Topic :: Software Development
|
|
21
|
-
Classifier: Topic :: Software Development :: Code Generators
|
|
22
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
-
Requires-Python: >=3.10
|
|
24
|
-
Description-Content-Type: text/markdown
|
|
25
|
-
License-File: LICENSE
|
|
26
|
-
Requires-Dist: tiktoken>=0.7.0
|
|
27
|
-
Requires-Dist: fastmcp>=2.0.0
|
|
28
|
-
Requires-Dist: watchdog>=4.0.0
|
|
29
|
-
Requires-Dist: tree-sitter>=0.22.0
|
|
30
|
-
Requires-Dist: tree-sitter-python>=0.21.0
|
|
31
|
-
Requires-Dist: tree-sitter-javascript>=0.21.0
|
|
32
|
-
Requires-Dist: tree-sitter-typescript>=0.21.0
|
|
33
|
-
Requires-Dist: tree-sitter-html>=0.21.0
|
|
34
|
-
Requires-Dist: tree-sitter-markdown>=0.3.2
|
|
35
|
-
Requires-Dist: tree-sitter-css>=0.21.0
|
|
36
|
-
Requires-Dist: tree-sitter-go>=0.21.0
|
|
37
|
-
Requires-Dist: tree-sitter-rust>=0.21.0
|
|
38
|
-
Requires-Dist: tree-sitter-json>=0.21.0
|
|
39
|
-
Requires-Dist: tree-sitter-yaml>=0.6.0
|
|
40
|
-
Requires-Dist: flask>=3.0.0
|
|
41
|
-
Requires-Dist: rich>=13.0.0
|
|
42
|
-
Requires-Dist: click>=8.0.0
|
|
43
|
-
Requires-Dist: pyyaml>=6.0.0
|
|
44
|
-
Provides-Extra: vector
|
|
45
|
-
Requires-Dist: scikit-learn>=1.3.0; extra == "vector"
|
|
46
|
-
Requires-Dist: numpy>=1.24.0; extra == "vector"
|
|
47
|
-
Requires-Dist: chromadb>=0.4.24; extra == "vector"
|
|
48
|
-
Provides-Extra: tui
|
|
49
|
-
Requires-Dist: textual>=0.50.0; extra == "tui"
|
|
50
|
-
Provides-Extra: telemetry
|
|
51
|
-
Requires-Dist: sentry-sdk>=2.0.0; extra == "telemetry"
|
|
52
|
-
Provides-Extra: dev
|
|
53
|
-
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
54
|
-
Requires-Dist: pyright>=1.1.350; extra == "dev"
|
|
55
|
-
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
56
|
-
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
57
|
-
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
58
|
-
Requires-Dist: numpy>=1.24.0; extra == "dev"
|
|
59
|
-
Requires-Dist: scikit-learn>=1.3.0; extra == "dev"
|
|
60
|
-
Dynamic: license-file
|
|
61
|
-
|
|
62
|
-
<h1 align="center">Code Context Control (C3)</h1>
|
|
63
|
-
|
|
64
|
-
<p align="center">
|
|
65
|
-
<strong>The local code-intelligence layer for AI coding tools.</strong><br>
|
|
66
|
-
Stop burning tokens on whole-file reads, blind greps, and unbounded log dumps.<br>
|
|
67
|
-
Works with Claude Code, Codex, Gemini CLI, and VS Code Copilot.
|
|
68
|
-
</p>
|
|
69
|
-
|
|
70
|
-
<p align="center">
|
|
71
|
-
<a href="LICENSE"><img alt="License: Apache-2.0" src="https://img.shields.io/badge/license-Apache--2.0-blue.svg"></a>
|
|
72
|
-
<img alt="Python 3.10+" src="https://img.shields.io/badge/python-3.10%2B-blue.svg">
|
|
73
|
-
<img alt="Platforms" src="https://img.shields.io/badge/platform-windows%20%7C%20macos%20%7C%20linux-lightgrey">
|
|
74
|
-
<img alt="Status: Beta" src="https://img.shields.io/badge/status-beta-yellow">
|
|
75
|
-
</p>
|
|
76
|
-
|
|
77
|
-
<p align="center">
|
|
78
|
-
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/c3_ui.png" alt="C3 web dashboard" width="820">
|
|
79
|
-
</p>
|
|
80
|
-
|
|
81
|
-
---
|
|
82
|
-
|
|
83
|
-
## Why C3?
|
|
84
|
-
|
|
85
|
-
LLM-driven coding tools have one expensive failure mode: they read too much. They `cat` whole files, regex the entire repo, dump 10k-line logs into context, and burn through budget before they touch a single line of code.
|
|
86
|
-
|
|
87
|
-
**C3 is a thin, local layer that sits between your IDE and your repo and forces the model to read like a senior engineer:**
|
|
88
|
-
|
|
89
|
-
| Without C3 | With C3 |
|
|
90
|
-
|---|---|
|
|
91
|
-
| `Read` whole 2k-line file | `c3_compress` → 70% smaller map, then `c3_read(symbols=…)` for the exact function |
|
|
92
|
-
| `Grep` the whole repo blindly | `c3_search` returns ranked candidates with TF-IDF + symbol awareness |
|
|
93
|
-
| Dump full `pytest` output into the prompt | `c3_filter` distills 500 lines → 30 actionable ones |
|
|
94
|
-
| Edit, hope it compiled | `c3_edit` writes via a ledger + `c3_validate` runs pyright/tsc automatically |
|
|
95
|
-
| `Bash` test runs that hang on Windows | `c3_shell` returns structured `{exit_code, stdout, stderr, duration}` with auto-filter |
|
|
96
|
-
| Lose context on `/clear` | `c3_session(snapshot)` + `c3_memory` persist decisions across sessions |
|
|
97
|
-
|
|
98
|
-
Everything runs **locally**. No source code or prompts ever leave your machine unless you explicitly opt into a third-party model API.
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## Install
|
|
103
|
-
|
|
104
|
-
Requires Python 3.10+.
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
# Clone and install
|
|
108
|
-
git clone https://github.com/drknowhow/code-context-control.git
|
|
109
|
-
cd code-context-control
|
|
110
|
-
pip install .
|
|
111
|
-
|
|
112
|
-
# Initialize a project (interactive)
|
|
113
|
-
c3 init /path/to/your/project
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
This walks you through:
|
|
117
|
-
1. IDE selection (Claude Code / Codex / Gemini / VS Code).
|
|
118
|
-
2. Optional local `git init`.
|
|
119
|
-
3. MCP server registration.
|
|
120
|
-
4. (Claude Code only) Permission tier selection.
|
|
121
|
-
|
|
122
|
-
Headless / scripted install:
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
c3 init /path/to/project --force --ide claude --mcp-mode direct --permissions standard
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
## What you get
|
|
131
|
-
|
|
132
|
-
### Surgical reads
|
|
133
|
-
```text
|
|
134
|
-
c3_compress(file_path="services/router.py", mode="map") # structural outline (~30% tokens)
|
|
135
|
-
c3_read(file_path="services/router.py", symbols=["ModelRouter._classify_features"])
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Ranked search
|
|
139
|
-
```text
|
|
140
|
-
c3_search(query="prompt cache hit rate", action="code", top_k=5)
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Safer edits + automatic validation
|
|
144
|
-
```text
|
|
145
|
-
c3_edit(file_path=..., old_string=..., new_string=..., summary="fix off-by-one")
|
|
146
|
-
c3_validate(file_path=...) # pyright/tsc if installed; syntax check otherwise
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Distilled output
|
|
150
|
-
```text
|
|
151
|
-
c3_filter(text=pytest_output) # 500 lines → 30 actionable
|
|
152
|
-
c3_shell(cmd="pytest -x") # structured, auto-filtered
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Persistent memory
|
|
156
|
-
```text
|
|
157
|
-
c3_memory(action="recall", query="why did we switch to direct MCP mode")
|
|
158
|
-
c3_session(action="snapshot") # before /clear
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### A web dashboard for everything you've done
|
|
162
|
-
<p align="center">
|
|
163
|
-
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/c3_hub.png" alt="C3 Project Hub" width="820">
|
|
164
|
-
</p>
|
|
165
|
-
|
|
166
|
-
```bash
|
|
167
|
-
c3-hub # http://127.0.0.1:3330
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
The Hub gives you per-project edit ledgers, session history, memory graphs, benchmark dashboards, and IDE/MCP profile management.
|
|
171
|
-
|
|
172
|
-
<p align="center">
|
|
173
|
-
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/c3_hub_notifications.png" alt="C3 Notifications" width="400">
|
|
174
|
-
<img src="https://raw.githubusercontent.com/drknowhow/code-context-control/main/docs/screenshots/c3_hub_ide_modal.png" alt="C3 IDE config" width="400">
|
|
175
|
-
</p>
|
|
176
|
-
|
|
177
|
-
---
|
|
178
|
-
|
|
179
|
-
## Tiered local AI (optional)
|
|
180
|
-
|
|
181
|
-
C3 ships with optional Ollama integration for offload work the primary model shouldn't waste context on:
|
|
182
|
-
|
|
183
|
-
| Tier | Model class | Used for |
|
|
184
|
-
|---|---|---|
|
|
185
|
-
| **Nano** | `qwen2:0.5b` | Sub-100 ms intent routing |
|
|
186
|
-
| **Micro** | `deepseek-r1:1.5b` | Last-turn Q&A, summarization |
|
|
187
|
-
| **Base** | `llama3.2:3b`+ | Code analysis, technical reasoning |
|
|
188
|
-
|
|
189
|
-
```text
|
|
190
|
-
c3_delegate(task="summarize this 4k-line stacktrace", backend="ollama")
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
Ollama is **optional**. C3 works fine without it.
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
## Permissions (Claude Code only)
|
|
198
|
-
|
|
199
|
-
C3 can manage Claude Code permission tiers via `.claude/settings.local.json`:
|
|
200
|
-
|
|
201
|
-
| Tier | What it allows |
|
|
202
|
-
|---|---|
|
|
203
|
-
| `read-only` | Exploration — no file writes, no git writes, no installs |
|
|
204
|
-
| `standard` | Normal dev workflow — edit, build, test, local git **(recommended)** |
|
|
205
|
-
| `permissive` | Full trust — everything except destructive ops |
|
|
206
|
-
|
|
207
|
-
All tiers always allow C3 MCP tools and include a deny list that blocks `rm -rf`, `sudo`, `git push --force`, etc.
|
|
208
|
-
|
|
209
|
-
```bash
|
|
210
|
-
c3 permissions show
|
|
211
|
-
c3 permissions standard
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
---
|
|
215
|
-
|
|
216
|
-
## Benchmarks
|
|
217
|
-
|
|
218
|
-
```bash
|
|
219
|
-
c3 benchmark /path/to/project
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
Runs C3 against open task suites (Aider Polyglot, SWE-bench Lite) with local-Ollama delegate measurement included.
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
|
-
## Security & Privacy
|
|
227
|
-
|
|
228
|
-
C3 runs locally. The Hub binds to `127.0.0.1` by default. **Do not expose the Hub to a public network without putting auth/TLS in front of it.**
|
|
229
|
-
|
|
230
|
-
**No telemetry by default.** Opt-in Sentry crash reporting is available via the `[telemetry]` extra and requires both `SENTRY_DSN` (your project) and `C3_TELEMETRY_OPT_IN=1` to activate. Even when enabled, request bodies, local variables, and prompts are stripped before sending.
|
|
231
|
-
|
|
232
|
-
See [`SECURITY.md`](SECURITY.md) for vulnerability reports, the full hardening guide, and the telemetry scrubbing details.
|
|
233
|
-
|
|
234
|
-
---
|
|
235
|
-
|
|
236
|
-
## License
|
|
237
|
-
|
|
238
|
-
- **Current OSS license** — Apache License 2.0 ([`LICENSE`](LICENSE)). Free for any use, including commercial. Modify, fork, redistribute — all permitted under Apache-2.0 terms.
|
|
239
|
-
- **Third-party deps** — see [`THIRD_PARTY_LICENSES.md`](THIRD_PARTY_LICENSES.md).
|
|
240
|
-
|
|
241
|
-
The author may introduce a paid offering or relicense future major versions; no commitment either way. Releases already published under Apache-2.0 (including all 2.x versions) keep their Apache-2.0 grant — that grant is irrevocable. Background and FAQ in [`LICENSING.md`](LICENSING.md). No warranty or support obligation; see LICENSE Sections 7–8.
|
|
242
|
-
|
|
243
|
-
---
|
|
244
|
-
|
|
245
|
-
## Links
|
|
246
|
-
|
|
247
|
-
- **Changelog:** [`CHANGELOG.md`](CHANGELOG.md)
|
|
248
|
-
- **Security policy:** [`SECURITY.md`](SECURITY.md)
|
|
249
|
-
- **Issues / questions:** open a GitHub issue or email `dtselenc@gmail.com`
|
|
File without changes
|
{code_context_control-2.28.2.dist-info → code_context_control-2.29.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{code_context_control-2.28.2.dist-info → code_context_control-2.29.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|
{code_context_control-2.28.2.dist-info → code_context_control-2.29.0.dist-info}/top_level.txt
RENAMED
|
File without changes
|