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.
- package/README.md +140 -41
- package/package.json +15 -3
- package/src/__pycache__/auto_update.cpython-310.pyc +0 -0
- package/src/__pycache__/hnsw_index.cpython-310.pyc +0 -0
- package/src/__pycache__/hnsw_index.cpython-314.pyc +0 -0
- package/src/__pycache__/kg_populate.cpython-310.pyc +0 -0
- package/src/__pycache__/knowledge_graph.cpython-310.pyc +0 -0
- package/src/__pycache__/plugin_loader.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_coordination.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_credentials.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_learnings.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_menu.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_reminders.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_reminders_crud.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_sessions.cpython-310.pyc +0 -0
- package/src/__pycache__/tools_task_history.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_core.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_decay.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_ingest.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_memory.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_search.cpython-314.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-310.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-312.pyc +0 -0
- package/src/cognitive/__pycache__/_trust.cpython-314.pyc +0 -0
- package/src/crons/manifest.json +106 -0
- package/src/crons/sync.py +217 -0
- package/src/dashboard/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/dashboard/__pycache__/app.cpython-310.pyc +0 -0
- package/src/dashboard/app.py +16 -2
- package/src/dashboard/templates/dashboard.html +3 -2
- package/src/db/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/db/__pycache__/__init__.cpython-312.pyc +0 -0
- package/src/db/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_core.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_credentials.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_entities.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_episodic.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_evolution.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_fts.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_learnings.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_reminders.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_schema.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_sessions.cpython-314.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-310.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-312.pyc +0 -0
- package/src/db/__pycache__/_tasks.cpython-314.pyc +0 -0
- package/src/db/_episodic.py +1 -1
- package/src/db/_reminders.py +9 -5
- package/src/hooks/session-stop.sh +2 -1
- package/src/plugins/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/adaptive_mode.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-310.pyc +0 -0
- package/src/plugins/__pycache__/update.cpython-310.pyc +0 -0
- package/src/plugins/core_rules.py +34 -17
- package/src/plugins/update.py +18 -0
- package/src/scripts/check-context.py +4 -7
- package/src/scripts/deep-sleep/__pycache__/extract.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/apply_findings.py +512 -167
- package/src/scripts/deep-sleep/collect.py +480 -0
- package/src/scripts/deep-sleep/extract-prompt.md +233 -0
- package/src/scripts/deep-sleep/extract.py +249 -0
- package/src/scripts/deep-sleep/synthesize-prompt.md +168 -0
- package/src/scripts/deep-sleep/synthesize.py +191 -0
- package/src/scripts/nexo-catchup.py +5 -8
- package/src/scripts/nexo-daily-self-audit.py +28 -19
- package/src/scripts/nexo-deep-sleep.sh +31 -16
- package/src/scripts/nexo-evolution-run.py +5 -20
- package/src/scripts/nexo-followup-hygiene.py +4 -2
- package/src/scripts/nexo-github-monitor.py +6 -9
- package/src/scripts/nexo-immune.py +4 -17
- package/src/scripts/nexo-learning-validator.py +0 -29
- package/src/scripts/nexo-postmortem-consolidator.py +9 -20
- package/src/scripts/nexo-proactive-dashboard.py +1 -0
- package/src/scripts/nexo-sleep.py +8 -18
- package/src/scripts/nexo-synthesis.py +8 -19
- package/src/tools_menu.py +1 -1
- package/src/tools_sessions.py +67 -0
- package/src/__pycache__/auto_close_sessions.cpython-310.pyc +0 -0
- package/src/__pycache__/auto_close_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/auto_update.cpython-314.pyc +0 -0
- package/src/__pycache__/claim_graph.cpython-310.pyc +0 -0
- package/src/__pycache__/claim_graph.cpython-314.pyc +0 -0
- package/src/__pycache__/evolution_cycle.cpython-310.pyc +0 -0
- package/src/__pycache__/evolution_cycle.cpython-314.pyc +0 -0
- package/src/__pycache__/kg_populate.cpython-314.pyc +0 -0
- package/src/__pycache__/knowledge_graph.cpython-314.pyc +0 -0
- package/src/__pycache__/maintenance.cpython-310.pyc +0 -0
- package/src/__pycache__/maintenance.cpython-314.pyc +0 -0
- package/src/__pycache__/migrate_embeddings.cpython-310.pyc +0 -0
- package/src/__pycache__/migrate_embeddings.cpython-314.pyc +0 -0
- package/src/__pycache__/plugin_loader.cpython-314.pyc +0 -0
- package/src/__pycache__/server.cpython-310.pyc +0 -0
- package/src/__pycache__/server.cpython-314.pyc +0 -0
- package/src/__pycache__/storage_router.cpython-310.pyc +0 -0
- package/src/__pycache__/storage_router.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_coordination.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_credentials.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_learnings.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_menu.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_reminders.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_reminders_crud.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_sessions.cpython-314.pyc +0 -0
- package/src/__pycache__/tools_task_history.cpython-314.pyc +0 -0
- package/src/dashboard/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/dashboard/__pycache__/app.cpython-314.pyc +0 -0
- package/src/hooks/__pycache__/auto_capture.cpython-310.pyc +0 -0
- package/src/hooks/__pycache__/auto_capture.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/agents.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/artifact_registry.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/backup.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/cognitive_memory.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/core_rules.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/cortex.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/entities.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/episodic_memory.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/evolution.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/guard.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/knowledge_graph_tools.cpython-314.pyc +0 -0
- package/src/plugins/__pycache__/preferences.cpython-314.pyc +0 -0
- package/src/rules/__pycache__/__init__.cpython-310.pyc +0 -0
- package/src/rules/__pycache__/__init__.cpython-314.pyc +0 -0
- package/src/rules/__pycache__/migrate.cpython-310.pyc +0 -0
- package/src/rules/__pycache__/migrate.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/check-context.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/check-context.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-auto-update.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-auto-update.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-catchup.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-catchup.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-cognitive-decay.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-daily-self-audit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-evolution-run.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-evolution-run.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-followup-hygiene.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-github-monitor.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-github-monitor.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-immune.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-immune.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-install.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-install.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-housekeep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-validator.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-learning-validator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-migrate.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-migrate.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-postmortem-consolidator.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-pre-commit.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-pre-commit.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-proactive-dashboard.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-reflection.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-reflection.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-runtime-preflight.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-email.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-email.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-reply.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-send-reply.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-sleep.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-sleep.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-synthesis.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-synthesis.cpython-314.pyc +0 -0
- package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-310.pyc +0 -0
- package/src/scripts/__pycache__/nexo-watchdog-smoke.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/analyze_session.cpython-310.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/analyze_session.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/apply_findings.cpython-310.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/apply_findings.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/collect_transcripts.cpython-310.pyc +0 -0
- package/src/scripts/deep-sleep/__pycache__/collect_transcripts.cpython-314.pyc +0 -0
- package/src/scripts/deep-sleep/analyze_session.py +0 -217
- package/src/scripts/deep-sleep/collect_transcripts.py +0 -145
- package/src/scripts/deep-sleep/prompt.md +0 -109
- package/tests/__pycache__/__init__.cpython-310.pyc +0 -0
- package/tests/__pycache__/__init__.cpython-314.pyc +0 -0
- package/tests/__pycache__/conftest.cpython-310-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/conftest.cpython-310.pyc +0 -0
- package/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_cognitive.cpython-310-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_cognitive.cpython-310.pyc +0 -0
- package/tests/__pycache__/test_cognitive.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_knowledge_graph.cpython-310-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_knowledge_graph.cpython-310.pyc +0 -0
- package/tests/__pycache__/test_knowledge_graph.cpython-314-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_migrations.cpython-310-pytest-9.0.2.pyc +0 -0
- package/tests/__pycache__/test_migrations.cpython-310.pyc +0 -0
- 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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|