clm-kernel 0.0.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 (333) hide show
  1. clm/__init__.py +230 -0
  2. clm/__main__.py +25 -0
  3. clm/cross_cutting/__init__.py +50 -0
  4. clm/cross_cutting/_test_runner.py +36 -0
  5. clm/cross_cutting/async_utils.py +25 -0
  6. clm/cross_cutting/cel_builder.py +123 -0
  7. clm/cross_cutting/cel_converter.py +697 -0
  8. clm/cross_cutting/cel_engine.py +252 -0
  9. clm/cross_cutting/cel_ops.py +72 -0
  10. clm/cross_cutting/cel_ops_db.py +157 -0
  11. clm/cross_cutting/cel_ops_files.py +99 -0
  12. clm/cross_cutting/cel_ops_state.py +71 -0
  13. clm/cross_cutting/cel_ops_strings.py +64 -0
  14. clm/cross_cutting/cel_resolver.py +63 -0
  15. clm/cross_cutting/cel_slices.py +56 -0
  16. clm/cross_cutting/cel_store.py +330 -0
  17. clm/cross_cutting/clm_logger.py +140 -0
  18. clm/cross_cutting/config/__init__.py +155 -0
  19. clm/cross_cutting/config/config_constants.py +392 -0
  20. clm/cross_cutting/config/env_parameters.py +90 -0
  21. clm/cross_cutting/config/logging.py +378 -0
  22. clm/cross_cutting/config/settings.py +504 -0
  23. clm/cross_cutting/context.py +5 -0
  24. clm/cross_cutting/domain_models.py +55 -0
  25. clm/cross_cutting/domain_types.py +61 -0
  26. clm/cross_cutting/effects.py +67 -0
  27. clm/cross_cutting/errors.py +153 -0
  28. clm/cross_cutting/governance.py +331 -0
  29. clm/cross_cutting/guards.py +41 -0
  30. clm/cross_cutting/mcard.py +321 -0
  31. clm/cross_cutting/native.py +185 -0
  32. clm/cross_cutting/observability.py +289 -0
  33. clm/cross_cutting/pcard.py +434 -0
  34. clm/cross_cutting/registry.py +71 -0
  35. clm/cross_cutting/resources.py +84 -0
  36. clm/cross_cutting/secrets.py +44 -0
  37. clm/cross_cutting/subprocess_utils.py +118 -0
  38. clm/cross_cutting/telemetry.py +164 -0
  39. clm/cross_cutting/testing/__init__.py +8 -0
  40. clm/cross_cutting/testing/comparator.py +187 -0
  41. clm/cross_cutting/testing/datasets.py +87 -0
  42. clm/cross_cutting/timeutil.py +20 -0
  43. clm/cross_cutting/types/new_type_check.py +0 -0
  44. clm/cross_cutting/utils/__init__.py +1 -0
  45. clm/cross_cutting/utils/url_safety.py +50 -0
  46. clm/cross_cutting/vcard.py +560 -0
  47. clm/cross_cutting/writer.py +118 -0
  48. clm/layer0/README.md +50 -0
  49. clm/layer0/__init__.py +223 -0
  50. clm/layer0/algebra.py +60 -0
  51. clm/layer0/algebra_baldwin.py +133 -0
  52. clm/layer0/algebra_baldwin_generators.py +36 -0
  53. clm/layer0/algebra_equivalence.py +262 -0
  54. clm/layer0/algebra_ingest.py +306 -0
  55. clm/layer0/algebra_manifest_io.py +156 -0
  56. clm/layer0/assembler.py +481 -0
  57. clm/layer0/cel/__init__.py +23 -0
  58. clm/layer0/cel_eval.py +491 -0
  59. clm/layer0/codec.py +106 -0
  60. clm/layer0/context.py +122 -0
  61. clm/layer0/db.py +160 -0
  62. clm/layer0/exceptions.py +73 -0
  63. clm/layer0/fibration.py +106 -0
  64. clm/layer0/gates.py +50 -0
  65. clm/layer0/hash.py +80 -0
  66. clm/layer0/loader.py +826 -0
  67. clm/layer0/math_utils.py +163 -0
  68. clm/layer0/mcard.py +96 -0
  69. clm/layer0/model/__init__.py +0 -0
  70. clm/layer0/model/action.py +223 -0
  71. clm/layer0/model/card.py +280 -0
  72. clm/layer0/model/card_triad.py +427 -0
  73. clm/layer0/model/compatibility.py +60 -0
  74. clm/layer0/model/dictionary.py +46 -0
  75. clm/layer0/model/dots.py +331 -0
  76. clm/layer0/model/event_producer.py +88 -0
  77. clm/layer0/model/g_time.py +89 -0
  78. clm/layer0/model/handle.py +163 -0
  79. clm/layer0/model/hash/__init__.py +6 -0
  80. clm/layer0/model/hash/algorithms/__init__.py +3 -0
  81. clm/layer0/model/hash/algorithms/custom_hash.py +7 -0
  82. clm/layer0/model/hash/algorithms/local_sha256.py +178 -0
  83. clm/layer0/model/hash/constants.py +53 -0
  84. clm/layer0/model/hash/enums.py +60 -0
  85. clm/layer0/model/hash/validator.py +307 -0
  86. clm/layer0/model/interpreter.py +275 -0
  87. clm/layer0/model/pagination.py +45 -0
  88. clm/layer0/model/pcard.py +716 -0
  89. clm/layer0/model/schema.py +44 -0
  90. clm/layer0/model/utils/__init__.py +1 -0
  91. clm/layer0/model/utils/content_analyzer.py +152 -0
  92. clm/layer0/model/validators/__init__.py +1 -0
  93. clm/layer0/model/validators/base_validator.py +44 -0
  94. clm/layer0/model/validators/binary_validator.py +66 -0
  95. clm/layer0/model/validators/text_validator.py +82 -0
  96. clm/layer0/model/validators/validation_registry.py +49 -0
  97. clm/layer0/model/vcard.py +823 -0
  98. clm/layer0/model/vcard_ext/__init__.py +39 -0
  99. clm/layer0/model/vcard_ext/core.py +60 -0
  100. clm/layer0/model/vcard_ext/network.py +23 -0
  101. clm/layer0/model/vcard_ext/observability.py +63 -0
  102. clm/layer0/model/vcard_ext/storage.py +60 -0
  103. clm/layer0/model/vcard_ext/vendors.py +211 -0
  104. clm/layer0/model/vcard_sandwich.py +119 -0
  105. clm/layer0/model/vcard_vocabulary.py +463 -0
  106. clm/layer0/model/workflow.py +274 -0
  107. clm/layer0/narrative.py +431 -0
  108. clm/layer0/ontology/collection.py +125 -0
  109. clm/layer0/parser.py +429 -0
  110. clm/layer0/relations.py +73 -0
  111. clm/layer0/rule_evaluator.py +251 -0
  112. clm/layer0/schema/__init__.py +38 -0
  113. clm/layer0/schema/ddl.py +97 -0
  114. clm/layer0/templates/abstract.yaml +99 -0
  115. clm/layer0/templates/balanced.yaml +253 -0
  116. clm/layer0/templates/concrete.yaml +172 -0
  117. clm/layer0/type_registry.py +223 -0
  118. clm/layer0/types.py +204 -0
  119. clm/layer0/utils.py +886 -0
  120. clm/layer0/verifier_core.py +262 -0
  121. clm/layer1/__init__.py +31 -0
  122. clm/layer1/action_dispatcher.py +275 -0
  123. clm/layer1/adapter_registry.py +233 -0
  124. clm/layer1/algebra_baldwin_generators.py +192 -0
  125. clm/layer1/arrow_compiler.py +525 -0
  126. clm/layer1/batch_processor.py +152 -0
  127. clm/layer1/cel/__init__.py +27 -0
  128. clm/layer1/cel/builder.py +173 -0
  129. clm/layer1/engine.py +759 -0
  130. clm/layer1/engine_async.py +89 -0
  131. clm/layer1/eval_callback.py +49 -0
  132. clm/layer1/gate_composer.py +229 -0
  133. clm/layer1/hoare_sandwich.py +298 -0
  134. clm/layer1/identity_guard.py +348 -0
  135. clm/layer1/matrix.py +138 -0
  136. clm/layer1/neural_runtime.py +221 -0
  137. clm/layer1/operations/__init__.py +123 -0
  138. clm/layer1/operations/augment.py +156 -0
  139. clm/layer1/operations/base.py +109 -0
  140. clm/layer1/operations/builtins.py +338 -0
  141. clm/layer1/operations/dispatch.py +144 -0
  142. clm/layer1/operations/exclude.py +107 -0
  143. clm/layer1/operations/handle.py +197 -0
  144. clm/layer1/operations/identity.py +139 -0
  145. clm/layer1/operations/invert.py +66 -0
  146. clm/layer1/operations/loader.py +116 -0
  147. clm/layer1/operations/manipulators/__init__.py +23 -0
  148. clm/layer1/operations/manipulators/document.py +211 -0
  149. clm/layer1/operations/manipulators/media.py +134 -0
  150. clm/layer1/operations/manipulators/structured.py +159 -0
  151. clm/layer1/operations/manipulators/tabular.py +334 -0
  152. clm/layer1/operations/port.py +65 -0
  153. clm/layer1/operations/services.py +529 -0
  154. clm/layer1/operations/split.py +117 -0
  155. clm/layer1/operations/substitute.py +75 -0
  156. clm/layer1/petri.py +1052 -0
  157. clm/layer1/pocketflow_orchestrator.py +408 -0
  158. clm/layer1/pocketflow_scheduler.py +519 -0
  159. clm/layer1/protocols.py +197 -0
  160. clm/layer1/runtime_adapter.py +718 -0
  161. clm/layer1/runtimes/__init__.py +71 -0
  162. clm/layer1/runtimes/_generated_assertions.py +125 -0
  163. clm/layer1/runtimes/_generated_types.py +42 -0
  164. clm/layer1/runtimes/base.py +265 -0
  165. clm/layer1/runtimes/binary.py +101 -0
  166. clm/layer1/runtimes/factory.py +203 -0
  167. clm/layer1/runtimes/javascript.py +321 -0
  168. clm/layer1/runtimes/javascript_runtime.js +33 -0
  169. clm/layer1/runtimes/js_scripts/isomorphism.mjs +22 -0
  170. clm/layer1/runtimes/js_scripts/loader.mjs +52 -0
  171. clm/layer1/runtimes/lambda_calc.py +126 -0
  172. clm/layer1/runtimes/python.py +288 -0
  173. clm/layer1/runtimes/python_runtime.py +41 -0
  174. clm/layer1/runtimes/script.py +114 -0
  175. clm/layer1/sandbox.py +334 -0
  176. clm/layer1/savepoint_guard.py +136 -0
  177. clm/layer1/sparse_net.py +122 -0
  178. clm/layer1/spec_tester.py +165 -0
  179. clm/layer1/store.py +295 -0
  180. clm/layer1/test_runner.py +498 -0
  181. clm/layer1/type_inspector.py +166 -0
  182. clm/layer1/verifier.py +772 -0
  183. clm/layer1/vm.py +91 -0
  184. clm/layer2/__init__.py +78 -0
  185. clm/layer2/card_collection.py +678 -0
  186. clm/layer2/classifier.py +479 -0
  187. clm/layer2/engine/__init__.py +1 -0
  188. clm/layer2/engine/abstract_sql_engine.py +320 -0
  189. clm/layer2/engine/base.py +193 -0
  190. clm/layer2/engine/duckdb_engine.py +576 -0
  191. clm/layer2/engine/sqlite_engine.py +518 -0
  192. clm/layer2/fiber/__init__.py +31 -0
  193. clm/layer2/fiber/context.py +89 -0
  194. clm/layer2/fiber/disposable.py +163 -0
  195. clm/layer2/fiber/fiber.py +125 -0
  196. clm/layer2/fiber/hoare.py +191 -0
  197. clm/layer2/file_io.py +376 -0
  198. clm/layer2/file_registrar.py +77 -0
  199. clm/layer2/ingest.py +202 -0
  200. clm/layer2/lineage.py +322 -0
  201. clm/layer2/mcard_fs.py +262 -0
  202. clm/layer2/merkle.py +11 -0
  203. clm/layer2/mime.py +244 -0
  204. clm/layer2/mime_detector.py +245 -0
  205. clm/layer2/private_collection.py +118 -0
  206. clm/layer2/shared_store.py +11 -0
  207. clm/layer2/storage/__init__.py +190 -0
  208. clm/layer2/storage/db_bitemporal.py +141 -0
  209. clm/layer2/storage/db_driver.py +331 -0
  210. clm/layer2/storage/db_hyperlinks.py +214 -0
  211. clm/layer2/storage/db_pool.py +139 -0
  212. clm/layer2/storage/db_repository.py +229 -0
  213. clm/layer2/storage/savepoint.py +78 -0
  214. clm/layer2/storage/sqlite_wal.py +74 -0
  215. clm/layer2/tridb.py +245 -0
  216. clm/layer2/vcard_receipt.py +71 -0
  217. clm/layer2/vfs.py +17 -0
  218. clm/layer3/__init__.py +141 -0
  219. clm/layer3/agency/__init__.py +19 -0
  220. clm/layer3/agency/config.py +220 -0
  221. clm/layer3/agency/providers/__init__.py +15 -0
  222. clm/layer3/agency/providers/base.py +91 -0
  223. clm/layer3/agency/providers/mlc_llm.py +169 -0
  224. clm/layer3/agency/providers/ollama.py +203 -0
  225. clm/layer3/agency/router.py +71 -0
  226. clm/layer3/agency/runtime.py +524 -0
  227. clm/layer3/gateway.py +353 -0
  228. clm/layer3/open_interpreter_adapter.py +159 -0
  229. clm/layer3/rag/__init__.py +70 -0
  230. clm/layer3/rag/cli.py +228 -0
  231. clm/layer3/rag/config.py +153 -0
  232. clm/layer3/rag/embeddings/__init__.py +16 -0
  233. clm/layer3/rag/embeddings/base.py +79 -0
  234. clm/layer3/rag/embeddings/ollama.py +211 -0
  235. clm/layer3/rag/embeddings/vision.py +290 -0
  236. clm/layer3/rag/engine.py +276 -0
  237. clm/layer3/rag/graph/__init__.py +22 -0
  238. clm/layer3/rag/graph/community.py +198 -0
  239. clm/layer3/rag/graph/engine.py +433 -0
  240. clm/layer3/rag/graph/extractor.py +255 -0
  241. clm/layer3/rag/graph/schema.py +115 -0
  242. clm/layer3/rag/graph/store.py +641 -0
  243. clm/layer3/rag/indexer.py +333 -0
  244. clm/layer3/rag/llm_providers.json +52 -0
  245. clm/layer3/rag/semantic_versioning.py +313 -0
  246. clm/layer3/rag/vector/__init__.py +28 -0
  247. clm/layer3/rag/vector/handle_vector_store.py +749 -0
  248. clm/layer3/rag/vector/schema.py +151 -0
  249. clm/layer3/rag/vector/store.py +632 -0
  250. clm/layer3/reticulum/__init__.py +55 -0
  251. clm/layer3/reticulum/bridge.py +74 -0
  252. clm/layer3/reticulum/daemon.py +88 -0
  253. clm/layer3/reticulum/discovery.py +164 -0
  254. clm/layer3/reticulum/identity.py +107 -0
  255. clm/layer3/reticulum/media.py +148 -0
  256. clm/layer3/reticulum/transport.py +133 -0
  257. clm/layer3/satori/__init__.py +209 -0
  258. clm/layer3/satori/alpha_conversion.py +168 -0
  259. clm/layer3/satori/beta_reduction.py +359 -0
  260. clm/layer3/satori/continuation.py +38 -0
  261. clm/layer3/satori/eta_conversion.py +205 -0
  262. clm/layer3/satori/free_variables.py +163 -0
  263. clm/layer3/satori/io_effects.py +370 -0
  264. clm/layer3/satori/lambda_runtime.py +726 -0
  265. clm/layer3/satori/lambda_term.py +230 -0
  266. clm/layer3/satori/sheaf_audit.py +261 -0
  267. clm/layer3/satori/speech_act.py +279 -0
  268. clm/layer4/README.md +9 -0
  269. clm/layer4/__init__.py +67 -0
  270. clm/layer4/_sovereign_executor.py +580 -0
  271. clm/layer4/action_commands.py +719 -0
  272. clm/layer4/api.py +236 -0
  273. clm/layer4/bootstrap/__init__.py +13 -0
  274. clm/layer4/bootstrap/bootstrapper.py +118 -0
  275. clm/layer4/bootstrap/cleanup.py +44 -0
  276. clm/layer4/bootstrap/clm_hooks.py +49 -0
  277. clm/layer4/bootstrap/genesis.py +77 -0
  278. clm/layer4/bootstrap/injection.py +125 -0
  279. clm/layer4/cli.py +549 -0
  280. clm/layer4/collection_manager.py +553 -0
  281. clm/layer4/commands/__init__.py +81 -0
  282. clm/layer4/commands/action_cmd.py +42 -0
  283. clm/layer4/commands/check_cmd.py +82 -0
  284. clm/layer4/commands/envelope.py +73 -0
  285. clm/layer4/commands/evaluate_cmd.py +224 -0
  286. clm/layer4/commands/mcard_cmd.py +24 -0
  287. clm/layer4/commands/petri_cmd.py +92 -0
  288. clm/layer4/commands/tridb_cmd.py +319 -0
  289. clm/layer4/commands/version_cmd.py +27 -0
  290. clm/layer4/diff_commands.py +167 -0
  291. clm/layer4/eoa_sponsor.py +232 -0
  292. clm/layer4/events.py +69 -0
  293. clm/layer4/flux_gateway.py +133 -0
  294. clm/layer4/gateway.py +91 -0
  295. clm/layer4/loader.py +307 -0
  296. clm/layer4/mcard_commands.py +166 -0
  297. clm/layer4/membrane.py +273 -0
  298. clm/layer4/navigation.py +50 -0
  299. clm/layer4/protocol/protocol_entry_python.py +108 -0
  300. clm/layer4/protocol/python_sync.py +126 -0
  301. clm/layer4/runner.py +150 -0
  302. clm/layer4/satori.py +262 -0
  303. clm/layer4/server/python_sync_client.py +150 -0
  304. clm/layer4/server/python_sync_server.py +139 -0
  305. clm/layer4/server/service_api.py +315 -0
  306. clm/layer4/server/start_servers.py +107 -0
  307. clm/layer4/server/stop_servers.py +24 -0
  308. clm/layer4/simulated_optimization_fix.py +0 -0
  309. clm/layer4/storage.py +463 -0
  310. clm/layer4/tridb_admin.py +268 -0
  311. clm/layer4/websocket/test_ws_client.py +104 -0
  312. clm/layer4/websocket/test_ws_server.py +138 -0
  313. clm/layer4/websocket/ws_server_local.py +160 -0
  314. clm/layer5/__init__.py +51 -0
  315. clm/layer5/async_batch_writer.py +133 -0
  316. clm/layer5/learning.py +276 -0
  317. clm/layer5/telemetry_hook.py +107 -0
  318. clm/layer5/type_lattice.py +238 -0
  319. clm/py.typed +0 -0
  320. clm/schemas/__init__.py +652 -0
  321. clm/schemas/abstract_face_schema.yaml +77 -0
  322. clm/schemas/cel_schema.yaml +416 -0
  323. clm/schemas/lifecycle/__init__.py +45 -0
  324. clm/schemas/lifecycle/models.py +123 -0
  325. clm/schemas/lifecycle/service.py +364 -0
  326. clm/schemas/mcard_schema.sql +192 -0
  327. clm/schemas/mcard_vector_schema.sql +283 -0
  328. clm/schemas/mlp-context-v1.json +91 -0
  329. clm_kernel-0.0.0.dist-info/METADATA +121 -0
  330. clm_kernel-0.0.0.dist-info/RECORD +333 -0
  331. clm_kernel-0.0.0.dist-info/WHEEL +4 -0
  332. clm_kernel-0.0.0.dist-info/entry_points.txt +3 -0
  333. clm_kernel-0.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,319 @@
1
+ """Tri-Database Subcommand Handlers.
2
+
3
+ Sprint 412 / DEL-412-04.
4
+ Normative Spec: docs/spec/30-interfaces/universal_subcommand_grammar.md §2.3 - §2.6
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import os
11
+ import sys
12
+
13
+ from clm.cross_cutting.clm_logger import get_logger
14
+ from clm.layer0.utils import resolve_clm_path
15
+ from clm.layer2.storage import (
16
+ CLMDB,
17
+ init_tridb,
18
+ memorize_mcard,
19
+ register_handle,
20
+ resolve_handle_content,
21
+ resolve_handle_hash,
22
+ )
23
+ from clm.layer4.commands.envelope import format_error, format_failure, format_success
24
+
25
+ _logger = get_logger("clm.layer4.commands.tridb")
26
+
27
+
28
+ def init_tridb_cmd(args: list[str]) -> int:
29
+ """Handle 'init-tridb' subcommand."""
30
+ if "--help" in args or "-h" in args:
31
+ print("Usage: clm init-tridb [--memory] [--path <dir>] [--json]")
32
+ return 0
33
+
34
+ as_json = "--json" in args
35
+ is_memory = "--memory" in args
36
+ target_path: str | None = None
37
+
38
+ i = 0
39
+ while i < len(args):
40
+ if args[i] == "--path":
41
+ if i + 1 >= len(args):
42
+ if as_json:
43
+ print(format_error("MISSING_ARG", "Option --path requires a directory argument"))
44
+ else:
45
+ print("Error: Option --path requires a directory argument", file=sys.stderr)
46
+ return 2
47
+ target_path = args[i + 1]
48
+ i += 2
49
+ else:
50
+ i += 1
51
+
52
+ if target_path is not None:
53
+ if not os.path.exists(target_path):
54
+ try:
55
+ os.makedirs(target_path, exist_ok=True)
56
+ except Exception as e:
57
+ if as_json:
58
+ print(format_error("INVALID_PATH", f"Cannot access path: {target_path} ({e})"))
59
+ else:
60
+ print(f"Error: Invalid directory path: {target_path} ({e})", file=sys.stderr)
61
+ return 2
62
+
63
+ clm_root = target_path or os.environ.get("CLM_ROOT") or os.getcwd()
64
+ if is_memory:
65
+ os.environ["CLM_ZERO_FS"] = "1"
66
+
67
+ try:
68
+ rc = init_tridb(clm_root)
69
+ if rc == 0:
70
+ if as_json:
71
+ data = {
72
+ "pillars": ["agent_identities", "content_memory", "execution_log"],
73
+ "path": clm_root,
74
+ "memory": is_memory,
75
+ "initialized": True,
76
+ }
77
+ print(format_success(data))
78
+ return 0
79
+ else:
80
+ if as_json:
81
+ print(format_failure("TRIDB_INIT_FAILED", "Database initialization returned non-zero code"))
82
+ return 1
83
+ except Exception as e:
84
+ if as_json:
85
+ print(format_failure("TRIDB_INIT_EXCEPTION", str(e)))
86
+ else:
87
+ _logger.error(f"Failed to initialize Tri-Database: {e}")
88
+ return 1
89
+
90
+
91
+ def memorize_mcard_cmd(args: list[str]) -> int:
92
+ """Handle 'memorize-mcard' subcommand."""
93
+ if "--help" in args or "-h" in args:
94
+ print("Usage: clm memorize-mcard <file> [--pillar <name>] [--handle <name>] [--json]")
95
+ return 0
96
+
97
+ as_json = "--json" in args
98
+ clean_args = [a for a in args if a != "--json"]
99
+
100
+ pillar = "auto"
101
+ handle: str | None = None
102
+ pos_args: list[str] = []
103
+
104
+ i = 0
105
+ while i < len(clean_args):
106
+ if clean_args[i] == "--pillar" and i + 1 < len(clean_args):
107
+ pillar = clean_args[i + 1]
108
+ i += 2
109
+ elif clean_args[i] == "--handle" and i + 1 < len(clean_args):
110
+ handle = clean_args[i + 1]
111
+ i += 2
112
+ elif clean_args[i].startswith("--"):
113
+ i += 1
114
+ else:
115
+ pos_args.append(clean_args[i])
116
+ i += 1
117
+
118
+ if not pos_args:
119
+ if as_json:
120
+ print(format_error("MISSING_ARG", "Usage: memorize-mcard <file> [pillar]"))
121
+ else:
122
+ print("Usage: memorize-mcard <file> [pillar]", file=sys.stderr)
123
+ return 2
124
+
125
+ file_path = pos_args[0]
126
+ if len(pos_args) > 1 and pillar == "auto":
127
+ pillar = pos_args[1]
128
+
129
+ clm_root = os.environ.get("CLM_ROOT") or os.getcwd()
130
+ resolved_path = resolve_clm_path(clm_root, file_path)
131
+
132
+ if not os.path.exists(resolved_path):
133
+ if as_json:
134
+ print(format_error("FILE_NOT_FOUND", f"File not found: {file_path}"))
135
+ else:
136
+ print(f"File not found: {file_path}", file=sys.stderr)
137
+ return 2
138
+
139
+ try:
140
+ rc = memorize_mcard(clm_root, resolved_path, pillar)
141
+ if handle and rc in (0, 2):
142
+ register_handle(clm_root, handle, resolved_path)
143
+
144
+ if rc in (0, 2):
145
+ if as_json:
146
+ from clm.layer0.hash import compute_blake3
147
+ with open(resolved_path, "rb") as f:
148
+ h = compute_blake3(f.read())
149
+ sz = os.path.getsize(resolved_path)
150
+ data = {
151
+ "hash": h,
152
+ "bytes": sz,
153
+ "handle": handle,
154
+ "pillar": pillar,
155
+ }
156
+ print(format_success(data))
157
+ return 0 if as_json else rc
158
+ else:
159
+ if as_json:
160
+ print(format_failure("STORAGE_ERROR", f"Failed to memorize {file_path}"))
161
+ return 1
162
+ except Exception as e:
163
+ if as_json:
164
+ print(format_failure("STORAGE_EXCEPTION", str(e)))
165
+ else:
166
+ _logger.error(f"Error memorizing card {file_path}: {e}")
167
+ return 1
168
+
169
+
170
+ def resolve_handle_cmd(args: list[str]) -> int:
171
+ """Handle 'resolve-handle' subcommand."""
172
+ if "--help" in args or "-h" in args:
173
+ print("Usage: clm resolve-handle <handle> [--pillar <name>] [--hash-only] [--json]")
174
+ return 0
175
+
176
+ as_json = "--json" in args
177
+ hash_only = "--hash-only" in args
178
+ clean_args = [a for a in args if a not in ("--json", "--hash-only")]
179
+
180
+ pillar = "content_memory"
181
+ pos_args: list[str] = []
182
+
183
+ i = 0
184
+ while i < len(clean_args):
185
+ if clean_args[i] == "--pillar" and i + 1 < len(clean_args):
186
+ pillar = clean_args[i + 1]
187
+ i += 2
188
+ elif clean_args[i].startswith("--"):
189
+ i += 1
190
+ else:
191
+ pos_args.append(clean_args[i])
192
+ i += 1
193
+
194
+ if not pos_args:
195
+ if as_json:
196
+ print(format_error("MISSING_ARG", "Usage: resolve-handle <handle> [pillar]"))
197
+ else:
198
+ print("Usage: resolve-handle <handle> [pillar]", file=sys.stderr)
199
+ return 2
200
+
201
+ handle = pos_args[0]
202
+ if len(pos_args) > 1:
203
+ pillar = pos_args[1]
204
+
205
+ clm_root = os.environ.get("CLM_ROOT") or os.getcwd()
206
+ try:
207
+ h = resolve_handle_hash(clm_root, handle, pillar)
208
+ if not h:
209
+ if as_json:
210
+ print(format_failure("HANDLE_NOT_FOUND", f"Handle '{handle}' not found in pillar '{pillar}'"))
211
+ else:
212
+ print(f"Handle '{handle}' not found in pillar '{pillar}'", file=sys.stderr)
213
+ return 1
214
+
215
+ content = resolve_handle_content(clm_root, handle, pillar)
216
+ if as_json:
217
+ print(format_success({"handle": handle, "hash": h, "content": content, "pillar": pillar}))
218
+ elif hash_only:
219
+ print(h)
220
+ else:
221
+ print(content)
222
+ return 0
223
+ except Exception as e:
224
+ if as_json:
225
+ print(format_failure("RESOLVE_EXCEPTION", str(e)))
226
+ else:
227
+ _logger.error(f"Error resolving handle '{handle}': {e}")
228
+ return 1
229
+
230
+
231
+ def resolve_handle_hash_cmd(args: list[str]) -> int:
232
+ """Handle 'resolve-handle-hash' subcommand (backward compatibility)."""
233
+ if "--help" in args or "-h" in args:
234
+ print("Usage: clm resolve-handle-hash <handle> [pillar]")
235
+ return 0
236
+
237
+ if not args:
238
+ print("Usage: resolve-handle-hash <handle> [pillar]", file=sys.stderr)
239
+ return 2
240
+
241
+ handle = args[0]
242
+ pillar = args[1] if len(args) > 1 else "content_memory"
243
+ clm_root = os.environ.get("CLM_ROOT") or os.getcwd()
244
+ try:
245
+ h = resolve_handle_hash(clm_root, handle, pillar)
246
+ print(h)
247
+ return 0
248
+ except Exception as e:
249
+ _logger.error(f"Error resolving handle hash: {e}")
250
+ return 1
251
+
252
+
253
+ def register_handle_cmd(args: list[str]) -> int:
254
+ """Handle 'register-handle' subcommand (backward compatibility)."""
255
+ if "--help" in args or "-h" in args:
256
+ print("Usage: clm register-handle <handle> <content_source>")
257
+ return 0
258
+
259
+ if len(args) < 2:
260
+ print("Usage: register-handle <handle> <content_source>", file=sys.stderr)
261
+ return 2
262
+
263
+ handle = args[0]
264
+ source = args[1]
265
+ clm_root = os.environ.get("CLM_ROOT") or os.getcwd()
266
+ try:
267
+ return register_handle(clm_root, handle, source)
268
+ except Exception as e:
269
+ _logger.error(f"Error registering handle: {e}")
270
+ return 1
271
+
272
+
273
+ def sql_cmd(args: list[str]) -> int:
274
+ """Handle 'sql' subcommand."""
275
+ if "--help" in args or "-h" in args:
276
+ print("Usage: clm sql <pillar> <sql_query> [params...] [--json]")
277
+ return 0
278
+
279
+ as_json = "--json" in args
280
+ clean_args = [a for a in args if a != "--json"]
281
+
282
+ if len(clean_args) < 2:
283
+ if as_json:
284
+ print(format_error("MISSING_ARG", "Usage: sql <pillar> <sql_query> [params...]"))
285
+ else:
286
+ print("Usage: sql <pillar> <sql_query> [params...]", file=sys.stderr)
287
+ return 2
288
+
289
+ pillar = clean_args[0]
290
+ query = clean_args[1]
291
+ params = clean_args[2:] if len(clean_args) > 2 else None
292
+
293
+ known_pillars = {"content_memory", "execution_log", "agent_identities"}
294
+ if pillar not in known_pillars:
295
+ if as_json:
296
+ print(format_error("INVALID_PILLAR", f"Unknown pillar '{pillar}', expected one of {sorted(known_pillars)}"))
297
+ else:
298
+ print(f"Unknown pillar '{pillar}', expected one of {sorted(known_pillars)}", file=sys.stderr)
299
+ return 2
300
+
301
+ clm_root = os.environ.get("CLM_ROOT") or os.getcwd()
302
+ try:
303
+ db = CLMDB(clm_root)
304
+ raw_res = db.execute(pillar, query, params, as_json)
305
+ if as_json:
306
+ try:
307
+ parsed = json.loads(raw_res) if isinstance(raw_res, str) else raw_res
308
+ print(format_success(parsed))
309
+ except Exception:
310
+ print(format_success(raw_res))
311
+ else:
312
+ print(raw_res)
313
+ return 0
314
+ except Exception as e:
315
+ if as_json:
316
+ print(format_failure("SQL_ERROR", str(e)))
317
+ else:
318
+ _logger.error(f"SQL execution error: {e}")
319
+ return 1
@@ -0,0 +1,27 @@
1
+ """Version Subcommand Handler.
2
+
3
+ Sprint 412 / DEL-412-04.
4
+ Normative Spec: docs/spec/30-interfaces/universal_subcommand_grammar.md §2.12
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from clm.layer4.commands.envelope import format_success
10
+
11
+
12
+ def version_cmd(args: list[str]) -> int:
13
+ """Handle 'version' subcommand."""
14
+ as_json = "--json" in args
15
+ version_info = {
16
+ "runtime": "python",
17
+ "version": "0.4.0",
18
+ "layers": [0, 1, 2, 3, 4, 5],
19
+ "blake3": True,
20
+ "features": ["cpn_vm", "baldwin_algebra", "pocketflow", "satori", "continuous_learning"],
21
+ }
22
+
23
+ if as_json:
24
+ print(format_success(version_info))
25
+ else:
26
+ print("clm (runtime: python, version: 0.4.0, layers: [0, 1, 2, 3, 4, 5], blake3: true)")
27
+ return 0
@@ -0,0 +1,167 @@
1
+ """CLI Subcommands for MCard Diffing and Merkle Lineage (Sprint 396 / DEL-396-01).
2
+
3
+ Provides:
4
+ - clm mcard diff <uri1> <uri2> [--json]
5
+ - clm mcard lineage <uri> [--mermaid] [--dot] [--json]
6
+
7
+ Acceptance Criteria:
8
+ - AC-396-01-04: clm mcard diff prints structured diff (columns/keys added, removed, modified).
9
+ - Lineage tree visualizer prints ASCII or Mermaid provenance.
10
+
11
+ Layer 4 module.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import csv
17
+ import difflib
18
+ import io
19
+ import json
20
+ from typing import Any
21
+
22
+ from clm.cross_cutting.clm_logger import get_logger
23
+ from clm.layer2.ingest import get_default_fs
24
+ from clm.layer2.lineage import get_mcard_lineage
25
+ from clm.layer2.mcard_fs import MCardFileSystem
26
+
27
+ _logger = get_logger("clm.layer4.diff_commands")
28
+
29
+
30
+ def _clean_uri(uri: str) -> str:
31
+ if uri.startswith("mcard://"):
32
+ return "mcard:" + uri[len("mcard://"):]
33
+ return uri
34
+
35
+
36
+ def mcard_diff_cmd(
37
+ uri1: str,
38
+ uri2: str,
39
+ as_json: bool = False,
40
+ fs: MCardFileSystem | None = None,
41
+ ) -> int:
42
+ """Compute and display structural and semantic diff between two MCards."""
43
+ target_fs = fs or get_default_fs()
44
+ try:
45
+ card1 = target_fs.read_file(_clean_uri(uri1))
46
+ card2 = target_fs.read_file(_clean_uri(uri2))
47
+ except Exception as e:
48
+ _logger.error(f"Failed to read MCard for diff: {e}")
49
+ return 1
50
+
51
+ diff_report: dict[str, Any] = {
52
+ "uri1": uri1,
53
+ "uri2": uri2,
54
+ "hash_match": str(getattr(card1, "hash", None)) == str(getattr(card2, "hash", None)),
55
+ "metadata_diff": {},
56
+ "content_diff": {},
57
+ }
58
+
59
+ # 1. Metadata diff
60
+ meta1 = card1.metadata or {}
61
+ meta2 = card2.metadata or {}
62
+ all_keys = set(meta1.keys()).union(set(meta2.keys()))
63
+ for k in sorted(all_keys):
64
+ v1 = meta1.get(k)
65
+ v2 = meta2.get(k)
66
+ if v1 != v2:
67
+ diff_report["metadata_diff"][k] = {"card1": v1, "card2": v2}
68
+
69
+ # 2. Content diff
70
+ p1 = card1.payload
71
+ p2 = card2.payload
72
+
73
+ if isinstance(p1, str) and isinstance(p2, str):
74
+ # Check if CSV
75
+ if meta1.get("format") == "csv" or meta2.get("format") == "csv" or ("," in p1 and "\n" in p1):
76
+ try:
77
+ r1 = list(csv.reader(io.StringIO(p1.strip())))
78
+ r2 = list(csv.reader(io.StringIO(p2.strip())))
79
+ h1 = r1[0] if r1 else []
80
+ h2 = r2[0] if r2 else []
81
+
82
+ diff_report["content_diff"]["type"] = "tabular"
83
+ diff_report["content_diff"]["columns_removed"] = [c for c in h1 if c not in h2]
84
+ diff_report["content_diff"]["columns_added"] = [c for c in h2 if c not in h1]
85
+ diff_report["content_diff"]["row_count_delta"] = (len(r2) - 1) - (len(r1) - 1)
86
+ except Exception:
87
+ diff_report["content_diff"]["type"] = "text"
88
+ diff_lines = list(difflib.unified_diff(p1.splitlines(), p2.splitlines(), fromfile=uri1, tofile=uri2))
89
+ diff_report["content_diff"]["unified_diff"] = diff_lines[:50]
90
+ else:
91
+ diff_report["content_diff"]["type"] = "text"
92
+ diff_lines = list(difflib.unified_diff(p1.splitlines(), p2.splitlines(), fromfile=uri1, tofile=uri2))
93
+ diff_report["content_diff"]["unified_diff"] = diff_lines[:50]
94
+
95
+ elif isinstance(p1, dict) and isinstance(p2, dict):
96
+ diff_report["content_diff"]["type"] = "structured"
97
+ diff_report["content_diff"]["keys_removed"] = [k for k in p1 if k not in p2]
98
+ diff_report["content_diff"]["keys_added"] = [k for k in p2 if k not in p1]
99
+ diff_report["content_diff"]["keys_modified"] = [k for k in p1 if k in p2 and p1[k] != p2[k]]
100
+
101
+ elif isinstance(p1, bytes) and isinstance(p2, bytes):
102
+ diff_report["content_diff"]["type"] = "binary"
103
+ diff_report["content_diff"]["size_delta"] = len(p2) - len(p1)
104
+
105
+ if as_json:
106
+ print(json.dumps(diff_report, indent=2, default=str))
107
+ else:
108
+ print("=== MCard Diff ===")
109
+ print(f"Card 1: {uri1} [{str(getattr(card1, 'hash', ''))[:10]}]")
110
+ print(f"Card 2: {uri2} [{str(getattr(card2, 'hash', ''))[:10]}]")
111
+ print(f"Identical: {diff_report['hash_match']}")
112
+
113
+ if diff_report["metadata_diff"]:
114
+ print("\nMetadata Differences:")
115
+ for k, diff in diff_report["metadata_diff"].items():
116
+ print(f" {k}: {diff['card1']} -> {diff['card2']}")
117
+
118
+ cdiff = diff_report["content_diff"]
119
+ print(f"\nContent Diff ({cdiff.get('type', 'unknown')}):")
120
+ if cdiff.get("type") == "tabular":
121
+ if cdiff.get("columns_removed"):
122
+ print(f" - Removed columns: {cdiff['columns_removed']}")
123
+ if cdiff.get("columns_added"):
124
+ print(f" + Added columns: {cdiff['columns_added']}")
125
+ print(f" Rows delta: {cdiff.get('row_count_delta', 0):+d}")
126
+ elif cdiff.get("type") == "structured":
127
+ if cdiff.get("keys_removed"):
128
+ print(f" - Removed keys: {cdiff['keys_removed']}")
129
+ if cdiff.get("keys_added"):
130
+ print(f" + Added keys: {cdiff['keys_added']}")
131
+ if cdiff.get("keys_modified"):
132
+ print(f" ~ Modified keys: {cdiff['keys_modified']}")
133
+ elif cdiff.get("type") == "text":
134
+ for line in cdiff.get("unified_diff", []):
135
+ print(f" {line}")
136
+
137
+ return 0
138
+
139
+
140
+ def mcard_lineage_cmd(
141
+ uri: str,
142
+ as_mermaid: bool = False,
143
+ as_dot: bool = False,
144
+ as_json: bool = False,
145
+ ) -> int:
146
+ """Print causal lineage DAG for an MCard."""
147
+ try:
148
+ dag = get_mcard_lineage(uri)
149
+ if as_mermaid:
150
+ print(dag.to_mermaid())
151
+ elif as_dot:
152
+ print(dag.to_dot())
153
+ elif as_json:
154
+ nodes_data = [{"id": n.id, "type": n.type, "label": n.label, "hash": n.hash} for n in dag.nodes.values()]
155
+ edges_data = [{"source": e.source, "target": e.target, "relation": e.relation} for e in dag.edges]
156
+ print(json.dumps({"target": uri, "nodes": nodes_data, "edges": edges_data}, indent=2))
157
+ else:
158
+ print(f"=== Merkle Provenance Lineage for {uri} ===")
159
+ print(f"Reconstructed in: {dag.reconstruction_time_ms} ms")
160
+ print(f"Total Nodes: {len(dag.nodes)} | Total Edges: {len(dag.edges)}")
161
+ print(f"Root Inputs: {dag.get_root_inputs()}")
162
+ print("\nAncestry Graph (Mermaid):\n")
163
+ print(dag.to_mermaid())
164
+ return 0
165
+ except Exception as e:
166
+ _logger.error(f"Failed to retrieve lineage for '{uri}': {e}")
167
+ return 1