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
calc_terminal/doctor.py
ADDED
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
"""
|
|
2
|
+
cat doctor — diagnose and repair the CAT CLI installation.
|
|
3
|
+
|
|
4
|
+
Run it either way:
|
|
5
|
+
|
|
6
|
+
cat --doctor (when `cat` itself is already found)
|
|
7
|
+
python -m calc_terminal.doctor (always works — uses python.exe)
|
|
8
|
+
|
|
9
|
+
What it checks, in order:
|
|
10
|
+
|
|
11
|
+
1. Which Python / which environment (venv vs global vs Store Python).
|
|
12
|
+
2. Is the CAT package importable, and which version.
|
|
13
|
+
3. Does a `cat` launcher (cat.exe) exist in this interpreter's Scripts
|
|
14
|
+
directory, and is that directory on PATH (process PATH, per-user
|
|
15
|
+
registry PATH, per-machine registry PATH on Windows).
|
|
16
|
+
4. Windows only: the PowerShell built-in alias `cat -> Get-Content`,
|
|
17
|
+
which shadows ANY external cat.exe inside PowerShell sessions
|
|
18
|
+
(cmd.exe has no such conflict). Offer an idempotent $PROFILE fix.
|
|
19
|
+
5. POSIX: warn that /usr/bin/cat (coreutils) exists on essentially all
|
|
20
|
+
Unix shells and will keep winning unless the user aliases it.
|
|
21
|
+
|
|
22
|
+
Fixes are opt-in flags; nothing is modified without them:
|
|
23
|
+
|
|
24
|
+
--fix-path append the Scripts dir to the USER Path value in
|
|
25
|
+
the registry (HKCU\\Environment). Existing entries
|
|
26
|
+
are never rewritten, reordered or removed; the old
|
|
27
|
+
value is backed up to CCT's logs dir first; a
|
|
28
|
+
WM_SETTINGCHANGE broadcast tells running shells a
|
|
29
|
+
change happened (new terminals pick it up).
|
|
30
|
+
--fix-powershell add a marker-guarded snippet to the PowerShell
|
|
31
|
+
CurrentUserAllHosts profiles (Windows PowerShell
|
|
32
|
+
5.1 and PowerShell 7+ if installed) that removes
|
|
33
|
+
the built-in alias so the external cat.exe wins.
|
|
34
|
+
Get-Content/gc remain fully available for files.
|
|
35
|
+
|
|
36
|
+
Exit code: 0 when everything looks healthy, 1 when a problem was found.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
import os
|
|
40
|
+
import sys
|
|
41
|
+
|
|
42
|
+
_MARK_BEGIN = "# >>> CAT CLI (added by `cat --doctor --fix-powershell`) >>>"
|
|
43
|
+
_MARK_END = "# <<< CAT CLI <<<"
|
|
44
|
+
|
|
45
|
+
_PS_SNIPPET = f"""{_MARK_BEGIN}
|
|
46
|
+
# Lets the CAT app own `cat`; file reading stays on Get-Content/gc/type.
|
|
47
|
+
Remove-Item Alias:cat -Force -ErrorAction SilentlyContinue
|
|
48
|
+
{_MARK_END}
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _dist_version():
|
|
53
|
+
for dist in ("cct-cli", "cat-cli", "cct-ai-ide", "cat-ai", "cct"):
|
|
54
|
+
try:
|
|
55
|
+
from importlib.metadata import version
|
|
56
|
+
return version(dist)
|
|
57
|
+
except Exception:
|
|
58
|
+
continue
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _scripts_dirs():
|
|
63
|
+
"""Every directory where this interpreter could have dropped the
|
|
64
|
+
launcher: the normal scheme plus the user scheme (pip --user /
|
|
65
|
+
Microsoft Store Python both land there)."""
|
|
66
|
+
dirs = []
|
|
67
|
+
try:
|
|
68
|
+
import sysconfig
|
|
69
|
+
user_scheme = "nt_user" if sys.platform == "win32" else "posix_user"
|
|
70
|
+
for scheme in (None, user_scheme):
|
|
71
|
+
try:
|
|
72
|
+
d = sysconfig.get_path("scripts", scheme)
|
|
73
|
+
except Exception:
|
|
74
|
+
d = None
|
|
75
|
+
if d and d not in dirs:
|
|
76
|
+
dirs.append(d)
|
|
77
|
+
except Exception:
|
|
78
|
+
pass
|
|
79
|
+
return dirs
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _path_entries():
|
|
83
|
+
return [p.strip() for p in os.environ.get("PATH", "").split(os.pathsep)
|
|
84
|
+
if p.strip()]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _registry_user_path():
|
|
88
|
+
if sys.platform != "win32":
|
|
89
|
+
return None
|
|
90
|
+
try:
|
|
91
|
+
import winreg
|
|
92
|
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment") as k:
|
|
93
|
+
val, _typ = winreg.QueryValueEx(k, "Path")
|
|
94
|
+
return val
|
|
95
|
+
except FileNotFoundError:
|
|
96
|
+
return ""
|
|
97
|
+
except Exception:
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _registry_machine_path():
|
|
102
|
+
if sys.platform != "win32":
|
|
103
|
+
return None
|
|
104
|
+
try:
|
|
105
|
+
import winreg
|
|
106
|
+
key = "SYSTEM\\CurrentControlSet\\Control\\" \
|
|
107
|
+
"Session Manager\\Environment"
|
|
108
|
+
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, key) as k:
|
|
109
|
+
val, _typ = winreg.QueryValueEx(k, "Path")
|
|
110
|
+
return val
|
|
111
|
+
except Exception:
|
|
112
|
+
return None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _launcher_name():
|
|
116
|
+
return "cat.exe" if sys.platform == "win32" else "cat"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def find_launcher():
|
|
120
|
+
"""Return the absolute path of the installed `cat` launcher, or None."""
|
|
121
|
+
name = _launcher_name()
|
|
122
|
+
for d in _scripts_dirs():
|
|
123
|
+
p = os.path.join(d, name)
|
|
124
|
+
if os.path.isfile(p):
|
|
125
|
+
return p
|
|
126
|
+
# Fall back to whatever PATH resolves to right now.
|
|
127
|
+
for d in _path_entries():
|
|
128
|
+
p = os.path.join(d, name)
|
|
129
|
+
if os.path.isfile(p):
|
|
130
|
+
return p
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _broadcast_settings_change():
|
|
135
|
+
try:
|
|
136
|
+
import ctypes
|
|
137
|
+
HWND_BROADCAST = 0xFFFF
|
|
138
|
+
WM_SETTINGCHANGE = 0x001A
|
|
139
|
+
SMTO_ABORTIFHUNG = 0x0002
|
|
140
|
+
ctypes.windll.user32.SendMessageTimeoutW(
|
|
141
|
+
HWND_BROADCAST, WM_SETTINGCHANGE, 0,
|
|
142
|
+
"Environment", SMTO_ABORTIFHUNG, 5000, None)
|
|
143
|
+
except Exception:
|
|
144
|
+
pass
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def fix_path(assume_yes=False):
|
|
148
|
+
"""Append the Scripts dir to the USER Path (HKCU). Only ever appends;
|
|
149
|
+
never rewrites or removes existing entries."""
|
|
150
|
+
if sys.platform != "win32":
|
|
151
|
+
print(" --fix-path is only implemented for Windows.")
|
|
152
|
+
print(" On Unix, add your Scripts/bin dir to ~/.bashrc / "
|
|
153
|
+
"~/.zshrc manually.")
|
|
154
|
+
return 1
|
|
155
|
+
|
|
156
|
+
scripts = _scripts_dirs()
|
|
157
|
+
missing = [d for d in scripts
|
|
158
|
+
if d.lower() not in [e.lower() for e in _path_entries()]]
|
|
159
|
+
user_path = _registry_user_path()
|
|
160
|
+
if user_path is None:
|
|
161
|
+
print(" ! Could not read HKCU\\Environment\\Path (permissions?).")
|
|
162
|
+
return 1
|
|
163
|
+
|
|
164
|
+
to_add = []
|
|
165
|
+
for d in missing:
|
|
166
|
+
parts = [p.strip().lower() for p in user_path.split(os.pathsep)
|
|
167
|
+
if p.strip()]
|
|
168
|
+
if d.lower() not in parts:
|
|
169
|
+
to_add.append(d)
|
|
170
|
+
|
|
171
|
+
if not to_add:
|
|
172
|
+
print(" Scripts dir(s) already present in PATH entries:")
|
|
173
|
+
for d in scripts:
|
|
174
|
+
print(f" {d}")
|
|
175
|
+
print(" Nothing to change. Open a NEW terminal and run `cat`.")
|
|
176
|
+
return 0
|
|
177
|
+
|
|
178
|
+
backup_dir = None
|
|
179
|
+
try:
|
|
180
|
+
from .first_run import logs_dir
|
|
181
|
+
backup_dir = logs_dir()
|
|
182
|
+
os.makedirs(backup_dir, exist_ok=True)
|
|
183
|
+
backup = os.path.join(
|
|
184
|
+
backup_dir,
|
|
185
|
+
"path-backup-%s.txt"
|
|
186
|
+
% __import__("time").strftime("%Y%m%d-%H%M%S"))
|
|
187
|
+
with open(backup, "w", encoding="utf-8") as f:
|
|
188
|
+
f.write("Previous HKCU\\Environment\\Path:\n")
|
|
189
|
+
f.write(user_path + "\n")
|
|
190
|
+
print(f" Backed up current user PATH to: {backup}")
|
|
191
|
+
except Exception:
|
|
192
|
+
pass
|
|
193
|
+
|
|
194
|
+
new_value = user_path.rstrip(os.pathsep) + os.pathsep + \
|
|
195
|
+
os.pathsep.join(to_add) if user_path.strip() else \
|
|
196
|
+
os.pathsep.join(to_add)
|
|
197
|
+
|
|
198
|
+
if not assume_yes:
|
|
199
|
+
print(" About to APPEND to the *user* PATH (HKCU\\Environment):")
|
|
200
|
+
for d in to_add:
|
|
201
|
+
print(f" + {d}")
|
|
202
|
+
answer = input(" Proceed? [y/N] ").strip().lower()
|
|
203
|
+
if answer not in ("y", "yes"):
|
|
204
|
+
print(" Aborted — nothing was changed.")
|
|
205
|
+
return 1
|
|
206
|
+
|
|
207
|
+
import winreg
|
|
208
|
+
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Environment", 0,
|
|
209
|
+
winreg.KEY_SET_VALUE) as k:
|
|
210
|
+
winreg.SetValueEx(k, "Path", 0, winreg.REG_EXPAND_SZ, new_value)
|
|
211
|
+
_broadcast_settings_change()
|
|
212
|
+
print(" Done. User PATH updated (existing entries untouched).")
|
|
213
|
+
print(" IMPORTANT: open a NEW terminal window, then run `cat`.")
|
|
214
|
+
return 0
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _profile_paths():
|
|
218
|
+
"""CurrentUserAllHosts profiles for every PowerShell host installed.
|
|
219
|
+
|
|
220
|
+
NEVER hardcode ~\\Documents\\... : Windows redirects Documents to
|
|
221
|
+
OneDrive on many machines, and only PowerShell itself knows where
|
|
222
|
+
$PROFILE really lives. Ask each installed shell directly; fall back
|
|
223
|
+
to the classic paths only if querying fails."""
|
|
224
|
+
paths = []
|
|
225
|
+
for exe in ("powershell", "pwsh"):
|
|
226
|
+
try:
|
|
227
|
+
import subprocess
|
|
228
|
+
out = subprocess.run(
|
|
229
|
+
[exe, "-NoProfile", "-NonInteractive", "-Command",
|
|
230
|
+
"$PROFILE.CurrentUserAllHosts"],
|
|
231
|
+
capture_output=True, text=True, timeout=20)
|
|
232
|
+
p = (out.stdout or "").strip()
|
|
233
|
+
if out.returncode == 0 and p.lower().endswith(".ps1"):
|
|
234
|
+
if p not in paths:
|
|
235
|
+
paths.append(p)
|
|
236
|
+
except Exception:
|
|
237
|
+
continue
|
|
238
|
+
if not paths:
|
|
239
|
+
docs = os.path.join(os.path.expanduser("~"), "Documents")
|
|
240
|
+
paths.append(os.path.join(docs, "WindowsPowerShell", "profile.ps1"))
|
|
241
|
+
p7 = os.path.join(docs, "PowerShell", "profile.ps1")
|
|
242
|
+
if os.path.isdir(os.path.dirname(p7)):
|
|
243
|
+
paths.append(p7)
|
|
244
|
+
return paths
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def fix_powershell(assume_yes=False):
|
|
248
|
+
"""Make external cat.exe beat PowerShell's built-in alias by adding
|
|
249
|
+
an idempotent, marker-guarded Remove-Item line to the user's
|
|
250
|
+
AllHosts profiles. Never touches anything else in the profile."""
|
|
251
|
+
if sys.platform != "win32":
|
|
252
|
+
print(" The PowerShell alias conflict only exists on Windows.")
|
|
253
|
+
return 0
|
|
254
|
+
|
|
255
|
+
targets = _profile_paths()
|
|
256
|
+
if not assume_yes:
|
|
257
|
+
print(" This adds a small guarded snippet to these PowerShell"
|
|
258
|
+
" profiles\n (creating them if they don't exist yet):")
|
|
259
|
+
for p in targets:
|
|
260
|
+
print(f" {p}")
|
|
261
|
+
answer = input(" Proceed? [y/N] ").strip().lower()
|
|
262
|
+
if answer not in ("y", "yes"):
|
|
263
|
+
print(" Aborted — nothing was changed.")
|
|
264
|
+
return 1
|
|
265
|
+
|
|
266
|
+
changed = []
|
|
267
|
+
for p in targets:
|
|
268
|
+
try:
|
|
269
|
+
os.makedirs(os.path.dirname(p), exist_ok=True)
|
|
270
|
+
content = ""
|
|
271
|
+
if os.path.isfile(p):
|
|
272
|
+
with open(p, "r", encoding="utf-8-sig") as f:
|
|
273
|
+
content = f.read()
|
|
274
|
+
if _MARK_BEGIN in content:
|
|
275
|
+
continue
|
|
276
|
+
sep = "" if not content or content.endswith("\n") else "\n"
|
|
277
|
+
with open(p, "a", encoding="utf-8") as f:
|
|
278
|
+
f.write(sep + _PS_SNIPPET)
|
|
279
|
+
changed.append(p)
|
|
280
|
+
except Exception as e:
|
|
281
|
+
print(f" ! Could not update {p}: {e}")
|
|
282
|
+
return 1
|
|
283
|
+
if changed:
|
|
284
|
+
print(" Updated:")
|
|
285
|
+
for p in changed:
|
|
286
|
+
print(f" {p}")
|
|
287
|
+
print(" Open a NEW PowerShell window and run `cat`.")
|
|
288
|
+
print(" (Reading files still works: use Get-Content / gc / type.)")
|
|
289
|
+
else:
|
|
290
|
+
print(" Profiles already patched — nothing to do.")
|
|
291
|
+
return 0
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def report(fix=False):
|
|
295
|
+
"""Print the diagnosis. Returns 0 (healthy) or 1 (problems found).
|
|
296
|
+
With fix=True, offer interactive fixes after the report."""
|
|
297
|
+
# Windows console defaults to cp1252 when redirected — force UTF-8 so
|
|
298
|
+
# host diagnostics (which mention WebView2 → arrows) never crash.
|
|
299
|
+
for _s in (sys.stdout, sys.stderr):
|
|
300
|
+
try:
|
|
301
|
+
_s.reconfigure(encoding="utf-8", errors="replace")
|
|
302
|
+
except Exception:
|
|
303
|
+
pass
|
|
304
|
+
ok = True
|
|
305
|
+
print("CAT doctor")
|
|
306
|
+
print("=" * 60)
|
|
307
|
+
|
|
308
|
+
exe = sys.executable
|
|
309
|
+
venv = getattr(sys, "prefix", "") != getattr(sys, "base_prefix", "")
|
|
310
|
+
env_kind = ("virtualenv/venv: " + sys.prefix) if venv else \
|
|
311
|
+
"global interpreter"
|
|
312
|
+
store = "windowsapps" in exe.lower()
|
|
313
|
+
print(f"[1] Python : {exe}")
|
|
314
|
+
print(f" Environment : {env_kind}")
|
|
315
|
+
if store:
|
|
316
|
+
print(" (Microsoft Store Python)")
|
|
317
|
+
ver = _dist_version()
|
|
318
|
+
print(f"[2] CAT package : "
|
|
319
|
+
+ (f"installed, version {ver}" if ver
|
|
320
|
+
else "NOT installed for this interpreter "
|
|
321
|
+
"(run: pip install -e .)"))
|
|
322
|
+
if not ver:
|
|
323
|
+
ok = False
|
|
324
|
+
|
|
325
|
+
launcher = find_launcher()
|
|
326
|
+
scripts = _scripts_dirs()
|
|
327
|
+
print(f"[3] Launcher : {launcher or 'NOT FOUND'}")
|
|
328
|
+
for d in scripts:
|
|
329
|
+
on_process = d.lower() in [e.lower() for e in _path_entries()]
|
|
330
|
+
upath = _registry_user_path() or ""
|
|
331
|
+
on_user = d.lower() in [p.strip().lower()
|
|
332
|
+
for p in upath.split(os.pathsep)]
|
|
333
|
+
mpath = _registry_machine_path() or ""
|
|
334
|
+
on_machine = d.lower() in [p.strip().lower()
|
|
335
|
+
for p in mpath.split(os.pathsep)]
|
|
336
|
+
state = ("process PATH" if on_process else
|
|
337
|
+
"user PATH (new terminals OK)" if on_user else
|
|
338
|
+
"machine PATH (new terminals OK)" if on_machine else
|
|
339
|
+
"NOT ON PATH")
|
|
340
|
+
print(f" Scripts dir : {d}")
|
|
341
|
+
print(f" -> {state}")
|
|
342
|
+
if not (on_process or on_user or on_machine):
|
|
343
|
+
ok = False
|
|
344
|
+
if not launcher and ver:
|
|
345
|
+
ok = False
|
|
346
|
+
print(" Fix: reinstall with `pip install -e .` from the "
|
|
347
|
+
"CAT project folder.")
|
|
348
|
+
|
|
349
|
+
if sys.platform == "win32":
|
|
350
|
+
print("[4] PowerShell : built-in alias `cat -> Get-Content` "
|
|
351
|
+
"shadows cat.exe")
|
|
352
|
+
prof = _profile_paths()
|
|
353
|
+
patched = any(
|
|
354
|
+
os.path.isfile(p) and _MARK_BEGIN in open(
|
|
355
|
+
p, encoding="utf-8-sig", errors="ignore").read()
|
|
356
|
+
for p in prof)
|
|
357
|
+
print(f" Profile fix : {'applied' if patched else 'not applied'}"
|
|
358
|
+
f" ({', '.join(prof)})")
|
|
359
|
+
if not patched:
|
|
360
|
+
ok = False
|
|
361
|
+
print(" Effect : inside PowerShell, typing `cat` runs"
|
|
362
|
+
" Get-Content,")
|
|
363
|
+
print(" NOT the CAT app. cmd.exe / Windows"
|
|
364
|
+
" Terminal (cmd) are unaffected.")
|
|
365
|
+
else:
|
|
366
|
+
print("[4] Unix note : /usr/bin/cat (coreutils) keeps winning"
|
|
367
|
+
" over cat")
|
|
368
|
+
print(" in bash/zsh. Use a shell alias"
|
|
369
|
+
" (`alias cat=cat-ai`) or")
|
|
370
|
+
print(" launch via the full path to your"
|
|
371
|
+
" bin dir.")
|
|
372
|
+
|
|
373
|
+
# --- CAT Host (embedded Browser) diagnostics -------------------------
|
|
374
|
+
print("[5] CAT Host : unified Terminal + Browser window (embedded WebView2/Chromium)")
|
|
375
|
+
try:
|
|
376
|
+
from .host.launcher import get_host_status # type: ignore
|
|
377
|
+
st = get_host_status()
|
|
378
|
+
if st["available"]:
|
|
379
|
+
print(f" Engines : {', '.join(st['engines'])}")
|
|
380
|
+
print(f" Preferred : {st['preferred']}")
|
|
381
|
+
if st["webview2_runtime"]:
|
|
382
|
+
print(f" WebView2 : {st['webview2_runtime']} (Edge WebView2 Runtime — Windows)")
|
|
383
|
+
else:
|
|
384
|
+
print(" WebView2 : not detected (Edge runtime missing — install Edge)")
|
|
385
|
+
# Embedded check: Qt means truly inside CAT window
|
|
386
|
+
has_qt = any("Qt" in e for e in st["engines"])
|
|
387
|
+
if has_qt:
|
|
388
|
+
print(" Mode : EMBEDDED — Browser renders INSIDE CAT window (QWebEngineView child)")
|
|
389
|
+
elif any("pywebview" in e.lower() or "webview" in e.lower() for e in st["engines"]):
|
|
390
|
+
print(" Mode : pywebview/WebView2 window (real rendering, standalone window)")
|
|
391
|
+
else:
|
|
392
|
+
print(" Mode : Playwright external Chromium window (real rendering, but not embedded)")
|
|
393
|
+
print(" Fix : pip install PySide6 → true embedded browser inside CAT Host")
|
|
394
|
+
else:
|
|
395
|
+
print(f" Status : {st['reason']}")
|
|
396
|
+
print(" Install : pip install PySide6 (QWebEngineView embedded — the ONLY correct engine for Browser INSIDE CAT)")
|
|
397
|
+
print(" DO NOT use: pywebview/playwright separate-window fallbacks (not embedded)")
|
|
398
|
+
ok = False
|
|
399
|
+
except Exception as e:
|
|
400
|
+
print(f" Status : host diagnostics failed ({e})")
|
|
401
|
+
|
|
402
|
+
# --- CAT Terminal Tab / Window Identity ---------------------------
|
|
403
|
+
print("[6] Terminal Tab: CAT CLI branding & icon identity")
|
|
404
|
+
try:
|
|
405
|
+
from . import terminal_identity
|
|
406
|
+
ico = terminal_identity.get_icon_path()
|
|
407
|
+
if ico and os.path.isfile(ico):
|
|
408
|
+
print(f" Icon asset : {ico} ({os.path.getsize(ico)} bytes)")
|
|
409
|
+
else:
|
|
410
|
+
print(" Icon asset : missing or unreadable")
|
|
411
|
+
ok = False
|
|
412
|
+
|
|
413
|
+
if sys.platform == "win32":
|
|
414
|
+
wt_settings = terminal_identity.find_windows_terminal_settings()
|
|
415
|
+
if wt_settings:
|
|
416
|
+
import json
|
|
417
|
+
try:
|
|
418
|
+
with open(wt_settings, "r", encoding="utf-8-sig") as f:
|
|
419
|
+
wt_data = json.load(f)
|
|
420
|
+
has_wt_profile = any(
|
|
421
|
+
p.get("guid") == terminal_identity.WT_CAT_GUID or p.get("name") == "CAT CLI"
|
|
422
|
+
for p in wt_data.get("profiles", {}).get("list", [])
|
|
423
|
+
)
|
|
424
|
+
if has_wt_profile:
|
|
425
|
+
print(f" Win Terminal: CAT CLI profile configured in {wt_settings}")
|
|
426
|
+
else:
|
|
427
|
+
print(f" Win Terminal: profile NOT found in {wt_settings}")
|
|
428
|
+
print(" (run: cat --doctor --fix-terminal)")
|
|
429
|
+
ok = False
|
|
430
|
+
except Exception as e:
|
|
431
|
+
print(f" Win Terminal: error reading settings ({e})")
|
|
432
|
+
else:
|
|
433
|
+
print(" Win Terminal: settings.json not found (default conhost or not installed)")
|
|
434
|
+
print(f" Default Title: {terminal_identity.DEFAULT_TITLE}")
|
|
435
|
+
except Exception as e:
|
|
436
|
+
print(f" Terminal ID : diagnostics failed ({e})")
|
|
437
|
+
|
|
438
|
+
print("=" * 60)
|
|
439
|
+
if ok:
|
|
440
|
+
print("Everything looks good. Run `cat` from any terminal.")
|
|
441
|
+
print("On Windows for real browser inside CAT: cat --host or cat --host-browser")
|
|
442
|
+
else:
|
|
443
|
+
print("Problems detected. Suggested fixes:")
|
|
444
|
+
print(" python -m calc_terminal.doctor --fix-path"
|
|
445
|
+
" # add Scripts dir to user PATH")
|
|
446
|
+
print(" python -m calc_terminal.doctor --fix-powershell"
|
|
447
|
+
" # let cat.exe beat the PS alias")
|
|
448
|
+
print(" python -m calc_terminal.doctor --fix-terminal"
|
|
449
|
+
" # configure Windows Terminal profile & icon")
|
|
450
|
+
print(" pip install PySide6 # CAT Host embedded browser")
|
|
451
|
+
|
|
452
|
+
if fix:
|
|
453
|
+
print()
|
|
454
|
+
if sys.platform == "win32":
|
|
455
|
+
upath = _registry_user_path() or ""
|
|
456
|
+
need_path = any(
|
|
457
|
+
d.lower() not in [p.strip().lower()
|
|
458
|
+
for p in upath.split(os.pathsep)]
|
|
459
|
+
for d in scripts)
|
|
460
|
+
if need_path:
|
|
461
|
+
rc = fix_path()
|
|
462
|
+
if rc == 0:
|
|
463
|
+
ok = True
|
|
464
|
+
patched = any(
|
|
465
|
+
os.path.isfile(p) and _MARK_BEGIN in open(
|
|
466
|
+
p, encoding="utf-8-sig", errors="ignore").read()
|
|
467
|
+
for p in prof)
|
|
468
|
+
if not patched:
|
|
469
|
+
rc = fix_powershell()
|
|
470
|
+
if rc == 0:
|
|
471
|
+
ok = True
|
|
472
|
+
rc_term = fix_terminal()
|
|
473
|
+
if rc_term == 0:
|
|
474
|
+
ok = True
|
|
475
|
+
return 0 if ok else 1
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
def fix_terminal() -> int:
|
|
479
|
+
"""Ensure the CAT icon is generated and register the CAT CLI profile in Windows Terminal."""
|
|
480
|
+
try:
|
|
481
|
+
from . import terminal_identity
|
|
482
|
+
ico = terminal_identity.get_icon_path()
|
|
483
|
+
if not ico:
|
|
484
|
+
print(" [x] Could not generate or locate CAT icon (.ico).")
|
|
485
|
+
return 1
|
|
486
|
+
print(f" [v] CAT icon ready: {ico}")
|
|
487
|
+
if sys.platform == "win32":
|
|
488
|
+
ok, msg = terminal_identity.configure_windows_terminal_profile()
|
|
489
|
+
if ok:
|
|
490
|
+
print(f" [v] {msg}")
|
|
491
|
+
else:
|
|
492
|
+
print(f" [!] {msg}")
|
|
493
|
+
return 0
|
|
494
|
+
except Exception as e:
|
|
495
|
+
print(f" [x] Terminal fix failed: {e}")
|
|
496
|
+
return 1
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def main(argv=None):
|
|
500
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
501
|
+
fix_path_flag = "--fix-path" in argv
|
|
502
|
+
fix_ps_flag = "--fix-powershell" in argv
|
|
503
|
+
fix_terminal_flag = "--fix-terminal" in argv
|
|
504
|
+
yes = "--yes" in argv or "-y" in argv
|
|
505
|
+
if fix_path_flag or fix_ps_flag or fix_terminal_flag:
|
|
506
|
+
rc = 0
|
|
507
|
+
if fix_path_flag:
|
|
508
|
+
rc |= fix_path(assume_yes=yes)
|
|
509
|
+
if fix_ps_flag:
|
|
510
|
+
rc |= fix_powershell(assume_yes=yes)
|
|
511
|
+
if fix_terminal_flag:
|
|
512
|
+
rc |= fix_terminal()
|
|
513
|
+
return rc
|
|
514
|
+
return report(fix="--fix" in argv)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
if __name__ == "__main__":
|
|
518
|
+
raise SystemExit(main())
|
|
519
|
+
|