From e69e81135aed5bc5e4323336a3061b8c35969cd7 Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Thu, 25 Jun 2026 12:23:45 +0200 Subject: [PATCH] feat(services): add memory service and refactor CLI --- src/cli/commands/memory.ts | 47 +++----------------------- src/core/services/memoryService.ts | 53 ++++++++++++++++++++++++++++++ tests/memoryService.test.ts | 19 +++++++++++ 3 files changed, 77 insertions(+), 42 deletions(-) create mode 100644 src/core/services/memoryService.ts create mode 100644 tests/memoryService.test.ts diff --git a/src/cli/commands/memory.ts b/src/cli/commands/memory.ts index 7494d9f..eb11589 100644 --- a/src/cli/commands/memory.ts +++ b/src/cli/commands/memory.ts @@ -1,9 +1,6 @@ import { input, select } from '@inquirer/prompts'; -import { join } from 'path'; -import { getEntityDir } from '../../core/paths.js'; -import { writeEntity } from '../../core/files.js'; -import { MemorySchema, type Memory } from '../../core/schema.js'; -import { Index } from '../../core/index.js'; +import { addMemory, searchMemory, listMemory } from '../../core/services/memoryService.js'; +import type { Memory } from '../../core/schema.js'; export async function memoryAdd(cwd: string, options: Partial = {}): Promise { const title = options.title ?? await input({ message: 'Memory title:' }); @@ -19,61 +16,27 @@ export async function memoryAdd(cwd: string, options: Partial = {}): Pro }); const content = options.content ?? await input({ message: 'Content:' }); - const now = new Date().toISOString(); - const id = `MEM-${Date.now()}`; - const memory: Memory = MemorySchema.parse({ - id, - title, - category, - content, - createdAt: now, - updatedAt: now, - }); - - const filePath = join(getEntityDir(cwd, 'memory'), `${id}.md`); - writeEntity(filePath, memory, `# ${memory.title}\n\n${memory.content}`); - - const index = new Index(cwd); - index.upsert({ - id: memory.id, - type: 'memory', - title: memory.title, - content: memory.content, - filePath, - createdAt: memory.createdAt, - updatedAt: memory.updatedAt, - tags: JSON.stringify(memory.tags), - }); - index.close(); - + const memory = addMemory(cwd, { title, category, content }); console.log(`Memory saved as ${memory.id}.`); } export function memorySearch(cwd: string, query: string): void { - const index = new Index(cwd); - const results = index.search(query); - index.close(); - + const results = searchMemory(cwd, query); if (results.length === 0) { console.log('No results found.'); return; } - for (const r of results) { console.log(`[${r.type}] ${r.id}: ${r.title}`); } } export function memoryList(cwd: string): void { - const index = new Index(cwd); - const memories = index.list('memory'); - index.close(); - + const memories = listMemory(cwd); if (memories.length === 0) { console.log('No memory entries found.'); return; } - for (const m of memories) { console.log(`${m.id}: ${m.title}`); } diff --git a/src/core/services/memoryService.ts b/src/core/services/memoryService.ts new file mode 100644 index 0000000..f1225fe --- /dev/null +++ b/src/core/services/memoryService.ts @@ -0,0 +1,53 @@ +import { join } from 'path'; +import { getEntityDir } from '../paths.js'; +import { writeEntity } from '../files.js'; +import { MemorySchema, type Memory } from '../schema.js'; +import { Index } from '../index.js'; + +export function addMemory(cwd: string, options: Partial = {}): Memory { + const now = new Date().toISOString(); + const id = `MEM-${Date.now()}`; + const memory: Memory = MemorySchema.parse({ + id, + title: options.title ?? 'Memory', + category: options.category ?? 'technical', + content: options.content ?? '', + tags: options.tags ?? [], + relatedTasks: options.relatedTasks ?? [], + relatedDecisions: options.relatedDecisions ?? [], + createdAt: now, + updatedAt: now, + }); + + const filePath = join(getEntityDir(cwd, 'memory'), `${memory.id}.md`); + writeEntity(filePath, memory, `# ${memory.title}\n\n${memory.content}`); + + const index = new Index(cwd); + index.upsert({ + id: memory.id, + type: 'memory', + title: memory.title, + content: memory.content, + filePath, + createdAt: memory.createdAt, + updatedAt: memory.updatedAt, + tags: JSON.stringify(memory.tags), + }); + index.close(); + + return memory; +} + +export function searchMemory(cwd: string, query: string): ReturnType { + const index = new Index(cwd); + const results = index.search(query); + index.close(); + return results; +} + +export function listMemory(cwd: string): ReturnType { + const index = new Index(cwd); + const memories = index.list('memory'); + index.close(); + return memories; +} diff --git a/tests/memoryService.test.ts b/tests/memoryService.test.ts new file mode 100644 index 0000000..4bc08c7 --- /dev/null +++ b/tests/memoryService.test.ts @@ -0,0 +1,19 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { mkdtempSync, rmSync } from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { addMemory, searchMemory, listMemory } from '../src/core/services/memoryService.js'; + +describe('memoryService', () => { + let cwd: string; + + beforeEach(() => { cwd = mkdtempSync(join(tmpdir(), 'ah-mem-')); }); + afterEach(() => { rmSync(cwd, { recursive: true, force: true }); }); + + it('creates and searches memory', () => { + const m = addMemory(cwd, { title: 'DNS cache', category: 'technical', content: 'Use TTL' }); + expect(m.id.startsWith('MEM-')).toBe(true); + expect(searchMemory(cwd, 'TTL')).toHaveLength(1); + expect(listMemory(cwd)).toHaveLength(1); + }); +});