deja-cli 0.1.0__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.
- deja/__init__.py +0 -0
- deja/config.py +127 -0
- deja/core/__init__.py +0 -0
- deja/core/extractor.py +135 -0
- deja/core/reflection.py +364 -0
- deja/core/scheduler.py +65 -0
- deja/core/store.py +1413 -0
- deja/ingest/__init__.py +0 -0
- deja/ingest/watchers/__init__.py +0 -0
- deja/ingest/watchers/base.py +143 -0
- deja/ingest/watchers/claude_code.py +62 -0
- deja/ingest/watchers/codex_cli.py +95 -0
- deja/ingest/watchers/gemini_cli.py +96 -0
- deja/interfaces/__init__.py +0 -0
- deja/interfaces/cli.py +1967 -0
- deja/interfaces/mcp_server.py +96 -0
- deja/interfaces/web.py +104 -0
- deja/interfaces/web_ui/index.html +614 -0
- deja/llm/__init__.py +0 -0
- deja/llm/base.py +34 -0
- deja/llm/embedding.py +45 -0
- deja/llm/factory.py +90 -0
- deja/llm/providers/__init__.py +0 -0
- deja/llm/providers/anthropic.py +21 -0
- deja/llm/providers/ollama.py +30 -0
- deja/main.py +4 -0
- deja_cli-0.1.0.dist-info/METADATA +100 -0
- deja_cli-0.1.0.dist-info/RECORD +31 -0
- deja_cli-0.1.0.dist-info/WHEEL +4 -0
- deja_cli-0.1.0.dist-info/entry_points.txt +3 -0
- deja_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
deja/llm/factory.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Literal, Optional
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from deja.config import Config, LLMProviderConfig
|
|
9
|
+
from deja.llm.base import LLMAdapter
|
|
10
|
+
from deja.llm.embedding import EmbeddingAdapter, OllamaEmbeddingAdapter
|
|
11
|
+
from deja.llm.providers.anthropic import AnthropicAdapter
|
|
12
|
+
from deja.llm.providers.ollama import OllamaAdapter
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def _ollama_available(base_url: str) -> bool:
|
|
16
|
+
try:
|
|
17
|
+
async with httpx.AsyncClient(timeout=2.0) as client:
|
|
18
|
+
response = await client.get(f"{base_url.rstrip('/')}/api/tags")
|
|
19
|
+
return response.status_code == 200
|
|
20
|
+
except Exception:
|
|
21
|
+
return False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _build_adapter(provider_cfg: LLMProviderConfig) -> LLMAdapter:
|
|
25
|
+
if provider_cfg.provider == "ollama":
|
|
26
|
+
return OllamaAdapter(
|
|
27
|
+
model=provider_cfg.model,
|
|
28
|
+
base_url=provider_cfg.base_url,
|
|
29
|
+
)
|
|
30
|
+
elif provider_cfg.provider == "anthropic":
|
|
31
|
+
return AnthropicAdapter(
|
|
32
|
+
model=provider_cfg.model,
|
|
33
|
+
api_key=provider_cfg.api_key,
|
|
34
|
+
)
|
|
35
|
+
else:
|
|
36
|
+
raise ValueError(f"Unknown provider: {provider_cfg.provider}")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def create_adapter(
|
|
40
|
+
config: Config,
|
|
41
|
+
task: Literal["extraction", "reflection"] = "extraction",
|
|
42
|
+
) -> Optional[LLMAdapter]:
|
|
43
|
+
"""Create an LLM adapter for the given task.
|
|
44
|
+
|
|
45
|
+
Returns None when provider is 'none' — the agent handles extraction itself
|
|
46
|
+
via deja save, or the watcher falls back to saving raw content.
|
|
47
|
+
"""
|
|
48
|
+
provider_cfg = (
|
|
49
|
+
config.llm.extraction if task == "extraction" else config.llm.reflection
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if provider_cfg.provider == "none":
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
if provider_cfg.provider == "ollama":
|
|
56
|
+
if await _ollama_available(provider_cfg.base_url):
|
|
57
|
+
return _build_adapter(provider_cfg)
|
|
58
|
+
else:
|
|
59
|
+
print(
|
|
60
|
+
f"[deja] Ollama unavailable at {provider_cfg.base_url}, "
|
|
61
|
+
f"falling back to {config.llm.fallback.provider}",
|
|
62
|
+
file=sys.stderr,
|
|
63
|
+
)
|
|
64
|
+
return _build_adapter(config.llm.fallback)
|
|
65
|
+
|
|
66
|
+
return _build_adapter(provider_cfg)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def create_embedding_adapter(config: Config) -> Optional[EmbeddingAdapter]:
|
|
70
|
+
"""Create an embedding adapter for semantic search.
|
|
71
|
+
|
|
72
|
+
Returns None when provider is 'none' — search falls back to BM25 (FTS5) only.
|
|
73
|
+
"""
|
|
74
|
+
if config.embedding.provider == "none":
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
if config.embedding.provider == "ollama":
|
|
78
|
+
if await _ollama_available(config.embedding.base_url):
|
|
79
|
+
return OllamaEmbeddingAdapter(
|
|
80
|
+
model=config.embedding.model,
|
|
81
|
+
base_url=config.embedding.base_url,
|
|
82
|
+
)
|
|
83
|
+
print(
|
|
84
|
+
f"[deja] Ollama unavailable at {config.embedding.base_url} — embedding search disabled",
|
|
85
|
+
file=sys.stderr,
|
|
86
|
+
)
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
print(f"[deja] Unknown embedding provider: {config.embedding.provider}", file=sys.stderr)
|
|
90
|
+
return None
|
|
File without changes
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import anthropic
|
|
4
|
+
|
|
5
|
+
from deja.llm.base import LLMAdapter, LLMResponse
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AnthropicAdapter(LLMAdapter):
|
|
9
|
+
def __init__(self, model: str, api_key: str | None = None) -> None:
|
|
10
|
+
self.model = model
|
|
11
|
+
self._client = anthropic.AsyncAnthropic(api_key=api_key)
|
|
12
|
+
|
|
13
|
+
async def complete(self, system: str, user: str, **kwargs) -> LLMResponse:
|
|
14
|
+
message = await self._client.messages.create(
|
|
15
|
+
model=self.model,
|
|
16
|
+
max_tokens=4096,
|
|
17
|
+
system=system,
|
|
18
|
+
messages=[{"role": "user", "content": user}],
|
|
19
|
+
)
|
|
20
|
+
content = message.content[0].text
|
|
21
|
+
return LLMResponse(content=content, raw=message)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from deja.llm.base import LLMAdapter, LLMResponse
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OllamaAdapter(LLMAdapter):
|
|
9
|
+
def __init__(self, model: str, base_url: str = "http://localhost:11434") -> None:
|
|
10
|
+
self.model = model
|
|
11
|
+
self.base_url = base_url.rstrip("/")
|
|
12
|
+
|
|
13
|
+
async def complete(self, system: str, user: str, **kwargs) -> LLMResponse:
|
|
14
|
+
payload = {
|
|
15
|
+
"model": self.model,
|
|
16
|
+
"messages": [
|
|
17
|
+
{"role": "system", "content": system},
|
|
18
|
+
{"role": "user", "content": user},
|
|
19
|
+
],
|
|
20
|
+
"stream": False,
|
|
21
|
+
}
|
|
22
|
+
async with httpx.AsyncClient(timeout=120.0) as client:
|
|
23
|
+
response = await client.post(
|
|
24
|
+
f"{self.base_url}/api/chat",
|
|
25
|
+
json=payload,
|
|
26
|
+
)
|
|
27
|
+
response.raise_for_status()
|
|
28
|
+
data = response.json()
|
|
29
|
+
content = data["message"]["content"]
|
|
30
|
+
return LLMResponse(content=content, raw=data)
|
deja/main.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deja-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local-first persistent memory CLI for coding agents
|
|
5
|
+
Author-email: Mike <mike@bigtreeproduction.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: agent,claude,cli,developer-tools,llm,memory
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
16
|
+
Classifier: Topic :: Utilities
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Requires-Dist: aiosqlite>=0.20
|
|
19
|
+
Requires-Dist: anthropic>=0.34
|
|
20
|
+
Requires-Dist: apscheduler<4.0,>=3.10
|
|
21
|
+
Requires-Dist: fastapi>=0.115
|
|
22
|
+
Requires-Dist: httpx>=0.27
|
|
23
|
+
Requires-Dist: mcp>=1.26.0
|
|
24
|
+
Requires-Dist: pydantic-settings>=2.4
|
|
25
|
+
Requires-Dist: pydantic>=2.0
|
|
26
|
+
Requires-Dist: python-ulid>=2.0
|
|
27
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
|
+
Requires-Dist: typer>=0.12
|
|
29
|
+
Requires-Dist: uvicorn[standard]>=0.30
|
|
30
|
+
Requires-Dist: watchdog>=4.0
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# deja
|
|
34
|
+
|
|
35
|
+
Local-first persistent memory CLI for coding agents. Memories accumulate across sessions, deduplicate automatically, and stay on your machine.
|
|
36
|
+
|
|
37
|
+
## Install
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv tool install deja-cli
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
deja init # first-time setup
|
|
47
|
+
deja setup claude-code # inject memory instructions into Claude Code
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Core Commands
|
|
51
|
+
|
|
52
|
+
**Save a memory:**
|
|
53
|
+
```bash
|
|
54
|
+
deja save "Always use explicit error handling over try/catch" --type preference
|
|
55
|
+
deja save "Auth tokens stored in localStorage, not cookies" --type decision --project myapp
|
|
56
|
+
deja save "Prisma migrations fail silently if DB_URL has wrong port" --type gotcha --project myapp
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Load at session start:**
|
|
60
|
+
```bash
|
|
61
|
+
deja load --project myapp --context "what you're working on"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Search mid-session:**
|
|
65
|
+
```bash
|
|
66
|
+
deja search "authentication" --project myapp
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Extract memories from a session:**
|
|
70
|
+
```bash
|
|
71
|
+
deja save-session --project myapp
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Memory Types
|
|
75
|
+
|
|
76
|
+
| Type | When to use |
|
|
77
|
+
|---|---|
|
|
78
|
+
| `preference` | How you like to code — style, tools, habits |
|
|
79
|
+
| `pattern` | Reusable solution that applies across contexts |
|
|
80
|
+
| `decision` | Non-obvious architectural choice with reasoning |
|
|
81
|
+
| `gotcha` | Bug, trap, or non-obvious issue to avoid |
|
|
82
|
+
| `progress` | Current state of in-progress work |
|
|
83
|
+
| `procedure` | Ordered steps for a recurring task |
|
|
84
|
+
|
|
85
|
+
## Setup for Coding Agents
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
deja setup claude-code # Claude Code — global config + recall hooks
|
|
89
|
+
deja setup gemini-cli # Gemini CLI
|
|
90
|
+
deja setup codex # Codex CLI
|
|
91
|
+
deja setup cursor # Cursor
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## MCP Server
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
claude mcp add --scope user deja -- ~/.local/bin/deja-mcp
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Registers `memory_load`, `memory_save`, and `memory_search` as native tools in every Claude Code session.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
deja/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
deja/config.py,sha256=0QVDDA4xKNz6PbefZ2rIq4kH0VDmf09F4eqJd14erKw,3874
|
|
3
|
+
deja/main.py,sha256=nni8Tguta7JFkjcCbjy-fDZIn02dCzkEbUw9wnbOoWs,74
|
|
4
|
+
deja/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
deja/core/extractor.py,sha256=p40C6NJaSmwTw5dZN05R7n298E_6kBrPp2JOYxOR05s,4742
|
|
6
|
+
deja/core/reflection.py,sha256=IUgzaSZsgxRRnqJ2cFvxiG91HlpIWtFk8Kl3C5fbmc0,15721
|
|
7
|
+
deja/core/scheduler.py,sha256=uTFKZaCv4HWp_thYr4DMK4QoO535l6In_prfPcch9xk,1835
|
|
8
|
+
deja/core/store.py,sha256=2uMhX63BPFfRCV7RDjkNAXR5vJ0W2l32NL5nbadi03A,57378
|
|
9
|
+
deja/ingest/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
deja/ingest/watchers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
deja/ingest/watchers/base.py,sha256=0EnjNf96fHE-_YGZHzJuc4EkQFKNYQoxxA020_fhY3E,4950
|
|
12
|
+
deja/ingest/watchers/claude_code.py,sha256=lo_MpQf3F_dvSSjyhKFfaUXvyUXqNwUpkXCh5FRWBmY,2674
|
|
13
|
+
deja/ingest/watchers/codex_cli.py,sha256=Btj9FBA69nyHvpi5iDXHdfBIYbe9HMtZRMCq3yqUIIM,3458
|
|
14
|
+
deja/ingest/watchers/gemini_cli.py,sha256=bIEvPTWjniofdFxanN7cY44U1GFqLU74UzX-PA44C6g,3350
|
|
15
|
+
deja/interfaces/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
deja/interfaces/cli.py,sha256=oiafmZOHoSD6MDxkmoM7HfptVGfuP__anvk-oJd8wRA,76808
|
|
17
|
+
deja/interfaces/mcp_server.py,sha256=zo0Q1aHEmfM82jk6P-FacxYsPuhSSyH32YAgJq2OfuA,3198
|
|
18
|
+
deja/interfaces/web.py,sha256=2emjKBVTXhrIaxhw59RI5X1c8mV14sVJ4IIXERcbPwo,3136
|
|
19
|
+
deja/interfaces/web_ui/index.html,sha256=TE3JKlfqcli-dLfEcwR5eN5eNyMZd0YtmJYbdf_Ptis,17817
|
|
20
|
+
deja/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
deja/llm/base.py,sha256=bkkN-ylKfTJNOt756Y7YQO45RIbm23ttT7aS8g2UxhA,1062
|
|
22
|
+
deja/llm/embedding.py,sha256=gsZzU8hQFnfmk0i4yea9eRFzfGwG6QX9oSpR0wCh4OQ,1407
|
|
23
|
+
deja/llm/factory.py,sha256=Kmd4oRLqTPFFwAE1Wo5p9GBSN7HfdALgrmHU7b3H6_s,2972
|
|
24
|
+
deja/llm/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
+
deja/llm/providers/anthropic.py,sha256=Gmb6vjGN23vbLUBUdRTsFbcV9ndhaV3CcTgF6DICRkQ,693
|
|
26
|
+
deja/llm/providers/ollama.py,sha256=b0v8MJcOL7fV_zd5r1LKHXpRHBnpjMia-B2PNWVoYUs,988
|
|
27
|
+
deja_cli-0.1.0.dist-info/METADATA,sha256=Y1BIgPw1hyv2cy4tjvZPca4FVRnPX034VbGAU2vqaCY,2880
|
|
28
|
+
deja_cli-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
29
|
+
deja_cli-0.1.0.dist-info/entry_points.txt,sha256=1DFYa8oXV00iBZk-Yc4ZKOMhTmQGhI1qHtGfdOXA1dY,82
|
|
30
|
+
deja_cli-0.1.0.dist-info/licenses/LICENSE,sha256=bnwiEGciwVH_xm0PryA1FV7KGtkZMfVnnGdziRwI3u8,1061
|
|
31
|
+
deja_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mike
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|