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,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
+