dhee 3.2.0__tar.gz → 3.3.0__tar.gz

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 (275) hide show
  1. {dhee-3.2.0 → dhee-3.3.0}/PKG-INFO +44 -4
  2. {dhee-3.2.0 → dhee-3.3.0}/README.md +43 -3
  3. {dhee-3.2.0 → dhee-3.3.0}/dhee/__init__.py +1 -1
  4. {dhee-3.2.0 → dhee-3.3.0}/dhee/cli.py +68 -0
  5. dhee-3.3.0/dhee/hooks/claude_code/__init__.py +2 -0
  6. dhee-3.3.0/dhee/hooks/claude_code/__main__.py +256 -0
  7. dhee-3.3.0/dhee/hooks/claude_code/install.py +173 -0
  8. dhee-3.3.0/dhee/hooks/claude_code/privacy.py +34 -0
  9. dhee-3.3.0/dhee/hooks/claude_code/renderer.py +310 -0
  10. {dhee-3.2.0 → dhee-3.3.0}/dhee.egg-info/PKG-INFO +44 -4
  11. {dhee-3.2.0 → dhee-3.3.0}/dhee.egg-info/SOURCES.txt +7 -64
  12. {dhee-3.2.0 → dhee-3.3.0}/dhee.egg-info/top_level.txt +0 -3
  13. {dhee-3.2.0 → dhee-3.3.0}/pyproject.toml +1 -1
  14. dhee-3.3.0/tests/test_claude_code_hooks.py +388 -0
  15. dhee-3.2.0/engram-bridge/demo/demo-app/main.py +0 -65
  16. dhee-3.2.0/engram-bridge/demo/seed.py +0 -221
  17. dhee-3.2.0/engram-bridge/engram_bridge/__init__.py +0 -4
  18. dhee-3.2.0/engram-bridge/engram_bridge/agents/__init__.py +0 -6
  19. dhee-3.2.0/engram-bridge/engram_bridge/agents/base.py +0 -40
  20. dhee-3.2.0/engram-bridge/engram_bridge/agents/claude.py +0 -152
  21. dhee-3.2.0/engram-bridge/engram_bridge/agents/codex.py +0 -119
  22. dhee-3.2.0/engram-bridge/engram_bridge/agents/custom.py +0 -95
  23. dhee-3.2.0/engram-bridge/engram_bridge/bridge.py +0 -1856
  24. dhee-3.2.0/engram-bridge/engram_bridge/channels/__init__.py +0 -6
  25. dhee-3.2.0/engram-bridge/engram_bridge/channels/base.py +0 -44
  26. dhee-3.2.0/engram-bridge/engram_bridge/channels/telegram.py +0 -131
  27. dhee-3.2.0/engram-bridge/engram_bridge/channels/web.py +0 -1785
  28. dhee-3.2.0/engram-bridge/engram_bridge/config.py +0 -143
  29. dhee-3.2.0/engram-bridge/engram_bridge/coordination/__init__.py +0 -137
  30. dhee-3.2.0/engram-bridge/engram_bridge/utils.py +0 -43
  31. dhee-3.2.0/engram-enterprise/engram_enterprise/__init__.py +0 -20
  32. dhee-3.2.0/engram-enterprise/engram_enterprise/acceptance.py +0 -142
  33. dhee-3.2.0/engram-enterprise/engram_enterprise/active_memory.py +0 -406
  34. dhee-3.2.0/engram-enterprise/engram_enterprise/api/app.py +0 -1026
  35. dhee-3.2.0/engram-enterprise/engram_enterprise/api/auth.py +0 -76
  36. dhee-3.2.0/engram-enterprise/engram_enterprise/api/schemas.py +0 -170
  37. dhee-3.2.0/engram-enterprise/engram_enterprise/api/server.py +0 -34
  38. dhee-3.2.0/engram-enterprise/engram_enterprise/async_embedder.py +0 -125
  39. dhee-3.2.0/engram-enterprise/engram_enterprise/async_llm.py +0 -127
  40. dhee-3.2.0/engram-enterprise/engram_enterprise/async_memory.py +0 -481
  41. dhee-3.2.0/engram-enterprise/engram_enterprise/async_sqlite.py +0 -448
  42. dhee-3.2.0/engram-enterprise/engram_enterprise/cli.py +0 -547
  43. dhee-3.2.0/engram-enterprise/engram_enterprise/client.py +0 -418
  44. dhee-3.2.0/engram-enterprise/engram_enterprise/context_packer.py +0 -62
  45. dhee-3.2.0/engram-enterprise/engram_enterprise/dual_search.py +0 -168
  46. dhee-3.2.0/engram-enterprise/engram_enterprise/episodic_store.py +0 -290
  47. dhee-3.2.0/engram-enterprise/engram_enterprise/integrations/__init__.py +0 -0
  48. dhee-3.2.0/engram-enterprise/engram_enterprise/integrations/claude_code.py +0 -532
  49. dhee-3.2.0/engram-enterprise/engram_enterprise/integrations/openclaw.py +0 -177
  50. dhee-3.2.0/engram-enterprise/engram_enterprise/invariants.py +0 -110
  51. dhee-3.2.0/engram-enterprise/engram_enterprise/kernel.py +0 -1792
  52. dhee-3.2.0/engram-enterprise/engram_enterprise/main_cli.py +0 -455
  53. dhee-3.2.0/engram-enterprise/engram_enterprise/policy.py +0 -149
  54. dhee-3.2.0/engram-enterprise/engram_enterprise/provenance.py +0 -40
  55. dhee-3.2.0/engram-enterprise/engram_enterprise/refcounts.py +0 -45
  56. dhee-3.2.0/engram-enterprise/engram_enterprise/reranker.py +0 -76
  57. dhee-3.2.0/engram-enterprise/engram_enterprise/schema.py +0 -230
  58. dhee-3.2.0/engram-enterprise/engram_enterprise/staging_store.py +0 -97
  59. dhee-3.2.0/engram-enterprise/tests/test_enterprise.py +0 -117
  60. dhee-3.2.0/engram-metamemory/engram_metamemory/__init__.py +0 -19
  61. dhee-3.2.0/engram-metamemory/engram_metamemory/confidence.py +0 -162
  62. dhee-3.2.0/engram-metamemory/engram_metamemory/config.py +0 -51
  63. dhee-3.2.0/engram-metamemory/engram_metamemory/mcp_tools.py +0 -118
  64. dhee-3.2.0/engram-metamemory/engram_metamemory/metamemory.py +0 -346
  65. dhee-3.2.0/engram-router/engram_router/__init__.py +0 -22
  66. dhee-3.2.0/engram-router/engram_router/config.py +0 -15
  67. dhee-3.2.0/engram-router/engram_router/mcp_tools.py +0 -135
  68. dhee-3.2.0/engram-router/engram_router/registry.py +0 -196
  69. dhee-3.2.0/engram-router/engram_router/router.py +0 -310
  70. dhee-3.2.0/engram-warroom/engram_warroom/__init__.py +0 -18
  71. dhee-3.2.0/engram-warroom/engram_warroom/autopick.py +0 -99
  72. dhee-3.2.0/engram-warroom/engram_warroom/config.py +0 -17
  73. dhee-3.2.0/engram-warroom/engram_warroom/decision.py +0 -46
  74. dhee-3.2.0/engram-warroom/engram_warroom/failover.py +0 -120
  75. dhee-3.2.0/engram-warroom/engram_warroom/mcp_tools.py +0 -200
  76. dhee-3.2.0/engram-warroom/engram_warroom/monitor.py +0 -212
  77. dhee-3.2.0/engram-warroom/engram_warroom/warroom.py +0 -333
  78. {dhee-3.2.0 → dhee-3.3.0}/LICENSE +0 -0
  79. {dhee-3.2.0 → dhee-3.3.0}/dhee/benchmarks/__init__.py +0 -0
  80. {dhee-3.2.0 → dhee-3.3.0}/dhee/benchmarks/arc_agi.py +0 -0
  81. {dhee-3.2.0 → dhee-3.3.0}/dhee/benchmarks/hippocamp.py +0 -0
  82. {dhee-3.2.0 → dhee-3.3.0}/dhee/benchmarks/longmemeval.py +0 -0
  83. {dhee-3.2.0 → dhee-3.3.0}/dhee/benchmarks/raw_extractors.py +0 -0
  84. {dhee-3.2.0 → dhee-3.3.0}/dhee/checkpoint_runtime.py +0 -0
  85. {dhee-3.2.0 → dhee-3.3.0}/dhee/cli_config.py +0 -0
  86. {dhee-3.2.0 → dhee-3.3.0}/dhee/cli_mcp.py +0 -0
  87. {dhee-3.2.0 → dhee-3.3.0}/dhee/cli_setup.py +0 -0
  88. {dhee-3.2.0 → dhee-3.3.0}/dhee/configs/__init__.py +0 -0
  89. {dhee-3.2.0 → dhee-3.3.0}/dhee/configs/active.py +0 -0
  90. {dhee-3.2.0 → dhee-3.3.0}/dhee/configs/base.py +0 -0
  91. {dhee-3.2.0 → dhee-3.3.0}/dhee/configs/presets.py +0 -0
  92. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/__init__.py +0 -0
  93. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/alaya.py +0 -0
  94. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/answer_orchestration.py +0 -0
  95. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/belief.py +0 -0
  96. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/buddhi.py +0 -0
  97. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/category.py +0 -0
  98. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/cognition.py +0 -0
  99. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/cognition_kernel.py +0 -0
  100. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/conflict.py +0 -0
  101. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/contrastive.py +0 -0
  102. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/decay.py +0 -0
  103. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/distillation.py +0 -0
  104. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/echo.py +0 -0
  105. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/engram.py +0 -0
  106. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/engram_extractor.py +0 -0
  107. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/enrichment.py +0 -0
  108. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/episode.py +0 -0
  109. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/episodic_index.py +0 -0
  110. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/evolution.py +0 -0
  111. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/forgetting.py +0 -0
  112. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/fusion.py +0 -0
  113. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/graph.py +0 -0
  114. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/heuristic.py +0 -0
  115. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/intent.py +0 -0
  116. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/intention.py +0 -0
  117. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/kernel.py +0 -0
  118. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/log_parser.py +0 -0
  119. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/meta_buddhi.py +0 -0
  120. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/pattern_detector.py +0 -0
  121. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/policy.py +0 -0
  122. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/profile.py +0 -0
  123. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/resolvers.py +0 -0
  124. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/retrieval.py +0 -0
  125. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/samskara.py +0 -0
  126. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/scene.py +0 -0
  127. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/session_tracker.py +0 -0
  128. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/strategy.py +0 -0
  129. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/task_state.py +0 -0
  130. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/traces.py +0 -0
  131. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/trigger.py +0 -0
  132. {dhee-3.2.0 → dhee-3.3.0}/dhee/core/viveka.py +0 -0
  133. {dhee-3.2.0 → dhee-3.3.0}/dhee/db/__init__.py +0 -0
  134. {dhee-3.2.0 → dhee-3.3.0}/dhee/db/sqlite.py +0 -0
  135. {dhee-3.2.0 → dhee-3.3.0}/dhee/db/sqlite_analytics.py +0 -0
  136. {dhee-3.2.0 → dhee-3.3.0}/dhee/db/sqlite_common.py +0 -0
  137. {dhee-3.2.0 → dhee-3.3.0}/dhee/db/sqlite_domains.py +0 -0
  138. {dhee-3.2.0 → dhee-3.3.0}/dhee/debugger_api.py +0 -0
  139. {dhee-3.2.0 → dhee-3.3.0}/dhee/embeddings/__init__.py +0 -0
  140. {dhee-3.2.0 → dhee-3.3.0}/dhee/embeddings/base.py +0 -0
  141. {dhee-3.2.0 → dhee-3.3.0}/dhee/embeddings/gemini.py +0 -0
  142. {dhee-3.2.0 → dhee-3.3.0}/dhee/embeddings/nvidia.py +0 -0
  143. {dhee-3.2.0 → dhee-3.3.0}/dhee/embeddings/ollama.py +0 -0
  144. {dhee-3.2.0 → dhee-3.3.0}/dhee/embeddings/openai.py +0 -0
  145. {dhee-3.2.0 → dhee-3.3.0}/dhee/embeddings/qwen.py +0 -0
  146. {dhee-3.2.0 → dhee-3.3.0}/dhee/embeddings/simple.py +0 -0
  147. {dhee-3.2.0 → dhee-3.3.0}/dhee/exceptions.py +0 -0
  148. {dhee-3.2.0/dhee/llms → dhee-3.3.0/dhee/hooks}/__init__.py +0 -0
  149. {dhee-3.2.0/dhee/utils → dhee-3.3.0/dhee/llms}/__init__.py +0 -0
  150. {dhee-3.2.0 → dhee-3.3.0}/dhee/llms/base.py +0 -0
  151. {dhee-3.2.0 → dhee-3.3.0}/dhee/llms/dhee.py +0 -0
  152. {dhee-3.2.0 → dhee-3.3.0}/dhee/llms/gemini.py +0 -0
  153. {dhee-3.2.0 → dhee-3.3.0}/dhee/llms/mock.py +0 -0
  154. {dhee-3.2.0 → dhee-3.3.0}/dhee/llms/nvidia.py +0 -0
  155. {dhee-3.2.0 → dhee-3.3.0}/dhee/llms/ollama.py +0 -0
  156. {dhee-3.2.0 → dhee-3.3.0}/dhee/llms/openai.py +0 -0
  157. {dhee-3.2.0 → dhee-3.3.0}/dhee/llms/teacher_logger.py +0 -0
  158. {dhee-3.2.0 → dhee-3.3.0}/dhee/mcp_server.py +0 -0
  159. {dhee-3.2.0 → dhee-3.3.0}/dhee/mcp_slim.py +0 -0
  160. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/__init__.py +0 -0
  161. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/base.py +0 -0
  162. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/core.py +0 -0
  163. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/cost.py +0 -0
  164. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/episodic.py +0 -0
  165. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/main.py +0 -0
  166. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/orchestration.py +0 -0
  167. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/parallel.py +0 -0
  168. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/projects.py +0 -0
  169. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/reranker.py +0 -0
  170. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/retrieval_helpers.py +0 -0
  171. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/scene_profile.py +0 -0
  172. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/scoping.py +0 -0
  173. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/search_pipeline.py +0 -0
  174. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/smart.py +0 -0
  175. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/tasks.py +0 -0
  176. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/utils.py +0 -0
  177. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/vectors.py +0 -0
  178. {dhee-3.2.0 → dhee-3.3.0}/dhee/memory/write_pipeline.py +0 -0
  179. {dhee-3.2.0 → dhee-3.3.0}/dhee/observability.py +0 -0
  180. {dhee-3.2.0 → dhee-3.3.0}/dhee/plugin.py +0 -0
  181. {dhee-3.2.0 → dhee-3.3.0}/dhee/simple.py +0 -0
  182. {dhee-3.2.0 → dhee-3.3.0}/dhee/skills/__init__.py +0 -0
  183. {dhee-3.2.0 → dhee-3.3.0}/dhee/skills/discovery.py +0 -0
  184. {dhee-3.2.0 → dhee-3.3.0}/dhee/skills/executor.py +0 -0
  185. {dhee-3.2.0 → dhee-3.3.0}/dhee/skills/hashing.py +0 -0
  186. {dhee-3.2.0 → dhee-3.3.0}/dhee/skills/miner.py +0 -0
  187. {dhee-3.2.0 → dhee-3.3.0}/dhee/skills/outcomes.py +0 -0
  188. {dhee-3.2.0 → dhee-3.3.0}/dhee/skills/schema.py +0 -0
  189. {dhee-3.2.0 → dhee-3.3.0}/dhee/skills/store.py +0 -0
  190. {dhee-3.2.0 → dhee-3.3.0}/dhee/skills/structure.py +0 -0
  191. {dhee-3.2.0 → dhee-3.3.0}/dhee/skills/trajectory.py +0 -0
  192. {dhee-3.2.0/dhee/vector_stores → dhee-3.3.0/dhee/utils}/__init__.py +0 -0
  193. {dhee-3.2.0 → dhee-3.3.0}/dhee/utils/factory.py +0 -0
  194. {dhee-3.2.0 → dhee-3.3.0}/dhee/utils/math.py +0 -0
  195. {dhee-3.2.0 → dhee-3.3.0}/dhee/utils/prompts.py +0 -0
  196. {dhee-3.2.0 → dhee-3.3.0}/dhee/utils/repo_identity.py +0 -0
  197. {dhee-3.2.0/engram-enterprise/engram_enterprise/api → dhee-3.3.0/dhee/vector_stores}/__init__.py +0 -0
  198. {dhee-3.2.0 → dhee-3.3.0}/dhee/vector_stores/base.py +0 -0
  199. {dhee-3.2.0 → dhee-3.3.0}/dhee/vector_stores/memory.py +0 -0
  200. {dhee-3.2.0 → dhee-3.3.0}/dhee/vector_stores/sqlite_vec.py +0 -0
  201. {dhee-3.2.0 → dhee-3.3.0}/dhee/vector_stores/zvec_store.py +0 -0
  202. {dhee-3.2.0 → dhee-3.3.0}/dhee.egg-info/dependency_links.txt +0 -0
  203. {dhee-3.2.0 → dhee-3.3.0}/dhee.egg-info/entry_points.txt +0 -0
  204. {dhee-3.2.0 → dhee-3.3.0}/dhee.egg-info/requires.txt +0 -0
  205. {dhee-3.2.0 → dhee-3.3.0}/dheeModel/__init__.py +0 -0
  206. {dhee-3.2.0 → dhee-3.3.0}/dheeModel/client.py +0 -0
  207. {dhee-3.2.0 → dhee-3.3.0}/dheeModel/model/__init__.py +0 -0
  208. {dhee-3.2.0 → dhee-3.3.0}/dheeModel/model/dhee_model.py +0 -0
  209. {dhee-3.2.0 → dhee-3.3.0}/dheeModel/training/__init__.py +0 -0
  210. {dhee-3.2.0 → dhee-3.3.0}/dheeModel/training/data_formatter.py +0 -0
  211. {dhee-3.2.0 → dhee-3.3.0}/dheeModel/training/karma.py +0 -0
  212. {dhee-3.2.0 → dhee-3.3.0}/dheeModel/training/nididhyasana.py +0 -0
  213. {dhee-3.2.0 → dhee-3.3.0}/dheeModel/training/smrti.py +0 -0
  214. {dhee-3.2.0 → dhee-3.3.0}/dheeModel/training/train.py +0 -0
  215. {dhee-3.2.0 → dhee-3.3.0}/dhee_shared/__init__.py +0 -0
  216. {dhee-3.2.0 → dhee-3.3.0}/dhee_shared/model_paths.py +0 -0
  217. {dhee-3.2.0 → dhee-3.3.0}/engram/__init__.py +0 -0
  218. {dhee-3.2.0 → dhee-3.3.0}/engram-bridge/demo/demo-app/demo_app_smoke.py +0 -0
  219. {dhee-3.2.0 → dhee-3.3.0}/engram-bus/engram_bus/__init__.py +0 -0
  220. {dhee-3.2.0 → dhee-3.3.0}/engram-bus/engram_bus/bus.py +0 -0
  221. {dhee-3.2.0 → dhee-3.3.0}/engram-bus/engram_bus/pubsub.py +0 -0
  222. {dhee-3.2.0 → dhee-3.3.0}/engram-bus/engram_bus/server.py +0 -0
  223. {dhee-3.2.0 → dhee-3.3.0}/engram-bus/engram_bus/store.py +0 -0
  224. {dhee-3.2.0 → dhee-3.3.0}/engram-bus/engram_bus/workspace.py +0 -0
  225. {dhee-3.2.0 → dhee-3.3.0}/engram-bus/tests/test_bus.py +0 -0
  226. {dhee-3.2.0 → dhee-3.3.0}/setup.cfg +0 -0
  227. {dhee-3.2.0 → dhee-3.3.0}/tests/test_accel.py +0 -0
  228. {dhee-3.2.0 → dhee-3.3.0}/tests/test_accel_benchmark.py +0 -0
  229. {dhee-3.2.0 → dhee-3.3.0}/tests/test_auto_lifecycle.py +0 -0
  230. {dhee-3.2.0 → dhee-3.3.0}/tests/test_backward_compat.py +0 -0
  231. {dhee-3.2.0 → dhee-3.3.0}/tests/test_batch.py +0 -0
  232. {dhee-3.2.0 → dhee-3.3.0}/tests/test_belief_debugger.py +0 -0
  233. {dhee-3.2.0 → dhee-3.3.0}/tests/test_cognition_evals.py +0 -0
  234. {dhee-3.2.0 → dhee-3.3.0}/tests/test_cognition_kernel.py +0 -0
  235. {dhee-3.2.0 → dhee-3.3.0}/tests/test_cognition_v3.py +0 -0
  236. {dhee-3.2.0 → dhee-3.3.0}/tests/test_core_memory.py +0 -0
  237. {dhee-3.2.0 → dhee-3.3.0}/tests/test_cosine_similarity.py +0 -0
  238. {dhee-3.2.0 → dhee-3.3.0}/tests/test_dedup.py +0 -0
  239. {dhee-3.2.0 → dhee-3.3.0}/tests/test_deferred_enrichment.py +0 -0
  240. {dhee-3.2.0 → dhee-3.3.0}/tests/test_dhee_model_paths.py +0 -0
  241. {dhee-3.2.0 → dhee-3.3.0}/tests/test_distillation.py +0 -0
  242. {dhee-3.2.0 → dhee-3.3.0}/tests/test_e2e_all_features.py +0 -0
  243. {dhee-3.2.0 → dhee-3.3.0}/tests/test_forgetting.py +0 -0
  244. {dhee-3.2.0 → dhee-3.3.0}/tests/test_hashing.py +0 -0
  245. {dhee-3.2.0 → dhee-3.3.0}/tests/test_hippocamp_benchmark.py +0 -0
  246. {dhee-3.2.0 → dhee-3.3.0}/tests/test_intent.py +0 -0
  247. {dhee-3.2.0 → dhee-3.3.0}/tests/test_locomo_plus_runner.py +0 -0
  248. {dhee-3.2.0 → dhee-3.3.0}/tests/test_log_parser.py +0 -0
  249. {dhee-3.2.0 → dhee-3.3.0}/tests/test_mcp_tools_slim.py +0 -0
  250. {dhee-3.2.0 → dhee-3.3.0}/tests/test_memory_types.py +0 -0
  251. {dhee-3.2.0 → dhee-3.3.0}/tests/test_migration.py +0 -0
  252. {dhee-3.2.0 → dhee-3.3.0}/tests/test_miner.py +0 -0
  253. {dhee-3.2.0 → dhee-3.3.0}/tests/test_openclaw.py +0 -0
  254. {dhee-3.2.0 → dhee-3.3.0}/tests/test_orchestration_core.py +0 -0
  255. {dhee-3.2.0 → dhee-3.3.0}/tests/test_parallel.py +0 -0
  256. {dhee-3.2.0 → dhee-3.3.0}/tests/test_power_packages.py +0 -0
  257. {dhee-3.2.0 → dhee-3.3.0}/tests/test_presets.py +0 -0
  258. {dhee-3.2.0 → dhee-3.3.0}/tests/test_profile.py +0 -0
  259. {dhee-3.2.0 → dhee-3.3.0}/tests/test_projects.py +0 -0
  260. {dhee-3.2.0 → dhee-3.3.0}/tests/test_query_cache.py +0 -0
  261. {dhee-3.2.0 → dhee-3.3.0}/tests/test_scene.py +0 -0
  262. {dhee-3.2.0 → dhee-3.3.0}/tests/test_session_tracker.py +0 -0
  263. {dhee-3.2.0 → dhee-3.3.0}/tests/test_simple_zero_config.py +0 -0
  264. {dhee-3.2.0 → dhee-3.3.0}/tests/test_skills.py +0 -0
  265. {dhee-3.2.0 → dhee-3.3.0}/tests/test_smart_memory.py +0 -0
  266. {dhee-3.2.0 → dhee-3.3.0}/tests/test_sqlite_connection_pool.py +0 -0
  267. {dhee-3.2.0 → dhee-3.3.0}/tests/test_sqlite_vec.py +0 -0
  268. {dhee-3.2.0 → dhee-3.3.0}/tests/test_structural.py +0 -0
  269. {dhee-3.2.0 → dhee-3.3.0}/tests/test_structured_resolution.py +0 -0
  270. {dhee-3.2.0 → dhee-3.3.0}/tests/test_tasks.py +0 -0
  271. {dhee-3.2.0 → dhee-3.3.0}/tests/test_traces.py +0 -0
  272. {dhee-3.2.0 → dhee-3.3.0}/tests/test_trajectory.py +0 -0
  273. {dhee-3.2.0 → dhee-3.3.0}/tests/test_unified_enrichment.py +0 -0
  274. {dhee-3.2.0 → dhee-3.3.0}/tests/test_vector_store_factory.py +0 -0
  275. {dhee-3.2.0 → dhee-3.3.0}/tests/test_zvec_store.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dhee
3
- Version: 3.2.0
3
+ Version: 3.3.0
4
4
  Summary: Cognition layer for AI agents — persistent memory, performance tracking, and insight synthesis
5
5
  Author: Sankhya AI Labs
6
6
  License: MIT
@@ -75,10 +75,10 @@ Requires-Dist: twine>=5.0.0; extra == "dev"
75
75
  Dynamic: license-file
76
76
 
77
77
  <p align="center">
78
- <img src="docs/dhee-logo.png" alt="Dhee" width="80">
78
+ <img src="docs/dhee-logo.png" alt="Dhee" width="80"> <h1 align="center">Dhee</h1>
79
79
  </p>
80
80
 
81
- <h1 align="center">Dhee</h1>
81
+
82
82
 
83
83
  <h3 align="center">The cognition layer that turns your agent into a HyperAgent.</h3>
84
84
 
@@ -154,12 +154,42 @@ d.context("fixing auth bug")
154
154
  d.checkpoint("Fixed it", what_worked="git blame first")
155
155
  ```
156
156
 
157
+ ### Claude Code — Native Hooks (v3.3.0)
158
+
159
+ One command. Every Claude Code session becomes self-evolving.
160
+
161
+ ```bash
162
+ dhee install
163
+ ```
164
+
165
+ That's it. Dhee hooks into Claude Code's lifecycle — no CLAUDE.md bloat, no SKILL.md files, no markdown accumulation. Structured XML context injection, budgeted to ~630 tokens regardless of how much memory you have.
166
+
167
+ **What happens automatically:**
168
+
169
+ | Hook | When | What Dhee does |
170
+ |:-----|:-----|:---------------|
171
+ | `SessionStart` | Session opens | Injects last session, insights, performance trends, relevant memories |
172
+ | `UserPromptSubmit` | Every prompt | Surfaces memories relevant to what you just asked |
173
+ | `PostToolUse` | After Edit/Write/Bash | Captures what Claude did (secrets auto-stripped) |
174
+ | `PreCompact` | Before context compaction | Checkpoints state so nothing is lost |
175
+ | `Stop` | Session ends | Records outcomes — what worked, what failed, learnings |
176
+
177
+ Or start Claude Code directly with context:
178
+
179
+ ```bash
180
+ dhee task "fix the flaky auth test"
181
+ ```
182
+
183
+ **Why not CLAUDE.md?** Markdown files are static. After 6 months of accumulated knowledge, they rot — stale patterns sit at equal weight to current ones, no retrieval ranking, no forgetting. Dhee uses vector memory with strength-based decay. Relevant memories surface. Irrelevant ones fade. The context budget stays constant at ~630 tokens whether you have 50 memories or 50,000.
184
+
157
185
  ### CLI
158
186
 
159
187
  ```bash
160
188
  dhee remember "User prefers Python"
161
189
  dhee recall "programming language"
162
190
  dhee checkpoint "Fixed auth bug" --what-worked "checked logs"
191
+ dhee install # install Claude Code hooks
192
+ dhee uninstall-hooks # remove them
163
193
  ```
164
194
 
165
195
  ### Docker
@@ -295,8 +325,18 @@ These are surfaced through `context()` and `checkpoint()` automatically when ena
295
325
  ## Architecture
296
326
 
297
327
  ```
298
- Agent (Claude, GPT, Cursor, custom)
328
+ Claude Code (or any agent)
299
329
 
330
+ ├── SessionStart hook ──→ dhee.context() ──→ XML renderer ──→ system prompt injection
331
+ ├── UserPromptSubmit ───→ dhee.recall() ──→ ranked memories ──→ per-turn context
332
+ ├── PostToolUse ────────→ dhee.remember() ─→ privacy filter ──→ stored (0 LLM)
333
+ ├── PreCompact ─────────→ dhee.checkpoint() + re-inject context
334
+ └── Stop ───────────────→ dhee.checkpoint(what_worked, what_failed, outcome_score)
335
+ ```
336
+
337
+ The 4-operation API under the hooks:
338
+
339
+ ```
300
340
  ├── remember(content) → Engram: embed + store (0 LLM)
301
341
  ├── recall(query) → Engram: embed + vector search (0 LLM)
302
342
  ├── context(task) → Buddhi: performance + insights + intentions + memories
@@ -1,8 +1,8 @@
1
1
  <p align="center">
2
- <img src="docs/dhee-logo.png" alt="Dhee" width="80">
2
+ <img src="docs/dhee-logo.png" alt="Dhee" width="80"> <h1 align="center">Dhee</h1>
3
3
  </p>
4
4
 
5
- <h1 align="center">Dhee</h1>
5
+
6
6
 
7
7
  <h3 align="center">The cognition layer that turns your agent into a HyperAgent.</h3>
8
8
 
@@ -78,12 +78,42 @@ d.context("fixing auth bug")
78
78
  d.checkpoint("Fixed it", what_worked="git blame first")
79
79
  ```
80
80
 
81
+ ### Claude Code — Native Hooks (v3.3.0)
82
+
83
+ One command. Every Claude Code session becomes self-evolving.
84
+
85
+ ```bash
86
+ dhee install
87
+ ```
88
+
89
+ That's it. Dhee hooks into Claude Code's lifecycle — no CLAUDE.md bloat, no SKILL.md files, no markdown accumulation. Structured XML context injection, budgeted to ~630 tokens regardless of how much memory you have.
90
+
91
+ **What happens automatically:**
92
+
93
+ | Hook | When | What Dhee does |
94
+ |:-----|:-----|:---------------|
95
+ | `SessionStart` | Session opens | Injects last session, insights, performance trends, relevant memories |
96
+ | `UserPromptSubmit` | Every prompt | Surfaces memories relevant to what you just asked |
97
+ | `PostToolUse` | After Edit/Write/Bash | Captures what Claude did (secrets auto-stripped) |
98
+ | `PreCompact` | Before context compaction | Checkpoints state so nothing is lost |
99
+ | `Stop` | Session ends | Records outcomes — what worked, what failed, learnings |
100
+
101
+ Or start Claude Code directly with context:
102
+
103
+ ```bash
104
+ dhee task "fix the flaky auth test"
105
+ ```
106
+
107
+ **Why not CLAUDE.md?** Markdown files are static. After 6 months of accumulated knowledge, they rot — stale patterns sit at equal weight to current ones, no retrieval ranking, no forgetting. Dhee uses vector memory with strength-based decay. Relevant memories surface. Irrelevant ones fade. The context budget stays constant at ~630 tokens whether you have 50 memories or 50,000.
108
+
81
109
  ### CLI
82
110
 
83
111
  ```bash
84
112
  dhee remember "User prefers Python"
85
113
  dhee recall "programming language"
86
114
  dhee checkpoint "Fixed auth bug" --what-worked "checked logs"
115
+ dhee install # install Claude Code hooks
116
+ dhee uninstall-hooks # remove them
87
117
  ```
88
118
 
89
119
  ### Docker
@@ -219,8 +249,18 @@ These are surfaced through `context()` and `checkpoint()` automatically when ena
219
249
  ## Architecture
220
250
 
221
251
  ```
222
- Agent (Claude, GPT, Cursor, custom)
252
+ Claude Code (or any agent)
223
253
 
254
+ ├── SessionStart hook ──→ dhee.context() ──→ XML renderer ──→ system prompt injection
255
+ ├── UserPromptSubmit ───→ dhee.recall() ──→ ranked memories ──→ per-turn context
256
+ ├── PostToolUse ────────→ dhee.remember() ─→ privacy filter ──→ stored (0 LLM)
257
+ ├── PreCompact ─────────→ dhee.checkpoint() + re-inject context
258
+ └── Stop ───────────────→ dhee.checkpoint(what_worked, what_failed, outcome_score)
259
+ ```
260
+
261
+ The 4-operation API under the hooks:
262
+
263
+ ```
224
264
  ├── remember(content) → Engram: embed + store (0 LLM)
225
265
  ├── recall(query) → Engram: embed + vector search (0 LLM)
226
266
  ├── context(task) → Buddhi: performance + insights + intentions + memories
@@ -32,7 +32,7 @@ from dhee.configs.base import MemoryConfig, FadeMemConfig, EchoMemConfig, Catego
32
32
  # Default: CoreMemory (lightest, zero-config)
33
33
  Memory = CoreMemory
34
34
 
35
- __version__ = "3.2.0"
35
+ __version__ = "3.3.0"
36
36
  __all__ = [
37
37
  # Memory classes
38
38
  "Engram",
@@ -318,6 +318,58 @@ def cmd_uninstall(args: argparse.Namespace) -> None:
318
318
  print("Cancelled.")
319
319
 
320
320
 
321
+ def cmd_task(args: argparse.Namespace) -> None:
322
+ """Start Claude Code with Dhee cognition hooks."""
323
+ from dhee.hooks.claude_code.install import ensure_installed
324
+
325
+ result = ensure_installed()
326
+ if result.already_installed:
327
+ pass # hooks already in place
328
+ elif result.created or result.updated:
329
+ print(f" Dhee hooks installed → {result.settings_path}")
330
+
331
+ # Find claude executable
332
+ claude_bin = shutil.which("claude")
333
+ if not claude_bin:
334
+ print("Error: 'claude' not found in PATH. Install Claude Code first.", file=sys.stderr)
335
+ sys.exit(1)
336
+
337
+ # Build command
338
+ cmd = [claude_bin]
339
+ if args.print_mode:
340
+ cmd.append("--print")
341
+ if args.description:
342
+ cmd.append(args.description)
343
+
344
+ # Replace current process with claude
345
+ os.execvp(claude_bin, cmd)
346
+
347
+
348
+ def cmd_install_hooks(args: argparse.Namespace) -> None:
349
+ """Install Dhee hooks into Claude Code."""
350
+ from dhee.hooks.claude_code.install import install_hooks
351
+
352
+ result = install_hooks(force=args.force)
353
+ if result.already_installed and not args.force:
354
+ print(" Dhee hooks already installed.")
355
+ else:
356
+ action = "Created" if result.created else "Updated"
357
+ print(f" {action} {result.settings_path}")
358
+ print(f" Hooks: {', '.join(result.events)}")
359
+ if result.backed_up:
360
+ print(f" Backup: {result.backed_up}")
361
+
362
+
363
+ def cmd_uninstall_hooks(args: argparse.Namespace) -> None:
364
+ """Remove Dhee hooks from Claude Code."""
365
+ from dhee.hooks.claude_code.install import uninstall_hooks
366
+
367
+ if uninstall_hooks():
368
+ print(" Dhee hooks removed.")
369
+ else:
370
+ print(" No Dhee hooks found.")
371
+
372
+
321
373
  def cmd_benchmark(args: argparse.Namespace) -> None:
322
374
  """Run performance benchmarks."""
323
375
  import time
@@ -458,6 +510,19 @@ def build_parser() -> argparse.ArgumentParser:
458
510
  p_status = sub.add_parser("status", help="Show version, config, and agents")
459
511
  p_status.add_argument("--json", action="store_true", help="JSON output")
460
512
 
513
+ # task
514
+ p_task = sub.add_parser("task", help="Start Claude Code with Dhee cognition")
515
+ p_task.add_argument("description", nargs="?", default="", help="Task description")
516
+ p_task.add_argument("--user-id", default="default", help="User ID")
517
+ p_task.add_argument("--print", dest="print_mode", action="store_true", help="One-shot mode")
518
+
519
+ # install (hooks)
520
+ p_install = sub.add_parser("install", help="Install Dhee hooks into Claude Code")
521
+ p_install.add_argument("--force", action="store_true", help="Overwrite existing hooks")
522
+
523
+ # uninstall-hooks
524
+ sub.add_parser("uninstall-hooks", help="Remove Dhee hooks from Claude Code")
525
+
461
526
  # benchmark
462
527
  sub.add_parser("benchmark", help="Run performance benchmarks")
463
528
 
@@ -481,6 +546,9 @@ COMMAND_MAP = {
481
546
  "export": cmd_export,
482
547
  "import": cmd_import,
483
548
  "status": cmd_status,
549
+ "task": cmd_task,
550
+ "install": cmd_install_hooks,
551
+ "uninstall-hooks": cmd_uninstall_hooks,
484
552
  "benchmark": cmd_benchmark,
485
553
  "uninstall": cmd_uninstall,
486
554
  }
@@ -0,0 +1,2 @@
1
+ from dhee.hooks.claude_code.install import ensure_installed, install_hooks, uninstall_hooks
2
+ from dhee.hooks.claude_code.renderer import estimate_tokens, render_context
@@ -0,0 +1,256 @@
1
+ """Dhee Claude Code hook dispatch.
2
+
3
+ Usage::
4
+
5
+ python -m dhee.hooks.claude_code <event_name>
6
+
7
+ Reads the Claude Code hook payload from stdin (JSON).
8
+ Writes JSON response to stdout.
9
+ On any error, outputs ``{}`` — never fails the host agent.
10
+
11
+ Events handled:
12
+ SessionStart — inject full Dhee context (session + memories + insights)
13
+ UserPromptSubmit — inject relevant memories for the current prompt
14
+ PostToolUse — capture tool outcomes into Dhee memory
15
+ PreCompact — checkpoint state, re-inject context to survive compaction
16
+ Stop / SessionEnd — checkpoint session with outcomes
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import json
22
+ import os
23
+ import sys
24
+ from typing import Any
25
+
26
+ _MAX_REMEMBER_CHARS = 2000
27
+ _MAX_QUERY_CHARS = 200
28
+
29
+
30
+ def _get_dhee():
31
+ from dhee import Dhee
32
+
33
+ return Dhee(
34
+ user_id=os.environ.get("DHEE_USER_ID", "default"),
35
+ auto_context=False,
36
+ auto_checkpoint=False,
37
+ )
38
+
39
+
40
+ def _render(ctx: dict[str, Any], **kwargs: Any) -> str:
41
+ from dhee.hooks.claude_code.renderer import render_context
42
+
43
+ return render_context(ctx, **kwargs)
44
+
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # Handlers — each returns a dict for stdout JSON
48
+ # ---------------------------------------------------------------------------
49
+
50
+
51
+ def handle_session_start(payload: dict[str, Any]) -> dict[str, Any]:
52
+ dhee = _get_dhee()
53
+
54
+ task_desc = (
55
+ payload.get("task_description")
56
+ or payload.get("initial_prompt")
57
+ or payload.get("prompt")
58
+ or ""
59
+ )
60
+
61
+ ctx = dhee.context(
62
+ task_description=task_desc or None,
63
+ user_id=os.environ.get("DHEE_USER_ID", "default"),
64
+ )
65
+
66
+ if not ctx:
67
+ return {}
68
+
69
+ has_content = (
70
+ ctx.get("memories")
71
+ or ctx.get("last_session")
72
+ or ctx.get("insights")
73
+ or ctx.get("intentions")
74
+ or ctx.get("performance")
75
+ )
76
+ if not has_content:
77
+ return {}
78
+
79
+ xml = _render(ctx, task_description=task_desc or None)
80
+ return {"systemMessage": xml}
81
+
82
+
83
+ def handle_user_prompt(payload: dict[str, Any]) -> dict[str, Any]:
84
+ if isinstance(payload, dict):
85
+ prompt = str(payload.get("prompt", payload.get("content", "")))
86
+ elif isinstance(payload, str):
87
+ prompt = payload
88
+ else:
89
+ prompt = str(payload)
90
+
91
+ if not prompt.strip():
92
+ return {}
93
+
94
+ dhee = _get_dhee()
95
+ results = dhee.recall(query=prompt[:_MAX_QUERY_CHARS], limit=5)
96
+ if not results:
97
+ return {}
98
+
99
+ xml = _render({"memories": results}, max_tokens=500)
100
+ return {"systemMessage": xml}
101
+
102
+
103
+ def handle_post_tool(payload: dict[str, Any]) -> dict[str, Any]:
104
+ if not isinstance(payload, dict):
105
+ return {}
106
+
107
+ tool_name = payload.get("tool_name", "")
108
+ tool_input = payload.get("tool_input", {})
109
+ tool_result = payload.get("tool_result", "")
110
+ success = payload.get("success", True)
111
+
112
+ if not tool_name:
113
+ return {}
114
+
115
+ write_tools = {"Edit", "Write", "MultiEdit", "NotebookEdit"}
116
+ shell_tools = {"Bash", "BashOutput"}
117
+ if tool_name not in write_tools and tool_name not in shell_tools:
118
+ return {}
119
+
120
+ if tool_name in write_tools:
121
+ path = ""
122
+ if isinstance(tool_input, dict):
123
+ path = tool_input.get("file_path", tool_input.get("path", ""))
124
+ content = f"edited {path}" if path else f"used {tool_name}"
125
+ if not success:
126
+ content = f"failed to edit {path}: {str(tool_result)[:100]}"
127
+ else:
128
+ cmd = ""
129
+ if isinstance(tool_input, dict):
130
+ cmd = tool_input.get("command", "")[:150]
131
+ content = f"ran: {cmd}" if cmd else f"used {tool_name}"
132
+ if not success:
133
+ stderr = str(tool_result)[:200] if tool_result else "unknown error"
134
+ content = f"command failed: {cmd[:80]} — {stderr}"
135
+
136
+ from dhee.hooks.claude_code.privacy import filter_secrets
137
+
138
+ content = filter_secrets(content)
139
+ if len(content) < 10:
140
+ return {}
141
+
142
+ try:
143
+ dhee = _get_dhee()
144
+ dhee.remember(
145
+ content=content[:_MAX_REMEMBER_CHARS],
146
+ metadata={"source": "claude_code_hook", "tool": tool_name, "success": success},
147
+ )
148
+ except Exception:
149
+ pass
150
+
151
+ return {}
152
+
153
+
154
+ def handle_pre_compact(payload: dict[str, Any]) -> dict[str, Any]:
155
+ dhee = _get_dhee()
156
+
157
+ summary = "session compacted"
158
+ if isinstance(payload, dict):
159
+ summary = payload.get("summary", summary)
160
+
161
+ try:
162
+ dhee.checkpoint(summary=summary, status="compacted")
163
+ except Exception:
164
+ pass
165
+
166
+ ctx = dhee.context(user_id=os.environ.get("DHEE_USER_ID", "default"))
167
+ if not ctx:
168
+ return {}
169
+ xml = _render(ctx)
170
+ return {"systemMessage": xml}
171
+
172
+
173
+ def handle_stop(payload: dict[str, Any]) -> dict[str, Any]:
174
+ dhee = _get_dhee()
175
+
176
+ summary = "session ended"
177
+ task_type = None
178
+ outcome_score = None
179
+ what_worked = None
180
+ what_failed = None
181
+
182
+ if isinstance(payload, dict):
183
+ summary = payload.get("summary", payload.get("task_description", summary))
184
+ task_type = payload.get("task_type")
185
+ if payload.get("outcome_score") is not None:
186
+ try:
187
+ outcome_score = float(payload["outcome_score"])
188
+ except (TypeError, ValueError):
189
+ pass
190
+ what_worked = payload.get("what_worked")
191
+ what_failed = payload.get("what_failed")
192
+
193
+ try:
194
+ dhee.checkpoint(
195
+ summary=summary,
196
+ task_type=task_type,
197
+ outcome_score=outcome_score,
198
+ what_worked=what_worked,
199
+ what_failed=what_failed,
200
+ status="completed",
201
+ repo=os.getcwd(),
202
+ )
203
+ except Exception:
204
+ pass
205
+
206
+ return {}
207
+
208
+
209
+ # ---------------------------------------------------------------------------
210
+ # Dispatch
211
+ # ---------------------------------------------------------------------------
212
+
213
+ _HANDLERS = {
214
+ "SessionStart": handle_session_start,
215
+ "UserPromptSubmit": handle_user_prompt,
216
+ "PostToolUse": handle_post_tool,
217
+ "PreCompact": handle_pre_compact,
218
+ "Stop": handle_stop,
219
+ "SessionEnd": handle_stop,
220
+ }
221
+
222
+
223
+ def main() -> int:
224
+ if len(sys.argv) < 2:
225
+ sys.stderr.write("usage: python -m dhee.hooks.claude_code <event>\n")
226
+ sys.stdout.write("{}\n")
227
+ return 1
228
+
229
+ event = sys.argv[1]
230
+ handler = _HANDLERS.get(event)
231
+ if not handler:
232
+ sys.stdout.write("{}\n")
233
+ return 0
234
+
235
+ try:
236
+ raw = sys.stdin.read() or "{}"
237
+ except Exception:
238
+ raw = "{}"
239
+
240
+ try:
241
+ payload = json.loads(raw)
242
+ except json.JSONDecodeError:
243
+ payload = {"prompt": raw, "raw": raw}
244
+
245
+ try:
246
+ result = handler(payload)
247
+ sys.stdout.write(json.dumps(result or {}) + "\n")
248
+ except Exception as exc:
249
+ sys.stderr.write(f"dhee hook {event}: {exc}\n")
250
+ sys.stdout.write("{}\n")
251
+
252
+ return 0
253
+
254
+
255
+ if __name__ == "__main__":
256
+ raise SystemExit(main())
@@ -0,0 +1,173 @@
1
+ """Install Dhee hooks into Claude Code settings.
2
+
3
+ Writes hook entries into ``~/.claude/settings.json`` so that every Claude Code
4
+ session automatically gets Dhee cognition: memory injection on start, learning
5
+ on tool use, checkpoint on exit. No markdown files, no SKILL.md, no plugins
6
+ directory — just Python hooks in the agent's native lifecycle.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+ import shutil
13
+ import sys
14
+ from dataclasses import dataclass, field
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ HOOK_EVENTS: tuple[str, ...] = (
19
+ "SessionStart",
20
+ "UserPromptSubmit",
21
+ "PostToolUse",
22
+ "PreCompact",
23
+ "Stop",
24
+ "SessionEnd",
25
+ )
26
+
27
+ TOOL_MATCHERS: dict[str, str] = {
28
+ "PostToolUse": "Edit|Write|MultiEdit|Bash",
29
+ }
30
+
31
+
32
+ @dataclass
33
+ class InstallResult:
34
+ settings_path: Path
35
+ events: tuple[str, ...]
36
+ created: bool = False
37
+ updated: bool = False
38
+ already_installed: bool = False
39
+ backed_up: Path | None = None
40
+
41
+
42
+ def _settings_path() -> Path:
43
+ return Path.home() / ".claude" / "settings.json"
44
+
45
+
46
+ def _python_cmd() -> str:
47
+ return sys.executable or "python3"
48
+
49
+
50
+ def _build_entry(event: str) -> dict[str, Any]:
51
+ cmd = f"{_python_cmd()} -m dhee.hooks.claude_code {event}"
52
+ entry: dict[str, Any] = {
53
+ "hooks": [{"type": "command", "command": cmd, "timeout": 10}],
54
+ }
55
+ if event in TOOL_MATCHERS:
56
+ entry["matcher"] = TOOL_MATCHERS[event]
57
+ return entry
58
+
59
+
60
+ def _has_dhee_hook(entries: list[dict[str, Any]]) -> bool:
61
+ for entry in entries:
62
+ for hook in entry.get("hooks", []):
63
+ if "dhee.hooks.claude_code" in hook.get("command", ""):
64
+ return True
65
+ return False
66
+
67
+
68
+ def _all_installed(hooks: dict[str, Any], events: tuple[str, ...]) -> bool:
69
+ for event in events:
70
+ entries = hooks.get(event, [])
71
+ if not isinstance(entries, list) or not _has_dhee_hook(entries):
72
+ return False
73
+ return True
74
+
75
+
76
+ def install_hooks(
77
+ *,
78
+ force: bool = False,
79
+ events: tuple[str, ...] = HOOK_EVENTS,
80
+ ) -> InstallResult:
81
+ """Install Dhee hooks into ``~/.claude/settings.json``."""
82
+ path = _settings_path()
83
+ path.parent.mkdir(parents=True, exist_ok=True)
84
+
85
+ settings: dict[str, Any] = {}
86
+ if path.exists():
87
+ try:
88
+ settings = json.loads(path.read_text(encoding="utf-8"))
89
+ except (json.JSONDecodeError, Exception):
90
+ settings = {}
91
+
92
+ existing_hooks = settings.get("hooks", {})
93
+
94
+ if not force and _all_installed(existing_hooks, events):
95
+ return InstallResult(
96
+ settings_path=path,
97
+ events=events,
98
+ already_installed=True,
99
+ )
100
+
101
+ backed_up = None
102
+ if path.exists():
103
+ backup = path.with_suffix(".json.dhee-backup")
104
+ shutil.copy2(path, backup)
105
+ backed_up = backup
106
+
107
+ hooks = dict(existing_hooks)
108
+ for event in events:
109
+ our_entry = _build_entry(event)
110
+ if event in hooks and not force:
111
+ existing = hooks[event]
112
+ if isinstance(existing, list) and _has_dhee_hook(existing):
113
+ continue
114
+ if isinstance(existing, list):
115
+ existing.append(our_entry)
116
+ else:
117
+ hooks[event] = [our_entry]
118
+ else:
119
+ hooks[event] = [our_entry]
120
+
121
+ settings["hooks"] = hooks
122
+
123
+ created = not path.exists()
124
+ path.write_text(json.dumps(settings, indent=2) + "\n", encoding="utf-8")
125
+
126
+ return InstallResult(
127
+ settings_path=path,
128
+ events=events,
129
+ created=created,
130
+ updated=not created,
131
+ backed_up=backed_up,
132
+ )
133
+
134
+
135
+ def ensure_installed() -> InstallResult:
136
+ """Install hooks if not already present."""
137
+ return install_hooks()
138
+
139
+
140
+ def uninstall_hooks() -> bool:
141
+ """Remove Dhee hooks from settings.json."""
142
+ path = _settings_path()
143
+ if not path.exists():
144
+ return False
145
+
146
+ try:
147
+ settings = json.loads(path.read_text(encoding="utf-8"))
148
+ except Exception:
149
+ return False
150
+
151
+ hooks = settings.get("hooks", {})
152
+ changed = False
153
+
154
+ for event in list(hooks.keys()):
155
+ entries = hooks[event]
156
+ if not isinstance(entries, list):
157
+ continue
158
+ filtered = [
159
+ e for e in entries
160
+ if not any("dhee.hooks.claude_code" in h.get("command", "") for h in e.get("hooks", []))
161
+ ]
162
+ if len(filtered) != len(entries):
163
+ changed = True
164
+ if filtered:
165
+ hooks[event] = filtered
166
+ else:
167
+ del hooks[event]
168
+
169
+ if changed:
170
+ settings["hooks"] = hooks
171
+ path.write_text(json.dumps(settings, indent=2) + "\n", encoding="utf-8")
172
+
173
+ return changed