codent 0.3.1__tar.gz → 0.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. {codent-0.3.1 → codent-0.3.3}/PKG-INFO +1 -1
  2. {codent-0.3.1 → codent-0.3.3}/codent.egg-info/PKG-INFO +1 -1
  3. {codent-0.3.1 → codent-0.3.3}/codent.egg-info/SOURCES.txt +6 -1
  4. {codent-0.3.1 → codent-0.3.3}/codent.egg-info/top_level.txt +1 -0
  5. {codent-0.3.1 → codent-0.3.3}/cont.py +14 -1
  6. {codent-0.3.1 → codent-0.3.3}/core/cli.py +2 -0
  7. {codent-0.3.1 → codent-0.3.3}/core/guardian.py +28 -0
  8. codent-0.3.3/core/init_cmd.py +181 -0
  9. {codent-0.3.1 → codent-0.3.3}/core/runtime.py +2 -0
  10. {codent-0.3.1 → codent-0.3.3}/core/self_improve.py +14 -3
  11. {codent-0.3.1 → codent-0.3.3}/core/tool_helpers.py +17 -0
  12. {codent-0.3.1 → codent-0.3.3}/core/tools.py +8 -0
  13. {codent-0.3.1 → codent-0.3.3}/core/trace.py +109 -58
  14. {codent-0.3.1 → codent-0.3.3}/pyproject.toml +9 -2
  15. codent-0.3.3/vendor/__init__.py +0 -0
  16. codent-0.3.3/vendor/bin/__init__.py +0 -0
  17. codent-0.3.3/vendor/bin/ollama +0 -0
  18. codent-0.3.3/vendor/wheels/__init__.py +0 -0
  19. {codent-0.3.1 → codent-0.3.3}/codent.egg-info/dependency_links.txt +0 -0
  20. {codent-0.3.1 → codent-0.3.3}/codent.egg-info/entry_points.txt +0 -0
  21. {codent-0.3.1 → codent-0.3.3}/codent.egg-info/requires.txt +0 -0
  22. {codent-0.3.1 → codent-0.3.3}/cont_ui/__init__.py +0 -0
  23. {codent-0.3.1 → codent-0.3.3}/cont_ui/frontend.html +0 -0
  24. {codent-0.3.1 → codent-0.3.3}/cont_ui/server.py +0 -0
  25. {codent-0.3.1 → codent-0.3.3}/cont_watcher.py +0 -0
  26. {codent-0.3.1 → codent-0.3.3}/core/__init__.py +0 -0
  27. {codent-0.3.1 → codent-0.3.3}/core/adaptive_prompt.py +0 -0
  28. {codent-0.3.1 → codent-0.3.3}/core/advanced_overnight.py +0 -0
  29. {codent-0.3.1 → codent-0.3.3}/core/anomaly_tools.py +0 -0
  30. {codent-0.3.1 → codent-0.3.3}/core/app.py +0 -0
  31. {codent-0.3.1 → codent-0.3.3}/core/auto_eval.py +0 -0
  32. {codent-0.3.1 → codent-0.3.3}/core/automations.py +0 -0
  33. {codent-0.3.1 → codent-0.3.3}/core/benchmark.py +0 -0
  34. {codent-0.3.1 → codent-0.3.3}/core/bridge_server.py +0 -0
  35. {codent-0.3.1 → codent-0.3.3}/core/browser.py +0 -0
  36. {codent-0.3.1 → codent-0.3.3}/core/browser_agent.py +0 -0
  37. {codent-0.3.1 → codent-0.3.3}/core/budget_committee.py +0 -0
  38. {codent-0.3.1 → codent-0.3.3}/core/cc_auditor.py +0 -0
  39. {codent-0.3.1 → codent-0.3.3}/core/cleaning_tools.py +0 -0
  40. {codent-0.3.1 → codent-0.3.3}/core/context_utils.py +0 -0
  41. {codent-0.3.1 → codent-0.3.3}/core/curriculum_runner.py +0 -0
  42. {codent-0.3.1 → codent-0.3.3}/core/daemon.py +0 -0
  43. {codent-0.3.1 → codent-0.3.3}/core/data_tools.py +0 -0
  44. {codent-0.3.1 → codent-0.3.3}/core/decomposer.py +0 -0
  45. {codent-0.3.1 → codent-0.3.3}/core/domain_tools.py +0 -0
  46. {codent-0.3.1 → codent-0.3.3}/core/endpoint_discovery.py +0 -0
  47. {codent-0.3.1 → codent-0.3.3}/core/env_snapshot.py +0 -0
  48. {codent-0.3.1 → codent-0.3.3}/core/episodic.py +0 -0
  49. {codent-0.3.1 → codent-0.3.3}/core/escalation.py +0 -0
  50. {codent-0.3.1 → codent-0.3.3}/core/events.py +0 -0
  51. {codent-0.3.1 → codent-0.3.3}/core/feedback_training.py +0 -0
  52. {codent-0.3.1 → codent-0.3.3}/core/file_context.py +0 -0
  53. {codent-0.3.1 → codent-0.3.3}/core/fitness.py +0 -0
  54. {codent-0.3.1 → codent-0.3.3}/core/formatters.py +0 -0
  55. {codent-0.3.1 → codent-0.3.3}/core/genesis.py +0 -0
  56. {codent-0.3.1 → codent-0.3.3}/core/golden_test_runner.py +0 -0
  57. {codent-0.3.1 → codent-0.3.3}/core/hive_recall.py +0 -0
  58. {codent-0.3.1 → codent-0.3.3}/core/identity.py +0 -0
  59. {codent-0.3.1 → codent-0.3.3}/core/intent_detection.py +0 -0
  60. {codent-0.3.1 → codent-0.3.3}/core/intent_gate.py +0 -0
  61. {codent-0.3.1 → codent-0.3.3}/core/learning_loop.py +0 -0
  62. {codent-0.3.1 → codent-0.3.3}/core/lmstudio.py +0 -0
  63. {codent-0.3.1 → codent-0.3.3}/core/local_llm.py +0 -0
  64. {codent-0.3.1 → codent-0.3.3}/core/memory_eviction.py +0 -0
  65. {codent-0.3.1 → codent-0.3.3}/core/memory_gate.py +0 -0
  66. {codent-0.3.1 → codent-0.3.3}/core/memory_store.py +0 -0
  67. {codent-0.3.1 → codent-0.3.3}/core/ml_tools.py +0 -0
  68. {codent-0.3.1 → codent-0.3.3}/core/model.py +0 -0
  69. {codent-0.3.1 → codent-0.3.3}/core/model_routing.py +0 -0
  70. {codent-0.3.1 → codent-0.3.3}/core/network_audit.py +0 -0
  71. {codent-0.3.1 → codent-0.3.3}/core/ocr.py +0 -0
  72. {codent-0.3.1 → codent-0.3.3}/core/overfit_test.py +0 -0
  73. {codent-0.3.1 → codent-0.3.3}/core/overnight_training.py +0 -0
  74. {codent-0.3.1 → codent-0.3.3}/core/path_resolution.py +0 -0
  75. {codent-0.3.1 → codent-0.3.3}/core/postprocess.py +0 -0
  76. {codent-0.3.1 → codent-0.3.3}/core/prefetch.py +0 -0
  77. {codent-0.3.1 → codent-0.3.3}/core/prompt_patches.py +0 -0
  78. {codent-0.3.1 → codent-0.3.3}/core/prompts.py +0 -0
  79. {codent-0.3.1 → codent-0.3.3}/core/provider_router.py +0 -0
  80. {codent-0.3.1 → codent-0.3.3}/core/quick_overfit_test.py +0 -0
  81. {codent-0.3.1 → codent-0.3.3}/core/recipe_factory.py +0 -0
  82. {codent-0.3.1 → codent-0.3.3}/core/recipe_feedback.py +0 -0
  83. {codent-0.3.1 → codent-0.3.3}/core/recipe_learner.py +0 -0
  84. {codent-0.3.1 → codent-0.3.3}/core/recipes.py +0 -0
  85. {codent-0.3.1 → codent-0.3.3}/core/relation_discovery.py +0 -0
  86. {codent-0.3.1 → codent-0.3.3}/core/relay.py +0 -0
  87. {codent-0.3.1 → codent-0.3.3}/core/repl.py +0 -0
  88. {codent-0.3.1 → codent-0.3.3}/core/router.py +0 -0
  89. {codent-0.3.1 → codent-0.3.3}/core/rpc.py +0 -0
  90. {codent-0.3.1 → codent-0.3.3}/core/run_logger.py +0 -0
  91. {codent-0.3.1 → codent-0.3.3}/core/safety.py +0 -0
  92. {codent-0.3.1 → codent-0.3.3}/core/scheduler.py +0 -0
  93. {codent-0.3.1 → codent-0.3.3}/core/scratchpad.py +0 -0
  94. {codent-0.3.1 → codent-0.3.3}/core/spreading_activation.py +0 -0
  95. {codent-0.3.1 → codent-0.3.3}/core/sql_guard.py +0 -0
  96. {codent-0.3.1 → codent-0.3.3}/core/sticky_config.py +0 -0
  97. {codent-0.3.1 → codent-0.3.3}/core/structured_output.py +0 -0
  98. {codent-0.3.1 → codent-0.3.3}/core/system_executor.py +0 -0
  99. {codent-0.3.1 → codent-0.3.3}/core/system_map.py +0 -0
  100. {codent-0.3.1 → codent-0.3.3}/core/task_classifier.py +0 -0
  101. {codent-0.3.1 → codent-0.3.3}/core/task_utils.py +0 -0
  102. {codent-0.3.1 → codent-0.3.3}/core/teacher.py +0 -0
  103. {codent-0.3.1 → codent-0.3.3}/core/tool_budget.py +0 -0
  104. {codent-0.3.1 → codent-0.3.3}/core/tool_schemas.py +0 -0
  105. {codent-0.3.1 → codent-0.3.3}/core/types.py +0 -0
  106. {codent-0.3.1 → codent-0.3.3}/core/utils.py +0 -0
  107. {codent-0.3.1 → codent-0.3.3}/core/verification.py +0 -0
  108. {codent-0.3.1 → codent-0.3.3}/core/verify_minions.py +0 -0
  109. {codent-0.3.1 → codent-0.3.3}/core/vocabulary.py +0 -0
  110. {codent-0.3.1 → codent-0.3.3}/core/voice.py +0 -0
  111. {codent-0.3.1 → codent-0.3.3}/core/web.py +0 -0
  112. {codent-0.3.1 → codent-0.3.3}/core/yis_deep_training.py +0 -0
  113. {codent-0.3.1 → codent-0.3.3}/setup.cfg +0 -0
  114. {codent-0.3.1 → codent-0.3.3}/tests/test_auto_eval.py +0 -0
  115. {codent-0.3.1 → codent-0.3.3}/tests/test_browser.py +0 -0
  116. {codent-0.3.1 → codent-0.3.3}/tests/test_browser_agent.py +0 -0
  117. {codent-0.3.1 → codent-0.3.3}/tests/test_commands_and_teach.py +0 -0
  118. {codent-0.3.1 → codent-0.3.3}/tests/test_cont.py +0 -0
  119. {codent-0.3.1 → codent-0.3.3}/tests/test_corpus_parser.py +0 -0
  120. {codent-0.3.1 → codent-0.3.3}/tests/test_episodic.py +0 -0
  121. {codent-0.3.1 → codent-0.3.3}/tests/test_eviction.py +0 -0
  122. {codent-0.3.1 → codent-0.3.3}/tests/test_formatters.py +0 -0
  123. {codent-0.3.1 → codent-0.3.3}/tests/test_golden.py +0 -0
  124. {codent-0.3.1 → codent-0.3.3}/tests/test_health_report_valid_json.py +0 -0
  125. {codent-0.3.1 → codent-0.3.3}/tests/test_integrity_and_memory_gate.py +0 -0
  126. {codent-0.3.1 → codent-0.3.3}/tests/test_known_bugs.py +0 -0
  127. {codent-0.3.1 → codent-0.3.3}/tests/test_model_agnostic.py +0 -0
  128. {codent-0.3.1 → codent-0.3.3}/tests/test_new_tools.py +0 -0
  129. {codent-0.3.1 → codent-0.3.3}/tests/test_provider_router.py +0 -0
  130. {codent-0.3.1 → codent-0.3.3}/tests/test_recipes.py +0 -0
  131. {codent-0.3.1 → codent-0.3.3}/tests/test_teacher.py +0 -0
  132. {codent-0.3.1 → codent-0.3.3}/tests/test_verification.py +0 -0
  133. {codent-0.3.1 → codent-0.3.3}/tests/test_web_fetch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codent
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Requires-Python: >=3.10
5
5
  Requires-Dist: rich>=13.0
6
6
  Requires-Dist: openai>=1.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codent
3
- Version: 0.3.1
3
+ Version: 0.3.3
4
4
  Requires-Python: >=3.10
5
5
  Requires-Dist: rich>=13.0
6
6
  Requires-Dist: openai>=1.0
@@ -45,6 +45,7 @@ core/golden_test_runner.py
45
45
  core/guardian.py
46
46
  core/hive_recall.py
47
47
  core/identity.py
48
+ core/init_cmd.py
48
49
  core/intent_detection.py
49
50
  core/intent_gate.py
50
51
  core/learning_loop.py
@@ -123,4 +124,8 @@ tests/test_provider_router.py
123
124
  tests/test_recipes.py
124
125
  tests/test_teacher.py
125
126
  tests/test_verification.py
126
- tests/test_web_fetch.py
127
+ tests/test_web_fetch.py
128
+ vendor/__init__.py
129
+ vendor/bin/__init__.py
130
+ vendor/bin/ollama
131
+ vendor/wheels/__init__.py
@@ -2,3 +2,4 @@ cont
2
2
  cont_ui
3
3
  cont_watcher
4
4
  core
5
+ vendor
@@ -416,8 +416,12 @@ from core.provider_router import (
416
416
  )
417
417
 
418
418
  # Discover working base URL at startup (LM Studio → Ollama fallback)
419
- # Discover working base URL at startup (LM Studio → Ollama fallback)
419
+ # Skip the hard-exit when running offline-only flags (--init, --local-list, --help)
420
+ _OFFLINE_FLAGS = {"--init", "--local-list", "--help", "-h", "--version"}
421
+ _OFFLINE_MODE = any(a in _OFFLINE_FLAGS for a in sys.argv[1:])
420
422
  _PROVIDER_NAME = "lmstudio"
423
+ BASE_URL = "http://127.0.0.1:1234/v1"
424
+ _TRIED_URLS = []
421
425
  try:
422
426
  BASE_URL, _TRIED_URLS = discover_openai_base_url()
423
427
  except EndpointNotFoundError:
@@ -428,6 +432,9 @@ except EndpointNotFoundError:
428
432
  _TRIED_URLS = [OLLAMA_BASE]
429
433
  _PROVIDER_NAME = "ollama"
430
434
  console.print("[yellow]LM Studio unavailable, using Ollama fallback.[/yellow]")
435
+ elif _OFFLINE_MODE:
436
+ # --init / --local-list: provider gerek yok, sessizce devam et
437
+ _PROVIDER_NAME = "offline"
431
438
  else:
432
439
  console.print("[bold red]Error: No LLM provider available.[/bold red]")
433
440
  console.print("\n[yellow]Hint:[/yellow] Start LM Studio (port 1234) or Ollama (port 11434).")
@@ -1879,6 +1886,12 @@ def main():
1879
1886
  list_presets()
1880
1887
  sys.exit(0)
1881
1888
 
1889
+ # Handle --init (install bundled wheels for offline llama-cpp-python)
1890
+ if getattr(args, 'init', None) is not None:
1891
+ from core.init_cmd import run_init
1892
+ run_init(args.init or None)
1893
+ sys.exit(0)
1894
+
1882
1895
  # Handle all exit flags (--set-home, --show-home, --self-report, --memory-*, etc.)
1883
1896
  handle_exit_flags(args)
1884
1897
 
@@ -268,6 +268,8 @@ Model selection:
268
268
  # Local LLM — fallback GGUF presets (LM Studio kapalıysa)
269
269
  parser.add_argument("--local-list", action="store_true",
270
270
  help="List available local GGUF model presets")
271
+ parser.add_argument("--init", nargs="?", const="", metavar="GGUF_PATH",
272
+ help="Bundled wheel'lardan llama-cpp-python yükle (opsiyonel: GGUF path)")
271
273
 
272
274
  # Genesis — owner recognition
273
275
  parser.add_argument("--set-keyphrase", action="store_true",
@@ -1140,6 +1140,33 @@ def check_network_safety() -> tuple[bool, list[str]]:
1140
1140
 
1141
1141
  # ── Kontrol 27: Halüsinasyon guard ──
1142
1142
 
1143
+ def check_system_search_meta_filter() -> tuple[bool, list[str]]:
1144
+ """system_search META dizinleri (logs/, pheromone/, inbox/, __pycache__/)
1145
+ filtrelemeli — bunlar DATA değil, arama gürültüsü.
1146
+ """
1147
+ violations = []
1148
+ tools_path = Path(__file__).parent / "tools.py"
1149
+ if not tools_path.exists():
1150
+ return True, []
1151
+ src = tools_path.read_text(encoding="utf-8", errors="ignore")
1152
+ # system_search içinde meta filtre var mı?
1153
+ try:
1154
+ idx = src.index("def system_search")
1155
+ except ValueError:
1156
+ return True, []
1157
+ end = src.find("\ndef ", idx + 1)
1158
+ body = src[idx:end if end != -1 else len(src)]
1159
+ required = ["/logs/", "/.cont/hive/pheromone/",
1160
+ "/.cont/hive/inbox/", "/__pycache__/"]
1161
+ missing = [tok for tok in required if tok not in body]
1162
+ if missing:
1163
+ violations.append(
1164
+ f"system_search META filtresi eksik: {missing}. "
1165
+ "core/tools.py system_search içinde bu dizinler hariç tutulmalı."
1166
+ )
1167
+ return not violations, violations
1168
+
1169
+
1143
1170
  def check_hallucination_guard() -> tuple[bool, list[str]]:
1144
1171
  """Tablo/kolon iddiası yapan görevlerde SQL çalıştırılmış mı?
1145
1172
 
@@ -1365,6 +1392,7 @@ def run_guardian(full: bool = False) -> GuardianReport:
1365
1392
  ("cc_audit", check_cc_audit),
1366
1393
  ("network_safety", check_network_safety),
1367
1394
  ("hallucination_guard", check_hallucination_guard),
1395
+ ("system_search_meta_filter", check_system_search_meta_filter),
1368
1396
  ]
1369
1397
 
1370
1398
  checks = operational_checks + (code_checks if full else [])
@@ -0,0 +1,181 @@
1
+ """cont --init — bundled binaries + wheels for offline setup.
2
+
3
+ Adımlar:
4
+ 1. Ollama yüklü mü? Yoksa vendor/bin/ollama → ~/.local/bin/ kopyala
5
+ 2. ollama serve & başlat (zaten çalışmıyorsa)
6
+ 3. GGUF verildiyse `ollama create` ile model kaydet
7
+ 4. /api/tags ile test et
8
+
9
+ llama-cpp-python wheel kurulumu opsiyonel kalıyor (Ollama önceliklidir).
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import os
14
+ import shutil
15
+ import subprocess
16
+ import sys
17
+ import time
18
+ import urllib.request
19
+ from pathlib import Path
20
+
21
+ OLLAMA_PORT = 11434
22
+ OLLAMA_URL = f"http://127.0.0.1:{OLLAMA_PORT}"
23
+
24
+
25
+ def _vendor_dir(sub: str) -> Path:
26
+ """Locate vendor/<sub> in source tree OR installed package."""
27
+ here = Path(__file__).resolve().parent
28
+ candidate = here.parent / "vendor" / sub
29
+ if candidate.exists():
30
+ return candidate
31
+ try:
32
+ import vendor # type: ignore
33
+ candidate = Path(vendor.__file__).parent / sub
34
+ if candidate.exists():
35
+ return candidate
36
+ except ImportError:
37
+ pass
38
+ return candidate
39
+
40
+
41
+ def _which(name: str) -> str | None:
42
+ return shutil.which(name)
43
+
44
+
45
+ def _ollama_serve_running() -> bool:
46
+ try:
47
+ with urllib.request.urlopen(f"{OLLAMA_URL}/api/tags", timeout=2) as r:
48
+ return r.status == 200
49
+ except Exception:
50
+ return False
51
+
52
+
53
+ def _install_ollama() -> str:
54
+ """Copy bundled ollama to ~/.local/bin/. Returns installed path."""
55
+ src = _vendor_dir("bin") / "ollama"
56
+ if not src.exists():
57
+ print(f"[HATA] vendor/bin/ollama bulunamadı: {src}")
58
+ sys.exit(1)
59
+
60
+ dest_dir = Path.home() / ".local" / "bin"
61
+ dest_dir.mkdir(parents=True, exist_ok=True)
62
+ dest = dest_dir / "ollama"
63
+ shutil.copy2(src, dest)
64
+ dest.chmod(0o755)
65
+ print(f"[OK] ollama → {dest}")
66
+
67
+ # PATH uyarısı
68
+ if str(dest_dir) not in os.environ.get("PATH", "").split(":"):
69
+ print(f"[UYARI] {dest_dir} PATH'te değil. ~/.bashrc'ye ekle:")
70
+ print(f' export PATH="{dest_dir}:$PATH"')
71
+ return str(dest)
72
+
73
+
74
+ def _start_ollama_serve(ollama_bin: str) -> bool:
75
+ """Start `ollama serve` in background. Returns True if up within 10s."""
76
+ if _ollama_serve_running():
77
+ print("[OK] ollama serve zaten çalışıyor")
78
+ return True
79
+
80
+ print("[*] ollama serve başlatılıyor...")
81
+ log_path = Path.home() / ".cache" / "cont" / "ollama_serve.log"
82
+ log_path.parent.mkdir(parents=True, exist_ok=True)
83
+ log_fp = open(log_path, "ab")
84
+ subprocess.Popen(
85
+ [ollama_bin, "serve"],
86
+ stdout=log_fp, stderr=log_fp,
87
+ start_new_session=True,
88
+ )
89
+
90
+ for _ in range(20):
91
+ time.sleep(0.5)
92
+ if _ollama_serve_running():
93
+ print(f"[OK] ollama serve hazır (log: {log_path})")
94
+ return True
95
+
96
+ print(f"[HATA] ollama serve 10s içinde yanıt vermedi (log: {log_path})")
97
+ return False
98
+
99
+
100
+ def _create_model_from_gguf(ollama_bin: str, gguf_path: Path,
101
+ model_name: str = "cont-local") -> bool:
102
+ """Generate Modelfile + run `ollama create`."""
103
+ print(f"[*] {model_name} modeli {gguf_path.name} GGUF'undan oluşturuluyor...")
104
+ modelfile = Path.home() / ".cache" / "cont" / "Modelfile.cont"
105
+ modelfile.parent.mkdir(parents=True, exist_ok=True)
106
+ modelfile.write_text(f"FROM {gguf_path}\n", encoding="utf-8")
107
+
108
+ try:
109
+ subprocess.run(
110
+ [ollama_bin, "create", model_name, "-f", str(modelfile)],
111
+ check=True,
112
+ )
113
+ print(f"[OK] model kaydedildi: {model_name}")
114
+ return True
115
+ except subprocess.CalledProcessError as e:
116
+ print(f"[HATA] ollama create başarısız: {e}")
117
+ return False
118
+
119
+
120
+ def _smoke_test(model_name: str) -> bool:
121
+ """Hızlı /api/generate çağrısı — model yanıt veriyor mu?"""
122
+ print(f"[*] {model_name} smoke test...")
123
+ import json
124
+ body = json.dumps({
125
+ "model": model_name,
126
+ "prompt": "2+2=?",
127
+ "stream": False,
128
+ }).encode()
129
+ req = urllib.request.Request(
130
+ f"{OLLAMA_URL}/api/generate",
131
+ data=body,
132
+ headers={"Content-Type": "application/json"},
133
+ method="POST",
134
+ )
135
+ try:
136
+ with urllib.request.urlopen(req, timeout=120) as r:
137
+ resp = json.loads(r.read())
138
+ ans = (resp.get("response") or "").strip()[:80]
139
+ print(f"[OK] yanıt: {ans!r}")
140
+ return True
141
+ except Exception as e:
142
+ print(f"[HATA] smoke test başarısız: {e}")
143
+ return False
144
+
145
+
146
+ def run_init(gguf_path: str | None = None) -> None:
147
+ print("Cont init başlıyor...\n")
148
+
149
+ # 1. Ollama var mı?
150
+ ollama_bin = _which("ollama")
151
+ if ollama_bin:
152
+ print(f"[OK] ollama zaten yüklü: {ollama_bin}")
153
+ else:
154
+ ollama_bin = _install_ollama()
155
+
156
+ # 2. Servis ayağa kalksın
157
+ if not _start_ollama_serve(ollama_bin):
158
+ sys.exit(1)
159
+
160
+ # 3. GGUF verildiyse model oluştur
161
+ if gguf_path:
162
+ gp = Path(gguf_path).expanduser()
163
+ if not gp.exists():
164
+ print(f"[HATA] GGUF bulunamadı: {gp}")
165
+ sys.exit(1)
166
+
167
+ model_name = "cont-local"
168
+ if not _create_model_from_gguf(ollama_bin, gp, model_name):
169
+ sys.exit(1)
170
+
171
+ # 4. Test
172
+ if not _smoke_test(model_name):
173
+ sys.exit(1)
174
+
175
+ print(f"\n[BAŞARILI] Cont kullanıma hazır.")
176
+ print(f" Modeli kullanmak için: cont --model ollama:{model_name}")
177
+ else:
178
+ print("\n[INFO] GGUF verilmedi — model oluşturulmadı.")
179
+ print(" Daha sonra: cont --init <model.gguf>")
180
+
181
+ print("\nInit tamamlandı.")
@@ -1168,6 +1168,8 @@ def run_task(task: str, logger=None, event_callback=None) -> tuple:
1168
1168
  tool_choice="none" if budget_exhausted else "auto",
1169
1169
  base_url=getattr(_ctx, "base_url", ""),
1170
1170
  iteration=iteration,
1171
+ budget_used=budget.phase_calls,
1172
+ budget_total=budget.BUDGET_PER_PHASE,
1171
1173
  )
1172
1174
 
1173
1175
  _spin_stop = threading.Event()
@@ -457,9 +457,20 @@ class SelfImproveLoop:
457
457
  self._thread.start()
458
458
 
459
459
  def _print_background_status(self, message):
460
- """Print status without breaking input prompt."""
461
- sys.stdout.write(f"\r\033[K {message}\n")
462
- sys.stdout.flush()
460
+ """Write status to daemon log — NEVER to terminal (would break input).
461
+
462
+ Use `cont --triage` or `cont --status` to view recent activity.
463
+ """
464
+ try:
465
+ from datetime import datetime
466
+ from pathlib import Path
467
+ log_dir = Path.home() / ".config" / "cont" / "daemon"
468
+ log_dir.mkdir(parents=True, exist_ok=True)
469
+ log_file = log_dir / "self_improve.log"
470
+ with open(log_file, "a", encoding="utf-8") as f:
471
+ f.write(f"[{datetime.now().isoformat(timespec='seconds')}] {message}\n")
472
+ except Exception:
473
+ pass
463
474
 
464
475
  def _should_stop(self) -> bool:
465
476
  """Check if we should stop (user active or stop requested)."""
@@ -132,6 +132,23 @@ def extract_fenced_tool_requests(text: str) -> list:
132
132
  except json.JSONDecodeError:
133
133
  idx = brace + 1
134
134
 
135
+ # b3) Parenthesized form: (tool_name\n {...}) — Qwen3 emit fallback
136
+ # Example: "(web_search\n {\"query\": \"...\"})"
137
+ if "(" in text:
138
+ for m in re.finditer(r'\(\s*([a-z_][a-z0-9_]*)\s+', text):
139
+ name = m.group(1)
140
+ # Try raw_decode JSON starting after the tool name
141
+ tail = text[m.end():]
142
+ brace = tail.find("{")
143
+ if brace == -1 or brace > 5:
144
+ continue
145
+ try:
146
+ obj, _ = json.JSONDecoder().raw_decode(tail[brace:])
147
+ if isinstance(obj, dict):
148
+ tool_calls.append({"name": name, "args": obj})
149
+ except json.JSONDecodeError:
150
+ pass
151
+
135
152
  # c) Bare JSON tool call (Ollama Qwen3 fallback)
136
153
  # Pattern: yanıt bir tool call JSON ile başlıyor veya tek satır
137
154
  if not tool_calls and text.strip().startswith(("{", "[")):
@@ -779,6 +779,14 @@ def system_search(query: str, type: str = None, extension: str = None,
779
779
  extension=extension, project=project
780
780
  )
781
781
 
782
+ # META veri filtresi: log/pheromone/inbox/__pycache__ dosyaları DATA değil
783
+ _META_DIR_TOKENS = ("/logs/", "/.cont/hive/pheromone/",
784
+ "/.cont/hive/inbox/", "/__pycache__/")
785
+ results = [
786
+ r for r in results
787
+ if not any(tok in r.get("path", "") for tok in _META_DIR_TOKENS)
788
+ ]
789
+
782
790
  if not results:
783
791
  return json.dumps({
784
792
  "status": "not_found",
@@ -226,6 +226,40 @@ _ROLE_COLORS = {
226
226
  }
227
227
  _ROLE_TAGS = {"system": "S", "user": "U", "assistant": "A", "tool": "T"}
228
228
 
229
+ # FULL-mode role styling: tag → (rich color, label)
230
+ _FULL_ROLE_STYLE = {
231
+ "system": ("dim", "S"),
232
+ "user": ("bold white", "U"),
233
+ "assistant": ("green", "A"),
234
+ "tool": ("cyan", "T"),
235
+ }
236
+
237
+
238
+ def _box_lines(title: str, body_lines: list, color: str = "yellow", width: int = 70) -> list:
239
+ """Build a Rich-markup box: ┌─ title ──┐ / │ ... │ / └────────┘"""
240
+ inner_w = width - 2
241
+ title_str = f" {title} "
242
+ top = f"[{color}]┌─{title_str}{'─' * max(0, inner_w - len(title_str) - 1)}┐[/{color}]"
243
+ out = [top]
244
+ for ln in body_lines:
245
+ # Strip rich tags for length calc
246
+ plain = re.sub(r"\[/?[^\]]+\]", "", ln)
247
+ pad = max(0, inner_w - len(plain) - 1)
248
+ out.append(f"[{color}]│[/{color}] {ln}{' ' * pad}[{color}]│[/{color}]")
249
+ out.append(f"[{color}]└{'─' * inner_w}┘[/{color}]")
250
+ return out
251
+
252
+
253
+ def _budget_bar(used: int, total: int, width: int = 16) -> str:
254
+ """ASCII progress bar: ████░░░░░░░░ 3/12"""
255
+ if total <= 0:
256
+ return ""
257
+ filled = int(round(width * used / total))
258
+ filled = max(0, min(width, filled))
259
+ bar = "█" * filled + "░" * (width - filled)
260
+ color = "green" if used < total * 0.7 else ("yellow" if used < total else "red")
261
+ return f"[{color}]{bar}[/{color}] [dim]{used}/{total}[/dim]"
262
+
229
263
 
230
264
  # ── Public hooks ─────────────────────────────────────────────────
231
265
 
@@ -237,6 +271,8 @@ def trace_llm_request(
237
271
  tool_choice: Optional[str],
238
272
  base_url: str = "",
239
273
  iteration: int = 0,
274
+ budget_used: int = 0,
275
+ budget_total: int = 0,
240
276
  ):
241
277
  """Hook: BEFORE LLM API call. Caller must guard with is_off()."""
242
278
  if _level == TraceLevel.OFF:
@@ -254,42 +290,30 @@ def trace_llm_request(
254
290
  tc = tool_choice or "auto"
255
291
 
256
292
  if _level == TraceLevel.STORY:
257
- # ── STORY: grouped turn header + input section ──
258
- _rich("")
259
- _rich(f"[bold white on #333333] 🎬 TURN #{_turn_counter} [/bold white on #333333]"
260
- f" [dim]{provider}[/dim] [bold]{model}[/bold]"
261
- f" [dim]tools={tools_str} ~{est_tok} tok[/dim]")
262
-
263
- # Section A: INPUT (to LLM) — last K messages
264
- _rich("[bold #6699cc] 🧠 INPUT (to LLM)[/bold #6699cc]")
265
- tail = messages[-_last_k:]
266
- for m in tail:
267
- role = m.get("role", "?")
268
- tag = _ROLE_TAGS.get(role, "?")
269
- color = _ROLE_COLORS.get(role, "dim")
270
- raw_content = m.get("content", "") or ""
271
-
272
- # Special: tool results show tool name
273
- if role == "tool":
274
- tool_id = m.get("tool_call_id", "")
275
- short = _sanitize(_preview(raw_content, 120))
276
- _rich(f" [{color}]\\[T][/{color}] [dim]{short}[/dim]")
277
- else:
278
- short = _sanitize(_preview(raw_content, 140))
279
- _rich(f" [{color}]\\[{tag}][/{color}] {short}")
280
-
281
- elif _level == TraceLevel.COMPACT or _level == TraceLevel.FULL:
282
- # ── COMPACT/FULL: technical one-liner ──
293
+ # STORY: minimal turn header only — intent+result come from
294
+ # trace_story_intent and trace_tool_result.
295
+ _rich(f"\n[bold white on #333333] 🎬 TURN #{_turn_counter} [/bold white on #333333] "
296
+ f"[dim]{provider} {model} ~{est_tok}tok[/dim]")
297
+
298
+ elif _level == TraceLevel.COMPACT:
283
299
  _emit(
284
300
  f"LLM→ provider={provider} model={model} msgs={msg_count} "
285
301
  f"roles={roles} tools={tools_str} tool_choice={tc} est_tok_in={est_tok}"
286
302
  )
287
- if _level >= TraceLevel.FULL:
288
- _emit(f" base_url={_sanitize(base_url)}")
289
- for m in messages[-_last_k:]:
290
- r = _ROLE_TAGS.get(m.get("role", "?"), "?")
291
- content = _sanitize(_preview(m.get("content", "") or "", 200))
292
- _emit(f" [{r}] {content}")
303
+
304
+ elif _level == TraceLevel.FULL:
305
+ # FULL: rich formatted LLM INPUT block + budget bar
306
+ _rich("")
307
+ _rich(f"[bold white on #2255aa] 🎬 TURN #{_turn_counter} [/bold white on #2255aa] "
308
+ f"[dim]{provider} {model} • ~{est_tok}tok • tools={tools_str}/{tc}[/dim]")
309
+ if budget_total > 0:
310
+ _rich(f" [bold]📊 BÜTÇE:[/bold] {_budget_bar(budget_used, budget_total)} call")
311
+ _rich("[bold #6699cc]🧠 LLM INPUT[/bold #6699cc]")
312
+ for m in messages[-_last_k:]:
313
+ role = m.get("role", "?")
314
+ color, tag = _FULL_ROLE_STYLE.get(role, ("dim", "?"))
315
+ content = _sanitize(_preview(m.get("content", "") or "", 220))
316
+ _rich(f" [{color}]\\[{tag}][/{color}] {content}")
293
317
 
294
318
  _emit_jsonl({
295
319
  "event": "llm_request",
@@ -318,30 +342,27 @@ def trace_llm_response(
318
342
  out_chars = len(content or "")
319
343
 
320
344
  if _level == TraceLevel.STORY:
321
- # ── STORY: OUTPUT section ──
322
- _rich(f"[bold #66cc99] 🎭 OUTPUT (from LLM)[/bold #66cc99]"
323
- f" [dim]{latency_ms}ms {out_chars} chars[/dim]")
324
-
325
- if tool_calls_count:
326
- names = ", ".join(tool_names[:4]) if tool_names else f"{tool_calls_count} tool(s)"
327
- _rich(f" [yellow]→ Tool calls: {names}[/yellow]")
345
+ # STORY: minimal trace_story_intent handles intent+chain
346
+ if not tool_calls_count and content and content.strip():
347
+ short = _sanitize(_preview(content, 200))
348
+ _rich(f" [green]💬[/green] [dim]{short}[/dim] [dim]({latency_ms}ms)[/dim]")
328
349
 
329
- if content and content.strip():
330
- lines = content.strip().split("\n")
331
- for ln in lines[:_max_lines]:
332
- short = _sanitize(ln[:200])
333
- _rich(f" [dim]{short}[/dim]")
334
-
335
- elif _level == TraceLevel.COMPACT or _level == TraceLevel.FULL:
336
- # ── COMPACT/FULL: technical one-liner ──
350
+ elif _level == TraceLevel.COMPACT:
337
351
  line = f"LLM← stop={finish_reason} out_chars={out_chars} latency={latency_ms}ms"
338
352
  if tool_calls_count:
339
353
  line += f" tool_calls={tool_calls_count}"
340
354
  _emit(line)
341
355
 
342
- if _level >= TraceLevel.FULL and content:
343
- for ln in (content or "").split("\n")[:_max_lines]:
344
- _emit(f" | {_sanitize(ln[:200])}")
356
+ elif _level == TraceLevel.FULL:
357
+ # FULL: rich model thought block
358
+ _rich(f"[bold #66cc99]💭 MODEL DÜŞÜNCE[/bold #66cc99] "
359
+ f"[dim]{latency_ms}ms • {out_chars} chars • stop={finish_reason}[/dim]")
360
+ if content and content.strip():
361
+ for ln in content.strip().split("\n")[:_max_lines]:
362
+ _rich(f" [dim italic]\"{_sanitize(ln[:200])}\"[/dim italic]")
363
+ if tool_calls_count:
364
+ names = ", ".join(tool_names[:4]) if tool_names else f"{tool_calls_count} tool(s)"
365
+ _rich(f" [yellow]→ next: {names}[/yellow]")
345
366
 
346
367
  _emit_jsonl({
347
368
  "event": "llm_response",
@@ -365,11 +386,23 @@ def trace_tool_call(
365
386
  h = _args_hash(args)
366
387
 
367
388
  if _level == TraceLevel.STORY:
368
- # ── STORY: narrative tool beat ──
369
389
  human = _sanitize(_human_args(tool_name, args))
370
390
  _rich(f" [bold yellow]⚙️ {tool_name}[/bold yellow] [dim]{human}[/dim]")
391
+
392
+ elif _level == TraceLevel.FULL:
393
+ # FULL: 🔧 boxed tool call
394
+ _rich("[bold yellow]🔧 TOOL CALL[/bold yellow]")
395
+ body = []
396
+ for k, v in list(args.items())[:6]:
397
+ val = _sanitize(_preview(str(v), 60))
398
+ body.append(f"[dim]{k}:[/dim] {val}")
399
+ if not body:
400
+ body = ["[dim](no args)[/dim]"]
401
+ for ln in _box_lines(tool_name, body, color="yellow", width=72):
402
+ _rich(ln)
403
+
371
404
  else:
372
- # ── COMPACT/FULL: technical ──
405
+ # COMPACT
373
406
  prev = _sanitize(_preview(json.dumps(args, ensure_ascii=False), 160))
374
407
  _emit(f"TOOL→ {tool_name} args_hash={h} args_preview=\"{prev}\"")
375
408
 
@@ -398,15 +431,25 @@ def trace_tool_result(
398
431
  preview = _sanitize(_preview(result_preview, 120))
399
432
 
400
433
  if _level == TraceLevel.STORY:
401
- # ── STORY: narrative result ──
402
434
  if error:
403
435
  err_short = _sanitize(_preview(str(error), 80))
404
436
  _rich(f" [bold red]❌ ERROR[/bold red] [dim]{err_short} ({latency_ms}ms)[/dim]")
405
437
  else:
406
438
  _rich(f" [green]✅ ok[/green] [dim]{result_chars} chars {latency_ms}ms[/dim]"
407
439
  f" [dim italic]{preview[:80]}[/dim italic]")
440
+
441
+ elif _level == TraceLevel.FULL:
442
+ if error:
443
+ err_short = _sanitize(_preview(str(error), 100))
444
+ _rich(f" [bold red]→ ❌ {err_short}[/bold red] [dim]({latency_ms}ms)[/dim]")
445
+ else:
446
+ _rich(f" [bold green]→ ✅ {result_chars} chars[/bold green] [dim]({latency_ms}ms)[/dim]")
447
+ if result_preview:
448
+ for ln in result_preview.split("\n")[:_max_lines]:
449
+ _rich(f" [cyan]{_sanitize(ln[:200])}[/cyan]")
450
+
408
451
  else:
409
- # ── COMPACT/FULL: technical ──
452
+ # COMPACT
410
453
  status = "err" if error else "ok"
411
454
  line = f"TOOL← {status} {tool_name} chars={result_chars} latency={latency_ms}ms"
412
455
  if error:
@@ -415,10 +458,6 @@ def trace_tool_result(
415
458
  line += f" preview=\"{preview}\""
416
459
  _emit(line)
417
460
 
418
- if _level >= TraceLevel.FULL and result_preview:
419
- for ln in result_preview.split("\n")[:_max_lines]:
420
- _emit(f" | {_sanitize(ln[:200])}")
421
-
422
461
  _emit_jsonl({
423
462
  "event": "tool_result",
424
463
  "tool_name": tool_name,
@@ -538,6 +577,18 @@ def trace_memory_recall(
538
577
  content = _sanitize(_preview(snip.get("content", ""), 120))
539
578
  color = "green" if level == "HIGH" else "dim"
540
579
  _rich(f" [{color}]\\[{level}][/{color}] {content}")
580
+ elif _level == TraceLevel.FULL:
581
+ snips = list(top_snippets or [])
582
+ _rich(f"[bold #cc99ff]🧠 MEMORY[/bold #cc99ff] [dim]({source}, {result_count} hits)[/dim]")
583
+ for i, snip in enumerate(snips[:4]):
584
+ is_last = (i == min(3, len(snips) - 1))
585
+ branch = "└─" if is_last else "├─"
586
+ level = snip.get("level", "?")
587
+ content = _sanitize(_preview(snip.get("content", ""), 120))
588
+ color = "yellow" if level == "HIGH" else "dim"
589
+ _rich(f" [{color}]{branch} [{level}][/{color}] {content}")
590
+ if not snips:
591
+ _rich(f" [dim]└─ (no snippets)[/dim]")
541
592
  else:
542
593
  _emit(f"MEMORY {source}: {entity_count} entities → {result_count} results")
543
594
  for snip in (top_snippets or [])[:2]:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codent"
7
- version = "0.3.1"
7
+ version = "0.3.3"
8
8
  requires-python = ">=3.10"
9
9
  dependencies = [
10
10
  "rich>=13.0",
@@ -26,7 +26,14 @@ cont = "cont:main"
26
26
 
27
27
  [tool.setuptools]
28
28
  py-modules = ["cont", "cont_watcher"]
29
- packages = ["core", "cont_ui"]
29
+ packages = ["core", "cont_ui", "vendor", "vendor.wheels", "vendor.bin"]
30
+
31
+ [tool.setuptools.package-dir]
32
+ vendor = "vendor"
33
+ "vendor.wheels" = "vendor/wheels"
34
+ "vendor.bin" = "vendor/bin"
30
35
 
31
36
  [tool.setuptools.package-data]
32
37
  "*" = ["*.toml", "*.html", "*.yaml"]
38
+ "vendor.wheels" = ["*.whl"]
39
+ "vendor.bin" = ["ollama"]
File without changes
File without changes
Binary file
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes