From c03af8cd12efa0ddb55e6aec507cfe57cbba46c5 Mon Sep 17 00:00:00 2001 From: minorcell Date: Mon, 18 May 2026 02:44:55 +0800 Subject: [PATCH 1/2] refactor: tui --- packages/core/AGENTS.md | 14 --- packages/tools/AGENTS.md | 92 ------------------- packages/tools/CLAUDE.md | 1 - packages/tui/AGENTS.md | 18 ---- packages/tui/src/{ => app}/App.tsx | 72 +++++++-------- packages/tui/src/app/hooks/useApproval.ts | 18 ++++ packages/tui/src/commands/index.tsx | 7 +- packages/tui/src/commands/mcp/add.tsx | 2 +- packages/tui/src/commands/mcp/get.tsx | 2 +- packages/tui/src/commands/mcp/list.tsx | 2 +- packages/tui/src/commands/mcp/login.tsx | 2 +- packages/tui/src/commands/mcp/logout.tsx | 2 +- packages/tui/src/controllers/types.ts | 17 ---- .../approval}/ApprovalOverlay.tsx | 0 .../approval/approvalNotification.test.ts} | 2 +- .../approval/approvalNotification.ts} | 0 .../composer}/Composer.tsx | 15 ++- .../composer}/SuggestionPanel.tsx | 0 .../composer/composerInput.test.ts} | 2 +- .../composer/composerInput.ts} | 0 .../composer/composerKeys.test.ts} | 2 +- .../composer/composerKeys.ts} | 0 .../composer/fileSuggestions.test.ts} | 2 +- .../composer/fileSuggestions.ts} | 2 +- .../composer/pasteBurst.test.ts} | 2 +- .../composer/pasteBurst.ts} | 2 +- .../mcp}/McpActivationOverlay.tsx | 0 .../mcp/mcpHelpers.test.ts} | 2 +- .../mcp/mcpHelpers.ts} | 0 .../src/{ => features}/review/backend.test.ts | 0 .../tui/src/{ => features}/review/backend.ts | 0 .../session/historyParser.test.ts} | 4 +- .../session/historyParser.ts} | 2 +- .../session/sessionHistory.test.ts} | 2 +- .../session/sessionHistory.ts} | 0 .../src/{ => features}/setup/SetupWizard.tsx | 0 .../timeline}/Cells.tsx | 4 +- .../timeline}/ChatWidget.tsx | 2 +- .../timeline}/MarkdownRenderer.test.ts | 0 .../timeline}/MarkdownRenderer.tsx | 2 +- .../timeline/chatTimeline.test.ts} | 2 +- .../timeline/chatTimeline.ts} | 4 +- .../timeline/markdownParser.test.ts} | 2 +- .../timeline/markdownParser.ts} | 0 packages/tui/src/index.ts | 4 +- .../tui/src/{ => shared/lib}/constants.ts | 0 .../lib/taskPrompt.test.ts} | 2 +- .../lib/taskPrompt.ts} | 2 +- .../tui/src/{ => shared/lib}/utils.test.ts | 0 packages/tui/src/{ => shared/lib}/utils.ts | 2 +- packages/tui/src/{ => shared/lib}/version.ts | 0 .../src/{types.ts => shared/types/index.ts} | 18 ++++ .../src/{bottom_pane => shared/ui}/Footer.tsx | 0 packages/tui/src/slash/registry.test.ts | 71 -------------- packages/tui/src/slash/registry.ts | 1 - packages/tui/src/slash/types.ts | 7 -- 56 files changed, 108 insertions(+), 303 deletions(-) delete mode 100644 packages/core/AGENTS.md delete mode 100644 packages/tools/AGENTS.md delete mode 120000 packages/tools/CLAUDE.md delete mode 100644 packages/tui/AGENTS.md rename packages/tui/src/{ => app}/App.tsx (95%) create mode 100644 packages/tui/src/app/hooks/useApproval.ts delete mode 100644 packages/tui/src/controllers/types.ts rename packages/tui/src/{overlays => features/approval}/ApprovalOverlay.tsx (100%) rename packages/tui/src/{notifications/approval_notification.test.ts => features/approval/approvalNotification.test.ts} (98%) rename packages/tui/src/{notifications/approval_notification.ts => features/approval/approvalNotification.ts} (100%) rename packages/tui/src/{bottom_pane => features/composer}/Composer.tsx (98%) rename packages/tui/src/{bottom_pane => features/composer}/SuggestionPanel.tsx (100%) rename packages/tui/src/{bottom_pane/composer_input.test.ts => features/composer/composerInput.test.ts} (99%) rename packages/tui/src/{bottom_pane/composer_input.ts => features/composer/composerInput.ts} (100%) rename packages/tui/src/{bottom_pane/composer_keys.test.ts => features/composer/composerKeys.test.ts} (96%) rename packages/tui/src/{bottom_pane/composer_keys.ts => features/composer/composerKeys.ts} (100%) rename packages/tui/src/{controllers/file_suggestions.test.ts => features/composer/fileSuggestions.test.ts} (99%) rename packages/tui/src/{controllers/file_suggestions.ts => features/composer/fileSuggestions.ts} (90%) rename packages/tui/src/{bottom_pane/paste_burst.test.ts => features/composer/pasteBurst.test.ts} (98%) rename packages/tui/src/{bottom_pane/paste_burst.ts => features/composer/pasteBurst.ts} (99%) rename packages/tui/src/{overlays => features/mcp}/McpActivationOverlay.tsx (100%) rename packages/tui/src/{mcp_helpers.test.ts => features/mcp/mcpHelpers.test.ts} (94%) rename packages/tui/src/{mcp_helpers.ts => features/mcp/mcpHelpers.ts} (100%) rename packages/tui/src/{ => features}/review/backend.test.ts (100%) rename packages/tui/src/{ => features}/review/backend.ts (100%) rename packages/tui/src/{controllers/history_parser.test.ts => features/session/historyParser.test.ts} (95%) rename packages/tui/src/{controllers/history_parser.ts => features/session/historyParser.ts} (96%) rename packages/tui/src/{controllers/session_history.test.ts => features/session/sessionHistory.test.ts} (97%) rename packages/tui/src/{controllers/session_history.ts => features/session/sessionHistory.ts} (100%) rename packages/tui/src/{ => features}/setup/SetupWizard.tsx (100%) rename packages/tui/src/{chatwidget => features/timeline}/Cells.tsx (98%) rename packages/tui/src/{chatwidget => features/timeline}/ChatWidget.tsx (98%) rename packages/tui/src/{chatwidget => features/timeline}/MarkdownRenderer.test.ts (100%) rename packages/tui/src/{chatwidget => features/timeline}/MarkdownRenderer.tsx (99%) rename packages/tui/src/{state/chat_timeline.test.ts => features/timeline/chatTimeline.test.ts} (99%) rename packages/tui/src/{state/chat_timeline.ts => features/timeline/chatTimeline.ts} (99%) rename packages/tui/src/{chatwidget/markdown_parser.test.ts => features/timeline/markdownParser.test.ts} (98%) rename packages/tui/src/{chatwidget/markdown_parser.ts => features/timeline/markdownParser.ts} (100%) rename packages/tui/src/{ => shared/lib}/constants.ts (100%) rename packages/tui/src/{task_prompt.test.ts => shared/lib/taskPrompt.test.ts} (94%) rename packages/tui/src/{task_prompt.ts => shared/lib/taskPrompt.ts} (89%) rename packages/tui/src/{ => shared/lib}/utils.test.ts (100%) rename packages/tui/src/{ => shared/lib}/utils.ts (97%) rename packages/tui/src/{ => shared/lib}/version.ts (100%) rename packages/tui/src/{types.ts => shared/types/index.ts} (79%) rename packages/tui/src/{bottom_pane => shared/ui}/Footer.tsx (100%) delete mode 100644 packages/tui/src/slash/registry.test.ts delete mode 100644 packages/tui/src/slash/registry.ts delete mode 100644 packages/tui/src/slash/types.ts diff --git a/packages/core/AGENTS.md b/packages/core/AGENTS.md deleted file mode 100644 index c84cd7f..0000000 --- a/packages/core/AGENTS.md +++ /dev/null @@ -1,14 +0,0 @@ -# Core Package Guide - -This file defines local contribution rules for `packages/core`. - -## Scope - -- Owns session state machine, provider/config flows, and shared core types. -- Keep core logic deterministic and side-effect-light when possible. - -## Change Rules - -- Changes to config/provider contracts must include regression coverage for serialization and CLI argument flows. -- Keep public behavior changes synchronized with root docs and package docs. -- 需要维护agents.md的更新。 diff --git a/packages/tools/AGENTS.md b/packages/tools/AGENTS.md deleted file mode 100644 index 0671dd8..0000000 --- a/packages/tools/AGENTS.md +++ /dev/null @@ -1,92 +0,0 @@ -# Tools Package Guide - -This file describes the architecture and implementation rules for -`packages/tools`. - -## Purpose - -`packages/tools` is the single package responsible for: - -- Defining native tools. -- Loading and routing MCP tools. -- Executing tool calls via orchestrator. -- Applying approval and policy checks for tool execution. - -`packages/core` should call into tools, but should not implement tool runtime -logic. - -## Package Structure - -- `src/tools/*` - - Native tool implementations (codex-style tools such as - `exec_command`, `write_stdin`, `apply_patch`, `read_text_file`, - `read_media_file`, `read_files`, `write_file`, `edit_file`, - `list_directory`, `search_files`, `list_mcp_resources`, - `read_mcp_resource`, `update_plan`, `webfetch`, `get_memory`). - - `types.ts` provides `defineMcpTool(...)` to build unified tool objects. - - `mcp.ts` contains common `CallToolResult` helpers. -- `src/router/*` - - `index.ts`: ToolRouter entry (query/list/dispatch/tool definitions). - - `native/*`: in-memory native registry. - - `mcp/*`: MCP registry and connection pool. - - `types.ts`: shared tool and MCP config types. -- `src/orchestrator/*` - - Tool execution orchestration. - - Supports single/multi action execution, failure policy, status/error model. -- `src/approval/*` - - Risk classification, request fingerprinting, approval cache and decisions. -- `src/index.ts` - - Public exports for native tools, router, orchestrator, and approval. - -## Unified Tool Format - -All native tools must use one unified runtime shape (router-compatible): - -- `name: string` -- `description: string` -- `source: "native"` -- `inputSchema: JSON Schema` -- `validateInput(input) -> { ok: true, data } | { ok: false, error }` -- `execute(input) -> Promise` - -Use `defineMcpTool(...)` from `src/tools/types.ts` to define native tools. -Do not add a separate adaptation layer. - -## Routing Model - -Tool router distinguishes only: - -- Native tools (`source: "native"`). -- MCP tools (`source: "mcp"`). - -Both are exposed through the same `Tool` interface in `src/router/types.ts`. - -## Orchestrator and Approval Boundaries - -- Orchestrator owns execution lifecycle and result model. -- Approval module owns risk/decision/fingerprint logic. -- Router owns discovery/registration/dispatch. -- Native tool files should focus on tool-specific behavior only. - -## Adding a New Native Tool - -1. Add implementation under `src/tools/.ts`. -2. Define tool with `defineMcpTool(...)`. -3. Register export in `src/index.ts` via `TOOLKIT`/`TOOL_LIST`. -4. Add tests next to tool implementation (`*.test.ts`). -5. Verify: - - `pnpm run test:tools` - - `pnpm run build` - -## Testing Notes - -Prefer validating tool contracts through: - -- `tool.validateInput(...)` for input validation. -- `tool.execute(...)` for runtime behavior and output. - -Avoid relying on internal schema objects from test code. - -## Maintenance - -- 需要维护agents.md的更新。 diff --git a/packages/tools/CLAUDE.md b/packages/tools/CLAUDE.md deleted file mode 120000 index 47dc3e3..0000000 --- a/packages/tools/CLAUDE.md +++ /dev/null @@ -1 +0,0 @@ -AGENTS.md \ No newline at end of file diff --git a/packages/tui/AGENTS.md b/packages/tui/AGENTS.md deleted file mode 100644 index 5de9c94..0000000 --- a/packages/tui/AGENTS.md +++ /dev/null @@ -1,18 +0,0 @@ -# TUI Package Guide - -This file defines local contribution rules for `packages/tui`. - -## Scope - -- Owns interactive TUI runtime: `App`, chat transcript rendering, bottom pane input, slash commands, setup and approval overlays. -- Owns TUI-side controllers/state modules (`controllers/*`, `state/*`) and user-facing interaction behavior. -- Keep business/session logic in `packages/core`; keep executable bootstrap and argv handling in `src/cli.tsx` within this package. - -## Change Rules - -- Keep slash command behavior centralized in `src/slash/registry.ts`; avoid duplicate command dispatch paths. -- Keep UI state changes event-driven and testable (prefer pure transforms in `state/*` and parser/controller helpers). -- If shortcuts, slash commands, or approval UX change, sync `README.md` / `README.zh.md`. -- If shortcuts, slash commands, or approval UX change, sync `site/content/docs/cli-tui.md`. -- Add or update tests next to changed modules (`*.test.ts`) and run `pnpm run test:tui`. -- 需要维护agents.md的更新。 diff --git a/packages/tui/src/App.tsx b/packages/tui/src/app/App.tsx similarity index 95% rename from packages/tui/src/App.tsx rename to packages/tui/src/app/App.tsx index 4e22144..24cfe0b 100644 --- a/packages/tui/src/App.tsx +++ b/packages/tui/src/app/App.tsx @@ -16,33 +16,33 @@ import { type ModelProfileOverride, type ProviderConfig, } from '@memo/core' -import type { ApprovalDecision, ApprovalRequest } from '@memo/tools/approval' -import { ChatWidget } from './chatwidget/ChatWidget' -import { Composer } from './bottom_pane/Composer' -import { Footer } from './bottom_pane/Footer' -import { ApprovalOverlay } from './overlays/ApprovalOverlay' -import { McpActivationOverlay } from './overlays/McpActivationOverlay' -import { notifyApprovalRequested } from './notifications/approval_notification' -import { SetupWizard } from './setup/SetupWizard' -import { parseHistoryLog } from './controllers/history_parser' +import { useApproval } from './hooks/useApproval' +import { ChatWidget } from '../features/timeline/ChatWidget' +import { Composer } from '../features/composer/Composer' +import { Footer } from '../shared/ui/Footer' +import { ApprovalOverlay } from '../features/approval/ApprovalOverlay' +import { McpActivationOverlay } from '../features/mcp/McpActivationOverlay' +import { notifyApprovalRequested } from '../features/approval/approvalNotification' +import { SetupWizard } from '../features/setup/SetupWizard' +import { parseHistoryLog } from '../features/session/historyParser' import { chatTimelineReducer, createInitialTimelineState, type ChatTimelineAction, -} from './state/chat_timeline' -import { calculateContextPercent, inferParallelToolStatuses, inferToolStatus } from './utils' -import { checkForUpdate, findLocalPackageInfoSync } from './version' -import type { SessionHistoryEntry } from './controllers/session_history' -import { loadTaskPrompt } from './task_prompt' -import { resolveReviewBackend } from './review/backend' +} from '../features/timeline/chatTimeline' +import { calculateContextPercent, inferParallelToolStatuses, inferToolStatus } from '../shared/lib/utils' +import { checkForUpdate, findLocalPackageInfoSync } from '../shared/lib/version' +import type { SessionHistoryEntry } from '../features/session/sessionHistory' +import { loadTaskPrompt } from '../shared/lib/taskPrompt' +import { resolveReviewBackend } from '../features/review/backend' import { formatSlashCommand, PLAIN_EXIT_COMMAND, SLASH_COMMANDS, TOOL_PERMISSION_MODES, type ToolPermissionMode, -} from './constants' -import type { ParsedHistoryLog } from './controllers/history_parser' +} from '../shared/lib/constants' +import type { ParsedHistoryLog } from '../features/session/historyParser' export type AppProps = { sessionOptions: AgentSessionOptions @@ -147,10 +147,7 @@ export function App({ toolPermissionMode: defaultToolPermissionMode, }) - const [busy, setBusy] = useState(false) const [inputHistory, setInputHistory] = useState([]) - const [sessionLogPath, setSessionLogPath] = useState(null) - const [pendingHistoryMessages, setPendingHistoryMessages] = useState(null) const [contextLimit, setContextLimit] = useState( resolveContextLimit({ name: providerName, model }), @@ -165,14 +162,16 @@ export function App({ useState(initialActiveMcpServers) const [exitMessage, setExitMessage] = useState(null) + const [busy, setBusy] = useState(false) + const [sessionLogPath, setSessionLogPath] = useState(null) + const [pendingHistoryMessages, setPendingHistoryMessages] = useState(null) const [session, setSession] = useState(null) const sessionRef = useRef(null) - const currentTurnRef = useRef(null) const nextUserInputOverrideRef = useRef(null) - const [pendingApproval, setPendingApproval] = useState(null) - const approvalResolverRef = useRef<((decision: ApprovalDecision) => void) | null>(null) + const { pendingApproval, setPendingApproval, approvalResolverRef, handleApprovalDecision } = + useApproval() const localPackageInfo = useMemo(() => findLocalPackageInfoSync(), []) @@ -223,7 +222,7 @@ export function App({ toolPermissionMode === TOOL_PERMISSION_MODES.FULL || toolPermissionMode === TOOL_PERMISSION_MODES.NONE ? undefined - : (request: ApprovalRequest) => + : (request) => new Promise((resolve) => { void notifyApprovalRequested(request) setPendingApproval(request) @@ -374,6 +373,14 @@ export function App({ } }, [appendSystemMessage, deps, mcpSelectionPending, sessionOptionsState, setupPending]) + useEffect(() => { + return () => { + if (sessionRef.current) { + void sessionRef.current.close() + } + } + }, []) + useEffect(() => { let cancelled = false ;(async () => { @@ -390,14 +397,6 @@ export function App({ } }, [appendSystemMessage]) - useEffect(() => { - return () => { - if (sessionRef.current) { - void sessionRef.current.close() - } - } - }, []) - const handleExit = useCallback(async () => { const resolver = approvalResolverRef.current if (resolver) { @@ -835,15 +834,6 @@ export function App({ setPendingHistoryMessages(null) }, [pendingHistoryMessages, session]) - const handleApprovalDecision = useCallback((decision: ApprovalDecision) => { - const resolver = approvalResolverRef.current - if (resolver) { - resolver(decision) - approvalResolverRef.current = null - } - setPendingApproval(null) - }, []) - const contextPercent = calculateContextPercent(currentContextTokens, contextLimit) const chatHeader = useMemo( () => ({ diff --git a/packages/tui/src/app/hooks/useApproval.ts b/packages/tui/src/app/hooks/useApproval.ts new file mode 100644 index 0000000..c559140 --- /dev/null +++ b/packages/tui/src/app/hooks/useApproval.ts @@ -0,0 +1,18 @@ +import { useState, useRef, useCallback } from 'react' +import type { ApprovalDecision, ApprovalRequest } from '@memo/tools/approval' + +export function useApproval() { + const [pendingApproval, setPendingApproval] = useState(null) + const approvalResolverRef = useRef<((decision: ApprovalDecision) => void) | null>(null) + + const handleApprovalDecision = useCallback((decision: ApprovalDecision) => { + const resolver = approvalResolverRef.current + if (resolver) { + resolver(decision) + approvalResolverRef.current = null + } + setPendingApproval(null) + }, []) + + return { pendingApproval, setPendingApproval, approvalResolverRef, handleApprovalDecision } +} diff --git a/packages/tui/src/commands/index.tsx b/packages/tui/src/commands/index.tsx index dd63433..036b153 100644 --- a/packages/tui/src/commands/index.tsx +++ b/packages/tui/src/commands/index.tsx @@ -16,9 +16,9 @@ import { type AgentSessionOptions, type MemoConfig, } from '@memo/core' -import { App } from '../App' -import { parseHistoryLog } from '../controllers/history_parser' -import { loadSessionHistoryEntries } from '../controllers/session_history' +import { App } from '../app/App' +import { parseHistoryLog } from '../features/session/historyParser' +import { loadSessionHistoryEntries } from '../features/session/sessionHistory' export const options = zod.object({ once: zod @@ -40,6 +40,7 @@ export const options = zod.object({ export const args = zod .array(zod.string()) + .default([]) .describe(argument({ name: 'question', description: 'Your question' })) async function ensureProviderConfig(mode: 'plain' | 'tui') { diff --git a/packages/tui/src/commands/mcp/add.tsx b/packages/tui/src/commands/mcp/add.tsx index 09808f6..1860f05 100644 --- a/packages/tui/src/commands/mcp/add.tsx +++ b/packages/tui/src/commands/mcp/add.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react' import zod from 'zod' import { option, argument } from 'pastel' import { loadMemoConfig, writeMemoConfig, type MCPServerConfig } from '@memo/core' -import { parseEnvAssignment } from '../../mcp_helpers' +import { parseEnvAssignment } from '../../features/mcp/mcpHelpers' export const options = zod.object({ url: zod diff --git a/packages/tui/src/commands/mcp/get.tsx b/packages/tui/src/commands/mcp/get.tsx index 72c3d4a..8a6f3cf 100644 --- a/packages/tui/src/commands/mcp/get.tsx +++ b/packages/tui/src/commands/mcp/get.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react' import zod from 'zod' import { option, argument } from 'pastel' import { loadMemoConfig } from '@memo/core' -import { formatServer } from '../../mcp_helpers' +import { formatServer } from '../../features/mcp/mcpHelpers' export const options = zod.object({ json: zod diff --git a/packages/tui/src/commands/mcp/list.tsx b/packages/tui/src/commands/mcp/list.tsx index 7b27a55..ea03408 100644 --- a/packages/tui/src/commands/mcp/list.tsx +++ b/packages/tui/src/commands/mcp/list.tsx @@ -3,7 +3,7 @@ import zod from 'zod' import { option } from 'pastel' import { loadMemoConfig } from '@memo/core' import { getMcpAuthStatus } from '@memo/tools/router/mcp/oauth' -import { formatServer, oauthSettingsFromLoaded } from '../../mcp_helpers' +import { formatServer, oauthSettingsFromLoaded } from '../../features/mcp/mcpHelpers' import type { McpAuthStatus } from '@memo/tools/router/mcp/oauth' export const options = zod.object({ diff --git a/packages/tui/src/commands/mcp/login.tsx b/packages/tui/src/commands/mcp/login.tsx index 3910b10..77a297e 100644 --- a/packages/tui/src/commands/mcp/login.tsx +++ b/packages/tui/src/commands/mcp/login.tsx @@ -3,7 +3,7 @@ import zod from 'zod' import { option, argument } from 'pastel' import { loadMemoConfig } from '@memo/core' import { loginMcpServerOAuth } from '@memo/tools/router/mcp/oauth' -import { getErrorMessage, oauthSettingsFromLoaded } from '../../mcp_helpers' +import { getErrorMessage, oauthSettingsFromLoaded } from '../../features/mcp/mcpHelpers' export const options = zod.object({ scopes: zod diff --git a/packages/tui/src/commands/mcp/logout.tsx b/packages/tui/src/commands/mcp/logout.tsx index 2fe09c3..1ee3294 100644 --- a/packages/tui/src/commands/mcp/logout.tsx +++ b/packages/tui/src/commands/mcp/logout.tsx @@ -3,7 +3,7 @@ import zod from 'zod' import { argument } from 'pastel' import { loadMemoConfig } from '@memo/core' import { logoutMcpServerOAuth } from '@memo/tools/router/mcp/oauth' -import { getErrorMessage, oauthSettingsFromLoaded } from '../../mcp_helpers' +import { getErrorMessage, oauthSettingsFromLoaded } from '../../features/mcp/mcpHelpers' export const args = zod .array(zod.string()) diff --git a/packages/tui/src/controllers/types.ts b/packages/tui/src/controllers/types.ts deleted file mode 100644 index cf0669e..0000000 --- a/packages/tui/src/controllers/types.ts +++ /dev/null @@ -1,17 +0,0 @@ -export type FileSuggestion = { - id: string - path: string - name: string - parent?: string - isDir: boolean -} - -export type FileSuggestionRequest = { - cwd: string - query: string - limit?: number - maxDepth?: number - maxEntries?: number - respectGitIgnore?: boolean - ignoreGlobs?: string[] -} diff --git a/packages/tui/src/overlays/ApprovalOverlay.tsx b/packages/tui/src/features/approval/ApprovalOverlay.tsx similarity index 100% rename from packages/tui/src/overlays/ApprovalOverlay.tsx rename to packages/tui/src/features/approval/ApprovalOverlay.tsx diff --git a/packages/tui/src/notifications/approval_notification.test.ts b/packages/tui/src/features/approval/approvalNotification.test.ts similarity index 98% rename from packages/tui/src/notifications/approval_notification.test.ts rename to packages/tui/src/features/approval/approvalNotification.test.ts index 9ccda0a..ac7a31d 100644 --- a/packages/tui/src/notifications/approval_notification.test.ts +++ b/packages/tui/src/features/approval/approvalNotification.test.ts @@ -1,6 +1,6 @@ import type { ApprovalRequest } from '@memo/tools/approval' import { describe, expect, test, vi } from 'vitest' -import { buildDesktopNotificationCommand, notifyApprovalRequested } from './approval_notification' +import { buildDesktopNotificationCommand, notifyApprovalRequested } from './approvalNotification' const REQUEST: ApprovalRequest = { toolName: 'exec_command', diff --git a/packages/tui/src/notifications/approval_notification.ts b/packages/tui/src/features/approval/approvalNotification.ts similarity index 100% rename from packages/tui/src/notifications/approval_notification.ts rename to packages/tui/src/features/approval/approvalNotification.ts diff --git a/packages/tui/src/bottom_pane/Composer.tsx b/packages/tui/src/features/composer/Composer.tsx similarity index 98% rename from packages/tui/src/bottom_pane/Composer.tsx rename to packages/tui/src/features/composer/Composer.tsx index 02d9ee5..0318de3 100644 --- a/packages/tui/src/bottom_pane/Composer.tsx +++ b/packages/tui/src/features/composer/Composer.tsx @@ -1,10 +1,9 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Box, Text, useInput, useStdout } from 'ink' import type { MCPServerConfig, ProviderConfig } from '@memo/core' -import { resolveSlashCommand, SLASH_SPECS } from '../slash/registry' -import type { SlashContext } from '../slash/types' -import { getFileSuggestions } from '../controllers/file_suggestions' -import { loadSessionHistoryEntries, type SessionHistoryEntry } from '../controllers/session_history' +import { resolveSlashCommand, SLASH_SPECS, type SlashContext } from '@memo/core/runtime/slash' +import { getFileSuggestions } from './fileSuggestions' +import { loadSessionHistoryEntries, type SessionHistoryEntry } from '../session/sessionHistory' import { SuggestionPanel, type SuggestionItem } from './SuggestionPanel' import { backspaceAtCursor, @@ -20,15 +19,15 @@ import { moveCursorToLineStart, moveCursorVertical, type EditorBuffer, -} from './composer_input' -import { resolveDeleteKind } from './composer_keys' -import { PasteBurst, type PasteBurstFlushResult } from './paste_burst' +} from './composerInput' +import { resolveDeleteKind } from './composerKeys' +import { PasteBurst, type PasteBurstFlushResult } from './pasteBurst' import { formatSlashCommand, SLASH_COMMANDS, TOOL_PERMISSION_MODES, type ToolPermissionMode, -} from '../constants' +} from '../../shared/lib/constants' const DOUBLE_ESC_WINDOW_MS = 400 const MODELS_SLASH_PREFIX = formatSlashCommand(SLASH_COMMANDS.MODELS) diff --git a/packages/tui/src/bottom_pane/SuggestionPanel.tsx b/packages/tui/src/features/composer/SuggestionPanel.tsx similarity index 100% rename from packages/tui/src/bottom_pane/SuggestionPanel.tsx rename to packages/tui/src/features/composer/SuggestionPanel.tsx diff --git a/packages/tui/src/bottom_pane/composer_input.test.ts b/packages/tui/src/features/composer/composerInput.test.ts similarity index 99% rename from packages/tui/src/bottom_pane/composer_input.test.ts rename to packages/tui/src/features/composer/composerInput.test.ts index d0313c9..cca0b32 100644 --- a/packages/tui/src/bottom_pane/composer_input.test.ts +++ b/packages/tui/src/features/composer/composerInput.test.ts @@ -15,7 +15,7 @@ import { moveCursorToLineEnd, moveCursorToLineStart, moveCursorVertical, -} from './composer_input' +} from './composerInput' describe('composer_input', () => { test('inserts text at cursor', () => { diff --git a/packages/tui/src/bottom_pane/composer_input.ts b/packages/tui/src/features/composer/composerInput.ts similarity index 100% rename from packages/tui/src/bottom_pane/composer_input.ts rename to packages/tui/src/features/composer/composerInput.ts diff --git a/packages/tui/src/bottom_pane/composer_keys.test.ts b/packages/tui/src/features/composer/composerKeys.test.ts similarity index 96% rename from packages/tui/src/bottom_pane/composer_keys.test.ts rename to packages/tui/src/features/composer/composerKeys.test.ts index 1796575..179080f 100644 --- a/packages/tui/src/bottom_pane/composer_keys.test.ts +++ b/packages/tui/src/features/composer/composerKeys.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert' import { describe, test } from 'vitest' -import { resolveDeleteKind } from './composer_keys' +import { resolveDeleteKind } from './composerKeys' describe('composer_keys', () => { test('prefers explicit backspace flag', () => { diff --git a/packages/tui/src/bottom_pane/composer_keys.ts b/packages/tui/src/features/composer/composerKeys.ts similarity index 100% rename from packages/tui/src/bottom_pane/composer_keys.ts rename to packages/tui/src/features/composer/composerKeys.ts diff --git a/packages/tui/src/controllers/file_suggestions.test.ts b/packages/tui/src/features/composer/fileSuggestions.test.ts similarity index 99% rename from packages/tui/src/controllers/file_suggestions.test.ts rename to packages/tui/src/features/composer/fileSuggestions.test.ts index 122cf2d..1b2a460 100644 --- a/packages/tui/src/controllers/file_suggestions.test.ts +++ b/packages/tui/src/features/composer/fileSuggestions.test.ts @@ -3,7 +3,7 @@ import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, test } from 'vitest' -import { getFileSuggestions, invalidateFileSuggestionCache } from './file_suggestions' +import { getFileSuggestions, invalidateFileSuggestionCache } from './fileSuggestions' const cleanupDirs: string[] = [] diff --git a/packages/tui/src/controllers/file_suggestions.ts b/packages/tui/src/features/composer/fileSuggestions.ts similarity index 90% rename from packages/tui/src/controllers/file_suggestions.ts rename to packages/tui/src/features/composer/fileSuggestions.ts index 67bbf5a..70a3823 100644 --- a/packages/tui/src/controllers/file_suggestions.ts +++ b/packages/tui/src/features/composer/fileSuggestions.ts @@ -5,7 +5,7 @@ import { type FileSuggestion as CoreFileSuggestion, type FileSuggestionRequest as CoreFileSuggestionRequest, } from '@memo/core/runtime/file_suggestions' -import type { FileSuggestion, FileSuggestionRequest } from './types' +import type { FileSuggestion, FileSuggestionRequest } from '../../shared/types' export function normalizePath(input: string): string { return normalizeCorePath(input) diff --git a/packages/tui/src/bottom_pane/paste_burst.test.ts b/packages/tui/src/features/composer/pasteBurst.test.ts similarity index 98% rename from packages/tui/src/bottom_pane/paste_burst.test.ts rename to packages/tui/src/features/composer/pasteBurst.test.ts index ff5bd4b..0f1237d 100644 --- a/packages/tui/src/bottom_pane/paste_burst.test.ts +++ b/packages/tui/src/features/composer/pasteBurst.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert' import { describe, test } from 'vitest' -import { PasteBurst, retroStartIndex } from './paste_burst' +import { PasteBurst, retroStartIndex } from './pasteBurst' describe('paste_burst', () => { test('holds first ASCII char and flushes as typed after timeout', () => { diff --git a/packages/tui/src/bottom_pane/paste_burst.ts b/packages/tui/src/features/composer/pasteBurst.ts similarity index 99% rename from packages/tui/src/bottom_pane/paste_burst.ts rename to packages/tui/src/features/composer/pasteBurst.ts index 5fd9dcc..33d328d 100644 --- a/packages/tui/src/bottom_pane/paste_burst.ts +++ b/packages/tui/src/features/composer/pasteBurst.ts @@ -1,4 +1,4 @@ -import { previousCursorIndex } from './composer_input' +import { previousCursorIndex } from './composerInput' const DEFAULT_MIN_CHARS = 3 const DEFAULT_CHAR_INTERVAL_MS = 8 diff --git a/packages/tui/src/overlays/McpActivationOverlay.tsx b/packages/tui/src/features/mcp/McpActivationOverlay.tsx similarity index 100% rename from packages/tui/src/overlays/McpActivationOverlay.tsx rename to packages/tui/src/features/mcp/McpActivationOverlay.tsx diff --git a/packages/tui/src/mcp_helpers.test.ts b/packages/tui/src/features/mcp/mcpHelpers.test.ts similarity index 94% rename from packages/tui/src/mcp_helpers.test.ts rename to packages/tui/src/features/mcp/mcpHelpers.test.ts index 10d3d37..334555a 100644 --- a/packages/tui/src/mcp_helpers.test.ts +++ b/packages/tui/src/features/mcp/mcpHelpers.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from 'vitest' -import { getErrorMessage, parseEnvAssignment } from './mcp_helpers' +import { getErrorMessage, parseEnvAssignment } from './mcpHelpers' describe('mcp helpers', () => { test('parseEnvAssignment parses valid assignment', () => { diff --git a/packages/tui/src/mcp_helpers.ts b/packages/tui/src/features/mcp/mcpHelpers.ts similarity index 100% rename from packages/tui/src/mcp_helpers.ts rename to packages/tui/src/features/mcp/mcpHelpers.ts diff --git a/packages/tui/src/review/backend.test.ts b/packages/tui/src/features/review/backend.test.ts similarity index 100% rename from packages/tui/src/review/backend.test.ts rename to packages/tui/src/features/review/backend.test.ts diff --git a/packages/tui/src/review/backend.ts b/packages/tui/src/features/review/backend.ts similarity index 100% rename from packages/tui/src/review/backend.ts rename to packages/tui/src/features/review/backend.ts diff --git a/packages/tui/src/controllers/history_parser.test.ts b/packages/tui/src/features/session/historyParser.test.ts similarity index 95% rename from packages/tui/src/controllers/history_parser.test.ts rename to packages/tui/src/features/session/historyParser.test.ts index 211f17f..295c278 100644 --- a/packages/tui/src/controllers/history_parser.test.ts +++ b/packages/tui/src/features/session/historyParser.test.ts @@ -1,7 +1,7 @@ import assert from 'node:assert' import { describe, test } from 'vitest' -import { TOOL_STATUS } from '../types' -import { parseHistoryLog } from './history_parser' +import { TOOL_STATUS } from '../../shared/types' +import { parseHistoryLog } from './historyParser' function line(event: Record): string { return JSON.stringify({ diff --git a/packages/tui/src/controllers/history_parser.ts b/packages/tui/src/features/session/historyParser.ts similarity index 96% rename from packages/tui/src/controllers/history_parser.ts rename to packages/tui/src/features/session/historyParser.ts index 88ca050..25f3f9e 100644 --- a/packages/tui/src/controllers/history_parser.ts +++ b/packages/tui/src/features/session/historyParser.ts @@ -4,7 +4,7 @@ import { type SessionTurnDetail, type SessionTurnStep, } from '@memo/core' -import { TOOL_STATUS, type StepView, type TurnView } from '../types' +import { TOOL_STATUS, type StepView, type TurnView } from '../../shared/types' export type ParsedHistoryLog = { summary: string diff --git a/packages/tui/src/controllers/session_history.test.ts b/packages/tui/src/features/session/sessionHistory.test.ts similarity index 97% rename from packages/tui/src/controllers/session_history.test.ts rename to packages/tui/src/features/session/sessionHistory.test.ts index b2de855..139b859 100644 --- a/packages/tui/src/controllers/session_history.test.ts +++ b/packages/tui/src/features/session/sessionHistory.test.ts @@ -3,7 +3,7 @@ import { mkdir, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { describe, test } from 'vitest' -import { loadSessionHistoryEntries } from './session_history' +import { loadSessionHistoryEntries } from './sessionHistory' function buildHistoryLine(event: Record): string { return JSON.stringify({ diff --git a/packages/tui/src/controllers/session_history.ts b/packages/tui/src/features/session/sessionHistory.ts similarity index 100% rename from packages/tui/src/controllers/session_history.ts rename to packages/tui/src/features/session/sessionHistory.ts diff --git a/packages/tui/src/setup/SetupWizard.tsx b/packages/tui/src/features/setup/SetupWizard.tsx similarity index 100% rename from packages/tui/src/setup/SetupWizard.tsx rename to packages/tui/src/features/setup/SetupWizard.tsx diff --git a/packages/tui/src/chatwidget/Cells.tsx b/packages/tui/src/features/timeline/Cells.tsx similarity index 98% rename from packages/tui/src/chatwidget/Cells.tsx rename to packages/tui/src/features/timeline/Cells.tsx index cadc0cf..4b0f182 100644 --- a/packages/tui/src/chatwidget/Cells.tsx +++ b/packages/tui/src/features/timeline/Cells.tsx @@ -6,8 +6,8 @@ import { type StepView, type ToolStatus, type TurnView, -} from '../types' -import { looksLikePathInput, safeStringify, toRelativeDisplayPath, truncate } from '../utils' +} from '../../shared/types' +import { looksLikePathInput, safeStringify, toRelativeDisplayPath, truncate } from '../../shared/lib/utils' import { MarkdownRenderer } from './MarkdownRenderer' function statusColor(status?: ToolStatus): string { diff --git a/packages/tui/src/chatwidget/ChatWidget.tsx b/packages/tui/src/features/timeline/ChatWidget.tsx similarity index 98% rename from packages/tui/src/chatwidget/ChatWidget.tsx rename to packages/tui/src/features/timeline/ChatWidget.tsx index 0d784de..863f1a5 100644 --- a/packages/tui/src/chatwidget/ChatWidget.tsx +++ b/packages/tui/src/features/timeline/ChatWidget.tsx @@ -1,6 +1,6 @@ import { memo, useMemo, useRef, useEffect } from 'react' import { Box, Static, Text } from 'ink' -import type { SystemMessage, TurnView } from '../types' +import type { SystemMessage, TurnView } from '../../shared/types' import { SystemCell, TurnCell } from './Cells' type HeaderInfo = { diff --git a/packages/tui/src/chatwidget/MarkdownRenderer.test.ts b/packages/tui/src/features/timeline/MarkdownRenderer.test.ts similarity index 100% rename from packages/tui/src/chatwidget/MarkdownRenderer.test.ts rename to packages/tui/src/features/timeline/MarkdownRenderer.test.ts diff --git a/packages/tui/src/chatwidget/MarkdownRenderer.tsx b/packages/tui/src/features/timeline/MarkdownRenderer.tsx similarity index 99% rename from packages/tui/src/chatwidget/MarkdownRenderer.tsx rename to packages/tui/src/features/timeline/MarkdownRenderer.tsx index 013ed5e..2552d9c 100644 --- a/packages/tui/src/chatwidget/MarkdownRenderer.tsx +++ b/packages/tui/src/features/timeline/MarkdownRenderer.tsx @@ -5,7 +5,7 @@ import { parseMarkdownContent, type InlineNode, type MarkdownBlock, -} from './markdown_parser' +} from './markdownParser' const HORIZONTAL_RULE_TEXT = '———' diff --git a/packages/tui/src/state/chat_timeline.test.ts b/packages/tui/src/features/timeline/chatTimeline.test.ts similarity index 99% rename from packages/tui/src/state/chat_timeline.test.ts rename to packages/tui/src/features/timeline/chatTimeline.test.ts index 8504613..c511726 100644 --- a/packages/tui/src/state/chat_timeline.test.ts +++ b/packages/tui/src/features/timeline/chatTimeline.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert' import { describe, test } from 'vitest' -import { chatTimelineReducer, createInitialTimelineState } from './chat_timeline' +import { chatTimelineReducer, createInitialTimelineState } from './chatTimeline' describe('chatTimelineReducer', () => { test('creates turn and appends chunks', () => { diff --git a/packages/tui/src/state/chat_timeline.ts b/packages/tui/src/features/timeline/chatTimeline.ts similarity index 99% rename from packages/tui/src/state/chat_timeline.ts rename to packages/tui/src/features/timeline/chatTimeline.ts index 2ad6af8..224a539 100644 --- a/packages/tui/src/state/chat_timeline.ts +++ b/packages/tui/src/features/timeline/chatTimeline.ts @@ -6,8 +6,8 @@ import type { ToolAction, ToolStatus, TurnView, -} from '../types' -import { TOOL_STATUS } from '../types' +} from '../../shared/types' +import { TOOL_STATUS } from '../../shared/types' export type ChatTimelineState = { turns: TurnView[] diff --git a/packages/tui/src/chatwidget/markdown_parser.test.ts b/packages/tui/src/features/timeline/markdownParser.test.ts similarity index 98% rename from packages/tui/src/chatwidget/markdown_parser.test.ts rename to packages/tui/src/features/timeline/markdownParser.test.ts index c4cacf2..4ded7b6 100644 --- a/packages/tui/src/chatwidget/markdown_parser.test.ts +++ b/packages/tui/src/features/timeline/markdownParser.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert' import { describe, test } from 'vitest' -import { MARKDOWN_TEST_EXPORTS, parseInlineNodes, parseMarkdownContent } from './markdown_parser' +import { MARKDOWN_TEST_EXPORTS, parseInlineNodes, parseMarkdownContent } from './markdownParser' describe('markdown parser', () => { test('extracts think blocks and strips them from markdown', () => { diff --git a/packages/tui/src/chatwidget/markdown_parser.ts b/packages/tui/src/features/timeline/markdownParser.ts similarity index 100% rename from packages/tui/src/chatwidget/markdown_parser.ts rename to packages/tui/src/features/timeline/markdownParser.ts diff --git a/packages/tui/src/index.ts b/packages/tui/src/index.ts index af9340b..8d1b04a 100644 --- a/packages/tui/src/index.ts +++ b/packages/tui/src/index.ts @@ -1,2 +1,2 @@ -export { App, type AppProps } from './App' -export { findLocalPackageInfoSync } from './version' +export { App, type AppProps } from './app/App' +export { findLocalPackageInfoSync } from './shared/lib/version' diff --git a/packages/tui/src/constants.ts b/packages/tui/src/shared/lib/constants.ts similarity index 100% rename from packages/tui/src/constants.ts rename to packages/tui/src/shared/lib/constants.ts diff --git a/packages/tui/src/task_prompt.test.ts b/packages/tui/src/shared/lib/taskPrompt.test.ts similarity index 94% rename from packages/tui/src/task_prompt.test.ts rename to packages/tui/src/shared/lib/taskPrompt.test.ts index f20cbc6..4ce8d82 100644 --- a/packages/tui/src/task_prompt.test.ts +++ b/packages/tui/src/shared/lib/taskPrompt.test.ts @@ -1,6 +1,6 @@ import assert from 'node:assert' import { describe, test } from 'vitest' -import { loadTaskPrompt } from './task_prompt' +import { loadTaskPrompt } from './taskPrompt' describe('task prompt loader', () => { test('loads init prompt markdown', async () => { diff --git a/packages/tui/src/task_prompt.ts b/packages/tui/src/shared/lib/taskPrompt.ts similarity index 89% rename from packages/tui/src/task_prompt.ts rename to packages/tui/src/shared/lib/taskPrompt.ts index 1ef6bb1..8c0aada 100644 --- a/packages/tui/src/task_prompt.ts +++ b/packages/tui/src/shared/lib/taskPrompt.ts @@ -15,7 +15,7 @@ export async function loadTaskPrompt( vars: Record = {}, ): Promise { const __dirname = dirname(fileURLToPath(import.meta.url)) - const promptPath = join(__dirname, 'task-prompts', `${template}.md`) + const promptPath = join(__dirname, '../../task-prompts', `${template}.md`) const prompt = await readFile(promptPath, 'utf-8') return renderTemplate(prompt, vars) } diff --git a/packages/tui/src/utils.test.ts b/packages/tui/src/shared/lib/utils.test.ts similarity index 100% rename from packages/tui/src/utils.test.ts rename to packages/tui/src/shared/lib/utils.test.ts diff --git a/packages/tui/src/utils.ts b/packages/tui/src/shared/lib/utils.ts similarity index 97% rename from packages/tui/src/utils.ts rename to packages/tui/src/shared/lib/utils.ts index 46da9f6..4ed0cfd 100644 --- a/packages/tui/src/utils.ts +++ b/packages/tui/src/shared/lib/utils.ts @@ -1,6 +1,6 @@ import path from 'node:path' import type { ToolActionStatus } from '@memo/tools/orchestrator' -import { TOOL_STATUS, type ToolStatus } from './types' +import { TOOL_STATUS, type ToolStatus } from '../types' const TOOL_ACTION_STATUS_SUCCESS: ToolActionStatus = 'success' const CURRENT_DIRECTORY = '.' diff --git a/packages/tui/src/version.ts b/packages/tui/src/shared/lib/version.ts similarity index 100% rename from packages/tui/src/version.ts rename to packages/tui/src/shared/lib/version.ts diff --git a/packages/tui/src/types.ts b/packages/tui/src/shared/types/index.ts similarity index 79% rename from packages/tui/src/types.ts rename to packages/tui/src/shared/types/index.ts index 48fc38a..07bdb0d 100644 --- a/packages/tui/src/types.ts +++ b/packages/tui/src/shared/types/index.ts @@ -53,3 +53,21 @@ export type SystemMessage = { export type TimelineItem = | { type: 'system'; sequence: number; message: SystemMessage } | { type: 'turn'; sequence: number; turn: TurnView } + +export type FileSuggestion = { + id: string + path: string + name: string + parent?: string + isDir: boolean +} + +export type FileSuggestionRequest = { + cwd: string + query: string + limit?: number + maxDepth?: number + maxEntries?: number + respectGitIgnore?: boolean + ignoreGlobs?: string[] +} diff --git a/packages/tui/src/bottom_pane/Footer.tsx b/packages/tui/src/shared/ui/Footer.tsx similarity index 100% rename from packages/tui/src/bottom_pane/Footer.tsx rename to packages/tui/src/shared/ui/Footer.tsx diff --git a/packages/tui/src/slash/registry.test.ts b/packages/tui/src/slash/registry.test.ts deleted file mode 100644 index 20df84f..0000000 --- a/packages/tui/src/slash/registry.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import assert from 'node:assert' -import { describe, test } from 'vitest' -import { resolveSlashCommand, buildHelpText } from './registry' - -const context = { - configPath: '/tmp/config.toml', - providerName: 'deepseek', - model: 'deepseek-chat', - mcpServers: {}, - providers: [], - toolPermissionMode: 'once' as const, -} - -describe('slash registry', () => { - test('help text contains key commands', () => { - const help = buildHelpText() - assert.ok(help.includes('/help')) - assert.ok(help.includes('/models')) - assert.ok(help.includes('/compact')) - assert.ok(!help.includes('/context')) - }) - - test('unknown command returns message', () => { - const result = resolveSlashCommand('/$ foo', context) - assert.strictEqual(result.kind, 'message') - assert.strictEqual(result.title, 'Unknown') - }) - - test('tools command resolves mode switch', () => { - const result = resolveSlashCommand('/tools full', context) - assert.strictEqual(result.kind, 'set_tool_permission') - if (result.kind === 'set_tool_permission') { - assert.strictEqual(result.mode, 'full') - } - }) - - test('context command is removed and treated as unknown', () => { - const result = resolveSlashCommand('/context 120k', context) - assert.strictEqual(result.kind, 'message') - if (result.kind === 'message') { - assert.strictEqual(result.title, 'Unknown') - } - }) - - test('review command parses PR number', () => { - const result = resolveSlashCommand('/review 999', context) - assert.strictEqual(result.kind, 'review_pr') - if (result.kind === 'review_pr') { - assert.strictEqual(result.prNumber, 999) - } - }) - - test('review command parses PR URL', () => { - const result = resolveSlashCommand('/review https://github.com/acme/repo/pull/123', context) - assert.strictEqual(result.kind, 'review_pr') - if (result.kind === 'review_pr') { - assert.strictEqual(result.prNumber, 123) - } - }) - - test('review command validates missing argument', () => { - const result = resolveSlashCommand('/review', context) - assert.strictEqual(result.kind, 'message') - assert.strictEqual(result.title, 'Review') - }) - - test('compact command resolves correctly', () => { - const result = resolveSlashCommand('/compact', context) - assert.strictEqual(result.kind, 'compact') - }) -}) diff --git a/packages/tui/src/slash/registry.ts b/packages/tui/src/slash/registry.ts deleted file mode 100644 index 1fbbd80..0000000 --- a/packages/tui/src/slash/registry.ts +++ /dev/null @@ -1 +0,0 @@ -export { SLASH_SPECS, buildHelpText, resolveSlashCommand } from '@memo/core/runtime/slash' diff --git a/packages/tui/src/slash/types.ts b/packages/tui/src/slash/types.ts deleted file mode 100644 index 64c6305..0000000 --- a/packages/tui/src/slash/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type { - SlashContext, - SlashCommandResult, - SlashSpec, - SlashCommandName, -} from '@memo/core/runtime/slash' -export type { ToolPermissionMode } from '@memo/core/types' From 93fb20922cb610083ffd12f1c9458de051176ea9 Mon Sep 17 00:00:00 2001 From: minorcell Date: Mon, 18 May 2026 02:46:50 +0800 Subject: [PATCH 2/2] chore: v0.8.61 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 92fcb97..ed66aeb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@memo-code/memo", - "version": "0.8.60", + "version": "0.8.61", "private": false, "type": "module", "description": "A lightweight coding agent that runs in your terminal",