cli-memory-os 0.1.0__tar.gz → 0.1.3__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 (67) hide show
  1. {cli_memory_os-0.1.0/cli_memory_os.egg-info → cli_memory_os-0.1.3}/PKG-INFO +1 -1
  2. cli_memory_os-0.1.3/cli/commands/ask.py +67 -0
  3. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/benchmark.py +1 -2
  4. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/doctor.py +13 -2
  5. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/import_cmd.py +0 -1
  6. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/init.py +83 -86
  7. cli_memory_os-0.1.3/cli/commands/restart.py +23 -0
  8. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/status.py +1 -0
  9. cli_memory_os-0.1.3/cli/commands/sync.py +70 -0
  10. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/workspace.py +0 -2
  11. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/main.py +17 -29
  12. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/parser.py +6 -0
  13. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3/cli_memory_os.egg-info}/PKG-INFO +1 -1
  14. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/SOURCES.txt +6 -0
  15. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/embedder.py +0 -1
  16. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/llm.py +0 -1
  17. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/vector_store.py +0 -49
  18. cli_memory_os-0.1.3/infrastructure/compose.py +303 -0
  19. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/docker.py +24 -5
  20. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/health.py +53 -14
  21. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/observability.py +0 -1
  22. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/pyproject.toml +1 -1
  23. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/storage/db.py +0 -38
  24. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/storage/tech_detector.py +1 -39
  25. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/tests/test_cli_integration.py +31 -2
  26. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/tests/test_commands_phase2.py +24 -20
  27. cli_memory_os-0.1.3/tests/test_compose_manager.py +155 -0
  28. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/tests/test_first_time_user_experience.py +2 -1
  29. cli_memory_os-0.1.3/tests/test_infrastructure_stabilization.py +138 -0
  30. cli_memory_os-0.1.3/tests/test_sync_ask_cli.py +111 -0
  31. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/tests/test_v1_features.py +3 -5
  32. cli_memory_os-0.1.0/infrastructure/compose.py +0 -136
  33. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/LICENSE +0 -0
  34. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/MANIFEST.in +0 -0
  35. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/README.md +0 -0
  36. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/__init__.py +0 -0
  37. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/__init__.py +0 -0
  38. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/config_cmd.py +0 -0
  39. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/export.py +0 -0
  40. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/logs.py +0 -0
  41. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/monitor.py +0 -0
  42. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/plugins.py +0 -0
  43. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/start.py +0 -0
  44. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/stop.py +0 -0
  45. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli/commands/version.py +0 -0
  46. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/dependency_links.txt +0 -0
  47. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/entry_points.txt +0 -0
  48. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/requires.txt +0 -0
  49. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/cli_memory_os.egg-info/top_level.txt +0 -0
  50. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/__init__.py +0 -0
  51. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/base.py +0 -0
  52. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/github.py +0 -0
  53. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/gmail.py +0 -0
  54. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/notion.py +0 -0
  55. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/connectors/registry.py +0 -0
  56. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/__init__.py +0 -0
  57. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/chunker.py +0 -0
  58. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/core/context_builder.py +0 -0
  59. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/__init__.py +0 -0
  60. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/config.py +0 -0
  61. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/infrastructure/workspace.py +0 -0
  62. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/models/__init__.py +0 -0
  63. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/models/memory.py +0 -0
  64. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/setup.cfg +0 -0
  65. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/storage/__init__.py +0 -0
  66. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/storage/graph.py +0 -0
  67. {cli_memory_os-0.1.0 → cli_memory_os-0.1.3}/storage/schema.sql +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-memory-os
3
- Version: 0.1.0
3
+ Version: 0.1.3
4
4
  Summary: CLI-based Personal Knowledge Operating System
5
5
  Author-email: Anirudh T <anirudh200584@gmail.com>
6
6
  License-Expression: MIT
@@ -0,0 +1,67 @@
1
+ """
2
+ Command: memory-os ask
3
+
4
+ Queries the RAG pipeline with a question.
5
+ """
6
+
7
+ import sys
8
+ import sqlite3
9
+ from core.llm import run_hybrid_rag
10
+ from storage.db import init_db
11
+
12
+
13
+ def run_and_print_ask(query: str):
14
+ """Run RAG query pipeline and print the structured result blocks."""
15
+ rag_res = run_hybrid_rag(query)
16
+
17
+ print("========================================")
18
+ print("ANSWER")
19
+ print("========================================")
20
+ print(rag_res["answer"])
21
+ print("\n========================================")
22
+ print("SOURCES")
23
+ print("========================================")
24
+ if rag_res.get("sources"):
25
+ for s in rag_res["sources"]:
26
+ print(f"- {s}")
27
+ else:
28
+ print("None")
29
+ print("\n========================================")
30
+ print("REPOSITORIES USED")
31
+ print("========================================")
32
+ if rag_res.get("repositories"):
33
+ for r in rag_res["repositories"]:
34
+ print(f"- {r}")
35
+ else:
36
+ print("None")
37
+ print("\n========================================")
38
+ confidence = rag_res.get("confidence", 0.0)
39
+ print(f"Confidence: {confidence:.2f}")
40
+ print("========================================")
41
+
42
+
43
+ def execute(args):
44
+ """Run the ask command parser handler."""
45
+ # Ensure database is initialized
46
+ try:
47
+ init_db()
48
+ except sqlite3.OperationalError as e:
49
+ print("----------------------------------")
50
+ print("Workspace has not been initialized.")
51
+ print("")
52
+ print("Run:")
53
+ print("memory-os init")
54
+ print("----------------------------------")
55
+ sys.exit(1)
56
+
57
+ if not args.question:
58
+ print("Usage: memory-os ask <question>")
59
+ sys.exit(1)
60
+
61
+ query = " ".join(args.question)
62
+
63
+ try:
64
+ run_and_print_ask(query)
65
+ except Exception as e:
66
+ print(f"❌ Error executing query: {e}")
67
+ sys.exit(1)
@@ -6,8 +6,7 @@ graph lookups, LLM invocation, and RAG pipelines.
6
6
  """
7
7
 
8
8
  import time
9
- import sys
10
- from infrastructure.health import check_neo4j, check_qdrant, check_groq_api, check_memory_usage
9
+ from infrastructure.health import check_qdrant, check_groq_api, check_memory_usage
11
10
  from core.embedder import Embedder
12
11
 
13
12
 
@@ -53,8 +53,19 @@ def execute(args):
53
53
  "Active Plugins": "No plugins are authenticated. Run 'memory-os init' to set up connector logins.",
54
54
  }
55
55
  for name, detail in failed:
56
- rec = recommendations.get(name, "Check log file logs/memory_os.log for errors.")
57
- print(f" * {name}: {rec} (Detail: {detail})")
56
+ if name == "Neo4j" and "Authentication failed" in detail:
57
+ print(" * Neo4j: Neo4j is running but authentication failed.")
58
+ print(" Recommendations:")
59
+ print(" memory-os config set neo4j.user <username>")
60
+ print(" memory-os config set neo4j.password <password>")
61
+ print(" or")
62
+ print(" memory-os init")
63
+ elif name == "Neo4j" and "Unreachable" in detail:
64
+ rec = recommendations.get(name, "Check log file logs/memory_os.log for errors.")
65
+ print(f" * {name}: {rec} (Detail: {detail})")
66
+ else:
67
+ rec = recommendations.get(name, "Check log file logs/memory_os.log for errors.")
68
+ print(f" * {name}: {rec} (Detail: {detail})")
58
69
  print("─────────────────────────────")
59
70
  else:
60
71
  print("\n All checks passed.")
@@ -6,7 +6,6 @@ into the active workspace profile.
6
6
  """
7
7
 
8
8
  import sys
9
- import os
10
9
  import json
11
10
  import zipfile
12
11
  import shutil
@@ -1,26 +1,21 @@
1
1
  """
2
2
  Command: memory-os init
3
3
 
4
- Performs first-time setup and onboarding for Memory-OS.
5
- Provisions the workspace directory structure, creates config.toml,
6
- spins up Neo4j and Qdrant via docker compose, initializes SQLite,
7
- pre-warms the embedding model, and launches Composio toolkit OAuth flows.
4
+ Performs first-time setup and onboarding for Memory-OS using ComposeManager.
8
5
  """
9
6
 
10
7
  import sys
11
8
  import uuid
12
- import time
13
9
  import os
14
- from pathlib import Path
15
10
  from infrastructure.workspace import ensure_workspace, get_db_path
16
11
  from infrastructure.config import generate_default_config, save_config, load_config
17
- from infrastructure.docker import check_docker_installed, check_docker_compose_installed, check_docker_running
18
- from infrastructure.compose import compose_up, wait_for_services
19
- from infrastructure.health import run_all_checks
12
+ from infrastructure.compose import ComposeManager, wait_for_services
13
+ from infrastructure.health import run_all_checks, check_docker, check_neo4j, check_qdrant
20
14
  from storage.db import init_db
21
15
  from core.embedder import Embedder
22
16
 
23
17
 
18
+
24
19
  def get_input(prompt: str, secret: bool = False, default: str = "") -> str:
25
20
  """Prompt the user for input with an optional default value."""
26
21
  import getpass
@@ -38,14 +33,13 @@ def get_input(prompt: str, secret: bool = False, default: str = "") -> str:
38
33
 
39
34
 
40
35
  def execute(args):
41
- """Run the init command."""
36
+ """Run the init command wizard following the exact 11 steps."""
42
37
  print("==================================================")
43
38
  print("🧠 MEMORY-OS INITIALIZATION WIZARD")
44
39
  print("==================================================")
45
40
 
46
- # 1. System Dependency Checks
47
- print("\n[1/7] Checking system dependencies...")
48
-
41
+ # ── Step 1: Check Python ──────────────────────────────────
42
+ print("\n[Step 1/11] Checking Python version...")
49
43
  python_ok = sys.version_info >= (3, 12)
50
44
  py_ver = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
51
45
  if not python_ok:
@@ -53,52 +47,73 @@ def execute(args):
53
47
  sys.exit(1)
54
48
  print(f" ✓ Python version: {py_ver}")
55
49
 
56
- docker_ok, docker_ver = check_docker_installed()
50
+ # ── Step 2: Check Docker daemon ──────────────────────────
51
+ print("\n[Step 2/11] Checking Docker daemon...")
52
+ docker_ok, docker_detail = check_docker()
57
53
  if not docker_ok:
58
- print("----------------------------------")
59
- print("Docker is not installed or not in PATH.")
60
- print("")
61
- print("Please install Docker Desktop and rerun:")
62
- print("memory-os init")
63
- print("----------------------------------")
64
- sys.exit(1)
65
- print(f" ✓ Docker: {docker_ver}")
66
-
67
- compose_ok, compose_ver = check_docker_compose_installed()
68
- if not compose_ok:
69
- print("----------------------------------")
70
- print("Docker Compose is not installed.")
71
- print("")
72
- print("Please install Docker Compose and rerun:")
73
- print("memory-os init")
74
- print("----------------------------------")
75
- sys.exit(1)
76
- print(f" ✓ Docker Compose: {compose_ver}")
77
-
78
- if not check_docker_running():
79
54
  print("----------------------------------")
80
55
  print("Docker Desktop is not running.")
81
56
  print("")
57
+ print("stderr:")
58
+ print(docker_detail)
59
+ print("")
82
60
  print("Start Docker Desktop and rerun:")
83
61
  print("memory-os init")
84
62
  print("----------------------------------")
85
63
  sys.exit(1)
86
64
  print(" ✓ Docker Daemon: Running")
87
65
 
88
-
89
- # 2. Workspace Tree Setup
90
- print("\n[2/7] Creating workspace directories...")
66
+ # ── Step 3: Create workspace ─────────────────────────────
67
+ print("\n[Step 3/11] Creating workspace directories...")
91
68
  ensure_workspace("default")
92
69
  print(" ✓ Workspace path initialized at ~/.memory-os")
93
70
 
94
- # 3. Configure API Credentials & Settings
95
- print("\n[3/7] Configuring credentials...")
96
- neo4j_password = get_input("Set Neo4j database password", secret=True, default="memory_neo")
97
- groq_api_key = get_input("Enter Groq API Key", secret=True)
98
- composio_api_key = get_input("Enter Composio API Key", secret=True)
71
+ # ── Step 4: Generate docker-compose.yml ──────────────────
72
+ print("\n[Step 4/11] Generating docker-compose.yml...")
73
+ from infrastructure.config import get_config
74
+ existing_config = get_config()
75
+ existing_password = existing_config.get("neo4j", {}).get("password") or "memory_neo"
76
+
77
+ manager = ComposeManager()
78
+ manager.generate_compose(profile="default", password=existing_password)
79
+ print(" ✓ docker-compose.yml generated at ~/.memory-os/docker-compose.yml")
80
+
81
+ # ── Step 5: Launch Neo4j + Qdrant ─────────────────────────
82
+ print("\n[Step 5/11] Launching local services (Neo4j, Qdrant)...")
83
+ neo4j_ok, _ = check_neo4j()
84
+ qdrant_ok, _ = check_qdrant()
85
+ if neo4j_ok and qdrant_ok:
86
+ print(" ✓ Neo4j already running")
87
+ print(" ✓ Qdrant already running")
88
+ else:
89
+ if not manager.up(profile="default"):
90
+ print("❌ Failed to start docker compose services.")
91
+ sys.exit(1)
92
+ print(" ✓ Containers launched.")
93
+
94
+ # ── Step 6: Wait until healthy ────────────────────────────
95
+ print("\n[Step 6/11] Waiting for services to become healthy...")
96
+ if neo4j_ok and qdrant_ok:
97
+ print(" ✓ Services are healthy.")
98
+ else:
99
+ if not wait_for_services(timeout=60):
100
+ print("❌ Services failed to start or respond to health checks in time.")
101
+ sys.exit(1)
102
+ print(" ✓ Services are healthy.")
103
+
104
+ # ── Step 7: Initialize SQLite ─────────────────────────────
105
+ print("\n[Step 7/11] Initializing SQLite database...")
106
+ os.environ["MEMORY_OS_DB_PATH"] = str(get_db_path("default"))
107
+ init_db()
108
+ print(" ✓ SQLite database schema initialized.")
109
+
110
+ # ── Step 8: Configure API Keys ───────────────────────────
111
+ print("\n[Step 8/11] Configuring API keys & settings...")
112
+ neo4j_password = get_input("Set Neo4j database password", secret=True, default=existing_password)
113
+ groq_api_key = get_input("Enter Groq API Key", secret=True, default=existing_config.get("groq", {}).get("api_key", ""))
114
+ composio_api_key = get_input("Enter Composio API Key", secret=True, default=existing_config.get("composio", {}).get("api_key", ""))
99
115
 
100
- # Generate auto Composio User UUID
101
- composio_user_id = str(uuid.uuid4())
116
+ composio_user_id = existing_config.get("composio", {}).get("user_id") or str(uuid.uuid4())
102
117
 
103
118
  answers = {
104
119
  "neo4j_password": neo4j_password,
@@ -109,49 +124,16 @@ def execute(args):
109
124
 
110
125
  config_dict = generate_default_config(answers)
111
126
  save_config(config_dict)
112
-
113
- # Force reload config in current process memory
114
127
  load_config()
115
128
 
116
- # Overwrite MEMORY_OS_DB_PATH in environment dynamically to point to the workspace DB
117
- os.environ["MEMORY_OS_DB_PATH"] = str(get_db_path("default"))
129
+ if neo4j_password != existing_password:
130
+ manager.generate_compose(profile="default", password=neo4j_password)
131
+ print(" ✓ docker-compose.yml updated with new password.")
118
132
 
119
133
  print(" ✓ Configuration saved to ~/.memory-os/config.toml")
120
134
 
121
- # 4. Service Provisioning via Docker Compose
122
- print("\n[4/7] Provisioning local services (Neo4j, Qdrant)...")
123
- if not compose_up():
124
- print("❌ Failed to start docker compose services.")
125
- sys.exit(1)
126
- print(" ✓ Containers launched. Waiting for services to become healthy...")
127
-
128
- if not wait_for_services(timeout=60):
129
- print("❌ Services failed to start or respond to health checks in time.")
130
- sys.exit(1)
131
- print(" ✓ Services are healthy.")
132
-
133
- # 5. Schema & Database Initialization
134
- print("\n[5/7] Initializing SQLite database...")
135
- init_db()
136
- print(" ✓ SQLite database schema initialized.")
137
-
138
- # 6. Optional Embedding Model Warming
139
- print("\n[6/7] Preparing embedding model...")
140
- warm_model = get_input("Download and cache embedding model (all-MiniLM-L6-v2) now? (Y/n)", default="y").lower()
141
- if warm_model == "y":
142
- print(" Downloading model (this may take a minute)...")
143
- try:
144
- embedder = Embedder()
145
- _ = embedder.model
146
- print(" ✓ Embedding model cached successfully.")
147
- except Exception as e:
148
- print(f" ⚠️ Warning: Failed to cache model during setup: {e}")
149
- print(" Model will be downloaded automatically on the first query.")
150
- else:
151
- print(" Skipped model download.")
152
-
153
- # 7. Optional Composio Integration OAuth
154
- print("\n[7/7] Configuring integration connectors...")
135
+ # ── Step 9: Authenticate Composio ────────────────────────
136
+ print("\n[Step 9/11] Configuring integration connectors...")
155
137
  setup_connectors = get_input("Configure connectors (GitHub, Gmail, Notion) now? (Y/n)", default="y").lower()
156
138
  if setup_connectors == "y" and composio_api_key:
157
139
  try:
@@ -174,7 +156,6 @@ def execute(args):
174
156
  print(f" 👉 Please open this URL in your browser to complete authentication:\n {req.redirect_url}")
175
157
  print(" Waiting up to 30 seconds for completion...")
176
158
  try:
177
- # Wait for active connection
178
159
  req.wait_for_connection(timeout=30000)
179
160
  print(f" ✓ {name} connected successfully!")
180
161
  except Exception:
@@ -186,8 +167,24 @@ def execute(args):
186
167
  else:
187
168
  print(" Skipped connectors configuration.")
188
169
 
189
- # Verification Report
190
- print("\n==================================================")
170
+ # ── Step 10: Warm embedding model ─────────────────────────
171
+ print("\n[Step 10/11] Preparing embedding model...")
172
+ warm_model = get_input("Download and cache embedding model (all-MiniLM-L6-v2) now? (Y/n)", default="y").lower()
173
+ if warm_model == "y":
174
+ print(" Downloading model (this may take a minute)...")
175
+ try:
176
+ embedder = Embedder()
177
+ _ = embedder.model
178
+ print(" ✓ Embedding model cached successfully.")
179
+ except Exception as e:
180
+ print(f" ⚠️ Warning: Failed to cache model during setup: {e}")
181
+ print(" Model will be downloaded automatically on the first query.")
182
+ else:
183
+ print(" Skipped model download.")
184
+
185
+ # ── Step 11: Done ─────────────────────────────────────────
186
+ print("\n[Step 11/11] Running initial health checks...")
187
+ print("==================================================")
191
188
  print("🏥 RUNNING INITIAL HEALTH CHECKS")
192
189
  print("==================================================")
193
190
  checks = run_all_checks()
@@ -0,0 +1,23 @@
1
+ """
2
+ Command: memory-os restart
3
+
4
+ Restarts the Docker services (Neo4j and Qdrant) via ComposeManager.
5
+ """
6
+
7
+ import sys
8
+ from infrastructure.compose import ComposeManager, wait_for_services
9
+
10
+
11
+ def execute(args):
12
+ """Run the restart command."""
13
+ print("Restarting Memory-OS local services (Neo4j, Qdrant)...")
14
+ if not ComposeManager().restart():
15
+ print("❌ Failed to restart docker compose services.")
16
+ sys.exit(1)
17
+
18
+ print("Waiting for services to become healthy...")
19
+ if not wait_for_services(timeout=60):
20
+ print("❌ Services failed to start or respond to health checks in time.")
21
+ sys.exit(1)
22
+
23
+ print("✓ Services restarted successfully and are healthy.")
@@ -72,6 +72,7 @@ def execute(args):
72
72
  print(f"Documents : 0")
73
73
  print(f"Emails : 0")
74
74
  print(f"Vectors : 0")
75
+ print(f"Embedding Model : {embedding_model}")
75
76
  print("")
76
77
  print("Workspace is initialized but contains no indexed data.")
77
78
  print("")
@@ -0,0 +1,70 @@
1
+ """
2
+ Command: memory-os sync
3
+
4
+ Synchronizes data from connected sources (GitHub, Gmail, Notion),
5
+ chunks documents, generates embeddings, and syncs databases.
6
+ """
7
+
8
+ import sys
9
+ import time
10
+ import logging
11
+ import sqlite3
12
+ from storage.db import init_db, clear_all
13
+ from core.vector_store import run_reindexing, close_qdrant_client
14
+ from storage.graph import GraphStore
15
+ from connectors.github import sync_github
16
+ from connectors.gmail import sync_gmail
17
+ from connectors.notion import sync_notion
18
+
19
+
20
+ def execute(args):
21
+ """Run the synchronization pipeline."""
22
+ # Ensure database is initialized
23
+ try:
24
+ init_db()
25
+ except sqlite3.OperationalError as e:
26
+ print("----------------------------------")
27
+ print("Workspace has not been initialized.")
28
+ print("")
29
+ print("Run:")
30
+ print("memory-os init")
31
+ print("----------------------------------")
32
+ sys.exit(1)
33
+
34
+ start_time = time.perf_counter()
35
+
36
+ rebuild = getattr(args, "rebuild", False)
37
+ source = getattr(args, "source", None)
38
+
39
+ if rebuild:
40
+ print("Performing full rebuild reset...")
41
+ clear_all()
42
+ close_qdrant_client()
43
+ run_reindexing()
44
+ GraphStore().clear_graph()
45
+
46
+ # Sync based on source selection
47
+ if source == "github":
48
+ print("Syncing GitHub source...")
49
+ sync_github()
50
+ elif source == "gmail":
51
+ print("Syncing Gmail source...")
52
+ sync_gmail()
53
+ elif source == "notion":
54
+ print("Syncing Notion source...")
55
+ sync_notion()
56
+ else:
57
+ print("Syncing all sources (GitHub, Gmail, Notion)...")
58
+ sync_github()
59
+ sync_gmail()
60
+ sync_notion()
61
+
62
+ print("Running document chunking and vector reindexing...")
63
+ run_reindexing()
64
+
65
+ print("Extracting and syncing knowledge graph relationships...")
66
+ GraphStore().extract_and_sync_graph()
67
+
68
+ duration = time.perf_counter() - start_time
69
+ print(f"\nSync complete. Total Duration: {duration:.2f}s")
70
+ logging.getLogger("main").info(f"Sync completed in {duration:.2f}s")
@@ -5,10 +5,8 @@ Provides management of active workspaces and profile structures.
5
5
  """
6
6
 
7
7
  import sys
8
- import shutil
9
8
  import os
10
9
  from datetime import datetime
11
- from pathlib import Path
12
10
  from infrastructure.workspace import (
13
11
  get_active_profile,
14
12
  list_profiles,
@@ -115,7 +115,6 @@ def run_interactive():
115
115
  close_qdrant_client,
116
116
  )
117
117
  from core.embedder import Embedder
118
- from core.llm import run_hybrid_rag
119
118
  from storage.graph import GraphStore
120
119
  from connectors.github import sync_github
121
120
  from connectors.gmail import sync_gmail
@@ -283,30 +282,11 @@ def run_interactive():
283
282
  if not arg:
284
283
  print("Usage: ask <question>")
285
284
  else:
286
- rag_res = run_hybrid_rag(arg)
287
- print("========================================")
288
- print("ANSWER")
289
- print("========================================")
290
- print(rag_res["answer"])
291
- print("\n========================================")
292
- print("SOURCES")
293
- print("========================================")
294
- if rag_res["sources"]:
295
- for s in rag_res["sources"]:
296
- print(f"- {s}")
297
- else:
298
- print("None")
299
- print("\n========================================")
300
- print("REPOSITORIES USED")
301
- print("========================================")
302
- if rag_res["repositories"]:
303
- for r in rag_res["repositories"]:
304
- print(f"- {r}")
305
- else:
306
- print("None")
307
- print("\n========================================")
308
- print(f"Confidence: {rag_res['confidence']:.2f}")
309
- print("========================================")
285
+ from cli.commands.ask import run_and_print_ask
286
+ try:
287
+ run_and_print_ask(arg)
288
+ except Exception as e:
289
+ print(f"❌ Error executing query: {e}")
310
290
 
311
291
  elif cmd == "repo-info":
312
292
  if not arg:
@@ -414,6 +394,9 @@ def route_command(args):
414
394
  elif command == "stop":
415
395
  from cli.commands.stop import execute
416
396
  execute(args)
397
+ elif command == "restart":
398
+ from cli.commands.restart import execute
399
+ execute(args)
417
400
  elif command == "plugins":
418
401
  from cli.commands.plugins import execute
419
402
  execute(args)
@@ -438,11 +421,16 @@ def route_command(args):
438
421
  elif command == "logs":
439
422
  from cli.commands.logs import execute
440
423
  execute(args)
424
+ elif command == "sync":
425
+ from cli.commands.sync import execute
426
+ execute(args)
427
+ elif command == "ask":
428
+ from cli.commands.ask import execute
429
+ execute(args)
441
430
  else:
442
- # For commands not yet implemented, show a helpful message
443
- print(f"Command '{command}' is not yet implemented.")
444
- print("Run 'memory-os --help' for available commands.")
445
- print("Run 'memory-os' (no arguments) for the interactive REPL.")
431
+ from cli.parser import build_parser
432
+ build_parser().print_help()
433
+ sys.exit(1)
446
434
 
447
435
 
448
436
 
@@ -118,6 +118,12 @@ def build_parser() -> argparse.ArgumentParser:
118
118
  help="Stop Docker services (preserves data)",
119
119
  )
120
120
 
121
+ # ── restart ────────────────────────────────────────────────
122
+ subparsers.add_parser(
123
+ "restart",
124
+ help="Restart Docker services (Neo4j, Qdrant)",
125
+ )
126
+
121
127
  # ── version ───────────────────────────────────────────────
122
128
  subparsers.add_parser(
123
129
  "version",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-memory-os
3
- Version: 0.1.0
3
+ Version: 0.1.3
4
4
  Summary: CLI-based Personal Knowledge Operating System
5
5
  Author-email: Anirudh T <anirudh200584@gmail.com>
6
6
  License-Expression: MIT
@@ -6,6 +6,7 @@ cli/__init__.py
6
6
  cli/main.py
7
7
  cli/parser.py
8
8
  cli/commands/__init__.py
9
+ cli/commands/ask.py
9
10
  cli/commands/benchmark.py
10
11
  cli/commands/config_cmd.py
11
12
  cli/commands/doctor.py
@@ -15,9 +16,11 @@ cli/commands/init.py
15
16
  cli/commands/logs.py
16
17
  cli/commands/monitor.py
17
18
  cli/commands/plugins.py
19
+ cli/commands/restart.py
18
20
  cli/commands/start.py
19
21
  cli/commands/status.py
20
22
  cli/commands/stop.py
23
+ cli/commands/sync.py
21
24
  cli/commands/version.py
22
25
  cli/commands/workspace.py
23
26
  cli_memory_os.egg-info/PKG-INFO
@@ -54,5 +57,8 @@ storage/schema.sql
54
57
  storage/tech_detector.py
55
58
  tests/test_cli_integration.py
56
59
  tests/test_commands_phase2.py
60
+ tests/test_compose_manager.py
57
61
  tests/test_first_time_user_experience.py
62
+ tests/test_infrastructure_stabilization.py
63
+ tests/test_sync_ask_cli.py
58
64
  tests/test_v1_features.py
@@ -1,4 +1,3 @@
1
- import os
2
1
 
3
2
  class Embedder:
4
3
  _model = None
@@ -2,7 +2,6 @@ import os
2
2
  import time
3
3
  import logging
4
4
  from groq import Groq
5
- from core.vector_store import hybrid_search
6
5
  from storage.graph import GraphStore
7
6
 
8
7
  logger = logging.getLogger("llm")