code-context-control 2.28.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.
Files changed (150) hide show
  1. cli/__init__.py +1 -0
  2. cli/_hook_utils.py +99 -0
  3. cli/c3.py +6152 -0
  4. cli/commands/__init__.py +1 -0
  5. cli/commands/common.py +312 -0
  6. cli/commands/parser.py +286 -0
  7. cli/docs.html +3178 -0
  8. cli/edits.html +878 -0
  9. cli/hook_auto_snapshot.py +142 -0
  10. cli/hook_c3_signal.py +61 -0
  11. cli/hook_c3read.py +116 -0
  12. cli/hook_edit_ledger.py +213 -0
  13. cli/hook_edit_unlock.py +170 -0
  14. cli/hook_filter.py +130 -0
  15. cli/hook_ghost_files.py +238 -0
  16. cli/hook_pretool_enforce.py +334 -0
  17. cli/hook_read.py +200 -0
  18. cli/hook_session_stats.py +62 -0
  19. cli/hook_terse_advisor.py +190 -0
  20. cli/hub.html +3764 -0
  21. cli/hub_server.py +1619 -0
  22. cli/mcp_proxy.py +428 -0
  23. cli/mcp_server.py +660 -0
  24. cli/server.py +2985 -0
  25. cli/tools/__init__.py +4 -0
  26. cli/tools/_helpers.py +65 -0
  27. cli/tools/agent.py +1165 -0
  28. cli/tools/compress.py +215 -0
  29. cli/tools/delegate.py +1184 -0
  30. cli/tools/edit.py +313 -0
  31. cli/tools/edits.py +118 -0
  32. cli/tools/filter.py +285 -0
  33. cli/tools/impact.py +163 -0
  34. cli/tools/memory.py +469 -0
  35. cli/tools/read.py +224 -0
  36. cli/tools/search.py +337 -0
  37. cli/tools/session.py +95 -0
  38. cli/tools/shell.py +193 -0
  39. cli/tools/status.py +306 -0
  40. cli/tools/validate.py +310 -0
  41. cli/ui/api.js +36 -0
  42. cli/ui/app.js +207 -0
  43. cli/ui/components/chat.js +758 -0
  44. cli/ui/components/dashboard.js +689 -0
  45. cli/ui/components/edits.js +220 -0
  46. cli/ui/components/instructions.js +481 -0
  47. cli/ui/components/memory.js +626 -0
  48. cli/ui/components/sessions.js +606 -0
  49. cli/ui/components/settings.js +1404 -0
  50. cli/ui/components/sidebar.js +156 -0
  51. cli/ui/icons.js +51 -0
  52. cli/ui/shared.js +119 -0
  53. cli/ui/theme.js +22 -0
  54. cli/ui.html +168 -0
  55. cli/ui_legacy.html +6797 -0
  56. cli/ui_nano.html +503 -0
  57. code_context_control-2.28.0.dist-info/METADATA +248 -0
  58. code_context_control-2.28.0.dist-info/RECORD +150 -0
  59. code_context_control-2.28.0.dist-info/WHEEL +5 -0
  60. code_context_control-2.28.0.dist-info/entry_points.txt +4 -0
  61. code_context_control-2.28.0.dist-info/licenses/LICENSE +201 -0
  62. code_context_control-2.28.0.dist-info/top_level.txt +5 -0
  63. core/__init__.py +75 -0
  64. core/config.py +269 -0
  65. core/ide.py +188 -0
  66. oracle/__init__.py +1 -0
  67. oracle/config.py +75 -0
  68. oracle/oracle.html +3900 -0
  69. oracle/oracle_server.py +663 -0
  70. oracle/services/__init__.py +1 -0
  71. oracle/services/c3_bridge.py +210 -0
  72. oracle/services/chat_engine.py +1103 -0
  73. oracle/services/chat_store.py +155 -0
  74. oracle/services/cross_memory.py +154 -0
  75. oracle/services/federated_graph.py +463 -0
  76. oracle/services/health_checker.py +117 -0
  77. oracle/services/insight_engine.py +307 -0
  78. oracle/services/memory_reader.py +106 -0
  79. oracle/services/memory_writer.py +182 -0
  80. oracle/services/ollama_bridge.py +332 -0
  81. oracle/services/project_scanner.py +87 -0
  82. oracle/services/review_agent.py +206 -0
  83. services/__init__.py +1 -0
  84. services/activity_log.py +93 -0
  85. services/agent_base.py +124 -0
  86. services/agents.py +1529 -0
  87. services/auto_memory.py +407 -0
  88. services/bench/__init__.py +6 -0
  89. services/bench/external/__init__.py +29 -0
  90. services/bench/external/aider_polyglot.py +405 -0
  91. services/bench/external/swe_bench.py +485 -0
  92. services/benchmark_dashboard.py +596 -0
  93. services/claude_md.py +785 -0
  94. services/compressor.py +592 -0
  95. services/context_snapshot.py +356 -0
  96. services/conversation_store.py +870 -0
  97. services/doc_index.py +537 -0
  98. services/e2e_benchmark.py +2884 -0
  99. services/e2e_evaluator.py +396 -0
  100. services/e2e_tasks.py +743 -0
  101. services/edit_ledger.py +459 -0
  102. services/embedding_index.py +341 -0
  103. services/error_reporting.py +123 -0
  104. services/file_memory.py +734 -0
  105. services/hub_service.py +585 -0
  106. services/indexer.py +712 -0
  107. services/memory.py +318 -0
  108. services/memory_consolidator.py +538 -0
  109. services/memory_graph.py +382 -0
  110. services/memory_grounder.py +304 -0
  111. services/memory_scorer.py +246 -0
  112. services/metrics.py +86 -0
  113. services/notifications.py +209 -0
  114. services/ollama_client.py +201 -0
  115. services/output_filter.py +488 -0
  116. services/parser.py +1238 -0
  117. services/project_manager.py +579 -0
  118. services/protocol.py +306 -0
  119. services/proxy_state.py +152 -0
  120. services/retrieval_broker.py +129 -0
  121. services/router.py +414 -0
  122. services/runtime.py +326 -0
  123. services/session_benchmark.py +1945 -0
  124. services/session_manager.py +1026 -0
  125. services/session_preloader.py +251 -0
  126. services/text_index.py +90 -0
  127. services/tool_classifier.py +176 -0
  128. services/transcript_index.py +340 -0
  129. services/validation_cache.py +155 -0
  130. services/vector_store.py +299 -0
  131. services/version_tracker.py +271 -0
  132. services/watcher.py +192 -0
  133. tui/__init__.py +0 -0
  134. tui/backend.py +59 -0
  135. tui/main.py +145 -0
  136. tui/screens/__init__.py +1 -0
  137. tui/screens/benchmark_view.py +109 -0
  138. tui/screens/claudemd_view.py +46 -0
  139. tui/screens/compress_view.py +52 -0
  140. tui/screens/index_view.py +74 -0
  141. tui/screens/init_view.py +82 -0
  142. tui/screens/mcp_view.py +73 -0
  143. tui/screens/optimize_view.py +41 -0
  144. tui/screens/pipe_view.py +46 -0
  145. tui/screens/projects_view.py +355 -0
  146. tui/screens/search_view.py +55 -0
  147. tui/screens/session_view.py +143 -0
  148. tui/screens/stats.py +158 -0
  149. tui/screens/ui_view.py +54 -0
  150. tui/theme.tcss +335 -0
cli/tools/compress.py ADDED
@@ -0,0 +1,215 @@
1
+ """c3_compress — Token-efficient file summaries (6 modes: map, dense_map, smart, diff, bug_scan, ast).
2
+ Supports comma-separated paths for batch compression with parallel execution."""
3
+
4
+ import json
5
+ import shutil
6
+ import subprocess
7
+ import sys
8
+ from concurrent.futures import ThreadPoolExecutor, as_completed
9
+ from pathlib import Path
10
+
11
+ from core import count_tokens
12
+
13
+
14
+ def _run_memory_mcp_cli(args: list, cwd: str, timeout: int = 30) -> tuple:
15
+ """Run codebase-memory-mcp CLI and return (success, output_or_error)."""
16
+ binary = shutil.which("codebase-memory-mcp")
17
+ if not binary:
18
+ return False, "not_installed"
19
+ kwargs: dict = {}
20
+ if sys.platform == "win32":
21
+ kwargs["creationflags"] = subprocess.CREATE_NO_WINDOW
22
+ try:
23
+ result = subprocess.run(
24
+ [binary] + args,
25
+ capture_output=True, text=True, timeout=timeout,
26
+ stdin=subprocess.DEVNULL, cwd=cwd, **kwargs,
27
+ )
28
+ if result.returncode == 0 and result.stdout.strip():
29
+ return True, result.stdout.strip()
30
+ return False, result.stderr.strip() or f"exit {result.returncode}"
31
+ except subprocess.TimeoutExpired:
32
+ return False, "timeout"
33
+ except Exception as e:
34
+ return False, str(e)
35
+
36
+
37
+ def _compress_ast(file_path: str, svc, finalize, maybe_facts) -> str:
38
+ """Use codebase-memory-mcp binary for AST knowledge-graph structural analysis."""
39
+ cwd = str(svc.project_path)
40
+ ok, out = _run_memory_mcp_cli(["cli", "get_architecture"], cwd)
41
+
42
+ if not ok and out == "not_installed":
43
+ return finalize(
44
+ "c3_compress", {"file_path": file_path, "mode": "ast"},
45
+ "[compress:ast] codebase-memory-mcp not installed.\n"
46
+ "Install (macOS/Linux): curl -fsSL https://raw.githubusercontent.com/DeusData/codebase-memory-mcp/main/install.sh | bash\n"
47
+ "Install (Windows): see https://github.com/DeusData/codebase-memory-mcp\n"
48
+ "Tip: use c3_compress(mode='map') for C3's built-in structural analysis.",
49
+ "not_installed",
50
+ )
51
+ if not ok:
52
+ hint = "Run: codebase-memory-mcp index" if not out or "not indexed" in out.lower() else out
53
+ return finalize(
54
+ "c3_compress", {"file_path": file_path, "mode": "ast"},
55
+ f"[compress:ast] {hint}\nTip: say 'Index this project' with codebase-memory-mcp active.",
56
+ "not_indexed",
57
+ )
58
+
59
+ try:
60
+ data = json.loads(out)
61
+ except json.JSONDecodeError:
62
+ return finalize("c3_compress", {"file_path": file_path, "mode": "ast"},
63
+ f"[compress:ast] Unexpected output — try: codebase-memory-mcp index\n{out[:200]}",
64
+ "error")
65
+
66
+ lines = ["[compress:ast] codebase-memory-mcp knowledge graph"]
67
+ if data.get("languages"):
68
+ lines.append(f"Languages: {', '.join(str(l) for l in data['languages'])}")
69
+ if data.get("entry_points"):
70
+ eps = [str(e) for e in data["entry_points"][:6]]
71
+ lines.append(f"Entry points: {', '.join(eps)}")
72
+ if data.get("packages"):
73
+ pkgs = [str(p) for p in data["packages"][:12]]
74
+ lines.append(f"Packages: {', '.join(pkgs)}")
75
+ if data.get("layers"):
76
+ layers = [str(l) for l in data["layers"][:8]]
77
+ lines.append(f"Layers: {', '.join(layers)}")
78
+ if data.get("hotspots"):
79
+ lines.append("Hotspots (high-churn):")
80
+ for h in data["hotspots"][:6]:
81
+ lines.append(f" {h}")
82
+ if data.get("clusters"):
83
+ lines.append(f"Clusters: {len(data['clusters'])} functional modules detected")
84
+ if data.get("routes"):
85
+ lines.append(f"Routes: {len(data['routes'])} HTTP endpoints")
86
+
87
+ return finalize("c3_compress", {"file_path": file_path, "mode": "ast"},
88
+ "\n".join(lines), "ast")
89
+
90
+
91
+ def handle_compress(file_path: str, mode: str, svc,
92
+ finalize, maybe_facts) -> str:
93
+ # Validate mode
94
+ valid_modes = ("map", "dense_map", "smart", "diff", "bug_scan", "ast")
95
+ if mode == "ast":
96
+ return _compress_ast(file_path, svc, finalize, maybe_facts)
97
+ if mode not in valid_modes:
98
+ # Graceful migration for removed modes
99
+ if mode in ("structure", "outline"):
100
+ mode = "map"
101
+ else:
102
+ return finalize("c3_compress", {"file_path": file_path, "mode": mode},
103
+ f"[compress:error] Unknown mode '{mode}'. Use: {', '.join(valid_modes)}",
104
+ "error")
105
+
106
+ # Batch dispatch: comma-separated paths
107
+ if "," in file_path:
108
+ paths = [p.strip() for p in file_path.split(",") if p.strip()]
109
+ return _compress_batch(paths, mode, svc, finalize, maybe_facts)
110
+
111
+ return _compress_single(file_path, mode, svc, finalize, maybe_facts)
112
+
113
+
114
+ def _compress_single(file_path: str, mode: str, svc, finalize, maybe_facts) -> str:
115
+ """Compress a single file."""
116
+ full = Path(svc.project_path) / file_path
117
+ if not full.exists():
118
+ full = Path(file_path)
119
+
120
+ if mode in ("map", "dense_map"):
121
+ if not full.exists():
122
+ return "[file_map:error] not found"
123
+ rel = str(full.resolve().relative_to(Path(svc.project_path).resolve())).replace("\\", "/")
124
+ queued = svc.file_memory.drain_queue()
125
+ completed = []
126
+ failed = []
127
+ for qp in queued[:10]:
128
+ try:
129
+ if svc.file_memory.update(qp):
130
+ completed.append(qp)
131
+ else:
132
+ failed.append(qp)
133
+ except Exception:
134
+ failed.append(qp)
135
+ if completed:
136
+ svc.file_memory.complete_updates(completed)
137
+ if failed:
138
+ svc.file_memory.complete_updates(failed, failed=True)
139
+ res = (svc.file_memory.get_or_build_dense_map(rel)
140
+ if mode == "dense_map"
141
+ else svc.file_memory.get_or_build_map(rel))
142
+ map_tokens = 0
143
+ try:
144
+ raw_tokens = count_tokens(full.read_text(encoding="utf-8", errors="replace"))
145
+ map_tokens = count_tokens(res)
146
+ summary = f"{raw_tokens}->{map_tokens}tok"
147
+ except Exception:
148
+ summary = "mapped"
149
+ return finalize("c3_compress", {"file_path": file_path, "mode": mode}, res, summary,
150
+ response_tokens=map_tokens)
151
+
152
+ res = svc.compressor.compress_file(str(full), mode)
153
+ if "error" in res:
154
+ return f"Error: {res['error']}"
155
+ resp = res['compressed']
156
+ summary = f"{res['original_tokens']}->{res['compressed_tokens']}tok"
157
+ return finalize("c3_compress", {"file_path": file_path},
158
+ resp + maybe_facts(svc, Path(file_path).name), summary)
159
+
160
+
161
+ def _compress_batch(paths: list, mode: str, svc, finalize, maybe_facts) -> str:
162
+ """Compress multiple files in parallel, return combined report."""
163
+ max_files = 10
164
+ paths = paths[:max_files]
165
+
166
+ results = {}
167
+
168
+ def _do_one(fp):
169
+ try:
170
+ full = Path(svc.project_path) / fp
171
+ if not full.exists():
172
+ full = Path(fp)
173
+ if not full.exists():
174
+ return fp, None, "not found"
175
+
176
+ if mode in ("map", "dense_map"):
177
+ rel = str(full.resolve().relative_to(
178
+ Path(svc.project_path).resolve())).replace("\\", "/")
179
+ res = (svc.file_memory.get_or_build_dense_map(rel)
180
+ if mode == "dense_map"
181
+ else svc.file_memory.get_or_build_map(rel))
182
+ try:
183
+ raw_tok = count_tokens(full.read_text(encoding="utf-8", errors="replace"))
184
+ map_tok = count_tokens(res)
185
+ return fp, res, f"{raw_tok}->{map_tok}tok"
186
+ except Exception:
187
+ return fp, res, "mapped"
188
+ else:
189
+ res = svc.compressor.compress_file(str(full), mode)
190
+ if "error" in res:
191
+ return fp, None, res["error"]
192
+ return fp, res["compressed"], f"{res['original_tokens']}->{res['compressed_tokens']}tok"
193
+ except Exception as e:
194
+ return fp, None, str(e)
195
+
196
+ with ThreadPoolExecutor(max_workers=min(len(paths), 8)) as pool:
197
+ futures = {pool.submit(_do_one, fp): fp for fp in paths}
198
+ for fut in as_completed(futures):
199
+ fp, compressed, summary = fut.result()
200
+ results[fp] = (compressed, summary)
201
+
202
+ parts = []
203
+ total_ok = 0
204
+ for fp in paths:
205
+ compressed, summary = results.get(fp, (None, "unknown"))
206
+ if compressed:
207
+ parts.append(f"## {fp} ({summary})\n{compressed}")
208
+ total_ok += 1
209
+ else:
210
+ parts.append(f"## {fp} — ERROR: {summary}")
211
+
212
+ header = f"[compress:batch] {total_ok}/{len(paths)} files ({mode})"
213
+ body = header + "\n\n" + "\n\n".join(parts)
214
+ return finalize("c3_compress", {"file_path": ",".join(paths), "mode": mode, "batch": True},
215
+ body, f"batch {total_ok}/{len(paths)}")