remdb 0.3.7__py3-none-any.whl

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 (187) hide show
  1. rem/__init__.py +2 -0
  2. rem/agentic/README.md +650 -0
  3. rem/agentic/__init__.py +39 -0
  4. rem/agentic/agents/README.md +155 -0
  5. rem/agentic/agents/__init__.py +8 -0
  6. rem/agentic/context.py +148 -0
  7. rem/agentic/context_builder.py +329 -0
  8. rem/agentic/mcp/__init__.py +0 -0
  9. rem/agentic/mcp/tool_wrapper.py +107 -0
  10. rem/agentic/otel/__init__.py +5 -0
  11. rem/agentic/otel/setup.py +151 -0
  12. rem/agentic/providers/phoenix.py +674 -0
  13. rem/agentic/providers/pydantic_ai.py +572 -0
  14. rem/agentic/query.py +117 -0
  15. rem/agentic/query_helper.py +89 -0
  16. rem/agentic/schema.py +396 -0
  17. rem/agentic/serialization.py +245 -0
  18. rem/agentic/tools/__init__.py +5 -0
  19. rem/agentic/tools/rem_tools.py +231 -0
  20. rem/api/README.md +420 -0
  21. rem/api/main.py +324 -0
  22. rem/api/mcp_router/prompts.py +182 -0
  23. rem/api/mcp_router/resources.py +536 -0
  24. rem/api/mcp_router/server.py +213 -0
  25. rem/api/mcp_router/tools.py +584 -0
  26. rem/api/routers/auth.py +229 -0
  27. rem/api/routers/chat/__init__.py +5 -0
  28. rem/api/routers/chat/completions.py +281 -0
  29. rem/api/routers/chat/json_utils.py +76 -0
  30. rem/api/routers/chat/models.py +124 -0
  31. rem/api/routers/chat/streaming.py +185 -0
  32. rem/auth/README.md +258 -0
  33. rem/auth/__init__.py +26 -0
  34. rem/auth/middleware.py +100 -0
  35. rem/auth/providers/__init__.py +13 -0
  36. rem/auth/providers/base.py +376 -0
  37. rem/auth/providers/google.py +163 -0
  38. rem/auth/providers/microsoft.py +237 -0
  39. rem/cli/README.md +455 -0
  40. rem/cli/__init__.py +8 -0
  41. rem/cli/commands/README.md +126 -0
  42. rem/cli/commands/__init__.py +3 -0
  43. rem/cli/commands/ask.py +566 -0
  44. rem/cli/commands/configure.py +497 -0
  45. rem/cli/commands/db.py +493 -0
  46. rem/cli/commands/dreaming.py +324 -0
  47. rem/cli/commands/experiments.py +1302 -0
  48. rem/cli/commands/mcp.py +66 -0
  49. rem/cli/commands/process.py +245 -0
  50. rem/cli/commands/schema.py +183 -0
  51. rem/cli/commands/serve.py +106 -0
  52. rem/cli/dreaming.py +363 -0
  53. rem/cli/main.py +96 -0
  54. rem/config.py +237 -0
  55. rem/mcp_server.py +41 -0
  56. rem/models/core/__init__.py +49 -0
  57. rem/models/core/core_model.py +64 -0
  58. rem/models/core/engram.py +333 -0
  59. rem/models/core/experiment.py +628 -0
  60. rem/models/core/inline_edge.py +132 -0
  61. rem/models/core/rem_query.py +243 -0
  62. rem/models/entities/__init__.py +43 -0
  63. rem/models/entities/file.py +57 -0
  64. rem/models/entities/image_resource.py +88 -0
  65. rem/models/entities/message.py +35 -0
  66. rem/models/entities/moment.py +123 -0
  67. rem/models/entities/ontology.py +191 -0
  68. rem/models/entities/ontology_config.py +131 -0
  69. rem/models/entities/resource.py +95 -0
  70. rem/models/entities/schema.py +87 -0
  71. rem/models/entities/user.py +85 -0
  72. rem/py.typed +0 -0
  73. rem/schemas/README.md +507 -0
  74. rem/schemas/__init__.py +6 -0
  75. rem/schemas/agents/README.md +92 -0
  76. rem/schemas/agents/core/moment-builder.yaml +178 -0
  77. rem/schemas/agents/core/rem-query-agent.yaml +226 -0
  78. rem/schemas/agents/core/resource-affinity-assessor.yaml +99 -0
  79. rem/schemas/agents/core/simple-assistant.yaml +19 -0
  80. rem/schemas/agents/core/user-profile-builder.yaml +163 -0
  81. rem/schemas/agents/examples/contract-analyzer.yaml +317 -0
  82. rem/schemas/agents/examples/contract-extractor.yaml +134 -0
  83. rem/schemas/agents/examples/cv-parser.yaml +263 -0
  84. rem/schemas/agents/examples/hello-world.yaml +37 -0
  85. rem/schemas/agents/examples/query.yaml +54 -0
  86. rem/schemas/agents/examples/simple.yaml +21 -0
  87. rem/schemas/agents/examples/test.yaml +29 -0
  88. rem/schemas/agents/rem.yaml +128 -0
  89. rem/schemas/evaluators/hello-world/default.yaml +77 -0
  90. rem/schemas/evaluators/rem/faithfulness.yaml +219 -0
  91. rem/schemas/evaluators/rem/lookup-correctness.yaml +182 -0
  92. rem/schemas/evaluators/rem/retrieval-precision.yaml +199 -0
  93. rem/schemas/evaluators/rem/retrieval-recall.yaml +211 -0
  94. rem/schemas/evaluators/rem/search-correctness.yaml +192 -0
  95. rem/services/__init__.py +16 -0
  96. rem/services/audio/INTEGRATION.md +308 -0
  97. rem/services/audio/README.md +376 -0
  98. rem/services/audio/__init__.py +15 -0
  99. rem/services/audio/chunker.py +354 -0
  100. rem/services/audio/transcriber.py +259 -0
  101. rem/services/content/README.md +1269 -0
  102. rem/services/content/__init__.py +5 -0
  103. rem/services/content/providers.py +801 -0
  104. rem/services/content/service.py +676 -0
  105. rem/services/dreaming/README.md +230 -0
  106. rem/services/dreaming/__init__.py +53 -0
  107. rem/services/dreaming/affinity_service.py +336 -0
  108. rem/services/dreaming/moment_service.py +264 -0
  109. rem/services/dreaming/ontology_service.py +54 -0
  110. rem/services/dreaming/user_model_service.py +297 -0
  111. rem/services/dreaming/utils.py +39 -0
  112. rem/services/embeddings/__init__.py +11 -0
  113. rem/services/embeddings/api.py +120 -0
  114. rem/services/embeddings/worker.py +421 -0
  115. rem/services/fs/README.md +662 -0
  116. rem/services/fs/__init__.py +62 -0
  117. rem/services/fs/examples.py +206 -0
  118. rem/services/fs/examples_paths.py +204 -0
  119. rem/services/fs/git_provider.py +935 -0
  120. rem/services/fs/local_provider.py +760 -0
  121. rem/services/fs/parsing-hooks-examples.md +172 -0
  122. rem/services/fs/paths.py +276 -0
  123. rem/services/fs/provider.py +460 -0
  124. rem/services/fs/s3_provider.py +1042 -0
  125. rem/services/fs/service.py +186 -0
  126. rem/services/git/README.md +1075 -0
  127. rem/services/git/__init__.py +17 -0
  128. rem/services/git/service.py +469 -0
  129. rem/services/phoenix/EXPERIMENT_DESIGN.md +1146 -0
  130. rem/services/phoenix/README.md +453 -0
  131. rem/services/phoenix/__init__.py +46 -0
  132. rem/services/phoenix/client.py +686 -0
  133. rem/services/phoenix/config.py +88 -0
  134. rem/services/phoenix/prompt_labels.py +477 -0
  135. rem/services/postgres/README.md +575 -0
  136. rem/services/postgres/__init__.py +23 -0
  137. rem/services/postgres/migration_service.py +427 -0
  138. rem/services/postgres/pydantic_to_sqlalchemy.py +232 -0
  139. rem/services/postgres/register_type.py +352 -0
  140. rem/services/postgres/repository.py +337 -0
  141. rem/services/postgres/schema_generator.py +379 -0
  142. rem/services/postgres/service.py +802 -0
  143. rem/services/postgres/sql_builder.py +354 -0
  144. rem/services/rem/README.md +304 -0
  145. rem/services/rem/__init__.py +23 -0
  146. rem/services/rem/exceptions.py +71 -0
  147. rem/services/rem/executor.py +293 -0
  148. rem/services/rem/parser.py +145 -0
  149. rem/services/rem/queries.py +196 -0
  150. rem/services/rem/query.py +371 -0
  151. rem/services/rem/service.py +527 -0
  152. rem/services/session/README.md +374 -0
  153. rem/services/session/__init__.py +6 -0
  154. rem/services/session/compression.py +360 -0
  155. rem/services/session/reload.py +77 -0
  156. rem/settings.py +1235 -0
  157. rem/sql/002_install_models.sql +1068 -0
  158. rem/sql/background_indexes.sql +42 -0
  159. rem/sql/install_models.sql +1038 -0
  160. rem/sql/migrations/001_install.sql +503 -0
  161. rem/sql/migrations/002_install_models.sql +1202 -0
  162. rem/utils/AGENTIC_CHUNKING.md +597 -0
  163. rem/utils/README.md +583 -0
  164. rem/utils/__init__.py +43 -0
  165. rem/utils/agentic_chunking.py +622 -0
  166. rem/utils/batch_ops.py +343 -0
  167. rem/utils/chunking.py +108 -0
  168. rem/utils/clip_embeddings.py +276 -0
  169. rem/utils/dict_utils.py +98 -0
  170. rem/utils/embeddings.py +423 -0
  171. rem/utils/examples/embeddings_example.py +305 -0
  172. rem/utils/examples/sql_types_example.py +202 -0
  173. rem/utils/markdown.py +16 -0
  174. rem/utils/model_helpers.py +236 -0
  175. rem/utils/schema_loader.py +336 -0
  176. rem/utils/sql_types.py +348 -0
  177. rem/utils/user_id.py +81 -0
  178. rem/utils/vision.py +330 -0
  179. rem/workers/README.md +506 -0
  180. rem/workers/__init__.py +5 -0
  181. rem/workers/dreaming.py +502 -0
  182. rem/workers/engram_processor.py +312 -0
  183. rem/workers/sqs_file_processor.py +193 -0
  184. remdb-0.3.7.dist-info/METADATA +1473 -0
  185. remdb-0.3.7.dist-info/RECORD +187 -0
  186. remdb-0.3.7.dist-info/WHEEL +4 -0
  187. remdb-0.3.7.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,497 @@
1
+ """
2
+ REM configuration wizard.
3
+
4
+ Interactive setup wizard for configuring REM on a new installation.
5
+
6
+ Usage:
7
+ rem configure # Interactive wizard
8
+ rem configure --install # Run wizard + install database tables
9
+ rem configure --show # Show current configuration
10
+ rem configure --edit # Open config file in editor
11
+ """
12
+
13
+ import os
14
+ import subprocess
15
+ from pathlib import Path
16
+
17
+ import click
18
+ from loguru import logger
19
+
20
+ from rem.config import (
21
+ config_exists,
22
+ ensure_config_dir,
23
+ get_config_path,
24
+ get_default_config,
25
+ load_config,
26
+ save_config,
27
+ validate_config,
28
+ )
29
+
30
+
31
+ def prompt_postgres_config(use_defaults: bool = False) -> dict:
32
+ """
33
+ Prompt user for PostgreSQL configuration.
34
+
35
+ Args:
36
+ use_defaults: If True, use all default values without prompting
37
+
38
+ Returns:
39
+ PostgreSQL configuration dictionary
40
+ """
41
+ click.echo("\n" + "=" * 60)
42
+ click.echo("PostgreSQL Configuration")
43
+ click.echo("=" * 60)
44
+
45
+ # Parse existing config if available
46
+ existing_conn = os.environ.get(
47
+ "POSTGRES__CONNECTION_STRING", "postgresql://rem:rem@localhost:5051/rem"
48
+ )
49
+
50
+ # Default values
51
+ host = "localhost"
52
+ port = 5050
53
+ database = "rem"
54
+ username = "rem"
55
+ password = "rem"
56
+ pool_min_size = 5
57
+ pool_max_size = 20
58
+
59
+ if use_defaults:
60
+ click.echo("\nUsing default PostgreSQL configuration:")
61
+ click.echo(f" Host: {host}")
62
+ click.echo(f" Port: {port}")
63
+ click.echo(f" Database: {database}")
64
+ click.echo(f" Username: {username}")
65
+ click.echo(f" Pool: {pool_min_size}-{pool_max_size} connections")
66
+ else:
67
+ # Prompt for components
68
+ click.echo(
69
+ "\nEnter PostgreSQL connection details (press Enter to use default):"
70
+ )
71
+ click.echo("Default: Package users on port 5051 (docker compose -f docker-compose.prebuilt.yml up -d)")
72
+ click.echo("Developers: Change port to 5050 if using docker-compose.yml (local build)")
73
+ click.echo("Custom DB: Enter your own host/port below")
74
+
75
+ host = click.prompt("Host", default=host)
76
+ port = click.prompt("Port", default=port, type=int)
77
+ database = click.prompt("Database name", default=database)
78
+ username = click.prompt("Username", default=username)
79
+ password = click.prompt("Password", default=password, hide_input=True)
80
+
81
+ # Additional pool settings
82
+ click.echo("\nConnection pool settings:")
83
+ pool_min_size = click.prompt("Pool minimum size", default=pool_min_size, type=int)
84
+ pool_max_size = click.prompt("Pool maximum size", default=pool_max_size, type=int)
85
+
86
+ # Build connection string
87
+ connection_string = f"postgresql://{username}:{password}@{host}:{port}/{database}"
88
+
89
+ return {
90
+ "connection_string": connection_string,
91
+ "pool_min_size": pool_min_size,
92
+ "pool_max_size": pool_max_size,
93
+ }
94
+
95
+
96
+ def prompt_llm_config(use_defaults: bool = False) -> dict:
97
+ """
98
+ Prompt user for LLM provider configuration.
99
+
100
+ Args:
101
+ use_defaults: If True, use all default values without prompting
102
+
103
+ Returns:
104
+ LLM configuration dictionary
105
+ """
106
+ click.echo("\n" + "=" * 60)
107
+ click.echo("LLM Provider Configuration")
108
+ click.echo("=" * 60)
109
+
110
+ config = {}
111
+
112
+ # Default values
113
+ default_model = "anthropic:claude-sonnet-4-5-20250929"
114
+ default_temperature = 0.5
115
+
116
+ if use_defaults:
117
+ click.echo("\nUsing default LLM configuration:")
118
+ click.echo(f" Model: {default_model}")
119
+ click.echo(f" Temperature: {default_temperature}")
120
+ click.echo(" API Keys: Not configured (set via environment variables)")
121
+ config["default_model"] = default_model
122
+ config["default_temperature"] = default_temperature
123
+ else:
124
+ # Default model
125
+ click.echo("\nDefault LLM model (format: provider:model-id)")
126
+ click.echo("Examples:")
127
+ click.echo(" - anthropic:claude-sonnet-4-5-20250929")
128
+ click.echo(" - openai:gpt-4o")
129
+ click.echo(" - openai:gpt-4o-mini")
130
+
131
+ config["default_model"] = click.prompt(
132
+ "Default model", default=default_model
133
+ )
134
+
135
+ # Temperature
136
+ config["default_temperature"] = click.prompt(
137
+ "Default temperature (0.0-1.0)", default=default_temperature, type=float
138
+ )
139
+
140
+ # API keys
141
+ click.echo("\nAPI Keys (optional - leave empty to skip):")
142
+
143
+ openai_key = click.prompt("OpenAI API key", default="", show_default=False)
144
+ if openai_key:
145
+ config["openai_api_key"] = openai_key
146
+
147
+ anthropic_key = click.prompt("Anthropic API key", default="", show_default=False)
148
+ if anthropic_key:
149
+ config["anthropic_api_key"] = anthropic_key
150
+
151
+ return config
152
+
153
+
154
+ def prompt_s3_config(use_defaults: bool = False) -> dict:
155
+ """
156
+ Prompt user for S3 storage configuration.
157
+
158
+ Args:
159
+ use_defaults: If True, skip S3 configuration (optional feature)
160
+
161
+ Returns:
162
+ S3 configuration dictionary
163
+ """
164
+ click.echo("\n" + "=" * 60)
165
+ click.echo("S3 Storage Configuration")
166
+ click.echo("=" * 60)
167
+
168
+ config = {}
169
+
170
+ if use_defaults:
171
+ click.echo("\nSkipping S3 configuration (optional - configure later if needed)")
172
+ return {}
173
+
174
+ click.echo("\nS3 storage is used for file uploads and processed content.")
175
+ use_s3 = click.confirm("Configure S3 storage?", default=False)
176
+
177
+ if not use_s3:
178
+ return {}
179
+
180
+ config["bucket_name"] = click.prompt("S3 bucket name", default="rem-storage")
181
+ config["region"] = click.prompt("AWS region", default="us-east-1")
182
+
183
+ # Optional: MinIO or LocalStack endpoint
184
+ use_custom_endpoint = click.confirm(
185
+ "Use custom S3 endpoint (MinIO, LocalStack)?", default=False
186
+ )
187
+ if use_custom_endpoint:
188
+ config["endpoint_url"] = click.prompt(
189
+ "Endpoint URL", default="http://localhost:9000"
190
+ )
191
+ config["access_key_id"] = click.prompt("Access key ID", default="minioadmin")
192
+ config["secret_access_key"] = click.prompt(
193
+ "Secret access key", default="minioadmin", hide_input=True
194
+ )
195
+
196
+ return config
197
+
198
+
199
+ def prompt_additional_env_vars(use_defaults: bool = False) -> dict:
200
+ """
201
+ Prompt user for additional environment variables.
202
+
203
+ Args:
204
+ use_defaults: If True, skip additional env vars (optional feature)
205
+
206
+ Returns:
207
+ Dictionary of custom environment variables
208
+ """
209
+ click.echo("\n" + "=" * 60)
210
+ click.echo("Additional Environment Variables")
211
+ click.echo("=" * 60)
212
+
213
+ env_vars: dict[str, str] = {}
214
+
215
+ if use_defaults:
216
+ click.echo("\nSkipping additional environment variables (configure later if needed)")
217
+ return env_vars
218
+
219
+ add_env = click.confirm(
220
+ "Add custom environment variables?", default=False
221
+ )
222
+
223
+ if not add_env:
224
+ return env_vars
225
+
226
+ click.echo("\nEnter environment variables (empty name to finish):")
227
+ while True:
228
+ name = click.prompt("Variable name", default="", show_default=False)
229
+ if not name:
230
+ break
231
+
232
+ value = click.prompt(f"Value for {name}")
233
+ env_vars[name] = value
234
+
235
+ return env_vars
236
+
237
+
238
+ @click.command("configure")
239
+ @click.option(
240
+ "--install",
241
+ is_flag=True,
242
+ help="Install database tables after configuration",
243
+ )
244
+ @click.option(
245
+ "--claude-desktop",
246
+ is_flag=True,
247
+ help="Register REM MCP server with Claude Desktop",
248
+ )
249
+ @click.option(
250
+ "--show",
251
+ is_flag=True,
252
+ help="Show current configuration",
253
+ )
254
+ @click.option(
255
+ "--edit",
256
+ is_flag=True,
257
+ help="Open configuration file in editor",
258
+ )
259
+ @click.option(
260
+ "--yes",
261
+ "-y",
262
+ is_flag=True,
263
+ help="Accept all defaults without prompting (non-interactive mode)",
264
+ )
265
+ def configure_command(install: bool, claude_desktop: bool, show: bool, edit: bool, yes: bool):
266
+ """
267
+ Configure REM installation.
268
+
269
+ Interactive wizard for setting up PostgreSQL, LLM providers, S3, and more.
270
+ Configuration is saved to ~/.rem/config.yaml and merged with environment variables.
271
+
272
+ Examples:
273
+ rem configure # Run interactive wizard
274
+ rem configure --yes # Accept all defaults (non-interactive)
275
+ rem configure --yes --install # Quick setup with defaults + install tables
276
+ rem configure --install # Run wizard + install database tables
277
+ rem configure --show # Show current configuration
278
+ rem configure --edit # Open config in $EDITOR
279
+ """
280
+ config_path = get_config_path()
281
+
282
+ # Show current configuration
283
+ if show:
284
+ if not config_exists():
285
+ click.echo(f"No configuration file found at {config_path}")
286
+ click.echo("Run 'rem configure' to create one.")
287
+ return
288
+
289
+ config = load_config()
290
+ click.echo(f"\nConfiguration file: {config_path}")
291
+ click.echo("=" * 60)
292
+
293
+ import yaml
294
+
295
+ click.echo(yaml.dump(config, default_flow_style=False, sort_keys=False))
296
+ return
297
+
298
+ # Edit configuration file
299
+ if edit:
300
+ if not config_exists():
301
+ click.echo(f"No configuration file found at {config_path}")
302
+ click.echo("Run 'rem configure' to create one first.")
303
+ return
304
+
305
+ editor = os.environ.get("EDITOR", "vim")
306
+ try:
307
+ subprocess.run([editor, str(config_path)], check=True)
308
+ click.echo(f"\nConfiguration saved to {config_path}")
309
+ except Exception as e:
310
+ click.echo(f"Error opening editor: {e}", err=True)
311
+ click.echo(f"Manually edit: {config_path}")
312
+ return
313
+
314
+ # Run interactive wizard
315
+ click.echo("\n" + "=" * 60)
316
+ click.echo("REM Configuration Wizard")
317
+ click.echo("=" * 60)
318
+
319
+ if yes:
320
+ click.echo("\nRunning in non-interactive mode (--yes flag)")
321
+ click.echo("Using default configuration values...")
322
+ else:
323
+ click.echo("\nThis wizard will help you configure REM for first-time use.")
324
+
325
+ click.echo(f"Configuration will be saved to: {config_path}")
326
+
327
+ # Check if config already exists
328
+ if config_exists():
329
+ click.echo(f"\nConfiguration file already exists at {config_path}")
330
+ if yes:
331
+ # In non-interactive mode, skip configuration creation
332
+ click.echo("Skipping configuration creation (file already exists)")
333
+ config = None # Will not save/validate
334
+ elif not click.confirm("Overwrite existing configuration?", default=False):
335
+ click.echo("Configuration unchanged.")
336
+ config = None # Will not save/validate
337
+ else:
338
+ # User confirmed overwrite - create new config
339
+ config = {}
340
+ config["postgres"] = prompt_postgres_config(use_defaults=yes)
341
+ config["llm"] = prompt_llm_config(use_defaults=yes)
342
+ s3_config = prompt_s3_config(use_defaults=yes)
343
+ if s3_config:
344
+ config["s3"] = s3_config
345
+ env_vars = prompt_additional_env_vars(use_defaults=yes)
346
+ if env_vars:
347
+ config["env"] = env_vars
348
+ else:
349
+ # No existing config - create new one
350
+ config = {}
351
+ config["postgres"] = prompt_postgres_config(use_defaults=yes)
352
+ config["llm"] = prompt_llm_config(use_defaults=yes)
353
+ s3_config = prompt_s3_config(use_defaults=yes)
354
+ if s3_config:
355
+ config["s3"] = s3_config
356
+ env_vars = prompt_additional_env_vars(use_defaults=yes)
357
+ if env_vars:
358
+ config["env"] = env_vars
359
+
360
+ # Validate and save configuration (only if we created one)
361
+ if config is not None:
362
+ click.echo("\n" + "=" * 60)
363
+ click.echo("Validating Configuration")
364
+ click.echo("=" * 60)
365
+
366
+ errors = validate_config(config)
367
+ if errors:
368
+ click.echo("\nConfiguration validation failed:")
369
+ for error in errors:
370
+ click.echo(f" ❌ {error}", err=True)
371
+ click.echo("\nPlease fix these errors and try again.")
372
+ return
373
+
374
+ click.echo("✅ Configuration is valid")
375
+
376
+ # Save configuration
377
+ try:
378
+ save_config(config)
379
+ click.echo(f"\n✅ Configuration saved to {config_path}")
380
+ except Exception as e:
381
+ click.echo(f"\n❌ Failed to save configuration: {e}", err=True)
382
+ return
383
+ else:
384
+ # Load existing config for use in install step
385
+ config = load_config() if config_exists() else {}
386
+
387
+ # Install database tables if requested
388
+ if install:
389
+ click.echo("\n" + "=" * 60)
390
+ click.echo("Database Installation")
391
+ click.echo("=" * 60)
392
+
393
+ if yes or click.confirm("\nInstall database tables?", default=True):
394
+ try:
395
+ # Import here to ensure config is loaded first
396
+ from rem.config import merge_config_to_env
397
+
398
+ # Merge config into environment before running migrations
399
+ merge_config_to_env(config)
400
+
401
+ click.echo("\nRunning database migrations...")
402
+
403
+ # Import and run migrations
404
+ from rem.cli.commands.db import migrate
405
+
406
+ # Create a context for the command and invoke it
407
+ ctx = click.Context(migrate)
408
+ ctx.invoke(migrate, install_only=False, models_only=False,
409
+ background_indexes=False, connection=None,
410
+ sql_dir=Path("rem/sql"))
411
+
412
+ click.echo("✅ Database installation complete")
413
+
414
+ except Exception as e:
415
+ click.echo(f"\n❌ Database installation failed: {e}", err=True)
416
+ click.echo("\nYou can manually install tables later with:")
417
+ click.echo(" rem db migrate")
418
+
419
+ # Register with Claude Desktop if requested
420
+ if claude_desktop:
421
+ click.echo("\n" + "=" * 60)
422
+ click.echo("Claude Desktop Integration")
423
+ click.echo("=" * 60)
424
+
425
+ try:
426
+ import shutil
427
+ from pathlib import Path
428
+ from fastmcp.mcp_config import update_config_file, StdioMCPServer
429
+
430
+ # Find Claude Desktop config path
431
+ if os.name == "nt": # Windows
432
+ config_dir = Path.home() / "AppData/Roaming/Claude"
433
+ elif os.name == "posix":
434
+ if Path.home() / "Library/Application Support/Claude":
435
+ config_dir = Path.home() / "Library/Application Support/Claude"
436
+ else:
437
+ config_dir = Path.home() / ".config/Claude"
438
+ else:
439
+ config_dir = Path.home() / ".config/Claude"
440
+
441
+ config_path = config_dir / "claude_desktop_config.json"
442
+
443
+ # Find rem executable
444
+ rem_executable = shutil.which("rem")
445
+ if not rem_executable:
446
+ click.echo("❌ 'rem' command not found in PATH", err=True)
447
+ return
448
+
449
+ # Create server config with all necessary env vars
450
+ env_vars = {
451
+ "POSTGRES__CONNECTION_STRING": config.get("postgres", {}).get("connection_string", "")
452
+ }
453
+
454
+ # Add LLM API keys if present
455
+ llm_config = config.get("llm", {})
456
+ if llm_config.get("openai_api_key"):
457
+ env_vars["LLM__OPENAI_API_KEY"] = llm_config["openai_api_key"]
458
+ if llm_config.get("anthropic_api_key"):
459
+ env_vars["LLM__ANTHROPIC_API_KEY"] = llm_config["anthropic_api_key"]
460
+
461
+ server_config = StdioMCPServer(
462
+ command=rem_executable,
463
+ args=["mcp"],
464
+ env=env_vars
465
+ )
466
+
467
+ # Update config file using FastMCP utility
468
+ update_config_file(config_path, "rem", server_config)
469
+
470
+ click.echo(f"✅ Registered REM MCP server with Claude Desktop")
471
+ click.echo(f"Config: {config_path}")
472
+ click.echo("\nRestart Claude Desktop to use the REM server.")
473
+
474
+ except Exception as e:
475
+ click.echo(f"❌ Failed: {e}", err=True)
476
+
477
+ # Next steps
478
+ click.echo("\n" + "=" * 60)
479
+ click.echo("Next Steps")
480
+ click.echo("=" * 60)
481
+ click.echo("\n1. Verify configuration:")
482
+ click.echo(" rem configure --show")
483
+ click.echo("\n2. Edit configuration (if needed):")
484
+ click.echo(" rem configure --edit")
485
+ if not install:
486
+ click.echo("\n3. Install database tables:")
487
+ click.echo(" rem db migrate")
488
+ click.echo("\n4. Start the API server:")
489
+ click.echo(" rem dev run-server")
490
+ click.echo("\n5. Test the installation:")
491
+ click.echo(" rem ask 'Hello, REM!'")
492
+ click.echo()
493
+
494
+
495
+ def register_command(cli_group):
496
+ """Register the configure command."""
497
+ cli_group.add_command(configure_command)