refactor: use FileService in importers for cloud compatibility · basicmachines-co/basic-memory@45ce181 · GitHub
Skip to content

Commit 45ce181

Browse files
phernandezclaude
andcommitted
refactor: use FileService in importers for cloud compatibility
Refactor importers to use FileService for all file operations instead of direct filesystem calls. This enables cloud environments to override file operations via dependency injection (e.g., S3FileService). Changes: - Add `to_markdown_string()` method to MarkdownProcessor for content serialization without file I/O - Update Importer base class to accept FileService and use it for: - `write_entity()` - now uses FileService.write_file() - `ensure_folder_exists()` - now async, uses FileService.ensure_directory() - Fix direct `mkdir()` calls in: - claude_projects_importer.py - memory_json_importer.py - Update deps.py to inject FileService into all importers (v1 and v2) - Update CLI commands to create and pass FileService to importers - Update tests to work with new FileService dependency This follows the pattern used by /knowledge API and SyncService, enabling cloud to override file operations by providing S3FileService via DI. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
1 parent 2744c4b commit 45ce181

14 files changed

Lines changed: 211 additions & 101 deletions

src/basic_memory/cli/commands/import_chatgpt.py

Lines changed: 10 additions & 7 deletions

src/basic_memory/cli/commands/import_claude_conversations.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,29 @@
33
import asyncio
44
import json
55
from pathlib import Path
6-
from typing import Annotated
6+
from typing import Annotated, Tuple
77

88
import typer
99
from basic_memory.cli.app import claude_app
1010
from basic_memory.config import ConfigManager, get_project_config
1111
from basic_memory.importers.claude_conversations_importer import ClaudeConversationsImporter
1212
from basic_memory.markdown import EntityParser, MarkdownProcessor
13+
from basic_memory.services.file_service import FileService
1314
from loguru import logger
1415
from rich.console import Console
1516
from rich.panel import Panel
1617

1718
console = Console()
1819

1920

20-
async def get_markdown_processor() -> MarkdownProcessor:
21-
"""Get MarkdownProcessor instance."""
21+
async def get_importer_dependencies() -> Tuple[MarkdownProcessor, FileService]:
22+
"""Get MarkdownProcessor and FileService instances for importers."""
2223
config = get_project_config()
2324
app_config = ConfigManager().config
2425
entity_parser = EntityParser(config.home)
25-
return MarkdownProcessor(entity_parser, app_config=app_config)
26+
markdown_processor = MarkdownProcessor(entity_parser, app_config=app_config)
27+
file_service = FileService(config.home, markdown_processor, app_config=app_config)
28+
return markdown_processor, file_service
2629

2730

2831
@claude_app.command(name="conversations", help="Import chat conversations from Claude.ai.")
@@ -50,11 +53,11 @@ def import_claude(
5053
typer.echo(f"Error: File not found: {conversations_json}", err=True)
5154
raise typer.Exit(1)
5255

53-
# Get markdown processor
54-
markdown_processor = asyncio.run(get_markdown_processor())
56+
# Get importer dependencies
57+
markdown_processor, file_service = asyncio.run(get_importer_dependencies())
5558

5659
# Create the importer
57-
importer = ClaudeConversationsImporter(config.home, markdown_processor)
60+
importer = ClaudeConversationsImporter(config.home, markdown_processor, file_service)
5861

5962
# Process the file
6063
base_path = config.home / folder

src/basic_memory/cli/commands/import_claude_projects.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,29 @@
33
import asyncio
44
import json
55
from pathlib import Path
6-
from typing import Annotated
6+
from typing import Annotated, Tuple
77

88
import typer
99
from basic_memory.cli.app import claude_app
1010
from basic_memory.config import ConfigManager, get_project_config
1111
from basic_memory.importers.claude_projects_importer import ClaudeProjectsImporter
1212
from basic_memory.markdown import EntityParser, MarkdownProcessor
13+
from basic_memory.services.file_service import FileService
1314
from loguru import logger
1415
from rich.console import Console
1516
from rich.panel import Panel
1617

1718
console = Console()
1819

1920

20-
async def get_markdown_processor() -> MarkdownProcessor:
21-
"""Get MarkdownProcessor instance."""
21+
async def get_importer_dependencies() -> Tuple[MarkdownProcessor, FileService]:
22+
"""Get MarkdownProcessor and FileService instances for importers."""
2223
config = get_project_config()
2324
app_config = ConfigManager().config
2425
entity_parser = EntityParser(config.home)
25-
return MarkdownProcessor(entity_parser, app_config=app_config)
26+
markdown_processor = MarkdownProcessor(entity_parser, app_config=app_config)
27+
file_service = FileService(config.home, markdown_processor, app_config=app_config)
28+
return markdown_processor, file_service
2629

2730

2831
@claude_app.command(name="projects", help="Import projects from Claude.ai.")
@@ -49,11 +52,11 @@ def import_projects(
4952
typer.echo(f"Error: File not found: {projects_json}", err=True)
5053
raise typer.Exit(1)
5154

52-
# Get markdown processor
53-
markdown_processor = asyncio.run(get_markdown_processor())
55+
# Get importer dependencies
56+
markdown_processor, file_service = asyncio.run(get_importer_dependencies())
5457

5558
# Create the importer
56-
importer = ClaudeProjectsImporter(config.home, markdown_processor)
59+
importer = ClaudeProjectsImporter(config.home, markdown_processor, file_service)
5760

5861
# Process the file
5962
base_path = config.home / base_folder if base_folder else config.home

src/basic_memory/cli/commands/import_memory_json.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,29 @@
33
import asyncio
44
import json
55
from pathlib import Path
6-
from typing import Annotated
6+
from typing import Annotated, Tuple
77

88
import typer
99
from basic_memory.cli.app import import_app
1010
from basic_memory.config import ConfigManager, get_project_config
1111
from basic_memory.importers.memory_json_importer import MemoryJsonImporter
1212
from basic_memory.markdown import EntityParser, MarkdownProcessor
13+
from basic_memory.services.file_service import FileService
1314
from loguru import logger
1415
from rich.console import Console
1516
from rich.panel import Panel
1617

1718
console = Console()
1819

1920

20-
async def get_markdown_processor() -> MarkdownProcessor:
21-
"""Get MarkdownProcessor instance."""
21+
async def get_importer_dependencies() -> Tuple[MarkdownProcessor, FileService]:
22+
"""Get MarkdownProcessor and FileService instances for importers."""
2223
config = get_project_config()
2324
app_config = ConfigManager().config
2425
entity_parser = EntityParser(config.home)
25-
return MarkdownProcessor(entity_parser, app_config=app_config)
26+
markdown_processor = MarkdownProcessor(entity_parser, app_config=app_config)
27+
file_service = FileService(config.home, markdown_processor, app_config=app_config)
28+
return markdown_processor, file_service
2629

2730

2831
@import_app.command()
@@ -48,11 +51,11 @@ def memory_json(
4851

4952
config = get_project_config()
5053
try:
51-
# Get markdown processor
52-
markdown_processor = asyncio.run(get_markdown_processor())
54+
# Get importer dependencies
55+
markdown_processor, file_service = asyncio.run(get_importer_dependencies())
5356

5457
# Create the importer
55-
importer = MemoryJsonImporter(config.home, markdown_processor)
58+
importer = MemoryJsonImporter(config.home, markdown_processor, file_service)
5659

5760
# Process the file
5861
base_path = config.home if not destination_folder else config.home / destination_folder

src/basic_memory/deps.py

Lines changed: 35 additions & 19 deletions

0 commit comments

Comments
 (0)