cct-cli 0.7.9.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 (325) hide show
  1. calc_terminal/__init__.py +14 -0
  2. calc_terminal/__main__.py +14 -0
  3. calc_terminal/activity.py +1334 -0
  4. calc_terminal/agent.py +3387 -0
  5. calc_terminal/agent_runtime.py +519 -0
  6. calc_terminal/ai_context.py +447 -0
  7. calc_terminal/ai_modes.py +752 -0
  8. calc_terminal/ai_personalization.py +286 -0
  9. calc_terminal/ai_preview_feedback.py +213 -0
  10. calc_terminal/aicore.py +2572 -0
  11. calc_terminal/anim.py +367 -0
  12. calc_terminal/app.py +3685 -0
  13. calc_terminal/art.py +639 -0
  14. calc_terminal/atomsim.py +368 -0
  15. calc_terminal/attachments.py +743 -0
  16. calc_terminal/benchmark_system.py +414 -0
  17. calc_terminal/browser/__init__.py +36 -0
  18. calc_terminal/browser/browser_state.py +346 -0
  19. calc_terminal/browser/devserver.py +176 -0
  20. calc_terminal/browser/engine.py +494 -0
  21. calc_terminal/browser/navigation.py +84 -0
  22. calc_terminal/browser/preview.py +429 -0
  23. calc_terminal/browser/preview_entry.py +95 -0
  24. calc_terminal/browser/project_detector.py +144 -0
  25. calc_terminal/browser/server.py +449 -0
  26. calc_terminal/browser/state.py +75 -0
  27. calc_terminal/browser/watcher.py +99 -0
  28. calc_terminal/browser_gui/__init__.py +1 -0
  29. calc_terminal/browser_gui/__main__.py +3 -0
  30. calc_terminal/browser_gui/launcher.py +173 -0
  31. calc_terminal/browser_gui/playwright_browser.py +117 -0
  32. calc_terminal/browser_gui/qt_browser.py +1501 -0
  33. calc_terminal/browser_gui/webview_browser.py +57 -0
  34. calc_terminal/capabilities/__init__.py +35 -0
  35. calc_terminal/capabilities/adapters/__init__.py +33 -0
  36. calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
  37. calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
  38. calc_terminal/capabilities/adapters/filesystem.py +206 -0
  39. calc_terminal/capabilities/adapters/git_adapter.py +202 -0
  40. calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
  41. calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
  42. calc_terminal/capabilities/adapters/platforms.py +200 -0
  43. calc_terminal/capabilities/adapters/python_exec.py +93 -0
  44. calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
  45. calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
  46. calc_terminal/capabilities/adapters/structural_bio.py +161 -0
  47. calc_terminal/capabilities/adapters/terminal.py +99 -0
  48. calc_terminal/capabilities/bus.py +178 -0
  49. calc_terminal/capabilities/discovery.py +207 -0
  50. calc_terminal/capabilities/schema.py +221 -0
  51. calc_terminal/cat.ico +0 -0
  52. calc_terminal/cat_browser.py +2018 -0
  53. calc_terminal/chat_store.py +703 -0
  54. calc_terminal/cli.py +1178 -0
  55. calc_terminal/code_editor.py +640 -0
  56. calc_terminal/collaboration.py +723 -0
  57. calc_terminal/commands_data.py +139 -0
  58. calc_terminal/compatibility_engine.py +352 -0
  59. calc_terminal/compute/__init__.py +31 -0
  60. calc_terminal/compute/fabric.py +350 -0
  61. calc_terminal/config.py +227 -0
  62. calc_terminal/core/__init__.py +41 -0
  63. calc_terminal/core/checkpoint.py +156 -0
  64. calc_terminal/core/input/__init__.py +45 -0
  65. calc_terminal/core/mode_registry.py +300 -0
  66. calc_terminal/core/project_graph.py +172 -0
  67. calc_terminal/core/recovery.py +129 -0
  68. calc_terminal/core/security_layer.py +112 -0
  69. calc_terminal/core/task_graph.py +202 -0
  70. calc_terminal/core/unified_runtime.py +184 -0
  71. calc_terminal/core/verification.py +257 -0
  72. calc_terminal/customization.py +1566 -0
  73. calc_terminal/derivations.py +153 -0
  74. calc_terminal/device_control.py +263 -0
  75. calc_terminal/diagnostics/__init__.py +27 -0
  76. calc_terminal/diagnostics/doctor_engine.py +382 -0
  77. calc_terminal/diagnostics/self_test.py +247 -0
  78. calc_terminal/doctor.py +519 -0
  79. calc_terminal/easter_eggs.py +274 -0
  80. calc_terminal/editor/__init__.py +1 -0
  81. calc_terminal/editor/actions.py +263 -0
  82. calc_terminal/editor/commands.py +160 -0
  83. calc_terminal/editor/shortcuts.py +226 -0
  84. calc_terminal/engine.py +259 -0
  85. calc_terminal/errors.py +120 -0
  86. calc_terminal/event_stream.py +146 -0
  87. calc_terminal/eventbus.py +133 -0
  88. calc_terminal/extensions.py +733 -0
  89. calc_terminal/fallback_cli.py +1321 -0
  90. calc_terminal/first_run.py +265 -0
  91. calc_terminal/fomoji_auth.py +1043 -0
  92. calc_terminal/formulas.py +82 -0
  93. calc_terminal/fs_cache.py +121 -0
  94. calc_terminal/fs_watcher.py +277 -0
  95. calc_terminal/game.py +193 -0
  96. calc_terminal/gen1.py +5 -0
  97. calc_terminal/generators.py +245 -0
  98. calc_terminal/gestures/__init__.py +42 -0
  99. calc_terminal/gestures/bindings.py +175 -0
  100. calc_terminal/gestures/manager.py +477 -0
  101. calc_terminal/goodbye.py +363 -0
  102. calc_terminal/gpu3d.py +290 -0
  103. calc_terminal/graphs.py +358 -0
  104. calc_terminal/hardware_analyzer.py +440 -0
  105. calc_terminal/host/__init__.py +30 -0
  106. calc_terminal/host/browser_manager.py +187 -0
  107. calc_terminal/host/desktop.py +1386 -0
  108. calc_terminal/host/launcher.py +395 -0
  109. calc_terminal/host/terminal.py +279 -0
  110. calc_terminal/identity.py +216 -0
  111. calc_terminal/input/__init__.py +54 -0
  112. calc_terminal/input/capabilities.py +258 -0
  113. calc_terminal/input/focus.py +87 -0
  114. calc_terminal/input/gestures.py +64 -0
  115. calc_terminal/input/pointer.py +114 -0
  116. calc_terminal/input/touch.py +345 -0
  117. calc_terminal/keys.py +84 -0
  118. calc_terminal/live_automation.py +165 -0
  119. calc_terminal/mathtext.py +433 -0
  120. calc_terminal/mcp.py +386 -0
  121. calc_terminal/memory.py +337 -0
  122. calc_terminal/memory_v2.py +479 -0
  123. calc_terminal/metrics.py +333 -0
  124. calc_terminal/mode_detection.py +146 -0
  125. calc_terminal/model.py +2431 -0
  126. calc_terminal/model_router.py +665 -0
  127. calc_terminal/models/__init__.py +0 -0
  128. calc_terminal/models/active_state.py +187 -0
  129. calc_terminal/models/dynamic_registry.py +584 -0
  130. calc_terminal/models/manager.py +781 -0
  131. calc_terminal/models/model_metadata.json +3526 -0
  132. calc_terminal/models/profiles.py +194 -0
  133. calc_terminal/models/registry.py +265 -0
  134. calc_terminal/models/schema.py +197 -0
  135. calc_terminal/models/validator.py +287 -0
  136. calc_terminal/models/verification_engine.py +368 -0
  137. calc_terminal/native_picker.py +215 -0
  138. calc_terminal/ollama_catalog.py +279 -0
  139. calc_terminal/ollama_download.py +233 -0
  140. calc_terminal/orchestrator.py +304 -0
  141. calc_terminal/package_research.py +322 -0
  142. calc_terminal/packages.py +1024 -0
  143. calc_terminal/pc_specs.py +116 -0
  144. calc_terminal/permissions.py +334 -0
  145. calc_terminal/pet.py +106 -0
  146. calc_terminal/pipeline.py +505 -0
  147. calc_terminal/platform/__init__.py +491 -0
  148. calc_terminal/platform/desktop.py +491 -0
  149. calc_terminal/platform/web.py +781 -0
  150. calc_terminal/preview/__init__.py +1 -0
  151. calc_terminal/preview/dev_server.py +303 -0
  152. calc_terminal/preview/diagnostics.py +131 -0
  153. calc_terminal/preview/live_reload.py +66 -0
  154. calc_terminal/preview/manager.py +129 -0
  155. calc_terminal/project_stats.py +209 -0
  156. calc_terminal/projects.py +328 -0
  157. calc_terminal/providers/__init__.py +0 -0
  158. calc_terminal/providers/adapters/__init__.py +80 -0
  159. calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
  160. calc_terminal/providers/adapters/base.py +106 -0
  161. calc_terminal/providers/adapters/chinese_adapters.py +420 -0
  162. calc_terminal/providers/adapters/gemini_adapter.py +101 -0
  163. calc_terminal/providers/adapters/ollama_adapter.py +83 -0
  164. calc_terminal/providers/adapters/openai_adapter.py +159 -0
  165. calc_terminal/providers/adapters/other_adapters.py +246 -0
  166. calc_terminal/providers/anthropic_provider.py +172 -0
  167. calc_terminal/providers/auto_update.py +416 -0
  168. calc_terminal/providers/base_provider.py +105 -0
  169. calc_terminal/providers/discovery_manager.py +207 -0
  170. calc_terminal/providers/gemini_provider.py +178 -0
  171. calc_terminal/providers/lifecycle.py +767 -0
  172. calc_terminal/providers/ollama_adapter.py +707 -0
  173. calc_terminal/providers/openai_provider.py +254 -0
  174. calc_terminal/providers/provider_manager.py +1827 -0
  175. calc_terminal/providers/providers.json +4075 -0
  176. calc_terminal/reactionsim.py +279 -0
  177. calc_terminal/registry.py +337 -0
  178. calc_terminal/report.py +162 -0
  179. calc_terminal/research/__init__.py +45 -0
  180. calc_terminal/research/artifact_intel.py +126 -0
  181. calc_terminal/research/data_lineage.py +123 -0
  182. calc_terminal/research/experiment_ledger.py +303 -0
  183. calc_terminal/research/reproducibility.py +131 -0
  184. calc_terminal/resilience/__init__.py +47 -0
  185. calc_terminal/resilience/agent_state.py +121 -0
  186. calc_terminal/resilience/capability_matcher.py +174 -0
  187. calc_terminal/resilience/circuit_breaker.py +158 -0
  188. calc_terminal/resilience/failover_engine.py +230 -0
  189. calc_terminal/resilience/health_monitor.py +192 -0
  190. calc_terminal/resilience/ollama_adapter.py +125 -0
  191. calc_terminal/resilience/orchestrator.py +312 -0
  192. calc_terminal/resilience/types.py +134 -0
  193. calc_terminal/sandbox.py +98 -0
  194. calc_terminal/scires.py +558 -0
  195. calc_terminal/security_scanner.py +126 -0
  196. calc_terminal/session.py +294 -0
  197. calc_terminal/sim3d.py +206 -0
  198. calc_terminal/solver.py +276 -0
  199. calc_terminal/sound.py +127 -0
  200. calc_terminal/task_reports.py +287 -0
  201. calc_terminal/terminal_host.py +201 -0
  202. calc_terminal/terminal_identity.py +411 -0
  203. calc_terminal/test_ai_mode_reliability.py +344 -0
  204. calc_terminal/test_browser.py +368 -0
  205. calc_terminal/test_code_editor_upgrade.py +485 -0
  206. calc_terminal/test_customization.py +1148 -0
  207. calc_terminal/test_customization_ui.py +612 -0
  208. calc_terminal/test_dynamic_registry.py +304 -0
  209. calc_terminal/test_extensions.py +436 -0
  210. calc_terminal/test_overhaul.py +557 -0
  211. calc_terminal/test_project_detect.py +255 -0
  212. calc_terminal/test_root_cause_fix.py +527 -0
  213. calc_terminal/test_stability.py +532 -0
  214. calc_terminal/test_terminal_identity.py +132 -0
  215. calc_terminal/test_v079_speed.py +460 -0
  216. calc_terminal/theme.py +1107 -0
  217. calc_terminal/timeline.py +139 -0
  218. calc_terminal/todos.py +246 -0
  219. calc_terminal/tool_call_normalizer.py +419 -0
  220. calc_terminal/tui.py +104 -0
  221. calc_terminal/ui/__init__.py +8 -0
  222. calc_terminal/ui/activity_panel.py +231 -0
  223. calc_terminal/ui/activity_stream_panel.py +238 -0
  224. calc_terminal/ui/animations.py +122 -0
  225. calc_terminal/ui/app.py +7271 -0
  226. calc_terminal/ui/attach_panel.py +597 -0
  227. calc_terminal/ui/attachments.py +424 -0
  228. calc_terminal/ui/backup_panel.py +810 -0
  229. calc_terminal/ui/browser_shell.py +887 -0
  230. calc_terminal/ui/cat_agent.py +357 -0
  231. calc_terminal/ui/chats_panel.py +899 -0
  232. calc_terminal/ui/command_palette.py +125 -0
  233. calc_terminal/ui/command_palette_modal.py +166 -0
  234. calc_terminal/ui/composer.py +1141 -0
  235. calc_terminal/ui/context_menu.py +197 -0
  236. calc_terminal/ui/conversation.py +1435 -0
  237. calc_terminal/ui/customization_panel.py +1229 -0
  238. calc_terminal/ui/dashboard.py +404 -0
  239. calc_terminal/ui/design_system.py +557 -0
  240. calc_terminal/ui/diff_panel.py +213 -0
  241. calc_terminal/ui/editor.py +2102 -0
  242. calc_terminal/ui/empty_state.py +302 -0
  243. calc_terminal/ui/events.py +487 -0
  244. calc_terminal/ui/extensions_panel.py +815 -0
  245. calc_terminal/ui/footer.py +166 -0
  246. calc_terminal/ui/gestures_panel.py +383 -0
  247. calc_terminal/ui/goodbye_screen.py +100 -0
  248. calc_terminal/ui/header.py +1034 -0
  249. calc_terminal/ui/help_panel.py +254 -0
  250. calc_terminal/ui/live_activities.py +914 -0
  251. calc_terminal/ui/mcp_panel.py +570 -0
  252. calc_terminal/ui/memory_center.py +524 -0
  253. calc_terminal/ui/mode_colors_panel.py +525 -0
  254. calc_terminal/ui/nav_screens.py +747 -0
  255. calc_terminal/ui/ollama_panel.py +536 -0
  256. calc_terminal/ui/palette.py +221 -0
  257. calc_terminal/ui/permission_panel.py +269 -0
  258. calc_terminal/ui/personalization_panel.py +517 -0
  259. calc_terminal/ui/personalize_center.py +1568 -0
  260. calc_terminal/ui/preview_panel.py +441 -0
  261. calc_terminal/ui/resizers.py +402 -0
  262. calc_terminal/ui/sidebar.py +1285 -0
  263. calc_terminal/ui/statusbar.py +168 -0
  264. calc_terminal/ui/theme_css.py +1396 -0
  265. calc_terminal/ui/thinking.py +226 -0
  266. calc_terminal/ui/timeline_panel.py +102 -0
  267. calc_terminal/ui/todo_panel.py +193 -0
  268. calc_terminal/ui/viewport.py +136 -0
  269. calc_terminal/ui/vision_panel.py +489 -0
  270. calc_terminal/ui/welcome_modal.py +343 -0
  271. calc_terminal/ui/widgets.py +160 -0
  272. calc_terminal/ui/workspace.py +831 -0
  273. calc_terminal/viewers/__init__.py +1 -0
  274. calc_terminal/viewers/document_viewer.py +252 -0
  275. calc_terminal/viewers/image_viewer.py +241 -0
  276. calc_terminal/viewers/pdf_viewer.py +203 -0
  277. calc_terminal/viewers/presentation_viewer.py +164 -0
  278. calc_terminal/viewers/registry.py +120 -0
  279. calc_terminal/viewers/spreadsheet_viewer.py +204 -0
  280. calc_terminal/vision/__init__.py +89 -0
  281. calc_terminal/vision/analysis.py +194 -0
  282. calc_terminal/vision/annotations.py +297 -0
  283. calc_terminal/vision/capture.py +171 -0
  284. calc_terminal/vision/context.py +231 -0
  285. calc_terminal/vision/correlation.py +169 -0
  286. calc_terminal/vision/cursor.py +258 -0
  287. calc_terminal/vision/events.py +66 -0
  288. calc_terminal/vision/frame_pipeline.py +259 -0
  289. calc_terminal/vision/priority.py +218 -0
  290. calc_terminal/vision/provider.py +180 -0
  291. calc_terminal/vision/safety.py +149 -0
  292. calc_terminal/vision/session.py +281 -0
  293. calc_terminal/vision/verify.py +162 -0
  294. calc_terminal/vision.py +514 -0
  295. calc_terminal/vscode_integration.py +113 -0
  296. calc_terminal/web/__init__.py +8 -0
  297. calc_terminal/web/cat_runtime.py +710 -0
  298. calc_terminal/web/server.py +2891 -0
  299. calc_terminal/web/static/css/app.css +3152 -0
  300. calc_terminal/web/static/icons/badge-72.png +0 -0
  301. calc_terminal/web/static/icons/cat.ico +0 -0
  302. calc_terminal/web/static/icons/icon-128.png +0 -0
  303. calc_terminal/web/static/icons/icon-144.png +0 -0
  304. calc_terminal/web/static/icons/icon-152.png +0 -0
  305. calc_terminal/web/static/icons/icon-192.png +0 -0
  306. calc_terminal/web/static/icons/icon-384.png +0 -0
  307. calc_terminal/web/static/icons/icon-512.png +0 -0
  308. calc_terminal/web/static/icons/icon-72.png +0 -0
  309. calc_terminal/web/static/icons/icon-96.png +0 -0
  310. calc_terminal/web/static/icons/icon.svg +34 -0
  311. calc_terminal/web/static/icons/new-project.png +0 -0
  312. calc_terminal/web/static/icons/open-project.png +0 -0
  313. calc_terminal/web/static/index.html +734 -0
  314. calc_terminal/web/static/js/app.js +2403 -0
  315. calc_terminal/web/static/manifest.json +88 -0
  316. calc_terminal/web/static/sw.js +230 -0
  317. calc_terminal/workflow_engine.py +769 -0
  318. calc_terminal/workspace.py +593 -0
  319. calc_terminal/workspace_index.py +385 -0
  320. cct_cli-0.7.9.0.dist-info/METADATA +210 -0
  321. cct_cli-0.7.9.0.dist-info/RECORD +325 -0
  322. cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
  323. cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
  324. cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
  325. cct_cli-0.7.9.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,257 @@
1
+ """
2
+ CAT Reality & Verification Engine per §8, §85, §86:
3
+ Never report successful completion based solely on model text.
4
+ Every AI claim is verified against genuine execution evidence.
5
+ Provides the `/cat verify` project quality pipeline.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import ast
11
+ import json
12
+ import os
13
+ import shutil
14
+ import subprocess
15
+ import time
16
+ import urllib.request
17
+ from dataclasses import dataclass, field
18
+ from enum import Enum
19
+ from typing import Any, Callable, Dict, List, Optional, Tuple
20
+
21
+
22
+ class RealityStatus(str, Enum):
23
+ VERIFIED = "VERIFIED"
24
+ NOT_VERIFIED = "NOT_VERIFIED"
25
+ PARTIALLY_VERIFIED = "PARTIALLY_VERIFIED"
26
+ FAILED = "FAILED"
27
+
28
+
29
+ @dataclass
30
+ class EvidenceItem:
31
+ check: str
32
+ passed: bool
33
+ details: str = ""
34
+ timestamp: float = field(default_factory=time.time)
35
+
36
+ def to_dict(self) -> Dict[str, Any]:
37
+ return {
38
+ "check": self.check,
39
+ "passed": self.passed,
40
+ "details": self.details,
41
+ "timestamp": self.timestamp,
42
+ }
43
+
44
+
45
+ @dataclass
46
+ class RealityVerification:
47
+ claim: str
48
+ status: RealityStatus
49
+ evidence: List[EvidenceItem] = field(default_factory=list)
50
+ summary: str = ""
51
+ timestamp: float = field(default_factory=time.time)
52
+
53
+ def to_dict(self) -> Dict[str, Any]:
54
+ return {
55
+ "claim": self.claim,
56
+ "status": self.status.value,
57
+ "evidence": [e.to_dict() for e in self.evidence],
58
+ "summary": self.summary,
59
+ "timestamp": self.timestamp,
60
+ }
61
+
62
+
63
+ class RealityEngine:
64
+ """Verifies AI assertions against real filesystem, runtime, network, and test evidence."""
65
+
66
+ def verify_file_modified(self, file_path: str, expected_snippet: str = "") -> EvidenceItem:
67
+ abs_path = os.path.abspath(os.path.expanduser(file_path))
68
+ if not os.path.exists(abs_path):
69
+ return EvidenceItem(check=f"File exists: {file_path}", passed=False, details="File does not exist on disk")
70
+ if expected_snippet:
71
+ try:
72
+ with open(abs_path, "r", encoding="utf-8", errors="replace") as f:
73
+ content = f.read()
74
+ if expected_snippet in content:
75
+ return EvidenceItem(check=f"File contains expected snippet: {file_path}", passed=True, details="Snippet verified in file")
76
+ return EvidenceItem(check=f"File contains expected snippet: {file_path}", passed=False, details="Snippet missing from file")
77
+ except Exception as e:
78
+ return EvidenceItem(check=f"File read: {file_path}", passed=False, details=str(e))
79
+ return EvidenceItem(check=f"File exists: {file_path}", passed=True, details=f"File exists ({os.path.getsize(abs_path)} bytes)")
80
+
81
+ def verify_python_syntax(self, file_path: str) -> EvidenceItem:
82
+ abs_path = os.path.abspath(os.path.expanduser(file_path))
83
+ if not os.path.exists(abs_path):
84
+ return EvidenceItem(check="Python syntax check", passed=False, details=f"File not found: {file_path}")
85
+ try:
86
+ with open(abs_path, "r", encoding="utf-8", errors="replace") as f:
87
+ code = f.read()
88
+ ast.parse(code, filename=abs_path)
89
+ return EvidenceItem(check=f"Python syntax valid: {os.path.basename(file_path)}", passed=True, details="AST parse succeeded with 0 errors")
90
+ except SyntaxError as se:
91
+ return EvidenceItem(check=f"Python syntax valid: {os.path.basename(file_path)}", passed=False, details=f"SyntaxError at line {se.lineno}: {se.msg}")
92
+ except Exception as e:
93
+ return EvidenceItem(check=f"Python syntax valid: {os.path.basename(file_path)}", passed=False, details=str(e))
94
+
95
+ def verify_ast_syntax(self, code_str: str) -> EvidenceItem:
96
+ """Directly verify syntax of a code snippet via Python AST."""
97
+ try:
98
+ ast.parse(code_str)
99
+ return EvidenceItem(check="AST syntax validation", passed=True, details="Code parsed successfully with 0 errors")
100
+ except SyntaxError as se:
101
+ return EvidenceItem(check="AST syntax validation", passed=False, details=f"SyntaxError at line {se.lineno}: {se.msg}")
102
+ except Exception as e:
103
+ return EvidenceItem(check="AST syntax validation", passed=False, details=str(e))
104
+
105
+ def verify_http_endpoint(self, url: str, expected_code: int = 200, timeout: float = 5.0) -> EvidenceItem:
106
+ try:
107
+ req = urllib.request.Request(url, headers={"User-Agent": "CAT-Reality-Engine/1.0"})
108
+ with urllib.request.urlopen(req, timeout=timeout) as resp:
109
+ code = resp.getcode()
110
+ passed = (code == expected_code)
111
+ return EvidenceItem(check=f"HTTP endpoint {url}", passed=passed, details=f"HTTP status code {code} (expected {expected_code})")
112
+ except urllib.error.HTTPError as he:
113
+ passed = (he.code == expected_code)
114
+ return EvidenceItem(check=f"HTTP endpoint {url}", passed=passed, details=f"HTTP status code {he.code}")
115
+ except Exception as e:
116
+ return EvidenceItem(check=f"HTTP endpoint {url}", passed=False, details=f"Connection failed: {e}")
117
+
118
+ def verify_browser_state(self) -> EvidenceItem:
119
+ try:
120
+ from ..browser.preview import PreviewController
121
+ ctrl = PreviewController.instance() if hasattr(PreviewController, "instance") else None
122
+ if ctrl and ctrl.running and ctrl.engine and ctrl.engine.available:
123
+ snap = ctrl.engine.snapshot()
124
+ err_count = len(snap.js_errors)
125
+ passed = (err_count == 0)
126
+ details = f"DOM loaded: '{snap.title}', console errors: {err_count}"
127
+ return EvidenceItem(check="Browser DOM & Console errors", passed=passed, details=details)
128
+ return EvidenceItem(check="Browser verification", passed=True, details="Browser preview not currently active (skipped)")
129
+ except Exception as e:
130
+ return EvidenceItem(check="Browser verification", passed=False, details=str(e))
131
+
132
+ def verify_test_suite(self, test_command: str = "pytest -q", cwd: Optional[str] = None, timeout: float = 60.0) -> EvidenceItem:
133
+ work_dir = cwd or os.getcwd()
134
+ try:
135
+ proc = subprocess.run(
136
+ test_command,
137
+ cwd=work_dir,
138
+ shell=True,
139
+ stdout=subprocess.PIPE,
140
+ stderr=subprocess.PIPE,
141
+ text=True,
142
+ timeout=timeout,
143
+ )
144
+ passed = (proc.returncode == 0)
145
+ details = proc.stdout.strip() or proc.stderr.strip()
146
+ # truncate for report
147
+ if len(details) > 300:
148
+ details = details[:300] + "..."
149
+ return EvidenceItem(check=f"Tests ({test_command})", passed=passed, details=details)
150
+ except subprocess.TimeoutExpired:
151
+ return EvidenceItem(check=f"Tests ({test_command})", passed=False, details=f"Test run timed out after {timeout}s")
152
+ except Exception as e:
153
+ return EvidenceItem(check=f"Tests ({test_command})", passed=False, details=str(e))
154
+
155
+ def verify_claim(self, claim: str, checks: List[Callable[[], EvidenceItem]]) -> RealityVerification:
156
+ evidence = []
157
+ for chk in checks:
158
+ try:
159
+ evidence.append(chk())
160
+ except Exception as e:
161
+ evidence.append(EvidenceItem(check="Verification check", passed=False, details=str(e)))
162
+
163
+ if not evidence:
164
+ status = RealityStatus.NOT_VERIFIED
165
+ summary = "No verifiable evidence provided."
166
+ elif all(e.passed for e in evidence):
167
+ status = RealityStatus.VERIFIED
168
+ summary = f"All {len(evidence)} checks passed with verified execution evidence."
169
+ elif any(e.passed for e in evidence):
170
+ status = RealityStatus.PARTIALLY_VERIFIED
171
+ failed_count = sum(1 for e in evidence if not e.passed)
172
+ summary = f"{len(evidence) - failed_count} passed, {failed_count} checks failed."
173
+ else:
174
+ status = RealityStatus.FAILED
175
+ summary = "All verification checks failed."
176
+
177
+ return RealityVerification(claim=claim, status=status, evidence=evidence, summary=summary)
178
+
179
+ def run_cat_verify(self, workspace_path: Optional[str] = None) -> Dict[str, Any]:
180
+ """Project-aware quality pipeline for `/cat verify` per §85."""
181
+ ws = workspace_path or os.getcwd()
182
+ evidence: List[EvidenceItem] = []
183
+
184
+ # 1. Syntax Check across python files in workspace
185
+ py_files = []
186
+ for root, dirs, files in os.walk(ws):
187
+ dirs[:] = [d for d in dirs if not d.startswith(".") and d not in ("venv", "node_modules", "__pycache__")]
188
+ for f in files:
189
+ if f.endswith(".py"):
190
+ py_files.append(os.path.join(root, f))
191
+ if len(py_files) >= 50:
192
+ break
193
+ if len(py_files) >= 50:
194
+ break
195
+
196
+ syntax_fails = 0
197
+ for pf in py_files:
198
+ ev = self.verify_python_syntax(pf)
199
+ if not ev.passed:
200
+ evidence.append(ev)
201
+ syntax_fails += 1
202
+
203
+ if syntax_fails == 0:
204
+ evidence.append(EvidenceItem(check="Python AST Syntax", passed=True, details=f"Verified {len(py_files)} source files clean"))
205
+
206
+ # 2. Dependency Health
207
+ has_reqs = os.path.exists(os.path.join(ws, "requirements.txt")) or os.path.exists(os.path.join(ws, "pyproject.toml"))
208
+ evidence.append(EvidenceItem(check="Project manifests", passed=has_reqs, details="requirements.txt / pyproject.toml present" if has_reqs else "No package manifest found"))
209
+
210
+ # 3. Git status
211
+ if shutil.which("git"):
212
+ try:
213
+ proc = subprocess.run(["git", "status", "--porcelain"], cwd=ws, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=5.0)
214
+ if proc.returncode == 0:
215
+ mod_count = len(proc.stdout.strip().splitlines()) if proc.stdout.strip() else 0
216
+ evidence.append(EvidenceItem(check="Git Working Tree", passed=True, details=f"{mod_count} uncommitted change(s)"))
217
+ except Exception:
218
+ pass
219
+
220
+ # 4. Tests (if pytest or test dir present)
221
+ tests_dir = os.path.join(ws, "tests") or os.path.join(ws, "test")
222
+ if os.path.exists(tests_dir) or os.path.exists(os.path.join(ws, "calc_terminal", "tests")):
223
+ # Fast test probe
224
+ test_ev = self.verify_test_suite(test_command="pytest --collect-only -q", cwd=ws, timeout=10.0)
225
+ evidence.append(test_ev)
226
+
227
+ passed_all = all(e.passed for e in evidence)
228
+ return {
229
+ "status": RealityStatus.VERIFIED.value if passed_all else RealityStatus.PARTIALLY_VERIFIED.value,
230
+ "passed": passed_all,
231
+ "evidence": [e.to_dict() for e in evidence],
232
+ "workspace": ws,
233
+ "summary": "Project verified healthy" if passed_all else "Verification found warnings or issues",
234
+ }
235
+
236
+ def format_evidence_table(self, verify_result: Dict[str, Any]) -> str:
237
+ """Format verify result dictionary into ASCII evidence table."""
238
+ lines = [
239
+ "============================================================",
240
+ " VERIFICATION EVIDENCE TABLE ",
241
+ "============================================================",
242
+ f"Workspace: {verify_result.get('workspace', '')}",
243
+ f"Status: {verify_result.get('status', '')} \u2014 {verify_result.get('summary', '')}",
244
+ "------------------------------------------------------------",
245
+ f"{'Check':<30} | {'Status':<6} | Details",
246
+ "------------------------------------------------------------",
247
+ ]
248
+ for ev in verify_result.get("evidence", []):
249
+ st = "PASS" if ev.get("passed") else "FAIL"
250
+ chk = ev.get("check", "")[:30]
251
+ det = ev.get("details", "")[:35]
252
+ lines.append(f"{chk:<30} | {st:<6} | {det}")
253
+ lines.append("============================================================")
254
+ return "\n".join(lines)
255
+
256
+
257
+ reality_engine = RealityEngine()