basic-memory 0.2.21__tar.gz → 0.4.0__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.
Potentially problematic release.
This version of basic-memory might be problematic. Click here for more details.
- {basic_memory-0.2.21 → basic_memory-0.4.0}/.gitignore +1 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/CHANGELOG.md +38 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/PKG-INFO +87 -1
- {basic_memory-0.2.21 → basic_memory-0.4.0}/README.md +86 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/installer/installer.py +1 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/installer/setup.py +8 -15
- {basic_memory-0.2.21 → basic_memory-0.4.0}/pyproject.toml +4 -2
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/__init__.py +1 -1
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/api/app.py +3 -24
- basic_memory-0.4.0/src/basic_memory/cli/app.py +20 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/cli/commands/db.py +1 -1
- basic_memory-0.4.0/src/basic_memory/cli/commands/import_chatgpt.py +255 -0
- basic_memory-0.4.0/src/basic_memory/cli/commands/import_claude_conversations.py +211 -0
- basic_memory-0.4.0/src/basic_memory/cli/commands/import_claude_projects.py +195 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/cli/commands/import_memory_json.py +8 -7
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/cli/commands/mcp.py +2 -2
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/cli/commands/status.py +5 -7
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/cli/commands/sync.py +44 -44
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/cli/main.py +10 -1
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/db.py +21 -28
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/mcp/async_client.py +1 -1
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/mcp/tools/notes.py +4 -1
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/sync/utils.py +1 -4
- basic_memory-0.4.0/tests/cli/test_import_chatgpt.py +276 -0
- basic_memory-0.4.0/tests/cli/test_import_claude_conversations.py +174 -0
- basic_memory-0.4.0/tests/cli/test_import_claude_projects.py +173 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/cli/test_import_memory_json.py +5 -5
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/conftest.py +2 -5
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/mcp/test_tool_notes.py +22 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/uv.lock +1 -1
- basic_memory-0.2.21/src/basic_memory/cli/app.py +0 -3
- {basic_memory-0.2.21 → basic_memory-0.4.0}/.github/workflows/pr-title.yml +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/.github/workflows/release.yml +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/.github/workflows/test.yml +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/.python-version +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/CITATION.cff +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/CODE_OF_CONDUCT.md +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/CONTRIBUTING.md +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/LICENSE +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/Makefile +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/alembic.ini +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/basic-memory.md +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/installer/Basic.icns +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/installer/README.md +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/installer/icon.svg +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/installer/make_icons.sh +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/memory.json +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/scripts/install.sh +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/alembic/README +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/alembic/env.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/alembic/migrations.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/alembic/script.py.mako +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/api/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/api/routers/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/api/routers/knowledge_router.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/api/routers/memory_router.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/api/routers/resource_router.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/api/routers/search_router.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/cli/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/cli/commands/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/config.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/deps.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/file_utils.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/markdown/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/markdown/entity_parser.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/markdown/markdown_processor.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/markdown/plugins.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/markdown/schemas.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/markdown/utils.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/mcp/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/mcp/server.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/mcp/tools/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/mcp/tools/knowledge.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/mcp/tools/memory.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/mcp/tools/search.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/mcp/tools/utils.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/models/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/models/base.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/models/knowledge.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/models/search.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/repository/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/repository/entity_repository.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/repository/observation_repository.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/repository/relation_repository.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/repository/repository.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/repository/search_repository.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/schemas/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/schemas/base.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/schemas/delete.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/schemas/discovery.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/schemas/memory.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/schemas/request.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/schemas/response.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/schemas/search.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/services/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/services/context_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/services/entity_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/services/exceptions.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/services/file_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/services/link_resolver.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/services/search_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/services/service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/sync/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/sync/file_change_scanner.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/sync/sync_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/sync/watch_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/src/basic_memory/utils.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/api/conftest.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/api/test_knowledge_router.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/api/test_memory_router.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/api/test_resource_router.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/api/test_search_router.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/cli/test_status.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/cli/test_sync.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/edit_file_test.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/markdown/__init__.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/markdown/test_entity_parser.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/markdown/test_markdown_plugins.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/markdown/test_markdown_processor.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/markdown/test_observation_edge_cases.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/markdown/test_parser_edge_cases.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/markdown/test_relation_edge_cases.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/markdown/test_task_detection.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/mcp/conftest.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/mcp/test_tool_get_entity.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/mcp/test_tool_knowledge.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/mcp/test_tool_memory.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/mcp/test_tool_search.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/mcp/test_tool_utils.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/repository/test_entity_repository.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/repository/test_observation_repository.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/repository/test_relation_repository.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/repository/test_repository.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/schemas/test_memory_url.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/schemas/test_schemas.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/schemas/test_search.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/services/test_context_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/services/test_entity_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/services/test_file_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/services/test_link_resolver.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/services/test_search_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/sync/test_file_change_scanner.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/sync/test_sync_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/sync/test_watch_service.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/test_basic_memory.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/utils/test_file_utils.py +0 -0
- {basic_memory-0.2.21 → basic_memory-0.4.0}/tests/utils/test_permalink_formatting.py +0 -0
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## v0.4.0 (2025-02-16)
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
- Import chatgpt conversation data ([#9](https://github.com/basicmachines-co/basic-memory/pull/9),
|
|
9
|
+
[`56f47d6`](https://github.com/basicmachines-co/basic-memory/commit/56f47d6812982437f207629e6ac9a82e0e56514e))
|
|
10
|
+
|
|
11
|
+
Co-authored-by: phernandez <phernandez@basicmachines.co>
|
|
12
|
+
|
|
13
|
+
- Import claude.ai data ([#8](https://github.com/basicmachines-co/basic-memory/pull/8),
|
|
14
|
+
[`a15c346`](https://github.com/basicmachines-co/basic-memory/commit/a15c346d5ebd44344b76bad877bb4d1073fcbc3b))
|
|
15
|
+
|
|
16
|
+
Import Claude.ai conversation and project data to basic-memory Markdown format.
|
|
17
|
+
|
|
18
|
+
---------
|
|
19
|
+
|
|
20
|
+
Co-authored-by: phernandez <phernandez@basicmachines.co>
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## v0.3.0 (2025-02-15)
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
- Refactor db schema migrate handling
|
|
28
|
+
([`ca632be`](https://github.com/basicmachines-co/basic-memory/commit/ca632beb6fed5881f4d8ba5ce698bb5bc681e6aa))
|
|
29
|
+
|
|
30
|
+
|
|
4
31
|
## v0.2.21 (2025-02-15)
|
|
5
32
|
|
|
6
33
|
### Bug Fixes
|
|
@@ -8,6 +35,17 @@
|
|
|
8
35
|
- Fix osx installer github action
|
|
9
36
|
([`65ebe5d`](https://github.com/basicmachines-co/basic-memory/commit/65ebe5d19491e5ff047c459d799498ad5dd9cd1a))
|
|
10
37
|
|
|
38
|
+
- Handle memory:// url format in read_note tool
|
|
39
|
+
([`e080373`](https://github.com/basicmachines-co/basic-memory/commit/e0803734e69eeb6c6d7432eea323c7a264cb8347))
|
|
40
|
+
|
|
41
|
+
- Remove create schema from init_db
|
|
42
|
+
([`674dd1f`](https://github.com/basicmachines-co/basic-memory/commit/674dd1fd47be9e60ac17508476c62254991df288))
|
|
43
|
+
|
|
44
|
+
### Features
|
|
45
|
+
|
|
46
|
+
- Set version in var, output version at startup
|
|
47
|
+
([`a91da13`](https://github.com/basicmachines-co/basic-memory/commit/a91da1396710e62587df1284da00137d156fc05e))
|
|
48
|
+
|
|
11
49
|
|
|
12
50
|
## v0.2.20 (2025-02-14)
|
|
13
51
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basic-memory
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Local-first knowledge management combining Zettelkasten with knowledge graphs
|
|
5
5
|
Project-URL: Homepage, https://github.com/basicmachines-co/basic-memory
|
|
6
6
|
Project-URL: Repository, https://github.com/basicmachines-co/basic-memory
|
|
@@ -286,6 +286,92 @@ Basic Memory is built on some key ideas:
|
|
|
286
286
|
- Simple text patterns can capture rich meaning
|
|
287
287
|
- Local-first doesn't mean feature-poor
|
|
288
288
|
|
|
289
|
+
## Importing data
|
|
290
|
+
|
|
291
|
+
Basic memory has cli commands to import data from several formats into Markdown files
|
|
292
|
+
|
|
293
|
+
### Claude.ai
|
|
294
|
+
|
|
295
|
+
First, request an export of your data from your Claude account. The data will be emailed to you in several files,
|
|
296
|
+
including
|
|
297
|
+
`conversations.json` and `projects.json`.
|
|
298
|
+
|
|
299
|
+
Import Claude.ai conversation data
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
basic-memory import claude conversations conversations.json
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The conversations will be turned into Markdown files and placed in the "conversations" folder by default (this can be
|
|
306
|
+
changed with the --folder arg).
|
|
307
|
+
|
|
308
|
+
Example:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
Importing chats from conversations.json...writing to .../basic-memory
|
|
312
|
+
Reading chat data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
|
313
|
+
╭────────────────────────────╮
|
|
314
|
+
│ Import complete! │
|
|
315
|
+
│ │
|
|
316
|
+
│ Imported 307 conversations │
|
|
317
|
+
│ Containing 7769 messages │
|
|
318
|
+
╰────────────────────────────╯
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Next, you can run the `sync` command to import the data into basic-memory
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
basic-memory sync
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
You can also import project data from Claude.ai
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
➜ basic-memory import claude projects
|
|
331
|
+
Importing projects from projects.json...writing to .../basic-memory/projects
|
|
332
|
+
Reading project data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
|
333
|
+
╭────────────────────────────────╮
|
|
334
|
+
│ Import complete! │
|
|
335
|
+
│ │
|
|
336
|
+
│ Imported 101 project documents │
|
|
337
|
+
│ Imported 32 prompt templates │
|
|
338
|
+
╰────────────────────────────────╯
|
|
339
|
+
|
|
340
|
+
Run 'basic-memory sync' to index the new files.
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Chat Gpt
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
➜ basic-memory import chatgpt
|
|
347
|
+
Importing chats from conversations.json...writing to .../basic-memory/conversations
|
|
348
|
+
|
|
349
|
+
Reading chat data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
|
350
|
+
╭────────────────────────────╮
|
|
351
|
+
│ Import complete! │
|
|
352
|
+
│ │
|
|
353
|
+
│ Imported 198 conversations │
|
|
354
|
+
│ Containing 11777 messages │
|
|
355
|
+
╰────────────────────────────╯
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Memory json
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
➜ basic-memory import memory-json
|
|
364
|
+
Importing from memory.json...writing to .../basic-memory
|
|
365
|
+
Reading memory.json... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
|
366
|
+
Creating entities... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
|
367
|
+
╭──────────────────────╮
|
|
368
|
+
│ Import complete! │
|
|
369
|
+
│ │
|
|
370
|
+
│ Created 126 entities │
|
|
371
|
+
│ Added 252 relations │
|
|
372
|
+
╰──────────────────────╯
|
|
373
|
+
```
|
|
374
|
+
|
|
289
375
|
## License
|
|
290
376
|
|
|
291
377
|
AGPL-3.0
|
|
@@ -253,6 +253,92 @@ Basic Memory is built on some key ideas:
|
|
|
253
253
|
- Simple text patterns can capture rich meaning
|
|
254
254
|
- Local-first doesn't mean feature-poor
|
|
255
255
|
|
|
256
|
+
## Importing data
|
|
257
|
+
|
|
258
|
+
Basic memory has cli commands to import data from several formats into Markdown files
|
|
259
|
+
|
|
260
|
+
### Claude.ai
|
|
261
|
+
|
|
262
|
+
First, request an export of your data from your Claude account. The data will be emailed to you in several files,
|
|
263
|
+
including
|
|
264
|
+
`conversations.json` and `projects.json`.
|
|
265
|
+
|
|
266
|
+
Import Claude.ai conversation data
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
basic-memory import claude conversations conversations.json
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
The conversations will be turned into Markdown files and placed in the "conversations" folder by default (this can be
|
|
273
|
+
changed with the --folder arg).
|
|
274
|
+
|
|
275
|
+
Example:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
Importing chats from conversations.json...writing to .../basic-memory
|
|
279
|
+
Reading chat data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
|
280
|
+
╭────────────────────────────╮
|
|
281
|
+
│ Import complete! │
|
|
282
|
+
│ │
|
|
283
|
+
│ Imported 307 conversations │
|
|
284
|
+
│ Containing 7769 messages │
|
|
285
|
+
╰────────────────────────────╯
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Next, you can run the `sync` command to import the data into basic-memory
|
|
289
|
+
|
|
290
|
+
```bash
|
|
291
|
+
basic-memory sync
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
You can also import project data from Claude.ai
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
➜ basic-memory import claude projects
|
|
298
|
+
Importing projects from projects.json...writing to .../basic-memory/projects
|
|
299
|
+
Reading project data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
|
300
|
+
╭────────────────────────────────╮
|
|
301
|
+
│ Import complete! │
|
|
302
|
+
│ │
|
|
303
|
+
│ Imported 101 project documents │
|
|
304
|
+
│ Imported 32 prompt templates │
|
|
305
|
+
╰────────────────────────────────╯
|
|
306
|
+
|
|
307
|
+
Run 'basic-memory sync' to index the new files.
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Chat Gpt
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
➜ basic-memory import chatgpt
|
|
314
|
+
Importing chats from conversations.json...writing to .../basic-memory/conversations
|
|
315
|
+
|
|
316
|
+
Reading chat data... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
|
317
|
+
╭────────────────────────────╮
|
|
318
|
+
│ Import complete! │
|
|
319
|
+
│ │
|
|
320
|
+
│ Imported 198 conversations │
|
|
321
|
+
│ Containing 11777 messages │
|
|
322
|
+
╰────────────────────────────╯
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Memory json
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
➜ basic-memory import memory-json
|
|
331
|
+
Importing from memory.json...writing to .../basic-memory
|
|
332
|
+
Reading memory.json... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
|
333
|
+
Creating entities... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
|
|
334
|
+
╭──────────────────────╮
|
|
335
|
+
│ Import complete! │
|
|
336
|
+
│ │
|
|
337
|
+
│ Created 126 entities │
|
|
338
|
+
│ Added 252 relations │
|
|
339
|
+
╰──────────────────────╯
|
|
340
|
+
```
|
|
341
|
+
|
|
256
342
|
## License
|
|
257
343
|
|
|
258
344
|
AGPL-3.0
|
|
@@ -4,31 +4,24 @@ import sys
|
|
|
4
4
|
# Build options for all platforms
|
|
5
5
|
build_exe_options = {
|
|
6
6
|
"packages": ["json", "pathlib"],
|
|
7
|
-
"excludes": [
|
|
8
|
-
"unittest",
|
|
9
|
-
"pydoc",
|
|
10
|
-
"test"
|
|
11
|
-
],
|
|
7
|
+
"excludes": ["unittest", "pydoc", "test"],
|
|
12
8
|
}
|
|
13
9
|
|
|
14
10
|
# Platform-specific options
|
|
15
11
|
if sys.platform == "win32":
|
|
16
12
|
base = "Win32GUI" # Use GUI base for Windows
|
|
17
|
-
build_exe_options.update(
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
build_exe_options.update(
|
|
14
|
+
{
|
|
15
|
+
"include_msvcr": True,
|
|
16
|
+
}
|
|
17
|
+
)
|
|
20
18
|
target_name = "Basic Memory Installer.exe"
|
|
21
19
|
else: # darwin
|
|
22
20
|
base = None # Don't use GUI base for macOS
|
|
23
21
|
target_name = "Basic Memory Installer"
|
|
24
22
|
|
|
25
23
|
executables = [
|
|
26
|
-
Executable(
|
|
27
|
-
script="installer.py",
|
|
28
|
-
target_name=target_name,
|
|
29
|
-
base=base,
|
|
30
|
-
icon="Basic.icns"
|
|
31
|
-
)
|
|
24
|
+
Executable(script="installer.py", target_name=target_name, base=base, icon="Basic.icns")
|
|
32
25
|
]
|
|
33
26
|
|
|
34
27
|
setup(
|
|
@@ -41,7 +34,7 @@ setup(
|
|
|
41
34
|
"bundle_name": "Basic Memory Installer",
|
|
42
35
|
"iconfile": "Basic.icns",
|
|
43
36
|
"codesign_identity": "-", # Force ad-hoc signing
|
|
44
|
-
}
|
|
37
|
+
},
|
|
45
38
|
},
|
|
46
39
|
executables=executables,
|
|
47
40
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "basic-memory"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "Local-first knowledge management combining Zettelkasten with knowledge graphs"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12.1"
|
|
@@ -84,7 +84,9 @@ pythonVersion = "3.12"
|
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
[tool.semantic_release]
|
|
87
|
-
|
|
87
|
+
version_variables = [
|
|
88
|
+
"src/basic_memory/__init__.py:__version__",
|
|
89
|
+
]
|
|
88
90
|
version_toml = [
|
|
89
91
|
"pyproject.toml:project.version",
|
|
90
92
|
]
|
|
@@ -6,38 +6,17 @@ from fastapi import FastAPI, HTTPException
|
|
|
6
6
|
from fastapi.exception_handlers import http_exception_handler
|
|
7
7
|
from loguru import logger
|
|
8
8
|
|
|
9
|
+
import basic_memory
|
|
9
10
|
from basic_memory import db
|
|
10
11
|
from basic_memory.config import config as app_config
|
|
11
12
|
from basic_memory.api.routers import knowledge, search, memory, resource
|
|
12
|
-
from alembic import command
|
|
13
|
-
from alembic.config import Config
|
|
14
|
-
|
|
15
|
-
from basic_memory.db import DatabaseType
|
|
16
|
-
from basic_memory.repository.search_repository import SearchRepository
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
async def run_migrations(): # pragma: no cover
|
|
20
|
-
"""Run any pending alembic migrations."""
|
|
21
|
-
logger.info("Running database migrations...")
|
|
22
|
-
try:
|
|
23
|
-
config = Config("alembic.ini")
|
|
24
|
-
command.upgrade(config, "head")
|
|
25
|
-
logger.info("Migrations completed successfully")
|
|
26
|
-
|
|
27
|
-
_, session_maker = await db.get_or_create_db(
|
|
28
|
-
app_config.database_path, DatabaseType.FILESYSTEM
|
|
29
|
-
)
|
|
30
|
-
await SearchRepository(session_maker).init_search_index()
|
|
31
|
-
except Exception as e:
|
|
32
|
-
logger.error(f"Error running migrations: {e}")
|
|
33
|
-
raise
|
|
34
13
|
|
|
35
14
|
|
|
36
15
|
@asynccontextmanager
|
|
37
16
|
async def lifespan(app: FastAPI): # pragma: no cover
|
|
38
17
|
"""Lifecycle manager for the FastAPI app."""
|
|
39
|
-
logger.info("Starting Basic Memory API")
|
|
40
|
-
await run_migrations()
|
|
18
|
+
logger.info(f"Starting Basic Memory API {basic_memory.__version__}")
|
|
19
|
+
await db.run_migrations(app_config)
|
|
41
20
|
yield
|
|
42
21
|
logger.info("Shutting down Basic Memory API")
|
|
43
22
|
await db.shutdown_db()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from basic_memory import db
|
|
6
|
+
from basic_memory.config import config
|
|
7
|
+
from basic_memory.utils import setup_logging
|
|
8
|
+
|
|
9
|
+
setup_logging(log_file=".basic-memory/basic-memory-cli.log") # pragma: no cover
|
|
10
|
+
|
|
11
|
+
asyncio.run(db.run_migrations(config))
|
|
12
|
+
|
|
13
|
+
app = typer.Typer(name="basic-memory")
|
|
14
|
+
|
|
15
|
+
import_app = typer.Typer()
|
|
16
|
+
app.add_typer(import_app, name="import")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
claude_app = typer.Typer()
|
|
20
|
+
import_app.add_typer(claude_app, name="claude")
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"""Import command for ChatGPT conversations."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Dict, Any, List, Annotated, Set, Optional
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
from loguru import logger
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn
|
|
14
|
+
|
|
15
|
+
from basic_memory.cli.app import import_app
|
|
16
|
+
from basic_memory.config import config
|
|
17
|
+
from basic_memory.markdown import EntityParser, MarkdownProcessor
|
|
18
|
+
from basic_memory.markdown.schemas import EntityMarkdown, EntityFrontmatter
|
|
19
|
+
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def clean_filename(text: str) -> str:
|
|
24
|
+
"""Convert text to safe filename."""
|
|
25
|
+
clean = "".join(c if c.isalnum() else "-" for c in text.lower()).strip("-")
|
|
26
|
+
return clean
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def format_timestamp(ts: float) -> str:
|
|
30
|
+
"""Format Unix timestamp for display."""
|
|
31
|
+
dt = datetime.fromtimestamp(ts)
|
|
32
|
+
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_message_content(message: Dict[str, Any]) -> str:
|
|
36
|
+
"""Extract clean message content."""
|
|
37
|
+
if not message or "content" not in message:
|
|
38
|
+
return "" # pragma: no cover
|
|
39
|
+
|
|
40
|
+
content = message["content"]
|
|
41
|
+
if content.get("content_type") == "text":
|
|
42
|
+
return "\n".join(content.get("parts", []))
|
|
43
|
+
elif content.get("content_type") == "code":
|
|
44
|
+
return f"```{content.get('language', '')}\n{content.get('text', '')}\n```"
|
|
45
|
+
return "" # pragma: no cover
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def traverse_messages(
|
|
49
|
+
mapping: Dict[str, Any], root_id: Optional[str], seen: Set[str]
|
|
50
|
+
) -> List[Dict[str, Any]]:
|
|
51
|
+
"""Traverse message tree and return messages in order."""
|
|
52
|
+
messages = []
|
|
53
|
+
node = mapping.get(root_id) if root_id else None
|
|
54
|
+
|
|
55
|
+
while node:
|
|
56
|
+
if node["id"] not in seen and node.get("message"):
|
|
57
|
+
seen.add(node["id"])
|
|
58
|
+
messages.append(node["message"])
|
|
59
|
+
|
|
60
|
+
# Follow children
|
|
61
|
+
children = node.get("children", [])
|
|
62
|
+
for child_id in children:
|
|
63
|
+
child_msgs = traverse_messages(mapping, child_id, seen)
|
|
64
|
+
messages.extend(child_msgs)
|
|
65
|
+
|
|
66
|
+
break # Don't follow siblings
|
|
67
|
+
|
|
68
|
+
return messages
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def format_chat_markdown(
|
|
72
|
+
title: str, mapping: Dict[str, Any], root_id: Optional[str], created_at: float, modified_at: float
|
|
73
|
+
) -> str:
|
|
74
|
+
"""Format chat as clean markdown."""
|
|
75
|
+
|
|
76
|
+
# Start with title
|
|
77
|
+
lines = [f"# {title}\n"]
|
|
78
|
+
|
|
79
|
+
# Traverse message tree
|
|
80
|
+
seen_msgs = set()
|
|
81
|
+
messages = traverse_messages(mapping, root_id, seen_msgs)
|
|
82
|
+
|
|
83
|
+
# Format each message
|
|
84
|
+
for msg in messages:
|
|
85
|
+
# Skip hidden messages
|
|
86
|
+
if msg.get("metadata", {}).get("is_visually_hidden_from_conversation"):
|
|
87
|
+
continue
|
|
88
|
+
|
|
89
|
+
# Get author and timestamp
|
|
90
|
+
author = msg["author"]["role"].title()
|
|
91
|
+
ts = format_timestamp(msg["create_time"]) if msg.get("create_time") else ""
|
|
92
|
+
|
|
93
|
+
# Add message header
|
|
94
|
+
lines.append(f"### {author} ({ts})")
|
|
95
|
+
|
|
96
|
+
# Add message content
|
|
97
|
+
content = get_message_content(msg)
|
|
98
|
+
if content:
|
|
99
|
+
lines.append(content)
|
|
100
|
+
|
|
101
|
+
# Add spacing
|
|
102
|
+
lines.append("")
|
|
103
|
+
|
|
104
|
+
return "\n".join(lines)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def format_chat_content(folder: str, conversation: Dict[str, Any]) -> EntityMarkdown:
|
|
108
|
+
"""Convert chat conversation to Basic Memory entity."""
|
|
109
|
+
|
|
110
|
+
# Extract timestamps
|
|
111
|
+
created_at = conversation["create_time"]
|
|
112
|
+
modified_at = conversation["update_time"]
|
|
113
|
+
|
|
114
|
+
root_id = None
|
|
115
|
+
# Find root message
|
|
116
|
+
for node_id, node in conversation["mapping"].items():
|
|
117
|
+
if node.get("parent") is None:
|
|
118
|
+
root_id = node_id
|
|
119
|
+
break
|
|
120
|
+
|
|
121
|
+
# Generate permalink
|
|
122
|
+
date_prefix = datetime.fromtimestamp(created_at).strftime("%Y%m%d")
|
|
123
|
+
clean_title = clean_filename(conversation["title"])
|
|
124
|
+
|
|
125
|
+
# Format content
|
|
126
|
+
content = format_chat_markdown(
|
|
127
|
+
title=conversation["title"],
|
|
128
|
+
mapping=conversation["mapping"],
|
|
129
|
+
root_id=root_id,
|
|
130
|
+
created_at=created_at,
|
|
131
|
+
modified_at=modified_at,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Create entity
|
|
135
|
+
entity = EntityMarkdown(
|
|
136
|
+
frontmatter=EntityFrontmatter(
|
|
137
|
+
metadata={
|
|
138
|
+
"type": "conversation",
|
|
139
|
+
"title": conversation["title"],
|
|
140
|
+
"created": format_timestamp(created_at),
|
|
141
|
+
"modified": format_timestamp(modified_at),
|
|
142
|
+
"permalink": f"{folder}/{date_prefix}-{clean_title}",
|
|
143
|
+
}
|
|
144
|
+
),
|
|
145
|
+
content=content,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return entity
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
async def process_chatgpt_json(
|
|
152
|
+
json_path: Path, folder: str, markdown_processor: MarkdownProcessor
|
|
153
|
+
) -> Dict[str, int]:
|
|
154
|
+
"""Import conversations from ChatGPT JSON format."""
|
|
155
|
+
|
|
156
|
+
with Progress(
|
|
157
|
+
SpinnerColumn(),
|
|
158
|
+
TextColumn("[progress.description]{task.description}"),
|
|
159
|
+
BarColumn(),
|
|
160
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
161
|
+
console=console,
|
|
162
|
+
) as progress:
|
|
163
|
+
read_task = progress.add_task("Reading chat data...", total=None)
|
|
164
|
+
|
|
165
|
+
# Read conversations
|
|
166
|
+
conversations = json.loads(json_path.read_text())
|
|
167
|
+
progress.update(read_task, total=len(conversations))
|
|
168
|
+
|
|
169
|
+
# Process each conversation
|
|
170
|
+
messages_imported = 0
|
|
171
|
+
chats_imported = 0
|
|
172
|
+
|
|
173
|
+
for chat in conversations:
|
|
174
|
+
# Convert to entity
|
|
175
|
+
entity = format_chat_content(folder, chat)
|
|
176
|
+
|
|
177
|
+
# Write file
|
|
178
|
+
file_path = config.home / f"{entity.frontmatter.metadata['permalink']}.md"
|
|
179
|
+
# logger.info(f"Writing file: {file_path.absolute()}")
|
|
180
|
+
await markdown_processor.write_file(file_path, entity)
|
|
181
|
+
|
|
182
|
+
# Count messages
|
|
183
|
+
msg_count = sum(
|
|
184
|
+
1
|
|
185
|
+
for node in chat["mapping"].values()
|
|
186
|
+
if node.get("message")
|
|
187
|
+
and not node.get("message", {})
|
|
188
|
+
.get("metadata", {})
|
|
189
|
+
.get("is_visually_hidden_from_conversation")
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
chats_imported += 1
|
|
193
|
+
messages_imported += msg_count
|
|
194
|
+
progress.update(read_task, advance=1)
|
|
195
|
+
|
|
196
|
+
return {"conversations": chats_imported, "messages": messages_imported}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
async def get_markdown_processor() -> MarkdownProcessor:
|
|
200
|
+
"""Get MarkdownProcessor instance."""
|
|
201
|
+
entity_parser = EntityParser(config.home)
|
|
202
|
+
return MarkdownProcessor(entity_parser)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@import_app.command(name="chatgpt", help="Import conversations from ChatGPT JSON export.")
|
|
206
|
+
def import_chatgpt(
|
|
207
|
+
conversations_json: Annotated[
|
|
208
|
+
Path, typer.Option(..., help="Path to ChatGPT conversations.json file")
|
|
209
|
+
] = Path("conversations.json"),
|
|
210
|
+
folder: Annotated[
|
|
211
|
+
str, typer.Option(help="The folder to place the files in.")
|
|
212
|
+
] = "conversations",
|
|
213
|
+
):
|
|
214
|
+
"""Import chat conversations from ChatGPT JSON format.
|
|
215
|
+
|
|
216
|
+
This command will:
|
|
217
|
+
1. Read the complex tree structure of messages
|
|
218
|
+
2. Convert them to linear markdown conversations
|
|
219
|
+
3. Save as clean, readable markdown files
|
|
220
|
+
|
|
221
|
+
After importing, run 'basic-memory sync' to index the new files.
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
try:
|
|
225
|
+
if conversations_json:
|
|
226
|
+
if not conversations_json.exists():
|
|
227
|
+
typer.echo(f"Error: File not found: {conversations_json}", err=True)
|
|
228
|
+
raise typer.Exit(1)
|
|
229
|
+
|
|
230
|
+
# Get markdown processor
|
|
231
|
+
markdown_processor = asyncio.run(get_markdown_processor())
|
|
232
|
+
|
|
233
|
+
# Process the file
|
|
234
|
+
base_path = config.home / folder
|
|
235
|
+
console.print(f"\nImporting chats from {conversations_json}...writing to {base_path}")
|
|
236
|
+
results = asyncio.run(
|
|
237
|
+
process_chatgpt_json(conversations_json, folder, markdown_processor)
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# Show results
|
|
241
|
+
console.print(
|
|
242
|
+
Panel(
|
|
243
|
+
f"[green]Import complete![/green]\n\n"
|
|
244
|
+
f"Imported {results['conversations']} conversations\n"
|
|
245
|
+
f"Containing {results['messages']} messages",
|
|
246
|
+
expand=False,
|
|
247
|
+
)
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
console.print("\nRun 'basic-memory sync' to index the new files.")
|
|
251
|
+
|
|
252
|
+
except Exception as e:
|
|
253
|
+
logger.error("Import failed")
|
|
254
|
+
typer.echo(f"Error during import: {e}", err=True)
|
|
255
|
+
raise typer.Exit(1)
|