diff --git a/docs/configuration.md b/docs/configuration.md index fae0f11..38d9fdd 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -14,7 +14,7 @@ Coding Code 的核心哲学是所有行为都可配置。本文档详细介绍 | `mcp.yaml` | `~/.codingcode/mcp.yaml` + `.codingcode/mcp.yaml` | MCP 服务配置 | [→ mcp.md](mcp.md) | | `hooks.yaml` | `~/.codingcode/hooks.yaml` + `.codingcode/hooks.yaml` | 钩子配置 | [→ hooks.md](hooks.md) | | `agents/*.md` | `~/.codingcode/agents/` + `.codingcode/agents/` | 子智能体 profile | [→ subagent.md](subagent.md) | -| `memory.md` | `~/.codingcode/memory.md` + `.codingcode/memory.md` | 长期记忆 | [→ memory.md](memory.md) | +| `memory.md` | `./.codingcode/memory.md` | 长期记忆(项目级) | [→ memory.md](memory.md) | --- @@ -41,8 +41,6 @@ context: memory: enabled: false # 启用长期记忆 model: "" # 记忆提取模型,空字符串回退主模型 - projectFile: ".codingcode/memory.md" # 项目记忆文件路径 - userFile: "~/.codingcode/memory.md" # 用户记忆文件路径 maxBytes: 16384 # 记忆文件最大字节数 promptMaxBytes: 8192 # 注入提示的最大字节数 extraTypes: [] # 自定义记忆类型 @@ -60,8 +58,6 @@ memory: | `context.compactionModel` | `''` | 上下文压缩使用的模型,空字符串回退到主会话 LLM | | `memory.enabled` | `false` | 是否启用长期记忆系统 | | `memory.model` | `''` | 记忆提取使用的模型,空字符串回退到主模型 | -| `memory.projectFile` | `'.codingcode/memory.md'` | 项目级记忆文件路径 | -| `memory.userFile` | `'~/.codingcode/memory.md'` | 用户级记忆文件路径 | | `memory.maxBytes` | `16384` | 单个记忆文件的最大字节数 | | `memory.promptMaxBytes` | `8192` | 注入 system prompt 的记忆内容最大字节数 | | `memory.extraTypes` | `[]` | 自定义记忆类型列表 | diff --git a/docs/memory.md b/docs/memory.md index 3beb16b..15b7289 100644 --- a/docs/memory.md +++ b/docs/memory.md @@ -6,12 +6,7 @@ Coding Code 支持跨会话的长期记忆,自动从对话中提取和存储 ## 内存类型 -| 类型 | 位置 | 作用 | -|---|---|---| -| **用户级** | `~/.codingcode/memory.md` | 跨所有项目的个人知识库 | -| **项目级** | `./.codingcode/memory.md` | 特定项目的上下文 | - -路径可在 `codingcode.yaml` 的 `memory.projectFile` 和 `memory.userFile` 中自定义。 +记忆文件存储在项目的 `.codingcode/memory.md` 中。 --- @@ -81,8 +76,6 @@ Agent 在每次会话后自动执行记忆提取: memory: enabled: true # 启用长期记忆(默认 false) model: "" # 记忆提取模型,空字符串回退到主模型 - projectFile: ".codingcode/memory.md" # 项目记忆文件路径 - userFile: "~/.codingcode/memory.md" # 用户记忆文件路径 maxBytes: 16384 # 记忆文件最大字节数 promptMaxBytes: 8192 # 注入提示的最大字节数 extraTypes: [] # 自定义记忆类型 diff --git a/packages/codingcode/src/memory/index.ts b/packages/codingcode/src/memory/index.ts index 87f1689..309d8ef 100644 --- a/packages/codingcode/src/memory/index.ts +++ b/packages/codingcode/src/memory/index.ts @@ -4,8 +4,7 @@ import { findSessionIndex, resolveSessionDir } from '../session/file-ops.js'; import type { SessionEvent } from '../session/types.js'; import { readMemoryFile, - resolveProjectMemoryPath, - resolveUserMemoryPath, + resolveMemoryPath, extractAutoBlock, replaceAutoBlock, mergeAutoBlocks, @@ -38,24 +37,13 @@ export class MemoryService extends Effect.Service()('Memory', { if (!getMemoryEnabled()) return ''; const cfg = getMemoryConfig(); - const projectPath = resolveProjectMemoryPath(cwd, cfg); - const userPath = resolveUserMemoryPath(cfg); - + const projectPath = resolveMemoryPath(cwd); const projectContent = readMemoryFile(projectPath); - const userContent = readMemoryFile(userPath); - const projectAuto = extractAutoBlock(projectContent); - const userAuto = extractAutoBlock(userContent); - - const parts = []; - if (projectAuto) parts.push(projectAuto); - if (userAuto) parts.push(userAuto); - if (parts.length === 0) return ''; - - const combined = parts.join('\n\n'); - const stripped = stripMarkersForPrompt(combined); + if (!projectAuto) return ''; + const stripped = stripMarkersForPrompt(projectAuto); const truncated = truncateForPrompt(stripped, cfg.promptMaxBytes); return truncated ? `## Long-term Memory\n\n${truncated}` : ''; @@ -129,15 +117,10 @@ export class MemoryService extends Effect.Service()('Memory', { } const cwd = sessionIndex.cwd; - const projectPath = resolveProjectMemoryPath(cwd, cfg); - const userPath = resolveUserMemoryPath(cfg); + const projectPath = resolveMemoryPath(cwd); const projectContent = readMemoryFile(projectPath); - const userContent = readMemoryFile(userPath); - - const projectAuto = extractAutoBlock(projectContent); - const userAuto = extractAutoBlock(userContent); - const currentAuto = [projectAuto, userAuto].filter(Boolean).join('\n\n'); + const currentAuto = extractAutoBlock(projectContent); try { let events: SessionEvent[] = []; diff --git a/packages/codingcode/src/memory/storage.ts b/packages/codingcode/src/memory/storage.ts index 835a14c..a2fd7dd 100644 --- a/packages/codingcode/src/memory/storage.ts +++ b/packages/codingcode/src/memory/storage.ts @@ -1,27 +1,8 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; -import * as os from 'node:os'; -import type { MemoryConfig } from '@codingcode/infra/config'; -// ── Path Resolution ── - -export function resolveProjectMemoryPath(cwd: string, cfg: MemoryConfig): string { - const filePath = cfg.projectFile; - if (path.isAbsolute(filePath)) { - return filePath; - } - return path.join(cwd, filePath); -} - -export function resolveUserMemoryPath(cfg: MemoryConfig): string { - const filePath = cfg.userFile; - if (filePath.startsWith('~')) { - return path.join(os.homedir(), filePath.slice(1)); - } - if (path.isAbsolute(filePath)) { - return filePath; - } - return path.join(os.homedir(), filePath); +export function resolveMemoryPath(cwd: string): string { + return path.join(cwd, '.codingcode', 'memory.md'); } // ── File Read/Write ── diff --git a/packages/codingcode/test/agent/agent-cache-stability.test.ts b/packages/codingcode/test/agent/agent-cache-stability.test.ts index 7333510..2867ef1 100644 --- a/packages/codingcode/test/agent/agent-cache-stability.test.ts +++ b/packages/codingcode/test/agent/agent-cache-stability.test.ts @@ -14,8 +14,6 @@ vi.mock('@codingcode/infra/config', () => ({ memory: { enabled: false, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/agent/agent-concurrent.test.ts b/packages/codingcode/test/agent/agent-concurrent.test.ts index 71ded62..430cf95 100644 --- a/packages/codingcode/test/agent/agent-concurrent.test.ts +++ b/packages/codingcode/test/agent/agent-concurrent.test.ts @@ -14,8 +14,6 @@ vi.mock('@codingcode/infra/config', () => ({ memory: { enabled: false, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/agent/agent-todo-event.test.ts b/packages/codingcode/test/agent/agent-todo-event.test.ts index 851a4bb..362d7e1 100644 --- a/packages/codingcode/test/agent/agent-todo-event.test.ts +++ b/packages/codingcode/test/agent/agent-todo-event.test.ts @@ -14,8 +14,6 @@ vi.mock('@codingcode/infra/config', () => ({ memory: { enabled: false, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/agent/agent.test.ts b/packages/codingcode/test/agent/agent.test.ts index e1b2391..895ef37 100644 --- a/packages/codingcode/test/agent/agent.test.ts +++ b/packages/codingcode/test/agent/agent.test.ts @@ -20,8 +20,6 @@ vi.mock('@codingcode/infra/config', () => ({ memory: { enabled: false, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/agent/config.test.ts b/packages/codingcode/test/agent/config.test.ts index 8886fc7..684f4f3 100644 --- a/packages/codingcode/test/agent/config.test.ts +++ b/packages/codingcode/test/agent/config.test.ts @@ -9,8 +9,6 @@ vi.mock('@codingcode/infra/config', () => ({ memory: { enabled: false, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/agent/hooks-deps-type.test.ts b/packages/codingcode/test/agent/hooks-deps-type.test.ts index f5895d9..e898637 100644 --- a/packages/codingcode/test/agent/hooks-deps-type.test.ts +++ b/packages/codingcode/test/agent/hooks-deps-type.test.ts @@ -14,8 +14,6 @@ vi.mock('@codingcode/infra/config', () => ({ memory: { enabled: false, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/agent/loop-options.test.ts b/packages/codingcode/test/agent/loop-options.test.ts index 081211c..5d103e2 100644 --- a/packages/codingcode/test/agent/loop-options.test.ts +++ b/packages/codingcode/test/agent/loop-options.test.ts @@ -14,8 +14,6 @@ vi.mock('@codingcode/infra/config', () => ({ memory: { enabled: false, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/agent/memory-snapshot.test.ts b/packages/codingcode/test/agent/memory-snapshot.test.ts index 421297c..0f00c49 100644 --- a/packages/codingcode/test/agent/memory-snapshot.test.ts +++ b/packages/codingcode/test/agent/memory-snapshot.test.ts @@ -14,8 +14,6 @@ vi.mock('@codingcode/infra/config', () => ({ memory: { enabled: false, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/agent/stop-hook.test.ts b/packages/codingcode/test/agent/stop-hook.test.ts index fd39033..1f75558 100644 --- a/packages/codingcode/test/agent/stop-hook.test.ts +++ b/packages/codingcode/test/agent/stop-hook.test.ts @@ -14,8 +14,6 @@ vi.mock('@codingcode/infra/config', () => ({ memory: { enabled: false, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/context/append-turn-end.test.ts b/packages/codingcode/test/context/append-turn-end.test.ts index 5ea8a24..672de42 100644 --- a/packages/codingcode/test/context/append-turn-end.test.ts +++ b/packages/codingcode/test/context/append-turn-end.test.ts @@ -14,8 +14,6 @@ vi.mock('@codingcode/infra/config', () => ({ memory: { enabled: false, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/memory/config.test.ts b/packages/codingcode/test/memory/config.test.ts index b260aa7..dbbddf7 100644 --- a/packages/codingcode/test/memory/config.test.ts +++ b/packages/codingcode/test/memory/config.test.ts @@ -27,8 +27,6 @@ function makeCfg(overrides?: Partial): MemoryConfig { return { enabled: true, model: '', - projectFile: '', - userFile: '', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], @@ -45,16 +43,7 @@ describe('Memory Config', () => { describe('getEffectiveTypes', () => { it('includes default types when enabled', () => { - const cfg: MemoryConfig = { - enabled: true, - model: '', - projectFile: '', - userFile: '', - maxBytes: 16384, - promptMaxBytes: 8192, - extraTypes: [], - disabledTypes: [], - }; + const cfg: MemoryConfig = makeCfg(); const types = getEffectiveTypes(cfg); expect(types).toHaveLength(3); @@ -71,16 +60,7 @@ describe('Memory Config', () => { enabled: true, }, ]; - const cfg: MemoryConfig = { - enabled: true, - model: '', - projectFile: '', - userFile: '', - maxBytes: 16384, - promptMaxBytes: 8192, - extraTypes: extra, - disabledTypes: [], - }; + const cfg: MemoryConfig = makeCfg({ extraTypes: extra }); const types = getEffectiveTypes(cfg); expect(types).toHaveLength(4); @@ -88,16 +68,7 @@ describe('Memory Config', () => { }); it('filters disabled types', () => { - const cfg: MemoryConfig = { - enabled: true, - model: '', - projectFile: '', - userFile: '', - maxBytes: 16384, - promptMaxBytes: 8192, - extraTypes: [], - disabledTypes: ['user', 'project'], - }; + const cfg: MemoryConfig = makeCfg({ disabledTypes: ['user', 'project'] }); const types = getEffectiveTypes(cfg); expect(types).toHaveLength(1); @@ -112,16 +83,7 @@ describe('Memory Config', () => { enabled: true, }, ]; - const cfg: MemoryConfig = { - enabled: true, - model: '', - projectFile: '', - userFile: '', - maxBytes: 16384, - promptMaxBytes: 8192, - extraTypes: extra, - disabledTypes: ['custom'], - }; + const cfg: MemoryConfig = makeCfg({ extraTypes: extra, disabledTypes: ['custom'] }); const types = getEffectiveTypes(cfg); expect(types.map((t) => t.name)).not.toContain('custom'); @@ -135,16 +97,7 @@ describe('Memory Config', () => { enabled: false, }, ]; - const cfg: MemoryConfig = { - enabled: true, - model: '', - projectFile: '', - userFile: '', - maxBytes: 16384, - promptMaxBytes: 8192, - extraTypes: extra, - disabledTypes: [], - }; + const cfg: MemoryConfig = makeCfg({ extraTypes: extra }); const types = getEffectiveTypes(cfg); expect(types.map((t) => t.name)).not.toContain('disabled_custom'); diff --git a/packages/codingcode/test/memory/index.test.ts b/packages/codingcode/test/memory/index.test.ts index b8b705b..762d7e9 100644 --- a/packages/codingcode/test/memory/index.test.ts +++ b/packages/codingcode/test/memory/index.test.ts @@ -47,8 +47,6 @@ vi.mock('../../src/memory/config.js', () => ({ getMemoryConfig: vi.fn(() => ({ enabled: false, model: '', - projectFile: '.codingcode/memory.md', - userFile: '~/.codingcode/memory.md', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], @@ -74,8 +72,6 @@ describe('Memory Index', () => { vi.mocked(getMemoryConfig).mockReturnValue({ enabled: true, model: '', - projectFile: '.codingcode/memory.md', - userFile: '~/.codingcode/memory.md', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], @@ -86,13 +82,11 @@ describe('Memory Index', () => { expect(result).toBe(''); }); - it('loads and combines memory from project and user files', async () => { + it('loads memory from project file', async () => { const { getMemoryConfig } = await import('../../src/memory/config.js'); vi.mocked(getMemoryConfig).mockReturnValue({ enabled: true, model: '', - projectFile: '.codingcode/memory.md', - userFile: '~/.codingcode/memory.md', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], @@ -121,8 +115,6 @@ describe('Memory Index', () => { vi.mocked(getMemoryConfig).mockReturnValue({ enabled: true, model: '', - projectFile: '.codingcode/memory.md', - userFile: '~/.codingcode/memory.md', maxBytes: 16384, promptMaxBytes: 100, extraTypes: [], @@ -156,8 +148,6 @@ describe('Memory Index', () => { vi.mocked(getMemoryConfig).mockReturnValue({ enabled: true, model: '', - projectFile: '.codingcode/memory.md', - userFile: '~/.codingcode/memory.md', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], @@ -173,8 +163,6 @@ describe('Memory Index', () => { vi.mocked(getMemoryConfig).mockReturnValue({ enabled: true, model: '', - projectFile: '.codingcode/memory.md', - userFile: '~/.codingcode/memory.md', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [], diff --git a/packages/codingcode/test/memory/storage.test.ts b/packages/codingcode/test/memory/storage.test.ts index c64598c..66e7ae1 100644 --- a/packages/codingcode/test/memory/storage.test.ts +++ b/packages/codingcode/test/memory/storage.test.ts @@ -3,8 +3,6 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; import * as os from 'node:os'; import { - resolveProjectMemoryPath, - resolveUserMemoryPath, readMemoryFile, extractAutoBlock, replaceAutoBlock, @@ -31,34 +29,6 @@ afterEach(() => { cleanup(); }); -describe('Path Resolution', () => { - it('resolves project memory path - relative', () => { - const cfg = { projectFile: '.codingcode/memory.md' } as any; - const result = resolveProjectMemoryPath(tmpDir, cfg); - expect(result).toBe(path.join(tmpDir, '.codingcode/memory.md')); - }); - - it('resolves project memory path - absolute', () => { - const absPath = path.join(tmpDir, 'absolute.md'); - const cfg = { projectFile: absPath } as any; - const result = resolveProjectMemoryPath(tmpDir, cfg); - expect(result).toBe(absPath); - }); - - it('resolves user memory path - tilde expansion', () => { - const cfg = { userFile: '~/.codingcode/memory.md' } as any; - const result = resolveUserMemoryPath(cfg); - expect(result).toBe(path.join(os.homedir(), '.codingcode/memory.md')); - }); - - it('resolves user memory path - absolute', () => { - const absPath = path.join(tmpDir, 'user.md'); - const cfg = { userFile: absPath } as any; - const result = resolveUserMemoryPath(cfg); - expect(result).toBe(absPath); - }); -}); - describe('File Operations', () => { it('reads non-existent file as empty string', () => { const result = readMemoryFile(path.join(tmpDir, 'nonexistent.md')); diff --git a/packages/desktop/src/settings/GlobalSettingsPage.tsx b/packages/desktop/src/settings/GlobalSettingsPage.tsx index 4cff266..addd4f4 100644 --- a/packages/desktop/src/settings/GlobalSettingsPage.tsx +++ b/packages/desktop/src/settings/GlobalSettingsPage.tsx @@ -5,9 +5,8 @@ import McpPanel from './McpPanel'; import HooksPanel from './HooksPanel'; import SubagentsPanel from './SubagentsPanel'; import SkillPanel from './SkillPanel'; -import MemoryPanel from './MemoryPanel'; -type Section = 'theme' | 'mcp' | 'hooks' | 'agents' | 'skills' | 'memory'; +type Section = 'theme' | 'mcp' | 'hooks' | 'agents' | 'skills'; const NAV_ITEMS: { id: Section; label: string }[] = [ { id: 'theme', label: '主题' }, @@ -15,7 +14,6 @@ const NAV_ITEMS: { id: Section; label: string }[] = [ { id: 'hooks', label: '钩子' }, { id: 'agents', label: '子智能体' }, { id: 'skills', label: 'Skills' }, - { id: 'memory', label: '记忆模式' }, ]; const THEMES = [ @@ -90,7 +88,6 @@ export default function GlobalSettingsPage() { {section === 'hooks' && } {section === 'agents' && } {section === 'skills' && } - {section === 'memory' && } diff --git a/packages/infra/src/config.ts b/packages/infra/src/config.ts index c3cdffa..4c67108 100644 --- a/packages/infra/src/config.ts +++ b/packages/infra/src/config.ts @@ -22,8 +22,6 @@ export interface MemoryConfig { * Use full id format "model@API_KEY_ENV" to avoid ambiguity (e.g. "deepseek-chat@DEEPSEEK_API_KEY"). * Can also use bare model id (e.g. "deepseek-chat") or display name, first match wins. */ model: string; - projectFile: string; - userFile: string; maxBytes: number; promptMaxBytes: number; extraTypes: MemoryTypeConfig[]; @@ -59,8 +57,6 @@ export const DEFAULT_MEMORY_TYPES: MemoryTypeConfig[] = [ export const DEFAULT_MEMORY: MemoryConfig = { enabled: false, model: '', - projectFile: '.codingcode/memory.md', - userFile: '~/.codingcode/memory.md', maxBytes: 16384, promptMaxBytes: 8192, extraTypes: [],