bone-agent 1.3.2 → 1.3.3

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 (65) hide show
  1. package/README.md +2 -2
  2. package/config.yaml.example +8 -0
  3. package/package.json +3 -2
  4. package/prompts/main/ask_questions.md +31 -0
  5. package/prompts/main/batch_independent_calls.md +5 -0
  6. package/prompts/main/casual_interactions.md +11 -0
  7. package/prompts/main/code_references.md +8 -0
  8. package/prompts/main/communication_style.md +12 -0
  9. package/prompts/main/context_reliability.md +12 -0
  10. package/prompts/main/conversational_tool_calling.md +15 -0
  11. package/prompts/main/dream.md +36 -0
  12. package/prompts/main/editing_pattern.md +13 -0
  13. package/prompts/main/error_handling.md +6 -0
  14. package/prompts/main/exploration_pattern.md +21 -0
  15. package/prompts/main/intro.md +1 -0
  16. package/prompts/main/obsidian.md +16 -0
  17. package/prompts/main/obsidian_project.md +79 -0
  18. package/prompts/main/professional_objectivity.md +3 -0
  19. package/prompts/main/targeted_searching.md +10 -0
  20. package/prompts/main/task_lists_pattern.md +8 -0
  21. package/prompts/main/temp_folder.md +9 -0
  22. package/prompts/main/think_before_acting.md +10 -0
  23. package/prompts/main/tone_and_style.md +4 -0
  24. package/prompts/main/tool_preferences.md +24 -0
  25. package/prompts/main/trust_subagent_context.md +21 -0
  26. package/prompts/main/when_to_use_sub_agent.md +7 -0
  27. package/prompts/micro/ask_questions.md +1 -0
  28. package/prompts/micro/batch_independent_calls.md +1 -0
  29. package/prompts/micro/casual_interactions.md +1 -0
  30. package/prompts/micro/code_references.md +1 -0
  31. package/prompts/micro/communication_style.md +1 -0
  32. package/prompts/micro/context_reliability.md +1 -0
  33. package/prompts/micro/conversational_tool_calling.md +1 -0
  34. package/prompts/micro/editing_pattern.md +1 -0
  35. package/prompts/micro/error_handling.md +1 -0
  36. package/prompts/micro/exploration_pattern.md +1 -0
  37. package/prompts/micro/intro.md +1 -0
  38. package/prompts/micro/obsidian.md +4 -0
  39. package/prompts/micro/obsidian_project.md +5 -0
  40. package/prompts/micro/professional_objectivity.md +1 -0
  41. package/prompts/micro/targeted_searching.md +1 -0
  42. package/prompts/micro/task_lists_pattern.md +1 -0
  43. package/prompts/micro/temp_folder.md +1 -0
  44. package/prompts/micro/think_before_acting.md +5 -0
  45. package/prompts/micro/tone_and_style.md +1 -0
  46. package/prompts/micro/tool_preferences.md +1 -0
  47. package/prompts/micro/trust_subagent_context.md +1 -0
  48. package/prompts/micro/when_to_use_sub_agent.md +1 -0
  49. package/src/core/agentic.py +1 -73
  50. package/src/core/chat_manager.py +42 -7
  51. package/src/core/config_manager.py +6 -0
  52. package/src/core/cron.py +57 -2
  53. package/src/core/memory.py +3 -90
  54. package/src/llm/config.py +28 -2
  55. package/src/llm/prompts.py +251 -497
  56. package/src/llm/providers.py +25 -6
  57. package/src/llm/token_tracker.py +17 -1
  58. package/src/tools/edit.py +8 -6
  59. package/src/tools/helpers/path_resolver.py +18 -12
  60. package/src/tools/rg_search.py +97 -30
  61. package/src/ui/commands.py +120 -5
  62. package/src/ui/displays.py +1 -0
  63. package/src/ui/main.py +1 -0
  64. package/src/utils/settings.py +19 -2
  65. package/src/utils/user_message_logger.py +120 -0
@@ -0,0 +1,120 @@
1
+ """Lightweight user-message logger for the dream memory system.
2
+
3
+ Appends one JSONL line per user message, one file per day per project.
4
+ Always on by default — no toggle needed.
5
+ """
6
+
7
+ import hashlib
8
+ import json
9
+ import logging
10
+ from datetime import datetime, timedelta
11
+ from pathlib import Path
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ # Base directory for daily message logs
16
+ CONVERSATIONS_DIR = Path.home() / ".bone" / "conversations"
17
+ RETENTION_DAYS = 7
18
+
19
+
20
+ def _project_suffix(project_dir: Path) -> str:
21
+ """Generate a short suffix from a project directory path.
22
+
23
+ Format: {dirname}_{first 6 chars of SHA256(path)}
24
+ Avoids collisions between repos with the same folder name.
25
+ """
26
+ path_str = str(project_dir.resolve())
27
+ h = hashlib.sha256(path_str.encode()).hexdigest()[:6]
28
+ return f"{project_dir.name}_{h}"
29
+
30
+
31
+ PROJECT_INDEX_FILE = CONVERSATIONS_DIR / ".project_index.jsonl"
32
+
33
+
34
+ def _register_project(key: str, project_dir: Path) -> None:
35
+ """Append a key→path mapping to the project index if not already present."""
36
+ resolved = str(project_dir.resolve())
37
+ # Check if this key already maps to this path
38
+ if PROJECT_INDEX_FILE.exists():
39
+ with open(PROJECT_INDEX_FILE, "r", encoding="utf-8") as f:
40
+ for line in f:
41
+ try:
42
+ entry = json.loads(line)
43
+ except json.JSONDecodeError:
44
+ continue
45
+ if entry.get("key") == key and entry.get("path") == resolved:
46
+ return # Already indexed
47
+ PROJECT_INDEX_FILE.parent.mkdir(parents=True, exist_ok=True)
48
+ with open(PROJECT_INDEX_FILE, "a", encoding="utf-8") as f:
49
+ f.write(json.dumps({"key": key, "path": resolved}) + "\n")
50
+
51
+
52
+ class UserMessageLogger:
53
+ """Logs user messages to daily JSONL files for later dream processing.
54
+
55
+ When a project_dir is provided, messages go to a per-project file:
56
+ {date}__{dirname}_{hash}.jsonl
57
+ Without a project_dir, messages go to the catch-all:
58
+ {date}.jsonl
59
+ """
60
+
61
+ def __init__(self, conversations_dir: Path | None = None):
62
+ self._dir = conversations_dir or CONVERSATIONS_DIR
63
+ self._dir.mkdir(parents=True, exist_ok=True)
64
+
65
+ def log_user_message(self, content: str, project_dir: Path | None = None) -> None:
66
+ """Append a single user message to today's JSONL file.
67
+
68
+ Args:
69
+ content: The user message text.
70
+ project_dir: Optional project root directory. If provided,
71
+ messages are written to a per-project file.
72
+
73
+ Opens in append mode and flushes immediately for crash safety.
74
+ Each message is one self-contained JSON line.
75
+ """
76
+ today = datetime.now().strftime("%Y-%m-%d")
77
+ if project_dir:
78
+ suffix = _project_suffix(project_dir)
79
+ _register_project(suffix, project_dir)
80
+ filepath = self._dir / f"{today}__{suffix}.jsonl"
81
+ else:
82
+ filepath = self._dir / f"{today}.jsonl"
83
+ entry = {"ts": datetime.now().isoformat(), "msg": content}
84
+ with open(filepath, "a", encoding="utf-8") as f:
85
+ f.write(json.dumps(entry, ensure_ascii=False) + "\n")
86
+
87
+ @staticmethod
88
+ def cleanup_old_files(directory: Path | None = None, retention_days: int = RETENTION_DAYS) -> int:
89
+ """Delete JSONL files older than retention_days. Returns count of files removed."""
90
+ target_dir = directory or CONVERSATIONS_DIR
91
+ if not target_dir.exists():
92
+ return 0
93
+
94
+ cutoff = datetime.now() - timedelta(days=retention_days)
95
+ removed = 0
96
+ surviving = set()
97
+ for f in target_dir.glob("*.jsonl"):
98
+ if f.stat().st_mtime < cutoff.timestamp():
99
+ f.unlink()
100
+ removed += 1
101
+ logger.debug("Removed old conversation log: %s", f.name)
102
+ else:
103
+ surviving.add(f.name)
104
+
105
+ # Prune stale entries from the project index
106
+ index_file = target_dir / ".project_index.jsonl"
107
+ if index_file.exists():
108
+ kept: list[str] = []
109
+ for line in index_file.read_text(encoding="utf-8").splitlines():
110
+ try:
111
+ entry = json.loads(line)
112
+ except json.JSONDecodeError:
113
+ continue
114
+ key = entry.get("key", "")
115
+ # Keep entry if any file matching its key still exists
116
+ if any(key in name for name in surviving):
117
+ kept.append(line)
118
+ index_file.write_text("\n".join(kept) + ("\n" if kept else ""), encoding="utf-8")
119
+
120
+ return removed