nexo-brain 2.0.0 → 2.1.0

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 (238) hide show
  1. package/README.md +140 -41
  2. package/package.json +15 -3
  3. package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
  4. package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
  5. package/src/__pycache__/hnsw_index.cpython-314.pyc +0 -0
  6. package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
  7. package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
  8. package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
  9. package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
  10. package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
  11. package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
  12. package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
  13. package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
  14. package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
  15. package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
  16. package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
  17. package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
  18. package/src/cognitive/__pycache__/__init__.cpython-312.pyc +0 -0
  19. package/src/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
  20. package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
  21. package/src/cognitive/__pycache__/_core.cpython-312.pyc +0 -0
  22. package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
  23. package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
  24. package/src/cognitive/__pycache__/_decay.cpython-312.pyc +0 -0
  25. package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
  26. package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
  27. package/src/cognitive/__pycache__/_ingest.cpython-312.pyc +0 -0
  28. package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
  29. package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
  30. package/src/cognitive/__pycache__/_memory.cpython-312.pyc +0 -0
  31. package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
  32. package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
  33. package/src/cognitive/__pycache__/_search.cpython-312.pyc +0 -0
  34. package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
  35. package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
  36. package/src/cognitive/__pycache__/_trust.cpython-312.pyc +0 -0
  37. package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
  38. package/src/crons/manifest.json +106 -0
  39. package/src/crons/sync.py +217 -0
  40. package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
  41. package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
  42. package/src/dashboard/app.py +16 -2
  43. package/src/dashboard/templates/dashboard.html +3 -2
  44. package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
  45. package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
  46. package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
  47. package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
  48. package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
  49. package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
  50. package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
  51. package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
  52. package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
  53. package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
  54. package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
  55. package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
  56. package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
  57. package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
  58. package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
  59. package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
  60. package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
  61. package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
  62. package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
  63. package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
  64. package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
  65. package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
  66. package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
  67. package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
  68. package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
  69. package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
  70. package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
  71. package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
  72. package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
  73. package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
  74. package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
  75. package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
  76. package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
  77. package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
  78. package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
  79. package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
  80. package/src/db/_episodic.py +1 -1
  81. package/src/db/_reminders.py +9 -5
  82. package/src/hooks/session-stop.sh +2 -1
  83. package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
  84. package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
  85. package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
  86. package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
  87. package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
  88. package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
  89. package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
  90. package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
  91. package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
  92. package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
  93. package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
  94. package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
  95. package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
  96. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
  97. package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
  98. package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
  99. package/src/plugins/core_rules.py +34 -17
  100. package/src/plugins/update.py +18 -0
  101. package/src/scripts/check-context.py +4 -7
  102. package/src/scripts/deep-sleep/__pycache__/extract.cpython-314.pyc +0 -0
  103. package/src/scripts/deep-sleep/apply_findings.py +512 -167
  104. package/src/scripts/deep-sleep/collect.py +480 -0
  105. package/src/scripts/deep-sleep/extract-prompt.md +233 -0
  106. package/src/scripts/deep-sleep/extract.py +249 -0
  107. package/src/scripts/deep-sleep/synthesize-prompt.md +168 -0
  108. package/src/scripts/deep-sleep/synthesize.py +191 -0
  109. package/src/scripts/nexo-catchup.py +5 -8
  110. package/src/scripts/nexo-daily-self-audit.py +28 -19
  111. package/src/scripts/nexo-deep-sleep.sh +31 -16
  112. package/src/scripts/nexo-evolution-run.py +5 -20
  113. package/src/scripts/nexo-followup-hygiene.py +4 -2
  114. package/src/scripts/nexo-github-monitor.py +6 -9
  115. package/src/scripts/nexo-immune.py +4 -17
  116. package/src/scripts/nexo-learning-validator.py +0 -29
  117. package/src/scripts/nexo-postmortem-consolidator.py +9 -20
  118. package/src/scripts/nexo-proactive-dashboard.py +1 -0
  119. package/src/scripts/nexo-sleep.py +8 -18
  120. package/src/scripts/nexo-synthesis.py +8 -19
  121. package/src/tools_menu.py +1 -1
  122. package/src/tools_sessions.py +67 -0
  123. package/src/__pycache__/auto_close_sessions.cpython-310.pyc +0 -0
  124. package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
  125. package/src/__pycache__/auto_update.cpython-314.pyc +0 -0
  126. package/src/__pycache__/claim_graph.cpython-310.pyc +0 -0
  127. package/src/__pycache__/claim_graph.cpython-314.pyc +0 -0
  128. package/src/__pycache__/evolution_cycle.cpython-310.pyc +0 -0
  129. package/src/__pycache__/evolution_cycle.cpython-314.pyc +0 -0
  130. package/src/__pycache__/kg_populate.cpython-314.pyc +0 -0
  131. package/src/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
  132. package/src/__pycache__/maintenance.cpython-310.pyc +0 -0
  133. package/src/__pycache__/maintenance.cpython-314.pyc +0 -0
  134. package/src/__pycache__/migrate_embeddings.cpython-310.pyc +0 -0
  135. package/src/__pycache__/migrate_embeddings.cpython-314.pyc +0 -0
  136. package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
  137. package/src/__pycache__/server.cpython-310.pyc +0 -0
  138. package/src/__pycache__/server.cpython-314.pyc +0 -0
  139. package/src/__pycache__/storage_router.cpython-310.pyc +0 -0
  140. package/src/__pycache__/storage_router.cpython-314.pyc +0 -0
  141. package/src/__pycache__/tools_coordination.cpython-314.pyc +0 -0
  142. package/src/__pycache__/tools_credentials.cpython-314.pyc +0 -0
  143. package/src/__pycache__/tools_learnings.cpython-314.pyc +0 -0
  144. package/src/__pycache__/tools_menu.cpython-314.pyc +0 -0
  145. package/src/__pycache__/tools_reminders.cpython-314.pyc +0 -0
  146. package/src/__pycache__/tools_reminders_crud.cpython-314.pyc +0 -0
  147. package/src/__pycache__/tools_sessions.cpython-314.pyc +0 -0
  148. package/src/__pycache__/tools_task_history.cpython-314.pyc +0 -0
  149. package/src/dashboard/__pycache__/__init__.cpython-314.pyc +0 -0
  150. package/src/dashboard/__pycache__/app.cpython-314.pyc +0 -0
  151. package/src/hooks/__pycache__/auto_capture.cpython-310.pyc +0 -0
  152. package/src/hooks/__pycache__/auto_capture.cpython-314.pyc +0 -0
  153. package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
  154. package/src/plugins/__pycache__/agents.cpython-314.pyc +0 -0
  155. package/src/plugins/__pycache__/artifact_registry.cpython-314.pyc +0 -0
  156. package/src/plugins/__pycache__/backup.cpython-314.pyc +0 -0
  157. package/src/plugins/__pycache__/cognitive_memory.cpython-314.pyc +0 -0
  158. package/src/plugins/__pycache__/core_rules.cpython-314.pyc +0 -0
  159. package/src/plugins/__pycache__/cortex.cpython-314.pyc +0 -0
  160. package/src/plugins/__pycache__/entities.cpython-314.pyc +0 -0
  161. package/src/plugins/__pycache__/episodic_memory.cpython-314.pyc +0 -0
  162. package/src/plugins/__pycache__/evolution.cpython-314.pyc +0 -0
  163. package/src/plugins/__pycache__/guard.cpython-314.pyc +0 -0
  164. package/src/plugins/__pycache__/knowledge_graph_tools.cpython-314.pyc +0 -0
  165. package/src/plugins/__pycache__/preferences.cpython-314.pyc +0 -0
  166. package/src/rules/__pycache__/__init__.cpython-310.pyc +0 -0
  167. package/src/rules/__pycache__/__init__.cpython-314.pyc +0 -0
  168. package/src/rules/__pycache__/migrate.cpython-310.pyc +0 -0
  169. package/src/rules/__pycache__/migrate.cpython-314.pyc +0 -0
  170. package/src/scripts/__pycache__/check-context.cpython-310.pyc +0 -0
  171. package/src/scripts/__pycache__/check-context.cpython-314.pyc +0 -0
  172. package/src/scripts/__pycache__/nexo-auto-update.cpython-310.pyc +0 -0
  173. package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
  174. package/src/scripts/__pycache__/nexo-catchup.cpython-310.pyc +0 -0
  175. package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
  176. package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-310.pyc +0 -0
  177. package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
  178. package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-310.pyc +0 -0
  179. package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
  180. package/src/scripts/__pycache__/nexo-evolution-run.cpython-310.pyc +0 -0
  181. package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
  182. package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-310.pyc +0 -0
  183. package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
  184. package/src/scripts/__pycache__/nexo-github-monitor.cpython-310.pyc +0 -0
  185. package/src/scripts/__pycache__/nexo-github-monitor.cpython-314.pyc +0 -0
  186. package/src/scripts/__pycache__/nexo-immune.cpython-310.pyc +0 -0
  187. package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
  188. package/src/scripts/__pycache__/nexo-install.cpython-310.pyc +0 -0
  189. package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
  190. package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-310.pyc +0 -0
  191. package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
  192. package/src/scripts/__pycache__/nexo-learning-validator.cpython-310.pyc +0 -0
  193. package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
  194. package/src/scripts/__pycache__/nexo-migrate.cpython-310.pyc +0 -0
  195. package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
  196. package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-310.pyc +0 -0
  197. package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
  198. package/src/scripts/__pycache__/nexo-pre-commit.cpython-310.pyc +0 -0
  199. package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
  200. package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-310.pyc +0 -0
  201. package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
  202. package/src/scripts/__pycache__/nexo-reflection.cpython-310.pyc +0 -0
  203. package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
  204. package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-310.pyc +0 -0
  205. package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
  206. package/src/scripts/__pycache__/nexo-send-email.cpython-310.pyc +0 -0
  207. package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
  208. package/src/scripts/__pycache__/nexo-send-reply.cpython-310.pyc +0 -0
  209. package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
  210. package/src/scripts/__pycache__/nexo-sleep.cpython-310.pyc +0 -0
  211. package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
  212. package/src/scripts/__pycache__/nexo-synthesis.cpython-310.pyc +0 -0
  213. package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
  214. package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-310.pyc +0 -0
  215. package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
  216. package/src/scripts/deep-sleep/__pycache__/analyze_session.cpython-310.pyc +0 -0
  217. package/src/scripts/deep-sleep/__pycache__/analyze_session.cpython-314.pyc +0 -0
  218. package/src/scripts/deep-sleep/__pycache__/apply_findings.cpython-310.pyc +0 -0
  219. package/src/scripts/deep-sleep/__pycache__/apply_findings.cpython-314.pyc +0 -0
  220. package/src/scripts/deep-sleep/__pycache__/collect_transcripts.cpython-310.pyc +0 -0
  221. package/src/scripts/deep-sleep/__pycache__/collect_transcripts.cpython-314.pyc +0 -0
  222. package/src/scripts/deep-sleep/analyze_session.py +0 -217
  223. package/src/scripts/deep-sleep/collect_transcripts.py +0 -145
  224. package/src/scripts/deep-sleep/prompt.md +0 -109
  225. package/tests/__pycache__/__init__.cpython-310.pyc +0 -0
  226. package/tests/__pycache__/__init__.cpython-314.pyc +0 -0
  227. package/tests/__pycache__/conftest.cpython-310-pytest-9.0.2.pyc +0 -0
  228. package/tests/__pycache__/conftest.cpython-310.pyc +0 -0
  229. package/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
  230. package/tests/__pycache__/test_cognitive.cpython-310-pytest-9.0.2.pyc +0 -0
  231. package/tests/__pycache__/test_cognitive.cpython-310.pyc +0 -0
  232. package/tests/__pycache__/test_cognitive.cpython-314-pytest-9.0.2.pyc +0 -0
  233. package/tests/__pycache__/test_knowledge_graph.cpython-310-pytest-9.0.2.pyc +0 -0
  234. package/tests/__pycache__/test_knowledge_graph.cpython-310.pyc +0 -0
  235. package/tests/__pycache__/test_knowledge_graph.cpython-314-pytest-9.0.2.pyc +0 -0
  236. package/tests/__pycache__/test_migrations.cpython-310-pytest-9.0.2.pyc +0 -0
  237. package/tests/__pycache__/test_migrations.cpython-310.pyc +0 -0
  238. package/tests/__pycache__/test_migrations.cpython-314-pytest-9.0.2.pyc +0 -0
@@ -1,217 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Deep Sleep — Step 2: Analyze transcripts with Claude CLI (bare mode).
4
- Sends each session to Claude opus for analysis, then consolidates findings.
5
- """
6
- import json
7
- import os
8
- import subprocess
9
- import sys
10
- from datetime import datetime
11
- from pathlib import Path
12
-
13
- NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
14
-
15
- PROMPT_FILE = Path(__file__).parent / "prompt.md"
16
- DEEP_SLEEP_DIR = NEXO_HOME / "operations" / "deep-sleep"
17
- MAX_TRANSCRIPT_CHARS = 150_000
18
-
19
-
20
- def build_transcript_text(session: dict) -> str:
21
- """Build a readable transcript from a session."""
22
- lines = [
23
- f"## Session: {session['session_file']}",
24
- f"Modified: {session['modified']}",
25
- f"Messages: {session['message_count']}, Tool uses: {session['tool_use_count']}",
26
- "",
27
- "### Conversation"
28
- ]
29
- for msg in session["messages"]:
30
- role = "USER" if msg["role"] == "user" else "NEXO"
31
- lines.append(f"\n**{role}:**")
32
- lines.append(msg["text"])
33
-
34
- if session["tool_uses"]:
35
- lines.append("\n### Tool Usage Log")
36
- for tu in session["tool_uses"]:
37
- file_info = f" [{tu['file'][:80]}]" if tu.get("file") else ""
38
- lines.append(f"- {tu['tool']}{file_info}")
39
-
40
- return "\n".join(lines)
41
-
42
-
43
- def find_api_key() -> str | None:
44
- """Find Anthropic API key from common locations."""
45
- # Environment variable
46
- key = os.environ.get("ANTHROPIC_API_KEY", "")
47
- if key:
48
- return key
49
-
50
- # Common file locations
51
- for path in [
52
- Path.home() / ".claude" / "anthropic-api-key.txt",
53
- Path.home() / ".anthropic" / "api_key",
54
- Path.home() / ".config" / "anthropic" / "api_key",
55
- ]:
56
- if path.exists():
57
- return path.read_text().strip()
58
-
59
- return None
60
-
61
-
62
- def analyze_with_claude(transcript: str, prompt: str) -> dict | None:
63
- """Send transcript to Claude CLI for analysis."""
64
- full_prompt = (
65
- f"{prompt}\n\n---\n\n# TODAY'S TRANSCRIPT\n\n{transcript}\n\n---\n\n"
66
- "Analyze this transcript and return the JSON output as specified. "
67
- "Return ONLY the JSON, no markdown code fences."
68
- )
69
-
70
- api_key = find_api_key()
71
- env = os.environ.copy()
72
- if api_key:
73
- env["ANTHROPIC_API_KEY"] = api_key
74
-
75
- try:
76
- result = subprocess.run(
77
- ["claude", "-p", full_prompt, "--model", "opus", "--output-format", "text", "--bare"],
78
- capture_output=True, text=True, timeout=300, env=env
79
- )
80
-
81
- if result.returncode != 0:
82
- print(f"Claude CLI error: {result.stderr[:500]}", file=sys.stderr)
83
- return None
84
-
85
- response_text = result.stdout.strip()
86
-
87
- # Strip markdown code fences if present
88
- if response_text.startswith("```"):
89
- lines = response_text.split("\n")
90
- response_text = "\n".join(lines[1:-1] if lines[-1].strip() == "```" else lines[1:])
91
- response_text = response_text.strip()
92
-
93
- # Find JSON object in response
94
- json_start = response_text.find("{")
95
- json_end = response_text.rfind("}") + 1
96
- if json_start >= 0 and json_end > json_start:
97
- response_text = response_text[json_start:json_end]
98
-
99
- return json.loads(response_text)
100
-
101
- except subprocess.TimeoutExpired:
102
- print("Claude CLI timeout (300s)", file=sys.stderr)
103
- return None
104
- except json.JSONDecodeError as e:
105
- print(f"Failed to parse Claude response: {e}", file=sys.stderr)
106
- return None
107
- except FileNotFoundError:
108
- print("Claude CLI not found. Install: npm install -g @anthropic-ai/claude-code", file=sys.stderr)
109
- return None
110
-
111
-
112
- def consolidate_findings(results: list[dict]) -> dict:
113
- """Merge findings from multiple sessions into one report."""
114
- consolidated = {
115
- "uncaptured_corrections": [],
116
- "uncaptured_ideas": [],
117
- "missed_commitments": [],
118
- "protocol_compliance": {
119
- "guard_check": {"required": 0, "executed": 0},
120
- "heartbeat_quality": {"total": 0, "with_good_context": 0},
121
- "trust_adjustments": {"corrections_detected": 0, "adjusted": 0},
122
- "learning_capture": {"errors_resolved": 0, "captured": 0},
123
- "change_log": {"production_edits": 0, "logged": 0},
124
- "feedback_capture": {"corrections": 0, "captured": 0},
125
- },
126
- "protocol_violations": [],
127
- "quality_issues": [],
128
- "auto_reinforcements": [],
129
- }
130
-
131
- for r in results:
132
- if not r:
133
- continue
134
- for key in ["uncaptured_corrections", "uncaptured_ideas", "missed_commitments",
135
- "protocol_violations", "quality_issues", "auto_reinforcements"]:
136
- consolidated[key].extend(r.get(key, []))
137
-
138
- pc = r.get("protocol_compliance", {})
139
- for key in consolidated["protocol_compliance"]:
140
- if key in pc and isinstance(pc[key], dict):
141
- for subkey in consolidated["protocol_compliance"][key]:
142
- consolidated["protocol_compliance"][key][subkey] += pc[key].get(subkey, 0)
143
-
144
- # Calculate rates
145
- for key, vals in consolidated["protocol_compliance"].items():
146
- keys = list(vals.keys())
147
- if len(keys) == 2:
148
- denominator = vals[keys[0]]
149
- numerator = vals[keys[1]]
150
- vals["rate"] = round(numerator / denominator, 2) if denominator > 0 else 1.0
151
-
152
- rates = [v.get("rate", 1.0) for v in consolidated["protocol_compliance"].values()]
153
- consolidated["protocol_compliance"]["overall_compliance"] = round(sum(rates) / len(rates), 2) if rates else 1.0
154
- consolidated["auto_reinforcements"] = list(set(consolidated["auto_reinforcements"]))
155
-
156
- return consolidated
157
-
158
-
159
- def main():
160
- date = sys.argv[1] if len(sys.argv) > 1 else datetime.now().strftime("%Y-%m-%d")
161
-
162
- transcripts_file = DEEP_SLEEP_DIR / f"{date}-transcripts.json"
163
- if not transcripts_file.exists():
164
- print(f"No transcripts found for {date}. Run collect_transcripts.py first.")
165
- sys.exit(1)
166
-
167
- with open(transcripts_file) as f:
168
- data = json.load(f)
169
-
170
- sessions = data["sessions"]
171
- print(f"Analyzing {len(sessions)} sessions from {date}...")
172
-
173
- prompt = PROMPT_FILE.read_text()
174
-
175
- results = []
176
- for i, session in enumerate(sessions):
177
- transcript = build_transcript_text(session)
178
-
179
- if len(transcript) < 500:
180
- print(f" Session {i+1}/{len(sessions)}: skipped (too short)")
181
- continue
182
-
183
- if len(transcript) > MAX_TRANSCRIPT_CHARS:
184
- transcript = transcript[:MAX_TRANSCRIPT_CHARS] + "\n\n[TRUNCATED]"
185
-
186
- print(f" Session {i+1}/{len(sessions)}: {session['session_file'][:12]}... ({len(transcript)} chars)")
187
- result = analyze_with_claude(transcript, prompt)
188
- if result:
189
- results.append(result)
190
- print(f" → {len(result.get('uncaptured_corrections', []))} corrections, "
191
- f"{len(result.get('protocol_violations', []))} violations")
192
- else:
193
- print(f" → Analysis failed")
194
-
195
- consolidated = consolidate_findings(results)
196
- consolidated["date"] = date
197
- consolidated["sessions_analyzed"] = len(results)
198
-
199
- n_corrections = len(consolidated["uncaptured_corrections"])
200
- n_violations = len(consolidated["protocol_violations"])
201
- compliance = consolidated["protocol_compliance"]["overall_compliance"]
202
- consolidated["summary"] = (
203
- f"Analyzed {len(results)} sessions. "
204
- f"Found {n_corrections} uncaptured corrections, {n_violations} protocol violations. "
205
- f"Overall compliance: {compliance:.0%}."
206
- )
207
-
208
- output_file = DEEP_SLEEP_DIR / f"{date}-analysis.json"
209
- with open(output_file, "w") as f:
210
- json.dump(consolidated, f, indent=2, ensure_ascii=False)
211
-
212
- print(f"\nResults: {output_file}")
213
- print(consolidated["summary"])
214
-
215
-
216
- if __name__ == "__main__":
217
- main()
@@ -1,145 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Deep Sleep — Step 1: Collect today's session transcripts.
4
- Reads Claude Code .jsonl files, extracts clean conversation text + tool usage.
5
- """
6
- import json
7
- import os
8
- import sys
9
- from datetime import datetime
10
- from pathlib import Path
11
-
12
- NEXO_HOME = Path(os.environ.get("NEXO_HOME", str(Path.home() / ".nexo")))
13
-
14
- MIN_USER_MESSAGES = 3 # Skip trivial sessions
15
-
16
-
17
- def find_sessions_dir() -> Path:
18
- """Find the Claude Code sessions directory dynamically."""
19
- claude_dir = Path.home() / ".claude" / "projects"
20
- if not claude_dir.exists():
21
- return claude_dir
22
-
23
- # Find the project directory (usually named after the home path)
24
- for d in claude_dir.iterdir():
25
- if d.is_dir() and list(d.glob("*.jsonl")):
26
- return d
27
-
28
- # Fallback: look for any .jsonl in the projects dir tree
29
- for jsonl in claude_dir.rglob("*.jsonl"):
30
- return jsonl.parent
31
-
32
- return claude_dir
33
-
34
-
35
- def extract_session(jsonl_path: str) -> dict | None:
36
- """Extract clean transcript from a session JSONL file."""
37
- messages = []
38
- tool_uses = []
39
- user_msg_count = 0
40
-
41
- try:
42
- with open(jsonl_path, "r") as f:
43
- for line in f:
44
- line = line.strip()
45
- if not line:
46
- continue
47
- try:
48
- d = json.loads(line)
49
- except json.JSONDecodeError:
50
- continue
51
-
52
- msg_type = d.get("type")
53
-
54
- # User messages
55
- if msg_type == "user":
56
- content = d.get("message", {}).get("content", "")
57
- if isinstance(content, str) and content.strip():
58
- if content.startswith("<system-reminder>"):
59
- continue
60
- messages.append({
61
- "role": "user",
62
- "text": content[:5000],
63
- "uuid": d.get("uuid", "")
64
- })
65
- user_msg_count += 1
66
-
67
- # Assistant messages
68
- elif msg_type in ("message", "assistant"):
69
- msg = d.get("message", {})
70
- content_blocks = msg.get("content", [])
71
- text_parts = []
72
- for block in content_blocks:
73
- if isinstance(block, dict):
74
- if block.get("type") == "text":
75
- text_parts.append(block.get("text", ""))
76
- elif block.get("type") == "tool_use":
77
- tool_uses.append({
78
- "tool": block.get("name", ""),
79
- "input_keys": list(block.get("input", {}).keys()) if isinstance(block.get("input"), dict) else [],
80
- "file": block.get("input", {}).get("file_path", "") or block.get("input", {}).get("command", "")[:100] if isinstance(block.get("input"), dict) else ""
81
- })
82
- if text_parts:
83
- combined = "\n".join(text_parts)[:5000]
84
- messages.append({
85
- "role": "assistant",
86
- "text": combined
87
- })
88
-
89
- except Exception as e:
90
- print(f"Error reading {jsonl_path}: {e}", file=sys.stderr)
91
- return None
92
-
93
- if user_msg_count < MIN_USER_MESSAGES:
94
- return None
95
-
96
- return {
97
- "session_file": os.path.basename(jsonl_path),
98
- "message_count": len(messages),
99
- "user_message_count": user_msg_count,
100
- "tool_use_count": len(tool_uses),
101
- "messages": messages,
102
- "tool_uses": tool_uses
103
- }
104
-
105
-
106
- def collect_date(target_date: str, sessions_dir: Path) -> list[dict]:
107
- """Collect all sessions modified on a given date."""
108
- sessions = []
109
- for f in sessions_dir.glob("*.jsonl"):
110
- mtime = datetime.fromtimestamp(f.stat().st_mtime)
111
- if mtime.strftime("%Y-%m-%d") == target_date:
112
- session = extract_session(str(f))
113
- if session:
114
- session["modified"] = mtime.isoformat()
115
- sessions.append(session)
116
- sessions.sort(key=lambda s: s["modified"])
117
- return sessions
118
-
119
-
120
- def main():
121
- date_arg = sys.argv[1] if len(sys.argv) > 1 else datetime.now().strftime("%Y-%m-%d")
122
- sessions_dir = find_sessions_dir()
123
-
124
- sessions = collect_date(date_arg, sessions_dir)
125
-
126
- output = {
127
- "date": date_arg,
128
- "sessions_found": len(sessions),
129
- "total_messages": sum(s["message_count"] for s in sessions),
130
- "total_tool_uses": sum(s["tool_use_count"] for s in sessions),
131
- "sessions": sessions
132
- }
133
-
134
- output_dir = NEXO_HOME / "operations" / "deep-sleep"
135
- output_dir.mkdir(parents=True, exist_ok=True)
136
- output_file = output_dir / f"{output['date']}-transcripts.json"
137
- with open(output_file, "w") as f:
138
- json.dump(output, f, indent=2, ensure_ascii=False)
139
-
140
- print(f"Collected {len(sessions)} sessions, {output['total_messages']} messages, {output['total_tool_uses']} tool uses")
141
- print(f"Output: {output_file}")
142
-
143
-
144
- if __name__ == "__main__":
145
- main()
@@ -1,109 +0,0 @@
1
- # Deep Sleep Analyst — Session Transcript Analysis
2
-
3
- You are NEXO's overnight analyst. You read the COMPLETE transcripts of today's sessions between the user and NEXO, and you find what NEXO missed.
4
-
5
- ## Your job
6
-
7
- NEXO captures feedback, learnings, and corrections during sessions — but it misses things. Your job is to find the gaps by reading what ACTUALLY happened (the transcript), not what NEXO thinks happened (the diary).
8
-
9
- ## What you analyze
10
-
11
- ### 1. Uncaptured corrections
12
- The user corrected NEXO but NEXO didn't save a learning or feedback memory.
13
- Signals: frustration tone, repeating the same instruction 2+ times, the user having to explain something twice, explicit corrections ("no", "wrong", "that's not it").
14
-
15
- ### 2. Repeated patterns
16
- The same correction appears multiple times in the day. This is a SYSTEMIC failure — it needs a strong learning with high severity.
17
-
18
- ### 3. Uncaptured ideas
19
- The user mentioned an idea, plan, or intention that nobody formalized. Signals: "we could", "we should", "I want", "I need", future-tense plans without deadlines.
20
-
21
- ### 4. Missed commitments
22
- The user said "I'll look at it tomorrow", "this week", "when I can" — was a followup created? If not, flag it.
23
-
24
- ### 5. Protocol compliance (from tool_uses)
25
- Check if NEXO followed its own protocols:
26
- - `nexo_guard_check` before Edit/Write on production files?
27
- - `nexo_heartbeat` called with meaningful context_hint?
28
- - `nexo_cognitive_trust` called after corrections?
29
- - `nexo_learning_add` called after resolving errors?
30
- - `nexo_followup_complete` called when the user confirmed completion ("done", "fixed", "already handled")?
31
- - `nexo_change_log` called after production code changes?
32
- - Feedback memory saved after corrections?
33
-
34
- ### 6. Quality assessment
35
- - Did NEXO declare work "complete"/"perfect" and the user had to correct after?
36
- - Was NEXO too verbose when the user wanted action?
37
- - Did NEXO delegate to subagents when it should have done the work directly?
38
-
39
- ## Output format
40
-
41
- Return ONLY valid JSON:
42
-
43
- ```json
44
- {
45
- "date": "YYYY-MM-DD",
46
- "sessions_analyzed": 5,
47
- "uncaptured_corrections": [
48
- {
49
- "quote": "User's exact words (max 100 chars)",
50
- "context": "What they were working on",
51
- "what_nexo_should_have_saved": "The learning/feedback content",
52
- "action": "learning_add|feedback_write|preference_set",
53
- "category": "ui|code|process|communication",
54
- "severity": "low|medium|high|critical",
55
- "times_repeated": 1
56
- }
57
- ],
58
- "uncaptured_ideas": [
59
- {
60
- "quote": "User's words",
61
- "idea": "What the idea is",
62
- "action": "reminder_create|followup_create",
63
- "suggested_date": "YYYY-MM-DD or null"
64
- }
65
- ],
66
- "missed_commitments": [
67
- {
68
- "quote": "User's words",
69
- "commitment": "What was promised",
70
- "action": "followup_create",
71
- "due_date": "YYYY-MM-DD"
72
- }
73
- ],
74
- "protocol_compliance": {
75
- "guard_check": {"required": 0, "executed": 0, "rate": 1.0},
76
- "heartbeat_quality": {"total": 0, "with_good_context": 0, "rate": 1.0},
77
- "trust_adjustments": {"corrections_detected": 0, "adjusted": 0, "rate": 1.0},
78
- "learning_capture": {"errors_resolved": 0, "captured": 0, "rate": 1.0},
79
- "change_log": {"production_edits": 0, "logged": 0, "rate": 1.0},
80
- "feedback_capture": {"corrections": 0, "captured": 0, "rate": 1.0},
81
- "overall_compliance": 1.0
82
- },
83
- "protocol_violations": [
84
- {
85
- "protocol": "guard_check|trust_adjustment|feedback_capture|...",
86
- "context": "What happened",
87
- "severity": "low|medium|high|critical"
88
- }
89
- ],
90
- "quality_issues": [
91
- {
92
- "issue": "Description of quality problem",
93
- "example": "Specific instance",
94
- "severity": "low|medium|high"
95
- }
96
- ],
97
- "auto_reinforcements": [
98
- "Specific rule to add or reinforce in CLAUDE.md or guard"
99
- ],
100
- "summary": "2-3 sentence overall assessment of the day"
101
- }
102
- ```
103
-
104
- ## Rules
105
- - Be SPECIFIC. Quote the user's exact words.
106
- - Only flag REAL issues. If NEXO did capture something correctly, don't flag it.
107
- - severity=critical means the user repeated the same correction 3+ times or expressed strong frustration
108
- - For protocol compliance, count ACTUAL tool_use entries in the transcript
109
- - If no issues found in a category, return empty array — don't invent problems