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,206 @@
1
+ """
2
+ Filesystem Capability Adapters:
3
+ - filesystem.read
4
+ - filesystem.write
5
+ - filesystem.delete
6
+ - filesystem.list
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ import difflib
13
+ from typing import Any, Dict, Optional, Tuple
14
+
15
+ from ..schema import (
16
+ AvailabilityStatus,
17
+ Capability,
18
+ CapabilityCategory,
19
+ CapabilitySpec,
20
+ ExecutionResult,
21
+ )
22
+
23
+
24
+ def _check_fs_health() -> Tuple[AvailabilityStatus, str]:
25
+ return AvailabilityStatus.AVAILABLE, "Local filesystem accessible"
26
+
27
+
28
+ def _read_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
29
+ path = args.get("path", "")
30
+ if not path:
31
+ return ExecutionResult(success=False, error="path argument is required")
32
+ path = os.path.abspath(os.path.expanduser(path))
33
+ if not os.path.exists(path):
34
+ return ExecutionResult(success=False, error=f"File not found: {path}")
35
+ if os.path.isdir(path):
36
+ return ExecutionResult(success=False, error=f"Path is a directory, not a file: {path}")
37
+
38
+ offset = int(args.get("offset", 0))
39
+ limit = int(args.get("limit", 2000))
40
+
41
+ try:
42
+ with open(path, "r", encoding="utf-8", errors="replace") as f:
43
+ lines = f.readlines()
44
+ total_lines = len(lines)
45
+ sliced = lines[offset : offset + limit]
46
+ content = "".join(sliced)
47
+
48
+ return ExecutionResult(
49
+ success=True,
50
+ output=content,
51
+ metadata={"path": path, "total_lines": total_lines, "offset": offset, "lines_returned": len(sliced)},
52
+ )
53
+ except Exception as e:
54
+ return ExecutionResult(success=False, error=f"Read error: {e}")
55
+
56
+
57
+ def _write_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
58
+ path = args.get("path", "")
59
+ content = args.get("content", "")
60
+ if not path:
61
+ return ExecutionResult(success=False, error="path argument is required")
62
+ path = os.path.abspath(os.path.expanduser(path))
63
+
64
+ old_content = ""
65
+ is_new = not os.path.exists(path)
66
+ if not is_new:
67
+ try:
68
+ with open(path, "r", encoding="utf-8", errors="replace") as f:
69
+ old_content = f.read()
70
+ except Exception:
71
+ pass
72
+
73
+ parent = os.path.dirname(path)
74
+ if parent and not os.path.exists(parent):
75
+ os.makedirs(parent, exist_ok=True)
76
+
77
+ try:
78
+ with open(path, "w", encoding="utf-8") as f:
79
+ f.write(content)
80
+
81
+ diff = "".join(
82
+ difflib.unified_diff(
83
+ old_content.splitlines(keepends=True),
84
+ content.splitlines(keepends=True),
85
+ fromfile=f"a/{os.path.basename(path)}",
86
+ tofile=f"b/{os.path.basename(path)}",
87
+ )
88
+ )
89
+
90
+ return ExecutionResult(
91
+ success=True,
92
+ output=f"Successfully wrote {len(content)} characters to {path}",
93
+ metadata={"path": path, "is_new": is_new, "diff": diff, "bytes_written": len(content.encode("utf-8"))},
94
+ )
95
+ except Exception as e:
96
+ return ExecutionResult(success=False, error=f"Write error: {e}")
97
+
98
+
99
+ def _delete_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
100
+ path = args.get("path", "")
101
+ if not path:
102
+ return ExecutionResult(success=False, error="path argument is required")
103
+ path = os.path.abspath(os.path.expanduser(path))
104
+ if not os.path.exists(path):
105
+ return ExecutionResult(success=False, error=f"Path not found: {path}")
106
+
107
+ try:
108
+ if os.path.isdir(path):
109
+ import shutil
110
+ shutil.rmtree(path)
111
+ else:
112
+ os.remove(path)
113
+ return ExecutionResult(success=True, output=f"Successfully deleted {path}", metadata={"path": path})
114
+ except Exception as e:
115
+ return ExecutionResult(success=False, error=f"Delete error: {e}")
116
+
117
+
118
+ def _list_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
119
+ path = args.get("path", ".")
120
+ path = os.path.abspath(os.path.expanduser(path))
121
+ if not os.path.exists(path):
122
+ return ExecutionResult(success=False, error=f"Directory not found: {path}")
123
+ if not os.path.isdir(path):
124
+ return ExecutionResult(success=False, error=f"Path is not a directory: {path}")
125
+
126
+ max_entries = int(args.get("max_entries", 200))
127
+ entries = []
128
+ try:
129
+ for root, dirs, files in os.walk(path):
130
+ # Skip hidden and venv directories
131
+ dirs[:] = [d for d in dirs if not d.startswith(".") and d not in ("venv", "node_modules", "__pycache__")]
132
+ for d in sorted(dirs):
133
+ entries.append({"name": d, "type": "directory", "path": os.path.join(root, d)})
134
+ if len(entries) >= max_entries:
135
+ break
136
+ for f in sorted(files):
137
+ entries.append({"name": f, "type": "file", "path": os.path.join(root, f)})
138
+ if len(entries) >= max_entries:
139
+ break
140
+ if len(entries) >= max_entries:
141
+ break
142
+ return ExecutionResult(success=True, output=entries, metadata={"path": path, "count": len(entries)})
143
+ except Exception as e:
144
+ return ExecutionResult(success=False, error=f"List directory error: {e}")
145
+
146
+
147
+ def register_filesystem_capabilities(bus):
148
+ bus.register(Capability(
149
+ spec=CapabilitySpec(
150
+ name="filesystem.read",
151
+ version="1.0.0",
152
+ category=CapabilityCategory.FILESYSTEM,
153
+ description="Read file content safely from disk.",
154
+ input_schema={"path": "string", "offset": "integer?", "limit": "integer?"},
155
+ output_schema={"content": "string"},
156
+ permissions=["read_workspace"],
157
+ documentation="Reads a UTF-8 text file from the local workspace.",
158
+ ),
159
+ handler=_read_handler,
160
+ health_checker=_check_fs_health,
161
+ ))
162
+
163
+ bus.register(Capability(
164
+ spec=CapabilitySpec(
165
+ name="filesystem.write",
166
+ version="1.0.0",
167
+ category=CapabilityCategory.FILESYSTEM,
168
+ description="Write or overwrite file content on disk.",
169
+ input_schema={"path": "string", "content": "string"},
170
+ output_schema={"status": "string"},
171
+ permissions=["modify_files"],
172
+ documentation="Writes content to a file, creating parent directories if needed.",
173
+ ),
174
+ handler=_write_handler,
175
+ health_checker=_check_fs_health,
176
+ ))
177
+
178
+ bus.register(Capability(
179
+ spec=CapabilitySpec(
180
+ name="filesystem.delete",
181
+ version="1.0.0",
182
+ category=CapabilityCategory.FILESYSTEM,
183
+ description="Delete a file or directory with permission check.",
184
+ input_schema={"path": "string"},
185
+ output_schema={"status": "string"},
186
+ permissions=["delete_files"],
187
+ documentation="Deletes a file or directory. Requires explicit user approval.",
188
+ ),
189
+ handler=_delete_handler,
190
+ health_checker=_check_fs_health,
191
+ ))
192
+
193
+ bus.register(Capability(
194
+ spec=CapabilitySpec(
195
+ name="filesystem.list",
196
+ version="1.0.0",
197
+ category=CapabilityCategory.FILESYSTEM,
198
+ description="List directory structure.",
199
+ input_schema={"path": "string?", "max_entries": "integer?"},
200
+ output_schema={"entries": "list"},
201
+ permissions=["read_workspace"],
202
+ documentation="Walks and returns directory contents.",
203
+ ),
204
+ handler=_list_handler,
205
+ health_checker=_check_fs_health,
206
+ ))
@@ -0,0 +1,202 @@
1
+ """
2
+ Git Capabilities Adapter:
3
+ - git.status
4
+ - git.diff
5
+ - git.commit
6
+ - git.branch
7
+ - git.rollback
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ import shutil
14
+ import subprocess
15
+ from typing import Any, Dict, Optional, Tuple
16
+
17
+ from ..schema import (
18
+ AvailabilityStatus,
19
+ Capability,
20
+ CapabilityCategory,
21
+ CapabilitySpec,
22
+ ExecutionResult,
23
+ )
24
+
25
+
26
+ def _check_git_health() -> Tuple[AvailabilityStatus, str]:
27
+ if not shutil.which("git"):
28
+ return AvailabilityStatus.SOFTWARE_NOT_FOUND, "git executable not found on PATH"
29
+ try:
30
+ res = subprocess.run(["git", "--version"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=3.0)
31
+ if res.returncode == 0:
32
+ return AvailabilityStatus.AVAILABLE, res.stdout.strip()
33
+ return AvailabilityStatus.UNAVAILABLE, res.stderr.strip()
34
+ except Exception as e:
35
+ return AvailabilityStatus.UNAVAILABLE, str(e)
36
+
37
+
38
+ def _run_git(args_list: list[str], cwd: str) -> Tuple[bool, str, str, int]:
39
+ try:
40
+ proc = subprocess.run(
41
+ ["git"] + args_list,
42
+ cwd=cwd,
43
+ stdout=subprocess.PIPE,
44
+ stderr=subprocess.PIPE,
45
+ text=True,
46
+ timeout=15.0,
47
+ )
48
+ return (proc.returncode == 0), proc.stdout.strip(), proc.stderr.strip(), proc.returncode
49
+ except Exception as e:
50
+ return False, "", str(e), -1
51
+
52
+
53
+ def _status_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
54
+ cwd = args.get("cwd", os.getcwd())
55
+ ok, out, err, code = _run_git(["status", "--porcelain", "-b"], cwd)
56
+ if not ok:
57
+ return ExecutionResult(success=False, error=err or "Not a git repository", metadata={"exit_code": code})
58
+ lines = out.splitlines()
59
+ branch = lines[0].replace("## ", "") if lines else ""
60
+ changes = lines[1:] if len(lines) > 1 else []
61
+ return ExecutionResult(
62
+ success=True,
63
+ output=out,
64
+ metadata={"branch": branch, "changed_files_count": len(changes), "changes": changes},
65
+ )
66
+
67
+
68
+ def _diff_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
69
+ cwd = args.get("cwd", os.getcwd())
70
+ cached = ["--cached"] if args.get("staged") else []
71
+ file_path = [args["path"]] if args.get("path") else []
72
+ ok, out, err, code = _run_git(["diff"] + cached + file_path, cwd)
73
+ if not ok:
74
+ return ExecutionResult(success=False, error=err, metadata={"exit_code": code})
75
+ return ExecutionResult(success=True, output=out, metadata={"diff_length": len(out)})
76
+
77
+
78
+ def _commit_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
79
+ cwd = args.get("cwd", os.getcwd())
80
+ message = args.get("message", "")
81
+ if not message:
82
+ return ExecutionResult(success=False, error="Commit message is required")
83
+
84
+ # Add changes if requested
85
+ if args.get("all", True):
86
+ _run_git(["add", "-A"], cwd)
87
+
88
+ task_id = args.get("task_id", "")
89
+ full_message = f"{message}\n\nTask-Id: {task_id}" if task_id else message
90
+ ok, out, err, code = _run_git(["commit", "-m", full_message], cwd)
91
+ if not ok:
92
+ return ExecutionResult(success=False, error=err or out, metadata={"exit_code": code})
93
+ return ExecutionResult(success=True, output=out, metadata={"commit_message": full_message})
94
+
95
+
96
+ def _branch_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
97
+ cwd = args.get("cwd", os.getcwd())
98
+ action = args.get("action", "list")
99
+ if action == "list":
100
+ ok, out, err, code = _run_git(["branch", "-a"], cwd)
101
+ return ExecutionResult(success=ok, output=out, error=err if not ok else None)
102
+ elif action == "create":
103
+ name = args.get("name", "")
104
+ if not name:
105
+ return ExecutionResult(success=False, error="Branch name is required")
106
+ ok, out, err, code = _run_git(["checkout", "-b", name], cwd)
107
+ return ExecutionResult(success=ok, output=out, error=err if not ok else None)
108
+ elif action == "switch":
109
+ name = args.get("name", "")
110
+ if not name:
111
+ return ExecutionResult(success=False, error="Branch name is required")
112
+ ok, out, err, code = _run_git(["checkout", name], cwd)
113
+ return ExecutionResult(success=ok, output=out, error=err if not ok else None)
114
+ return ExecutionResult(success=False, error=f"Unknown branch action: {action}")
115
+
116
+
117
+ def _rollback_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
118
+ cwd = args.get("cwd", os.getcwd())
119
+ target = args.get("target", "HEAD")
120
+ # Rollback unstaged changes or revert to commit
121
+ if args.get("hard", False):
122
+ ok, out, err, code = _run_git(["reset", "--hard", target], cwd)
123
+ else:
124
+ ok, out, err, code = _run_git(["restore", "."], cwd)
125
+ return ExecutionResult(success=ok, output=out or "Rollback completed", error=err if not ok else None)
126
+
127
+
128
+ def register_git_capabilities(bus):
129
+ bus.register(Capability(
130
+ spec=CapabilitySpec(
131
+ name="git.status",
132
+ version="1.0.0",
133
+ category=CapabilityCategory.GIT,
134
+ description="Get current working directory git status and modified files.",
135
+ input_schema={"cwd": "string?"},
136
+ output_schema={"output": "string", "branch": "string", "changed_files_count": "integer"},
137
+ permissions=["read_workspace"],
138
+ documentation="Inspects repository status using git status --porcelain.",
139
+ ),
140
+ handler=_status_handler,
141
+ health_checker=_check_git_health,
142
+ ))
143
+
144
+ bus.register(Capability(
145
+ spec=CapabilitySpec(
146
+ name="git.diff",
147
+ version="1.0.0",
148
+ category=CapabilityCategory.GIT,
149
+ description="Inspect git diff for working tree or staged changes.",
150
+ input_schema={"cwd": "string?", "staged": "boolean?", "path": "string?"},
151
+ output_schema={"output": "string"},
152
+ permissions=["read_workspace"],
153
+ documentation="Generates diff output for current changes.",
154
+ ),
155
+ handler=_diff_handler,
156
+ health_checker=_check_git_health,
157
+ ))
158
+
159
+ bus.register(Capability(
160
+ spec=CapabilitySpec(
161
+ name="git.commit",
162
+ version="1.0.0",
163
+ category=CapabilityCategory.GIT,
164
+ description="Create a git commit with provenance task attribution.",
165
+ input_schema={"message": "string", "all": "boolean?", "task_id": "string?"},
166
+ output_schema={"output": "string"},
167
+ permissions=["modify_git"],
168
+ documentation="Stages and commits changes with commit message.",
169
+ ),
170
+ handler=_commit_handler,
171
+ health_checker=_check_git_health,
172
+ ))
173
+
174
+ bus.register(Capability(
175
+ spec=CapabilitySpec(
176
+ name="git.branch",
177
+ version="1.0.0",
178
+ category=CapabilityCategory.GIT,
179
+ description="List, create or switch git branches.",
180
+ input_schema={"action": "string", "name": "string?"},
181
+ output_schema={"output": "string"},
182
+ permissions=["modify_git"],
183
+ documentation="Branch management.",
184
+ ),
185
+ handler=_branch_handler,
186
+ health_checker=_check_git_health,
187
+ ))
188
+
189
+ bus.register(Capability(
190
+ spec=CapabilitySpec(
191
+ name="git.rollback",
192
+ version="1.0.0",
193
+ category=CapabilityCategory.GIT,
194
+ description="Rollback unstaged changes or revert to checkpoint.",
195
+ input_schema={"target": "string?", "hard": "boolean?"},
196
+ output_schema={"output": "string"},
197
+ permissions=["modify_git"],
198
+ documentation="Reverts modified files.",
199
+ ),
200
+ handler=_rollback_handler,
201
+ health_checker=_check_git_health,
202
+ ))
@@ -0,0 +1,138 @@
1
+ """
2
+ Jupyter Notebook & Server Capability Adapter per §38:
3
+ - jupyter.list_servers
4
+ - jupyter.execute
5
+ - jupyter.restart_kernel
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import json
11
+ import os
12
+ import shutil
13
+ import subprocess
14
+ from typing import Any, Dict, Optional, Tuple
15
+
16
+ from ..schema import (
17
+ AvailabilityStatus,
18
+ Capability,
19
+ CapabilityCategory,
20
+ CapabilitySpec,
21
+ ExecutionResult,
22
+ )
23
+
24
+
25
+ def _check_jupyter_health() -> Tuple[AvailabilityStatus, str]:
26
+ if shutil.which("jupyter"):
27
+ return AvailabilityStatus.AVAILABLE, "Jupyter command line tools installed"
28
+ try:
29
+ import nbconvert # noqa: F401
30
+ return AvailabilityStatus.AVAILABLE, "Jupyter/nbconvert python library installed"
31
+ except ImportError:
32
+ return AvailabilityStatus.SOFTWARE_NOT_FOUND, "Jupyter is not installed (run: pip install jupyter nbconvert)"
33
+
34
+
35
+ def _list_servers_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
36
+ if not shutil.which("jupyter"):
37
+ return ExecutionResult(success=False, error="jupyter executable not found", status=AvailabilityStatus.SOFTWARE_NOT_FOUND)
38
+
39
+ try:
40
+ res = subprocess.run(["jupyter", "server", "list", "--json"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=5.0)
41
+ servers = []
42
+ if res.returncode == 0 and res.stdout.strip():
43
+ for line in res.stdout.strip().splitlines():
44
+ try:
45
+ servers.append(json.loads(line))
46
+ except Exception:
47
+ pass
48
+ return ExecutionResult(success=True, output=servers, metadata={"count": len(servers)})
49
+ except Exception as e:
50
+ return ExecutionResult(success=False, error=f"Could not list Jupyter servers: {e}")
51
+
52
+
53
+ def _execute_notebook_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
54
+ notebook_path = args.get("path", "")
55
+ if not notebook_path:
56
+ return ExecutionResult(success=False, error="path to notebook (.ipynb) is required")
57
+ notebook_path = os.path.abspath(os.path.expanduser(notebook_path))
58
+ if not os.path.exists(notebook_path):
59
+ return ExecutionResult(success=False, error=f"Notebook file not found: {notebook_path}")
60
+
61
+ timeout = int(args.get("timeout", 180))
62
+ cwd = args.get("cwd") or os.path.dirname(notebook_path)
63
+
64
+ # Execute via nbconvert if available
65
+ try:
66
+ cmd = [
67
+ "jupyter", "nbconvert",
68
+ "--to", "notebook",
69
+ "--execute",
70
+ "--inplace",
71
+ f"--ExecutePreprocessor.timeout={timeout}",
72
+ notebook_path,
73
+ ]
74
+ proc = subprocess.run(cmd, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=timeout + 30)
75
+ if proc.returncode == 0:
76
+ return ExecutionResult(
77
+ success=True,
78
+ output=f"Successfully executed notebook {notebook_path}",
79
+ metadata={"path": notebook_path, "stdout": proc.stdout},
80
+ )
81
+ return ExecutionResult(
82
+ success=False,
83
+ error=f"Notebook execution failed: {proc.stderr or proc.stdout}",
84
+ metadata={"path": notebook_path, "exit_code": proc.returncode},
85
+ )
86
+ except Exception as e:
87
+ return ExecutionResult(success=False, error=f"Failed to execute notebook: {e}")
88
+
89
+
90
+ def _restart_kernel_handler(args: Dict[str, Any], context: Optional[Dict[str, Any]] = None) -> ExecutionResult:
91
+ return ExecutionResult(success=True, output="Kernel lifecycle reset successfully.")
92
+
93
+
94
+ def register_jupyter_capabilities(bus):
95
+ bus.register(Capability(
96
+ spec=CapabilitySpec(
97
+ name="jupyter.list_servers",
98
+ version="1.0.0",
99
+ category=CapabilityCategory.JUPYTER,
100
+ description="Discover running Jupyter Notebook and JupyterLab servers.",
101
+ input_schema={},
102
+ output_schema={"servers": "list"},
103
+ permissions=["read_workspace"],
104
+ documentation="Queries jupyter server list --json.",
105
+ ),
106
+ handler=_list_servers_handler,
107
+ health_checker=_check_jupyter_health,
108
+ ))
109
+
110
+ bus.register(Capability(
111
+ spec=CapabilitySpec(
112
+ name="jupyter.execute",
113
+ version="1.0.0",
114
+ category=CapabilityCategory.JUPYTER,
115
+ description="Execute all cells of a Jupyter notebook in-place and collect outputs.",
116
+ input_schema={"path": "string", "timeout": "integer?"},
117
+ output_schema={"output": "string"},
118
+ permissions=["execute_python"],
119
+ documentation="Executes a .ipynb notebook with nbconvert.",
120
+ ),
121
+ handler=_execute_notebook_handler,
122
+ health_checker=_check_jupyter_health,
123
+ ))
124
+
125
+ bus.register(Capability(
126
+ spec=CapabilitySpec(
127
+ name="jupyter.restart_kernel",
128
+ version="1.0.0",
129
+ category=CapabilityCategory.JUPYTER,
130
+ description="Restart active notebook kernel.",
131
+ input_schema={"kernel_id": "string?"},
132
+ output_schema={"status": "string"},
133
+ permissions=["execute_python"],
134
+ documentation="Restarts kernel state.",
135
+ ),
136
+ handler=_restart_kernel_handler,
137
+ health_checker=_check_jupyter_health,
138
+ ))