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.
- calc_terminal/__init__.py +14 -0
- calc_terminal/__main__.py +14 -0
- calc_terminal/activity.py +1334 -0
- calc_terminal/agent.py +3387 -0
- calc_terminal/agent_runtime.py +519 -0
- calc_terminal/ai_context.py +447 -0
- calc_terminal/ai_modes.py +752 -0
- calc_terminal/ai_personalization.py +286 -0
- calc_terminal/ai_preview_feedback.py +213 -0
- calc_terminal/aicore.py +2572 -0
- calc_terminal/anim.py +367 -0
- calc_terminal/app.py +3685 -0
- calc_terminal/art.py +639 -0
- calc_terminal/atomsim.py +368 -0
- calc_terminal/attachments.py +743 -0
- calc_terminal/benchmark_system.py +414 -0
- calc_terminal/browser/__init__.py +36 -0
- calc_terminal/browser/browser_state.py +346 -0
- calc_terminal/browser/devserver.py +176 -0
- calc_terminal/browser/engine.py +494 -0
- calc_terminal/browser/navigation.py +84 -0
- calc_terminal/browser/preview.py +429 -0
- calc_terminal/browser/preview_entry.py +95 -0
- calc_terminal/browser/project_detector.py +144 -0
- calc_terminal/browser/server.py +449 -0
- calc_terminal/browser/state.py +75 -0
- calc_terminal/browser/watcher.py +99 -0
- calc_terminal/browser_gui/__init__.py +1 -0
- calc_terminal/browser_gui/__main__.py +3 -0
- calc_terminal/browser_gui/launcher.py +173 -0
- calc_terminal/browser_gui/playwright_browser.py +117 -0
- calc_terminal/browser_gui/qt_browser.py +1501 -0
- calc_terminal/browser_gui/webview_browser.py +57 -0
- calc_terminal/capabilities/__init__.py +35 -0
- calc_terminal/capabilities/adapters/__init__.py +33 -0
- calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
- calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
- calc_terminal/capabilities/adapters/filesystem.py +206 -0
- calc_terminal/capabilities/adapters/git_adapter.py +202 -0
- calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
- calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
- calc_terminal/capabilities/adapters/platforms.py +200 -0
- calc_terminal/capabilities/adapters/python_exec.py +93 -0
- calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
- calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
- calc_terminal/capabilities/adapters/structural_bio.py +161 -0
- calc_terminal/capabilities/adapters/terminal.py +99 -0
- calc_terminal/capabilities/bus.py +178 -0
- calc_terminal/capabilities/discovery.py +207 -0
- calc_terminal/capabilities/schema.py +221 -0
- calc_terminal/cat.ico +0 -0
- calc_terminal/cat_browser.py +2018 -0
- calc_terminal/chat_store.py +703 -0
- calc_terminal/cli.py +1178 -0
- calc_terminal/code_editor.py +640 -0
- calc_terminal/collaboration.py +723 -0
- calc_terminal/commands_data.py +139 -0
- calc_terminal/compatibility_engine.py +352 -0
- calc_terminal/compute/__init__.py +31 -0
- calc_terminal/compute/fabric.py +350 -0
- calc_terminal/config.py +227 -0
- calc_terminal/core/__init__.py +41 -0
- calc_terminal/core/checkpoint.py +156 -0
- calc_terminal/core/input/__init__.py +45 -0
- calc_terminal/core/mode_registry.py +300 -0
- calc_terminal/core/project_graph.py +172 -0
- calc_terminal/core/recovery.py +129 -0
- calc_terminal/core/security_layer.py +112 -0
- calc_terminal/core/task_graph.py +202 -0
- calc_terminal/core/unified_runtime.py +184 -0
- calc_terminal/core/verification.py +257 -0
- calc_terminal/customization.py +1566 -0
- calc_terminal/derivations.py +153 -0
- calc_terminal/device_control.py +263 -0
- calc_terminal/diagnostics/__init__.py +27 -0
- calc_terminal/diagnostics/doctor_engine.py +382 -0
- calc_terminal/diagnostics/self_test.py +247 -0
- calc_terminal/doctor.py +519 -0
- calc_terminal/easter_eggs.py +274 -0
- calc_terminal/editor/__init__.py +1 -0
- calc_terminal/editor/actions.py +263 -0
- calc_terminal/editor/commands.py +160 -0
- calc_terminal/editor/shortcuts.py +226 -0
- calc_terminal/engine.py +259 -0
- calc_terminal/errors.py +120 -0
- calc_terminal/event_stream.py +146 -0
- calc_terminal/eventbus.py +133 -0
- calc_terminal/extensions.py +733 -0
- calc_terminal/fallback_cli.py +1321 -0
- calc_terminal/first_run.py +265 -0
- calc_terminal/fomoji_auth.py +1043 -0
- calc_terminal/formulas.py +82 -0
- calc_terminal/fs_cache.py +121 -0
- calc_terminal/fs_watcher.py +277 -0
- calc_terminal/game.py +193 -0
- calc_terminal/gen1.py +5 -0
- calc_terminal/generators.py +245 -0
- calc_terminal/gestures/__init__.py +42 -0
- calc_terminal/gestures/bindings.py +175 -0
- calc_terminal/gestures/manager.py +477 -0
- calc_terminal/goodbye.py +363 -0
- calc_terminal/gpu3d.py +290 -0
- calc_terminal/graphs.py +358 -0
- calc_terminal/hardware_analyzer.py +440 -0
- calc_terminal/host/__init__.py +30 -0
- calc_terminal/host/browser_manager.py +187 -0
- calc_terminal/host/desktop.py +1386 -0
- calc_terminal/host/launcher.py +395 -0
- calc_terminal/host/terminal.py +279 -0
- calc_terminal/identity.py +216 -0
- calc_terminal/input/__init__.py +54 -0
- calc_terminal/input/capabilities.py +258 -0
- calc_terminal/input/focus.py +87 -0
- calc_terminal/input/gestures.py +64 -0
- calc_terminal/input/pointer.py +114 -0
- calc_terminal/input/touch.py +345 -0
- calc_terminal/keys.py +84 -0
- calc_terminal/live_automation.py +165 -0
- calc_terminal/mathtext.py +433 -0
- calc_terminal/mcp.py +386 -0
- calc_terminal/memory.py +337 -0
- calc_terminal/memory_v2.py +479 -0
- calc_terminal/metrics.py +333 -0
- calc_terminal/mode_detection.py +146 -0
- calc_terminal/model.py +2431 -0
- calc_terminal/model_router.py +665 -0
- calc_terminal/models/__init__.py +0 -0
- calc_terminal/models/active_state.py +187 -0
- calc_terminal/models/dynamic_registry.py +584 -0
- calc_terminal/models/manager.py +781 -0
- calc_terminal/models/model_metadata.json +3526 -0
- calc_terminal/models/profiles.py +194 -0
- calc_terminal/models/registry.py +265 -0
- calc_terminal/models/schema.py +197 -0
- calc_terminal/models/validator.py +287 -0
- calc_terminal/models/verification_engine.py +368 -0
- calc_terminal/native_picker.py +215 -0
- calc_terminal/ollama_catalog.py +279 -0
- calc_terminal/ollama_download.py +233 -0
- calc_terminal/orchestrator.py +304 -0
- calc_terminal/package_research.py +322 -0
- calc_terminal/packages.py +1024 -0
- calc_terminal/pc_specs.py +116 -0
- calc_terminal/permissions.py +334 -0
- calc_terminal/pet.py +106 -0
- calc_terminal/pipeline.py +505 -0
- calc_terminal/platform/__init__.py +491 -0
- calc_terminal/platform/desktop.py +491 -0
- calc_terminal/platform/web.py +781 -0
- calc_terminal/preview/__init__.py +1 -0
- calc_terminal/preview/dev_server.py +303 -0
- calc_terminal/preview/diagnostics.py +131 -0
- calc_terminal/preview/live_reload.py +66 -0
- calc_terminal/preview/manager.py +129 -0
- calc_terminal/project_stats.py +209 -0
- calc_terminal/projects.py +328 -0
- calc_terminal/providers/__init__.py +0 -0
- calc_terminal/providers/adapters/__init__.py +80 -0
- calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
- calc_terminal/providers/adapters/base.py +106 -0
- calc_terminal/providers/adapters/chinese_adapters.py +420 -0
- calc_terminal/providers/adapters/gemini_adapter.py +101 -0
- calc_terminal/providers/adapters/ollama_adapter.py +83 -0
- calc_terminal/providers/adapters/openai_adapter.py +159 -0
- calc_terminal/providers/adapters/other_adapters.py +246 -0
- calc_terminal/providers/anthropic_provider.py +172 -0
- calc_terminal/providers/auto_update.py +416 -0
- calc_terminal/providers/base_provider.py +105 -0
- calc_terminal/providers/discovery_manager.py +207 -0
- calc_terminal/providers/gemini_provider.py +178 -0
- calc_terminal/providers/lifecycle.py +767 -0
- calc_terminal/providers/ollama_adapter.py +707 -0
- calc_terminal/providers/openai_provider.py +254 -0
- calc_terminal/providers/provider_manager.py +1827 -0
- calc_terminal/providers/providers.json +4075 -0
- calc_terminal/reactionsim.py +279 -0
- calc_terminal/registry.py +337 -0
- calc_terminal/report.py +162 -0
- calc_terminal/research/__init__.py +45 -0
- calc_terminal/research/artifact_intel.py +126 -0
- calc_terminal/research/data_lineage.py +123 -0
- calc_terminal/research/experiment_ledger.py +303 -0
- calc_terminal/research/reproducibility.py +131 -0
- calc_terminal/resilience/__init__.py +47 -0
- calc_terminal/resilience/agent_state.py +121 -0
- calc_terminal/resilience/capability_matcher.py +174 -0
- calc_terminal/resilience/circuit_breaker.py +158 -0
- calc_terminal/resilience/failover_engine.py +230 -0
- calc_terminal/resilience/health_monitor.py +192 -0
- calc_terminal/resilience/ollama_adapter.py +125 -0
- calc_terminal/resilience/orchestrator.py +312 -0
- calc_terminal/resilience/types.py +134 -0
- calc_terminal/sandbox.py +98 -0
- calc_terminal/scires.py +558 -0
- calc_terminal/security_scanner.py +126 -0
- calc_terminal/session.py +294 -0
- calc_terminal/sim3d.py +206 -0
- calc_terminal/solver.py +276 -0
- calc_terminal/sound.py +127 -0
- calc_terminal/task_reports.py +287 -0
- calc_terminal/terminal_host.py +201 -0
- calc_terminal/terminal_identity.py +411 -0
- calc_terminal/test_ai_mode_reliability.py +344 -0
- calc_terminal/test_browser.py +368 -0
- calc_terminal/test_code_editor_upgrade.py +485 -0
- calc_terminal/test_customization.py +1148 -0
- calc_terminal/test_customization_ui.py +612 -0
- calc_terminal/test_dynamic_registry.py +304 -0
- calc_terminal/test_extensions.py +436 -0
- calc_terminal/test_overhaul.py +557 -0
- calc_terminal/test_project_detect.py +255 -0
- calc_terminal/test_root_cause_fix.py +527 -0
- calc_terminal/test_stability.py +532 -0
- calc_terminal/test_terminal_identity.py +132 -0
- calc_terminal/test_v079_speed.py +460 -0
- calc_terminal/theme.py +1107 -0
- calc_terminal/timeline.py +139 -0
- calc_terminal/todos.py +246 -0
- calc_terminal/tool_call_normalizer.py +419 -0
- calc_terminal/tui.py +104 -0
- calc_terminal/ui/__init__.py +8 -0
- calc_terminal/ui/activity_panel.py +231 -0
- calc_terminal/ui/activity_stream_panel.py +238 -0
- calc_terminal/ui/animations.py +122 -0
- calc_terminal/ui/app.py +7271 -0
- calc_terminal/ui/attach_panel.py +597 -0
- calc_terminal/ui/attachments.py +424 -0
- calc_terminal/ui/backup_panel.py +810 -0
- calc_terminal/ui/browser_shell.py +887 -0
- calc_terminal/ui/cat_agent.py +357 -0
- calc_terminal/ui/chats_panel.py +899 -0
- calc_terminal/ui/command_palette.py +125 -0
- calc_terminal/ui/command_palette_modal.py +166 -0
- calc_terminal/ui/composer.py +1141 -0
- calc_terminal/ui/context_menu.py +197 -0
- calc_terminal/ui/conversation.py +1435 -0
- calc_terminal/ui/customization_panel.py +1229 -0
- calc_terminal/ui/dashboard.py +404 -0
- calc_terminal/ui/design_system.py +557 -0
- calc_terminal/ui/diff_panel.py +213 -0
- calc_terminal/ui/editor.py +2102 -0
- calc_terminal/ui/empty_state.py +302 -0
- calc_terminal/ui/events.py +487 -0
- calc_terminal/ui/extensions_panel.py +815 -0
- calc_terminal/ui/footer.py +166 -0
- calc_terminal/ui/gestures_panel.py +383 -0
- calc_terminal/ui/goodbye_screen.py +100 -0
- calc_terminal/ui/header.py +1034 -0
- calc_terminal/ui/help_panel.py +254 -0
- calc_terminal/ui/live_activities.py +914 -0
- calc_terminal/ui/mcp_panel.py +570 -0
- calc_terminal/ui/memory_center.py +524 -0
- calc_terminal/ui/mode_colors_panel.py +525 -0
- calc_terminal/ui/nav_screens.py +747 -0
- calc_terminal/ui/ollama_panel.py +536 -0
- calc_terminal/ui/palette.py +221 -0
- calc_terminal/ui/permission_panel.py +269 -0
- calc_terminal/ui/personalization_panel.py +517 -0
- calc_terminal/ui/personalize_center.py +1568 -0
- calc_terminal/ui/preview_panel.py +441 -0
- calc_terminal/ui/resizers.py +402 -0
- calc_terminal/ui/sidebar.py +1285 -0
- calc_terminal/ui/statusbar.py +168 -0
- calc_terminal/ui/theme_css.py +1396 -0
- calc_terminal/ui/thinking.py +226 -0
- calc_terminal/ui/timeline_panel.py +102 -0
- calc_terminal/ui/todo_panel.py +193 -0
- calc_terminal/ui/viewport.py +136 -0
- calc_terminal/ui/vision_panel.py +489 -0
- calc_terminal/ui/welcome_modal.py +343 -0
- calc_terminal/ui/widgets.py +160 -0
- calc_terminal/ui/workspace.py +831 -0
- calc_terminal/viewers/__init__.py +1 -0
- calc_terminal/viewers/document_viewer.py +252 -0
- calc_terminal/viewers/image_viewer.py +241 -0
- calc_terminal/viewers/pdf_viewer.py +203 -0
- calc_terminal/viewers/presentation_viewer.py +164 -0
- calc_terminal/viewers/registry.py +120 -0
- calc_terminal/viewers/spreadsheet_viewer.py +204 -0
- calc_terminal/vision/__init__.py +89 -0
- calc_terminal/vision/analysis.py +194 -0
- calc_terminal/vision/annotations.py +297 -0
- calc_terminal/vision/capture.py +171 -0
- calc_terminal/vision/context.py +231 -0
- calc_terminal/vision/correlation.py +169 -0
- calc_terminal/vision/cursor.py +258 -0
- calc_terminal/vision/events.py +66 -0
- calc_terminal/vision/frame_pipeline.py +259 -0
- calc_terminal/vision/priority.py +218 -0
- calc_terminal/vision/provider.py +180 -0
- calc_terminal/vision/safety.py +149 -0
- calc_terminal/vision/session.py +281 -0
- calc_terminal/vision/verify.py +162 -0
- calc_terminal/vision.py +514 -0
- calc_terminal/vscode_integration.py +113 -0
- calc_terminal/web/__init__.py +8 -0
- calc_terminal/web/cat_runtime.py +710 -0
- calc_terminal/web/server.py +2891 -0
- calc_terminal/web/static/css/app.css +3152 -0
- calc_terminal/web/static/icons/badge-72.png +0 -0
- calc_terminal/web/static/icons/cat.ico +0 -0
- calc_terminal/web/static/icons/icon-128.png +0 -0
- calc_terminal/web/static/icons/icon-144.png +0 -0
- calc_terminal/web/static/icons/icon-152.png +0 -0
- calc_terminal/web/static/icons/icon-192.png +0 -0
- calc_terminal/web/static/icons/icon-384.png +0 -0
- calc_terminal/web/static/icons/icon-512.png +0 -0
- calc_terminal/web/static/icons/icon-72.png +0 -0
- calc_terminal/web/static/icons/icon-96.png +0 -0
- calc_terminal/web/static/icons/icon.svg +34 -0
- calc_terminal/web/static/icons/new-project.png +0 -0
- calc_terminal/web/static/icons/open-project.png +0 -0
- calc_terminal/web/static/index.html +734 -0
- calc_terminal/web/static/js/app.js +2403 -0
- calc_terminal/web/static/manifest.json +88 -0
- calc_terminal/web/static/sw.js +230 -0
- calc_terminal/workflow_engine.py +769 -0
- calc_terminal/workspace.py +593 -0
- calc_terminal/workspace_index.py +385 -0
- cct_cli-0.7.9.0.dist-info/METADATA +210 -0
- cct_cli-0.7.9.0.dist-info/RECORD +325 -0
- cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
- cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
- cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
- 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()
|