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,162 @@
1
+ """
2
+ CCT Report Generator — exports solved notebooks (from session history)
3
+ to a real Markdown file and a real PDF (via reportlab, already a
4
+ dependency). This is a straight, faithful export of the same
5
+ Question/Given/Find/Formula/Substitution/Calculation/Units/Verification
6
+ /Answer structure already rendered on screen — not a separate "fake"
7
+ summary.
8
+ """
9
+
10
+ import os
11
+ import re
12
+
13
+ from .mathtext import compose, mathify
14
+
15
+ PDF_AVAILABLE = True
16
+ try:
17
+ from reportlab.lib.pagesizes import LETTER
18
+ from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
19
+ from reportlab.lib.units import inch
20
+ from reportlab.lib import colors
21
+ from reportlab.platypus import (
22
+ SimpleDocTemplate, Paragraph, Spacer, ListFlowable, ListItem, HRFlowable
23
+ )
24
+ except Exception:
25
+ PDF_AVAILABLE = False
26
+
27
+
28
+ _ANSI_RE = re.compile(r"\x1b\[[0-9;]*m")
29
+
30
+
31
+ def _plain(s):
32
+ return _ANSI_RE.sub("", str(s))
33
+
34
+
35
+ def _notebook_to_markdown(nb, index):
36
+ lines = [f"## {index}. {nb.get('topic', 'Notebook')}", ""]
37
+ lines.append(f"**Question:** {_plain(nb.get('question', ''))}")
38
+ lines.append("")
39
+ lines.append("**Given data:**")
40
+ for g in nb.get("given", []):
41
+ lines.append(f"- {_plain(g)}")
42
+ lines.append("")
43
+ lines.append(f"**Find:** {_plain(nb.get('find', ''))}")
44
+ lines.append("")
45
+ lines.append("**Formula:**")
46
+ lines.append("```")
47
+ lines.append(_plain(compose(nb.get("formula", []))))
48
+ lines.append("```")
49
+ lines.append("")
50
+ lines.append("**Substitution:**")
51
+ lines.append("```")
52
+ lines.append(_plain(compose(nb.get("substitution", []))))
53
+ lines.append("```")
54
+ lines.append("")
55
+ calc = nb.get("calculation", [])
56
+ if calc:
57
+ lines.append("**Calculation:**")
58
+ for i, c in enumerate(calc, 1):
59
+ lines.append(f"{i}. {_plain(mathify(c))}")
60
+ lines.append("")
61
+ if nb.get("unit"):
62
+ lines.append(f"**Units:** {_plain(mathify(nb['unit']))}")
63
+ lines.append("")
64
+ if nb.get("verification"):
65
+ lines.append(f"**Verification:** {_plain(mathify(nb['verification']))}")
66
+ lines.append("")
67
+ lines.append(f"**Final answer:** {_plain(nb.get('final_answer', ''))}")
68
+ lines.append("")
69
+ lines.append("---")
70
+ lines.append("")
71
+ return lines
72
+
73
+
74
+ def export_history_markdown(history, path, title="CAT Notebook Export"):
75
+ if not history:
76
+ raise ValueError("No history to export.")
77
+ lines = [f"# {title}", ""]
78
+ lines.append(f"_{len(history)} notebook(s), exported from Chemistry Calc Terminal_")
79
+ lines.append("")
80
+ for i, nb in enumerate(history, 1):
81
+ lines.extend(_notebook_to_markdown(nb, i))
82
+ text = "\n".join(lines)
83
+ with open(path, "w", encoding="utf-8") as f:
84
+ f.write(text)
85
+ return path
86
+
87
+
88
+ def export_history_pdf(history, path, title="CAT Notebook Export"):
89
+ if not PDF_AVAILABLE:
90
+ raise RuntimeError("reportlab is not installed — run: pip install reportlab")
91
+ if not history:
92
+ raise ValueError("No history to export.")
93
+
94
+ doc = SimpleDocTemplate(path, pagesize=LETTER,
95
+ topMargin=0.7 * inch, bottomMargin=0.7 * inch,
96
+ leftMargin=0.75 * inch, rightMargin=0.75 * inch)
97
+ styles = getSampleStyleSheet()
98
+ h1 = ParagraphStyle("h1", parent=styles["Heading1"], textColor=colors.HexColor("#3b2a86"))
99
+ h2 = ParagraphStyle("h2", parent=styles["Heading2"], textColor=colors.HexColor("#1f6fb2"),
100
+ spaceBefore=14)
101
+ body = ParagraphStyle("body", parent=styles["BodyText"], leading=15)
102
+ mono = ParagraphStyle("mono", parent=styles["Code"], backColor=colors.HexColor("#f4f4f8"),
103
+ borderPadding=6, leading=13)
104
+ answer = ParagraphStyle("answer", parent=styles["BodyText"],
105
+ textColor=colors.HexColor("#0a7d3c"), fontName="Helvetica-Bold")
106
+
107
+ def esc(s):
108
+ return (_plain(s).replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;"))
109
+
110
+ story = [Paragraph(esc(title), h1),
111
+ Paragraph(f"{len(history)} notebook(s) — Chemistry Calc Terminal", body),
112
+ Spacer(1, 12)]
113
+
114
+ for i, nb in enumerate(history, 1):
115
+ story.append(Paragraph(f"{i}. {esc(nb.get('topic', 'Notebook'))}", h2))
116
+ story.append(Paragraph(f"<b>Question:</b> {esc(nb.get('question', ''))}", body))
117
+ given = nb.get("given", [])
118
+ if given:
119
+ story.append(Paragraph("<b>Given data:</b>", body))
120
+ story.append(ListFlowable(
121
+ [ListItem(Paragraph(esc(g), body)) for g in given], bulletType="bullet"))
122
+ story.append(Paragraph(f"<b>Find:</b> {esc(nb.get('find', ''))}", body))
123
+ story.append(Paragraph("<b>Formula:</b>", body))
124
+ story.append(Paragraph(esc(compose(nb.get("formula", []))).replace("\n", "<br/>"), mono))
125
+ story.append(Paragraph("<b>Substitution:</b>", body))
126
+ story.append(Paragraph(esc(compose(nb.get("substitution", []))).replace("\n", "<br/>"), mono))
127
+ calc = nb.get("calculation", [])
128
+ if calc:
129
+ story.append(Paragraph("<b>Calculation:</b>", body))
130
+ story.append(ListFlowable(
131
+ [ListItem(Paragraph(esc(mathify(c)), body)) for c in calc], bulletType="1"))
132
+ if nb.get("unit"):
133
+ story.append(Paragraph(f"<b>Units:</b> {esc(mathify(nb['unit']))}", body))
134
+ if nb.get("verification"):
135
+ story.append(Paragraph(f"<b>Verification:</b> {esc(mathify(nb['verification']))}", body))
136
+ story.append(Paragraph(f"Final answer: {esc(nb.get('final_answer', ''))}", answer))
137
+ story.append(HRFlowable(width="100%", color=colors.HexColor("#dddddd"), spaceBefore=10, spaceAfter=10))
138
+
139
+ doc.build(story)
140
+ return path
141
+
142
+
143
+ def default_export_dir():
144
+ """Notebook exports go into the shared Project Workspace now (spec
145
+ section 16, calc_terminal/workspace.py) instead of report.py's own
146
+ ~/CCT_exports — which, before this, was a *different* directory
147
+ from scires.py's ~/cct_exports (different casing, never the same
148
+ folder). Falls back to the old location only if the workspace
149
+ module can't be imported, so a broken workspace config never
150
+ blocks an export outright.
151
+
152
+ STRICT FIX v0.7.9.6: explicit ensure=True — this is a WRITE path
153
+ (the user explicitly requested an export), so directory creation
154
+ is authorized here. Read paths (summary/list_category) use
155
+ ensure=False and never create."""
156
+ try:
157
+ from . import workspace
158
+ return workspace.category_dir("reports", ensure=True)
159
+ except Exception:
160
+ d = os.path.join(os.path.expanduser("~"), "CCT_exports")
161
+ os.makedirs(d, exist_ok=True)
162
+ return d
@@ -0,0 +1,45 @@
1
+ """
2
+ CAT Research Subsystem per §48-§51, §58-§63, §97-§98:
3
+ - Experiment Ledger & Scientific Time Machine
4
+ - Data Lineage Graph
5
+ - Semantic Artifact Intelligence
6
+ - Reproducibility Engine & Package Generation
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from calc_terminal.research.experiment_ledger import (
12
+ ExperimentRecord,
13
+ ExperimentVersion,
14
+ ExperimentLedger,
15
+ experiment_ledger,
16
+ )
17
+ from calc_terminal.research.data_lineage import (
18
+ LineageNode,
19
+ DataLineageGraph,
20
+ data_lineage,
21
+ )
22
+ from calc_terminal.research.artifact_intel import (
23
+ ArtifactSemantics,
24
+ analyze_artifact,
25
+ )
26
+ from calc_terminal.research.reproducibility import (
27
+ ReproductionReport,
28
+ ReproducibilityEngine,
29
+ reproducibility_engine,
30
+ )
31
+
32
+ __all__ = [
33
+ "ExperimentRecord",
34
+ "ExperimentVersion",
35
+ "ExperimentLedger",
36
+ "experiment_ledger",
37
+ "LineageNode",
38
+ "DataLineageGraph",
39
+ "data_lineage",
40
+ "ArtifactSemantics",
41
+ "analyze_artifact",
42
+ "ReproductionReport",
43
+ "ReproducibilityEngine",
44
+ "reproducibility_engine",
45
+ ]
@@ -0,0 +1,126 @@
1
+ """
2
+ Semantic Artifact Intelligence per §51:
3
+ Understands the semantic domain and compatible tool capabilities for files:
4
+ - .pdb, .cif -> Protein 3D Structure
5
+ - .fasta, .fa -> Biological Sequence
6
+ - .safetensors, .pt -> Machine Learning Model
7
+ - .ipynb -> Jupyter Notebook
8
+ - .csv, .parquet -> Dataset
9
+ - .png, .svg -> Scientific Visualization
10
+ - .qasm -> Quantum Circuit
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import os
16
+ from dataclasses import dataclass
17
+ from typing import Any, Dict, List, Optional
18
+
19
+
20
+ @dataclass
21
+ class ArtifactSemantics:
22
+ path: str
23
+ filename: str
24
+ semantic_type: str
25
+ domain: str
26
+ recommended_capabilities: List[str]
27
+ description: str
28
+
29
+ def to_dict(self) -> Dict[str, Any]:
30
+ return {
31
+ "path": self.path,
32
+ "filename": self.filename,
33
+ "semantic_type": self.semantic_type,
34
+ "domain": self.domain,
35
+ "recommended_capabilities": self.recommended_capabilities,
36
+ "description": self.description,
37
+ }
38
+
39
+
40
+ def analyze_artifact(file_path: str) -> ArtifactSemantics:
41
+ """Classify and return semantic domain and recommended capabilities for an artifact."""
42
+ norm = os.path.normpath(file_path)
43
+ base = os.path.basename(norm).lower()
44
+ ext = os.path.splitext(base)[1].lower()
45
+
46
+ if ext in (".pdb", ".cif", ".mmcif"):
47
+ return ArtifactSemantics(
48
+ path=norm,
49
+ filename=base,
50
+ semantic_type="protein_structure",
51
+ domain="structural_biology",
52
+ recommended_capabilities=["pymol.open", "chimerax.open", "biopython.parse"],
53
+ description="3D macromolecular coordinate structure file",
54
+ )
55
+ elif ext in (".fasta", ".fa", ".fna", ".faa"):
56
+ return ArtifactSemantics(
57
+ path=norm,
58
+ filename=base,
59
+ semantic_type="biological_sequence",
60
+ domain="bioinformatics",
61
+ recommended_capabilities=["blast.search", "ncbi.query", "biopython.parse"],
62
+ description="Nucleotide or amino acid biological sequence file",
63
+ )
64
+ elif ext in (".safetensors", ".pt", ".pth", ".onnx", ".bin"):
65
+ return ArtifactSemantics(
66
+ path=norm,
67
+ filename=base,
68
+ semantic_type="ml_model",
69
+ domain="machine_learning",
70
+ recommended_capabilities=["pytorch.eval", "huggingface.model", "ml.detect_hardware"],
71
+ description="Trained neural network model weights or serialized computational graph",
72
+ )
73
+ elif ext == ".ipynb":
74
+ return ArtifactSemantics(
75
+ path=norm,
76
+ filename=base,
77
+ semantic_type="jupyter_notebook",
78
+ domain="notebook_computing",
79
+ recommended_capabilities=["jupyter.execute", "jupyter.list_servers", "python.execute"],
80
+ description="Interactive literate computing Jupyter notebook",
81
+ )
82
+ elif ext in (".csv", ".tsv", ".parquet", ".arrow", ".jsonl"):
83
+ return ArtifactSemantics(
84
+ path=norm,
85
+ filename=base,
86
+ semantic_type="dataset",
87
+ domain="data_science",
88
+ recommended_capabilities=["numpy.compute", "kaggle.dataset", "python.execute"],
89
+ description="Tabular or columnar scientific/machine learning dataset",
90
+ )
91
+ elif ext in (".png", ".svg", ".pdf", ".eps"):
92
+ return ArtifactSemantics(
93
+ path=norm,
94
+ filename=base,
95
+ semantic_type="scientific_figure",
96
+ domain="scientific_visualization",
97
+ recommended_capabilities=["browser.navigate", "filesystem.read"],
98
+ description="Rendered scientific visualization or publication figure",
99
+ )
100
+ elif ext in (".qasm",):
101
+ return ArtifactSemantics(
102
+ path=norm,
103
+ filename=base,
104
+ semantic_type="quantum_circuit",
105
+ domain="quantum_computing",
106
+ recommended_capabilities=["qiskit.run_circuit", "quantum.bloch_sphere"],
107
+ description="OpenQASM quantum circuit specification",
108
+ )
109
+ elif ext in (".py",):
110
+ return ArtifactSemantics(
111
+ path=norm,
112
+ filename=base,
113
+ semantic_type="source_code",
114
+ domain="software_engineering",
115
+ recommended_capabilities=["python.execute", "git.diff", "filesystem.read"],
116
+ description="Python executable source code",
117
+ )
118
+
119
+ return ArtifactSemantics(
120
+ path=norm,
121
+ filename=base,
122
+ semantic_type="generic_file",
123
+ domain="general",
124
+ recommended_capabilities=["filesystem.read"],
125
+ description="General file",
126
+ )
@@ -0,0 +1,123 @@
1
+ """
2
+ CAT Data Lineage Graph per §58, §97:
3
+ Tracks end-to-end transformation lineage from raw source to figures and publication reports.
4
+ Explains dependency chains when upstream or downstream artifacts change.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import time
10
+ import os
11
+ from dataclasses import dataclass, field
12
+ from typing import Any, Dict, List, Optional, Set
13
+
14
+
15
+ @dataclass
16
+ class LineageNode:
17
+ path: str
18
+ artifact_type: str # dataset, features, model, predictions, figure, report
19
+ sources: List[str] = field(default_factory=list) # parent artifact paths
20
+ transform_tool: str = "" # tool or script that produced this artifact
21
+ experiment_id: Optional[str] = None
22
+ task_id: Optional[str] = None
23
+ created_at: float = field(default_factory=time.time)
24
+ metadata: Dict[str, Any] = field(default_factory=dict)
25
+
26
+ def to_dict(self) -> Dict[str, Any]:
27
+ return {
28
+ "path": self.path,
29
+ "artifact_type": self.artifact_type,
30
+ "sources": self.sources,
31
+ "transform_tool": self.transform_tool,
32
+ "experiment_id": self.experiment_id,
33
+ "task_id": self.task_id,
34
+ "created_at": self.created_at,
35
+ "metadata": self.metadata,
36
+ }
37
+
38
+
39
+ class DataLineageGraph:
40
+ """DAG tracking artifact generation, transformations, and dependency explanations."""
41
+
42
+ def __init__(self):
43
+ self.nodes: Dict[str, LineageNode] = {}
44
+
45
+ def record_transformation(
46
+ self,
47
+ target_path: Optional[str] = None,
48
+ sources: Optional[List[str]] = None,
49
+ transform_tool: str = "",
50
+ artifact_type: str = "artifact",
51
+ experiment_id: Optional[str] = None,
52
+ task_id: Optional[str] = None,
53
+ metadata: Optional[Dict[str, Any]] = None,
54
+ output_path: Optional[str] = None,
55
+ ) -> LineageNode:
56
+ actual_path = target_path or output_path or ""
57
+ norm_target = os.path.normpath(actual_path)
58
+ norm_sources = [os.path.normpath(s) for s in (sources or [])]
59
+ node = LineageNode(
60
+ path=norm_target,
61
+ artifact_type=artifact_type,
62
+ sources=norm_sources,
63
+ transform_tool=transform_tool,
64
+ experiment_id=experiment_id,
65
+ task_id=task_id,
66
+ metadata=metadata or {},
67
+ )
68
+ self.nodes[norm_target] = node
69
+ return node
70
+
71
+ def get_upstream_lineage(self, target_path: str) -> List[str]:
72
+ """Return list of all ancestor artifacts that produced target_path."""
73
+ norm_target = os.path.normpath(target_path)
74
+ visited = []
75
+ queue = [norm_target]
76
+ while queue:
77
+ curr = queue.pop(0)
78
+ node = self.nodes.get(curr)
79
+ if node:
80
+ for s in node.sources:
81
+ if s not in visited:
82
+ visited.append(s)
83
+ queue.append(s)
84
+ return visited
85
+
86
+ def get_upstream(self, target_path: str) -> List[str]:
87
+ return self.get_upstream_lineage(target_path)
88
+
89
+ def get_downstream_impact(self, source_path: str) -> List[str]:
90
+ """Return list of all downstream artifacts dependent on source_path."""
91
+ norm_source = os.path.normpath(source_path)
92
+ impacted = []
93
+ for path, node in self.nodes.items():
94
+ if norm_source in node.sources:
95
+ impacted.append(path)
96
+ impacted.extend(self.get_downstream_impact(path))
97
+ return list(dict.fromkeys(impacted))
98
+
99
+ def get_downstream(self, source_path: str) -> List[str]:
100
+ return self.get_downstream_impact(source_path)
101
+
102
+ def explain_chain(self, target_path: str) -> str:
103
+ """Human-readable explanation of how this artifact was generated."""
104
+ norm = os.path.normpath(target_path)
105
+ node = self.nodes.get(norm)
106
+ if not node:
107
+ return f"No recorded lineage for artifact: {os.path.basename(target_path)}"
108
+
109
+ lines = [f"Artifact: {os.path.basename(norm)} ({node.artifact_type})"]
110
+ if node.transform_tool:
111
+ lines.append(f"Produced by: {node.transform_tool}")
112
+ if node.experiment_id:
113
+ lines.append(f"Experiment: {node.experiment_id}")
114
+ if node.sources:
115
+ lines.append("Source dependencies:")
116
+ for s in node.sources:
117
+ src_node = self.nodes.get(s)
118
+ tool_note = f" (via {src_node.transform_tool})" if src_node and src_node.transform_tool else ""
119
+ lines.append(f" └── {os.path.basename(s)}{tool_note}")
120
+ return "\n".join(lines)
121
+
122
+
123
+ data_lineage = DataLineageGraph()