runtime-memory 3.0.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.
- runtime_memory/__init__.py +28 -0
- runtime_memory/claude_code/__init__.py +48 -0
- runtime_memory/claude_code/commands.py +698 -0
- runtime_memory/claude_code/daemon.py +852 -0
- runtime_memory/claude_code/hooks.py +722 -0
- runtime_memory/cli/__init__.py +8 -0
- runtime_memory/cli/main.py +1936 -0
- runtime_memory/core/__init__.py +216 -0
- runtime_memory/core/config.py +473 -0
- runtime_memory/core/embeddings.py +908 -0
- runtime_memory/core/engine.py +1007 -0
- runtime_memory/core/exceptions.py +547 -0
- runtime_memory/core/legacy_env.py +39 -0
- runtime_memory/core/logging.py +160 -0
- runtime_memory/core/models.py +1051 -0
- runtime_memory/core/observability.py +725 -0
- runtime_memory/core/paths.py +30 -0
- runtime_memory/core/resilience.py +511 -0
- runtime_memory/core/retrieval.py +819 -0
- runtime_memory/core/storage.py +1105 -0
- runtime_memory/extraction/__init__.py +36 -0
- runtime_memory/extraction/extractor.py +1143 -0
- runtime_memory/hermes/__init__.py +39 -0
- runtime_memory/hermes/_base.py +154 -0
- runtime_memory/hermes/bridge.py +119 -0
- runtime_memory/hermes/plugin.yaml +13 -0
- runtime_memory/hermes/provider.py +536 -0
- runtime_memory/hermes/tools.py +230 -0
- runtime_memory/hermes/trace.py +177 -0
- runtime_memory/plugin/__init__.py +646 -0
- runtime_memory/sdk/__init__.py +97 -0
- runtime_memory/sdk/client.py +1577 -0
- runtime_memory/server/__init__.py +75 -0
- runtime_memory/server/api.py +1665 -0
- runtime_memory/server/mcp.py +1574 -0
- runtime_memory/server/static/css/styles.css +1110 -0
- runtime_memory/server/static/index.html +264 -0
- runtime_memory/server/static/js/api.js +294 -0
- runtime_memory/server/static/js/app.js +771 -0
- runtime_memory/tasks/__init__.py +114 -0
- runtime_memory/tasks/adapter.py +501 -0
- runtime_memory/tasks/claude_code_adapter.py +495 -0
- runtime_memory/tasks/claude_code_parser.py +339 -0
- runtime_memory/tasks/cli_bridge.py +415 -0
- runtime_memory/tasks/linking.py +397 -0
- runtime_memory/tasks/models.py +520 -0
- runtime_memory/tasks/outcomes.py +320 -0
- runtime_memory/tasks/parser.py +305 -0
- runtime_memory/tasks/unified_adapter.py +661 -0
- runtime_memory-3.0.0.dist-info/METADATA +497 -0
- runtime_memory-3.0.0.dist-info/RECORD +54 -0
- runtime_memory-3.0.0.dist-info/WHEEL +4 -0
- runtime_memory-3.0.0.dist-info/entry_points.txt +6 -0
- runtime_memory-3.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
"""Claude Code tasks adapter for Runtime Memory integration.
|
|
2
|
+
|
|
3
|
+
This module provides integration with Claude Code's native todos system,
|
|
4
|
+
enabling automatic outcome capture when tasks complete.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
>>> from runtime_memory.tasks import ClaudeCodeAdapter
|
|
8
|
+
>>> adapter = ClaudeCodeAdapter(engine)
|
|
9
|
+
>>> await adapter.initialize()
|
|
10
|
+
>>>
|
|
11
|
+
>>> # List tasks
|
|
12
|
+
>>> tasks = adapter.list_tasks()
|
|
13
|
+
>>>
|
|
14
|
+
>>> # Get context for current task
|
|
15
|
+
>>> context = await adapter.get_unified_context()
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import TYPE_CHECKING
|
|
23
|
+
|
|
24
|
+
from runtime_memory.core.models import Memory
|
|
25
|
+
from runtime_memory.tasks.claude_code_parser import ClaudeCodeParser
|
|
26
|
+
from runtime_memory.tasks.linking import TaskMemoryLinker
|
|
27
|
+
from runtime_memory.tasks.models import (
|
|
28
|
+
CLAUDE_CODE_STATUS_TO_OUTCOME,
|
|
29
|
+
ClaudeCodeTask,
|
|
30
|
+
ClaudeCodeTaskStatus,
|
|
31
|
+
TaskContext,
|
|
32
|
+
TaskSource,
|
|
33
|
+
TaskSyncResult,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from runtime_memory.core.engine import MemoryEngine
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ClaudeCodeAdapter:
|
|
43
|
+
"""Adapter for Claude Code todos integration.
|
|
44
|
+
|
|
45
|
+
Provides similar interface to BeadsAdapter but for Claude Code's
|
|
46
|
+
native task system at ~/.claude/todos/.
|
|
47
|
+
|
|
48
|
+
Key differences from Beads:
|
|
49
|
+
- Simpler status model (pending, in_progress, completed)
|
|
50
|
+
- No dependencies/blocking
|
|
51
|
+
- Tasks organized by session/agent
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
engine: MemoryEngine,
|
|
57
|
+
todos_dir: Path | str | None = None,
|
|
58
|
+
task_list_id: str | None = None,
|
|
59
|
+
auto_outcome_enabled: bool = True,
|
|
60
|
+
) -> None:
|
|
61
|
+
"""Initialize the Claude Code adapter.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
engine: The Runtime Memory engine.
|
|
65
|
+
todos_dir: Explicit path to todos directory (auto-discovers if None).
|
|
66
|
+
task_list_id: Filter to specific task list (session/agent).
|
|
67
|
+
auto_outcome_enabled: Whether to auto-record outcomes on task completion.
|
|
68
|
+
"""
|
|
69
|
+
self._engine = engine
|
|
70
|
+
self._todos_dir = Path(todos_dir) if todos_dir else None
|
|
71
|
+
self._task_list_id = task_list_id
|
|
72
|
+
|
|
73
|
+
# Initialize components
|
|
74
|
+
self._parser = ClaudeCodeParser(self._todos_dir, task_list_id)
|
|
75
|
+
self._linker: TaskMemoryLinker | None = None
|
|
76
|
+
|
|
77
|
+
# Configuration
|
|
78
|
+
self.auto_outcome_enabled = auto_outcome_enabled
|
|
79
|
+
|
|
80
|
+
self._initialized = False
|
|
81
|
+
|
|
82
|
+
async def initialize(self) -> None:
|
|
83
|
+
"""Initialize all components.
|
|
84
|
+
|
|
85
|
+
Creates the linker table and sets up components.
|
|
86
|
+
Must be called before using the adapter.
|
|
87
|
+
"""
|
|
88
|
+
if self._initialized:
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
# Get db_path from engine's storage
|
|
92
|
+
db_path = self._engine._storage.db_path
|
|
93
|
+
|
|
94
|
+
# Initialize linker
|
|
95
|
+
self._linker = TaskMemoryLinker(db_path)
|
|
96
|
+
await self._linker.initialize()
|
|
97
|
+
|
|
98
|
+
self._initialized = True
|
|
99
|
+
logger.debug("ClaudeCodeAdapter initialized")
|
|
100
|
+
|
|
101
|
+
def _ensure_initialized(self) -> None:
|
|
102
|
+
"""Ensure the adapter is initialized."""
|
|
103
|
+
if not self._initialized:
|
|
104
|
+
raise RuntimeError(
|
|
105
|
+
"ClaudeCodeAdapter not initialized. Call initialize() first."
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def is_available(self) -> bool:
|
|
110
|
+
"""Check if Claude Code todos are available."""
|
|
111
|
+
return self._parser.is_available()
|
|
112
|
+
|
|
113
|
+
# =========================================================================
|
|
114
|
+
# Task Operations
|
|
115
|
+
# =========================================================================
|
|
116
|
+
|
|
117
|
+
def get_task(self, task_id: str) -> ClaudeCodeTask | None:
|
|
118
|
+
"""Get a task by ID.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
task_id: The task ID (e.g., "cc-abc12345-0").
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
The task or None if not found.
|
|
125
|
+
"""
|
|
126
|
+
return self._parser.get_task(task_id)
|
|
127
|
+
|
|
128
|
+
def list_tasks(
|
|
129
|
+
self,
|
|
130
|
+
status: ClaudeCodeTaskStatus | None = None,
|
|
131
|
+
session_id: str | None = None,
|
|
132
|
+
) -> list[ClaudeCodeTask]:
|
|
133
|
+
"""List all tasks, optionally filtered.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
status: Optional status filter.
|
|
137
|
+
session_id: Optional session ID filter.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
List of matching tasks.
|
|
141
|
+
"""
|
|
142
|
+
return self._parser.list_tasks(status=status, session_id=session_id)
|
|
143
|
+
|
|
144
|
+
def get_ready_tasks(self) -> list[ClaudeCodeTask]:
|
|
145
|
+
"""Get tasks that are ready to work on.
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
List of tasks with pending status.
|
|
149
|
+
"""
|
|
150
|
+
return self._parser.get_ready_tasks()
|
|
151
|
+
|
|
152
|
+
def get_current_task(self) -> ClaudeCodeTask | None:
|
|
153
|
+
"""Get the currently active task (in_progress).
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
The in-progress task, or None if no task is active.
|
|
157
|
+
"""
|
|
158
|
+
in_progress = self._parser.get_in_progress_tasks()
|
|
159
|
+
return in_progress[0] if in_progress else None
|
|
160
|
+
|
|
161
|
+
def get_sessions(self) -> list[str]:
|
|
162
|
+
"""Get list of unique session IDs.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
List of session IDs that have tasks.
|
|
166
|
+
"""
|
|
167
|
+
return self._parser.get_sessions()
|
|
168
|
+
|
|
169
|
+
# =========================================================================
|
|
170
|
+
# Memory Linking
|
|
171
|
+
# =========================================================================
|
|
172
|
+
|
|
173
|
+
async def link_memory_to_task(
|
|
174
|
+
self,
|
|
175
|
+
task_id: str,
|
|
176
|
+
memory_id: str,
|
|
177
|
+
context: str | None = None,
|
|
178
|
+
) -> None:
|
|
179
|
+
"""Link a memory to a task.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
task_id: The task ID.
|
|
183
|
+
memory_id: The Runtime Memory memory ID.
|
|
184
|
+
context: Optional context about how memory was used.
|
|
185
|
+
"""
|
|
186
|
+
self._ensure_initialized()
|
|
187
|
+
await self._linker.link(task_id, memory_id, context)
|
|
188
|
+
|
|
189
|
+
async def link_memories_to_task(
|
|
190
|
+
self,
|
|
191
|
+
task_id: str,
|
|
192
|
+
memory_ids: list[str],
|
|
193
|
+
context: str | None = None,
|
|
194
|
+
) -> None:
|
|
195
|
+
"""Link multiple memories to a task.
|
|
196
|
+
|
|
197
|
+
Args:
|
|
198
|
+
task_id: The task ID.
|
|
199
|
+
memory_ids: List of Runtime Memory memory IDs.
|
|
200
|
+
context: Optional context about how memories were used.
|
|
201
|
+
"""
|
|
202
|
+
self._ensure_initialized()
|
|
203
|
+
await self._linker.link_many(task_id, memory_ids, context)
|
|
204
|
+
|
|
205
|
+
async def get_task_memories(self, task_id: str) -> list[Memory]:
|
|
206
|
+
"""Get all memories linked to a task.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
task_id: The task ID.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
List of Memory objects linked to the task.
|
|
213
|
+
"""
|
|
214
|
+
self._ensure_initialized()
|
|
215
|
+
links = await self._linker.get_memories_for_task(task_id)
|
|
216
|
+
memories = []
|
|
217
|
+
for link in links:
|
|
218
|
+
try:
|
|
219
|
+
memory = await self._engine.get(link.memory_id)
|
|
220
|
+
memories.append(memory)
|
|
221
|
+
except Exception:
|
|
222
|
+
logger.debug(f"Memory {link.memory_id} not found, skipping")
|
|
223
|
+
return memories
|
|
224
|
+
|
|
225
|
+
async def auto_link_search_results(
|
|
226
|
+
self,
|
|
227
|
+
task_id: str,
|
|
228
|
+
memory_ids: list[str],
|
|
229
|
+
) -> None:
|
|
230
|
+
"""Automatically link search results to the current task.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
task_id: The task ID.
|
|
234
|
+
memory_ids: Memory IDs from search results.
|
|
235
|
+
"""
|
|
236
|
+
self._ensure_initialized()
|
|
237
|
+
await self._linker.link_many(task_id, memory_ids, context="search_result")
|
|
238
|
+
|
|
239
|
+
# =========================================================================
|
|
240
|
+
# Outcome Capture
|
|
241
|
+
# =========================================================================
|
|
242
|
+
|
|
243
|
+
async def on_task_completed(self, task_id: str) -> int:
|
|
244
|
+
"""Handle a task being marked as completed.
|
|
245
|
+
|
|
246
|
+
Records "worked" outcome for all linked memories.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
task_id: The task ID.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Number of memories that had outcomes recorded.
|
|
253
|
+
"""
|
|
254
|
+
self._ensure_initialized()
|
|
255
|
+
|
|
256
|
+
if not self.auto_outcome_enabled:
|
|
257
|
+
return 0
|
|
258
|
+
|
|
259
|
+
# Get unresolved links for this task
|
|
260
|
+
links = await self._linker.get_unresolved_links(task_id)
|
|
261
|
+
if not links:
|
|
262
|
+
return 0
|
|
263
|
+
|
|
264
|
+
# Record outcome for all linked memories
|
|
265
|
+
memory_ids = [link.memory_id for link in links]
|
|
266
|
+
from runtime_memory.core.models import Outcome
|
|
267
|
+
|
|
268
|
+
await self._engine.record_outcome(memory_ids, Outcome.WORKED)
|
|
269
|
+
|
|
270
|
+
# Mark links as resolved
|
|
271
|
+
await self._linker.record_task_outcome(task_id, "worked")
|
|
272
|
+
|
|
273
|
+
logger.info(f"Recorded 'worked' outcome for {len(links)} memories on task {task_id}")
|
|
274
|
+
return len(links)
|
|
275
|
+
|
|
276
|
+
async def sync(self) -> TaskSyncResult:
|
|
277
|
+
"""Sync outcomes for all completed tasks.
|
|
278
|
+
|
|
279
|
+
Scans completed tasks and records outcomes for any that
|
|
280
|
+
have unresolved memory links.
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
TaskSyncResult with sync statistics.
|
|
284
|
+
"""
|
|
285
|
+
self._ensure_initialized()
|
|
286
|
+
|
|
287
|
+
result = TaskSyncResult(source=TaskSource.CLAUDE_CODE)
|
|
288
|
+
completed_tasks = self._parser.get_completed_tasks()
|
|
289
|
+
result.tasks_found = len(self._parser.list_tasks())
|
|
290
|
+
|
|
291
|
+
for task in completed_tasks:
|
|
292
|
+
try:
|
|
293
|
+
count = await self.on_task_completed(task.id)
|
|
294
|
+
if count > 0:
|
|
295
|
+
result.tasks_synced += 1
|
|
296
|
+
result.outcomes_recorded += count
|
|
297
|
+
except Exception as e:
|
|
298
|
+
result.errors.append(f"Failed to sync task {task.id}: {e}")
|
|
299
|
+
|
|
300
|
+
return result
|
|
301
|
+
|
|
302
|
+
# =========================================================================
|
|
303
|
+
# Context Generation
|
|
304
|
+
# =========================================================================
|
|
305
|
+
|
|
306
|
+
async def get_unified_context(
|
|
307
|
+
self,
|
|
308
|
+
task_id: str | None = None,
|
|
309
|
+
max_memories: int = 10,
|
|
310
|
+
) -> TaskContext | None:
|
|
311
|
+
"""Get unified context combining task info and relevant memories.
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
task_id: The task ID to get context for. If None, uses current task.
|
|
315
|
+
max_memories: Maximum number of memories to include.
|
|
316
|
+
|
|
317
|
+
Returns:
|
|
318
|
+
TaskContext object or None if no task found.
|
|
319
|
+
"""
|
|
320
|
+
self._ensure_initialized()
|
|
321
|
+
|
|
322
|
+
# Get task
|
|
323
|
+
if task_id:
|
|
324
|
+
task = self.get_task(task_id)
|
|
325
|
+
else:
|
|
326
|
+
task = self.get_current_task()
|
|
327
|
+
|
|
328
|
+
if not task:
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
# Get linked memories
|
|
332
|
+
memories = await self.get_task_memories(task.id)
|
|
333
|
+
|
|
334
|
+
# If no linked memories, search for relevant ones
|
|
335
|
+
if not memories and task.content:
|
|
336
|
+
search_query = task.content[:200]
|
|
337
|
+
results = await self._engine.search(
|
|
338
|
+
search_query,
|
|
339
|
+
limit=max_memories,
|
|
340
|
+
min_score=0.5,
|
|
341
|
+
track_usage=False,
|
|
342
|
+
)
|
|
343
|
+
memories = [r.memory for r in results if r.score >= 0.5]
|
|
344
|
+
|
|
345
|
+
# Limit memories
|
|
346
|
+
memories = memories[:max_memories]
|
|
347
|
+
|
|
348
|
+
# Create context
|
|
349
|
+
context = TaskContext(
|
|
350
|
+
task=task,
|
|
351
|
+
memories=memories,
|
|
352
|
+
source=TaskSource.CLAUDE_CODE,
|
|
353
|
+
)
|
|
354
|
+
context.formatted = context.to_markdown()
|
|
355
|
+
|
|
356
|
+
return context
|
|
357
|
+
|
|
358
|
+
async def get_context_for_injection(
|
|
359
|
+
self,
|
|
360
|
+
task_id: str | None = None,
|
|
361
|
+
max_memories: int = 10,
|
|
362
|
+
) -> str:
|
|
363
|
+
"""Get formatted context string for injection into prompts.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
task_id: The task ID to get context for. If None, uses current task.
|
|
367
|
+
max_memories: Maximum number of memories to include.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
Formatted markdown string for context injection.
|
|
371
|
+
"""
|
|
372
|
+
context = await self.get_unified_context(task_id, max_memories)
|
|
373
|
+
if not context:
|
|
374
|
+
return ""
|
|
375
|
+
return context.formatted
|
|
376
|
+
|
|
377
|
+
# =========================================================================
|
|
378
|
+
# Statistics
|
|
379
|
+
# =========================================================================
|
|
380
|
+
|
|
381
|
+
async def get_stats(self) -> dict:
|
|
382
|
+
"""Get statistics about Claude Code integration.
|
|
383
|
+
|
|
384
|
+
Returns:
|
|
385
|
+
Dict with statistics.
|
|
386
|
+
"""
|
|
387
|
+
self._ensure_initialized()
|
|
388
|
+
|
|
389
|
+
# Parser stats
|
|
390
|
+
parser_stats = self._parser.get_stats()
|
|
391
|
+
|
|
392
|
+
# Link stats
|
|
393
|
+
link_stats = await self._linker.get_stats()
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
"claude_code_available": self.is_available,
|
|
397
|
+
"todos_dir": str(self._parser.todos_dir) if self._parser.todos_dir else None,
|
|
398
|
+
"tasks": parser_stats,
|
|
399
|
+
"links": link_stats,
|
|
400
|
+
"auto_outcome_enabled": self.auto_outcome_enabled,
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
def refresh(self) -> int:
|
|
404
|
+
"""Refresh task cache from disk.
|
|
405
|
+
|
|
406
|
+
Returns:
|
|
407
|
+
Number of tasks loaded.
|
|
408
|
+
"""
|
|
409
|
+
return self._parser.refresh()
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
class NullClaudeCodeAdapter:
|
|
413
|
+
"""Null adapter for when Claude Code todos are not available.
|
|
414
|
+
|
|
415
|
+
Provides the same interface but does nothing, allowing graceful
|
|
416
|
+
degradation when Claude Code integration is not configured.
|
|
417
|
+
"""
|
|
418
|
+
|
|
419
|
+
@property
|
|
420
|
+
def is_available(self) -> bool:
|
|
421
|
+
return False
|
|
422
|
+
|
|
423
|
+
async def initialize(self) -> None:
|
|
424
|
+
pass
|
|
425
|
+
|
|
426
|
+
def get_task(self, task_id: str) -> None:
|
|
427
|
+
return None
|
|
428
|
+
|
|
429
|
+
def list_tasks(
|
|
430
|
+
self,
|
|
431
|
+
status: ClaudeCodeTaskStatus | None = None,
|
|
432
|
+
session_id: str | None = None,
|
|
433
|
+
) -> list:
|
|
434
|
+
return []
|
|
435
|
+
|
|
436
|
+
def get_ready_tasks(self) -> list:
|
|
437
|
+
return []
|
|
438
|
+
|
|
439
|
+
def get_current_task(self) -> None:
|
|
440
|
+
return None
|
|
441
|
+
|
|
442
|
+
def get_sessions(self) -> list:
|
|
443
|
+
return []
|
|
444
|
+
|
|
445
|
+
async def link_memory_to_task(self, *args, **kwargs) -> None:
|
|
446
|
+
pass
|
|
447
|
+
|
|
448
|
+
async def link_memories_to_task(self, *args, **kwargs) -> None:
|
|
449
|
+
pass
|
|
450
|
+
|
|
451
|
+
async def get_task_memories(self, task_id: str) -> list:
|
|
452
|
+
return []
|
|
453
|
+
|
|
454
|
+
async def on_task_completed(self, task_id: str) -> int:
|
|
455
|
+
return 0
|
|
456
|
+
|
|
457
|
+
async def sync(self) -> TaskSyncResult:
|
|
458
|
+
result = TaskSyncResult(source=TaskSource.CLAUDE_CODE)
|
|
459
|
+
result.warnings.append("Claude Code integration not available")
|
|
460
|
+
return result
|
|
461
|
+
|
|
462
|
+
async def get_unified_context(self, *args, **kwargs) -> None:
|
|
463
|
+
return None
|
|
464
|
+
|
|
465
|
+
async def get_context_for_injection(self, *args, **kwargs) -> str:
|
|
466
|
+
return ""
|
|
467
|
+
|
|
468
|
+
async def get_stats(self) -> dict:
|
|
469
|
+
return {"claude_code_available": False}
|
|
470
|
+
|
|
471
|
+
def refresh(self) -> int:
|
|
472
|
+
return 0
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def create_claude_code_adapter(
|
|
476
|
+
engine: MemoryEngine,
|
|
477
|
+
todos_dir: Path | str | None = None,
|
|
478
|
+
**kwargs,
|
|
479
|
+
) -> ClaudeCodeAdapter | NullClaudeCodeAdapter:
|
|
480
|
+
"""Factory function to create the appropriate adapter.
|
|
481
|
+
|
|
482
|
+
Returns a NullClaudeCodeAdapter if Claude Code is not available.
|
|
483
|
+
|
|
484
|
+
Args:
|
|
485
|
+
engine: The Runtime Memory engine.
|
|
486
|
+
todos_dir: Explicit path to todos directory.
|
|
487
|
+
**kwargs: Additional arguments for ClaudeCodeAdapter.
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
ClaudeCodeAdapter if available, NullClaudeCodeAdapter otherwise.
|
|
491
|
+
"""
|
|
492
|
+
adapter = ClaudeCodeAdapter(engine, todos_dir, **kwargs)
|
|
493
|
+
if adapter.is_available:
|
|
494
|
+
return adapter
|
|
495
|
+
return NullClaudeCodeAdapter()
|