get-claudia 1.3.0 → 1.3.1
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.
|
@@ -9,12 +9,13 @@ Starts the memory daemon with:
|
|
|
9
9
|
|
|
10
10
|
import argparse
|
|
11
11
|
import asyncio
|
|
12
|
+
import hashlib
|
|
12
13
|
import logging
|
|
13
14
|
import signal
|
|
14
15
|
import sys
|
|
15
16
|
from pathlib import Path
|
|
16
17
|
|
|
17
|
-
from .config import get_config
|
|
18
|
+
from .config import get_config, set_project_id
|
|
18
19
|
from .daemon.health import start_health_server, stop_health_server
|
|
19
20
|
from .daemon.scheduler import start_scheduler, stop_scheduler
|
|
20
21
|
from .database import get_db
|
|
@@ -26,6 +27,17 @@ logger = logging.getLogger(__name__)
|
|
|
26
27
|
_shutdown_requested = False
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
def get_project_hash(project_dir: str) -> str:
|
|
31
|
+
"""Generate consistent short hash from project directory path.
|
|
32
|
+
|
|
33
|
+
Uses SHA256 truncated to 12 characters for a good balance of:
|
|
34
|
+
- Uniqueness (12 hex chars = 48 bits = ~281 trillion combinations)
|
|
35
|
+
- Readability (short enough to see in file listings)
|
|
36
|
+
- Determinism (same path always produces same hash)
|
|
37
|
+
"""
|
|
38
|
+
return hashlib.sha256(project_dir.encode()).hexdigest()[:12]
|
|
39
|
+
|
|
40
|
+
|
|
29
41
|
def setup_logging(log_path: Path = None, debug: bool = False) -> None:
|
|
30
42
|
"""Configure logging"""
|
|
31
43
|
config = get_config()
|
|
@@ -58,16 +70,23 @@ def signal_handler(signum, frame):
|
|
|
58
70
|
_shutdown_requested = True
|
|
59
71
|
|
|
60
72
|
|
|
61
|
-
def run_daemon(mcp_mode: bool = True, debug: bool = False) -> None:
|
|
73
|
+
def run_daemon(mcp_mode: bool = True, debug: bool = False, project_id: str = None) -> None:
|
|
62
74
|
"""
|
|
63
75
|
Run the Claudia Memory Daemon.
|
|
64
76
|
|
|
65
77
|
Args:
|
|
66
78
|
mcp_mode: If True, run as MCP server (stdio mode)
|
|
67
79
|
debug: Enable debug logging
|
|
80
|
+
project_id: Optional project identifier for database isolation
|
|
68
81
|
"""
|
|
82
|
+
# Set project context before any config access
|
|
83
|
+
if project_id:
|
|
84
|
+
set_project_id(project_id)
|
|
85
|
+
|
|
69
86
|
setup_logging(debug=debug)
|
|
70
87
|
logger.info("Starting Claudia Memory Daemon")
|
|
88
|
+
if project_id:
|
|
89
|
+
logger.info(f"Project isolation enabled: {project_id}")
|
|
71
90
|
|
|
72
91
|
# Set up signal handlers
|
|
73
92
|
signal.signal(signal.SIGTERM, signal_handler)
|
|
@@ -137,9 +156,21 @@ def main():
|
|
|
137
156
|
action="store_true",
|
|
138
157
|
help="Check system health and exit",
|
|
139
158
|
)
|
|
159
|
+
parser.add_argument(
|
|
160
|
+
"--project-dir",
|
|
161
|
+
type=str,
|
|
162
|
+
help="Project directory for database isolation (creates project-specific database)",
|
|
163
|
+
)
|
|
140
164
|
|
|
141
165
|
args = parser.parse_args()
|
|
142
166
|
|
|
167
|
+
# Compute project ID from directory path if provided
|
|
168
|
+
project_id = None
|
|
169
|
+
if args.project_dir:
|
|
170
|
+
project_id = get_project_hash(args.project_dir)
|
|
171
|
+
# Set project context early for commands that don't call run_daemon
|
|
172
|
+
set_project_id(project_id)
|
|
173
|
+
|
|
143
174
|
if args.consolidate:
|
|
144
175
|
# One-shot consolidation
|
|
145
176
|
setup_logging(debug=args.debug)
|
|
@@ -165,7 +196,7 @@ def main():
|
|
|
165
196
|
return
|
|
166
197
|
|
|
167
198
|
# Run the daemon
|
|
168
|
-
run_daemon(mcp_mode=not args.standalone, debug=args.debug)
|
|
199
|
+
run_daemon(mcp_mode=not args.standalone, debug=args.debug, project_id=project_id)
|
|
169
200
|
|
|
170
201
|
|
|
171
202
|
if __name__ == "__main__":
|
|
@@ -42,8 +42,14 @@ class MemoryConfig:
|
|
|
42
42
|
log_path: Path = field(default_factory=lambda: Path.home() / ".claudia" / "daemon.log")
|
|
43
43
|
|
|
44
44
|
@classmethod
|
|
45
|
-
def load(cls) -> "MemoryConfig":
|
|
46
|
-
"""Load configuration from ~/.claudia/config.json, with defaults
|
|
45
|
+
def load(cls, project_id: Optional[str] = None) -> "MemoryConfig":
|
|
46
|
+
"""Load configuration from ~/.claudia/config.json, with defaults.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
project_id: Optional project identifier for database isolation.
|
|
50
|
+
When provided, the database path is overridden to
|
|
51
|
+
~/.claudia/memory/{project_id}.db for per-project isolation.
|
|
52
|
+
"""
|
|
47
53
|
config_path = Path.home() / ".claudia" / "config.json"
|
|
48
54
|
config = cls()
|
|
49
55
|
|
|
@@ -80,6 +86,11 @@ class MemoryConfig:
|
|
|
80
86
|
# Use defaults on error
|
|
81
87
|
pass
|
|
82
88
|
|
|
89
|
+
# Override database path for project isolation
|
|
90
|
+
# This ensures each project gets its own isolated database
|
|
91
|
+
if project_id:
|
|
92
|
+
config.db_path = Path.home() / ".claudia" / "memory" / f"{project_id}.db"
|
|
93
|
+
|
|
83
94
|
# Ensure directories exist
|
|
84
95
|
config.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
85
96
|
config.log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -109,13 +120,36 @@ class MemoryConfig:
|
|
|
109
120
|
json.dump(data, f, indent=2)
|
|
110
121
|
|
|
111
122
|
|
|
112
|
-
# Global config instance
|
|
123
|
+
# Global config instance and project context
|
|
113
124
|
_config: Optional[MemoryConfig] = None
|
|
125
|
+
_project_id: Optional[str] = None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def set_project_id(project_id: Optional[str]) -> None:
|
|
129
|
+
"""Set the project ID for database isolation.
|
|
130
|
+
|
|
131
|
+
This must be called before any access to get_config() to ensure
|
|
132
|
+
the correct project-specific database path is used.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
project_id: Hash of the project directory path, or None for global database.
|
|
136
|
+
"""
|
|
137
|
+
global _config, _project_id
|
|
138
|
+
|
|
139
|
+
# If project_id changes, invalidate cached config so it reloads
|
|
140
|
+
if project_id != _project_id:
|
|
141
|
+
_config = None
|
|
142
|
+
_project_id = project_id
|
|
114
143
|
|
|
115
144
|
|
|
116
145
|
def get_config() -> MemoryConfig:
|
|
117
|
-
"""Get or load the global configuration
|
|
118
|
-
|
|
146
|
+
"""Get or load the global configuration.
|
|
147
|
+
|
|
148
|
+
The configuration is project-aware. If set_project_id() was called,
|
|
149
|
+
the database path will be project-specific. Otherwise, the global
|
|
150
|
+
claudia.db is used for backward compatibility.
|
|
151
|
+
"""
|
|
152
|
+
global _config, _project_id
|
|
119
153
|
if _config is None:
|
|
120
|
-
_config = MemoryConfig.load()
|
|
154
|
+
_config = MemoryConfig.load(project_id=_project_id)
|
|
121
155
|
return _config
|
package/package.json
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
"claudia-memory": {
|
|
8
8
|
"_disabled": true,
|
|
9
9
|
"command": "~/.claudia/daemon/venv/bin/python",
|
|
10
|
-
"args": ["-m", "claudia_memory
|
|
11
|
-
"_description": "Enhanced memory with vector search. Install with: ~/.claudia/
|
|
10
|
+
"args": ["-m", "claudia_memory", "--project-dir", "${workspaceFolder}"],
|
|
11
|
+
"_description": "Enhanced memory with vector search (per-project isolation). Install with: ~/.claudia/daemon/scripts/install.sh"
|
|
12
12
|
},
|
|
13
13
|
|
|
14
14
|
"gmail": {
|