mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
The CLI already emits memory_saved events and stores Markdown memory files, but the desktop app had no usable surface for seeing or editing those writes. This adds a project memory API, a Settings memory editor, chat memory event cards, and routing from /memory or /context into the memory UI. Constraint: Memory files live under Claude project storage and must remain plain Markdown editable by users. Rejected: Only expose raw filesystem links | users need an in-app review and edit flow from chat. Confidence: high Scope-risk: moderate Directive: Keep memory storage project-scoped and preserve unknown Markdown content when editing. Tested: bun test src/server/__tests__/ws-memory-events.test.ts src/server/__tests__/memory.test.ts Tested: bun run check:server Tested: bun run check:desktop Tested: agent-browser E2E for chat memory card, Open Memory navigation, and responsive Markdown editor layout Not-tested: Live model auto-memory trigger rate with real provider credentials
73 lines
2.7 KiB
TypeScript
73 lines
2.7 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
findSlashToken,
|
|
insertSlashTrigger,
|
|
mergeSlashCommands,
|
|
replaceSlashCommand,
|
|
resolveSlashUiAction,
|
|
} from './composerUtils'
|
|
|
|
describe('composerUtils', () => {
|
|
it('finds slash token without trailing space', () => {
|
|
expect(findSlashToken('/rev', 4)).toEqual({ start: 0, filter: 'rev' })
|
|
expect(findSlashToken('hello /rev', 10)).toEqual({ start: 6, filter: 'rev' })
|
|
})
|
|
|
|
it('does not treat slash followed by a space as an active token', () => {
|
|
expect(findSlashToken('/ review', 8)).toBeNull()
|
|
})
|
|
|
|
it('inserts a slash trigger without appending a trailing space', () => {
|
|
expect(insertSlashTrigger('', 0)).toEqual({ value: '/', cursorPos: 1 })
|
|
expect(insertSlashTrigger('hello', 5)).toEqual({ value: 'hello /', cursorPos: 7 })
|
|
})
|
|
|
|
it('replaces the current slash token with a command and one trailing separator', () => {
|
|
expect(replaceSlashCommand('/rev', 4, 'review')).toEqual({
|
|
value: '/review ',
|
|
cursorPos: 8,
|
|
})
|
|
})
|
|
|
|
it('merges fallback commands so built-in entries like /clear remain visible', () => {
|
|
expect(
|
|
mergeSlashCommands([
|
|
{ name: 'help', description: '' },
|
|
]),
|
|
).toEqual(
|
|
expect.arrayContaining([
|
|
{ name: 'help', description: 'Show available desktop and agent commands' },
|
|
{ name: 'clear', description: 'Clear conversation history' },
|
|
{ name: 'context', description: 'Show current context usage' },
|
|
]),
|
|
)
|
|
})
|
|
|
|
it('keeps server-provided descriptions when they exist', () => {
|
|
expect(
|
|
mergeSlashCommands([
|
|
{ name: 'clear', description: 'Server description' },
|
|
]),
|
|
).toEqual(
|
|
expect.arrayContaining([
|
|
{ name: 'clear', description: 'Server description' },
|
|
]),
|
|
)
|
|
})
|
|
|
|
it('resolves hidden settings aliases without displaying duplicate fallback rows', () => {
|
|
expect(resolveSlashUiAction('plugins')).toEqual({ type: 'settings', tab: 'plugins' })
|
|
expect(resolveSlashUiAction('memory')).toEqual({ type: 'settings', tab: 'memory' })
|
|
expect(resolveSlashUiAction('doctor')).toEqual({ type: 'settings', tab: 'diagnostics' })
|
|
expect(mergeSlashCommands([]).map((command) => command.name)).toContain('plugin')
|
|
expect(mergeSlashCommands([]).map((command) => command.name)).toContain('memory')
|
|
expect(mergeSlashCommands([]).map((command) => command.name)).not.toContain('plugins')
|
|
})
|
|
|
|
it('routes session inspection commands to the desktop panel', () => {
|
|
expect(resolveSlashUiAction('cost')).toEqual({ type: 'panel', command: 'cost' })
|
|
expect(resolveSlashUiAction('context')).toEqual({ type: 'panel', command: 'context' })
|
|
expect(resolveSlashUiAction('status')).toEqual({ type: 'panel', command: 'status' })
|
|
})
|
|
})
|