mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Some cleanup
This commit is contained in:
parent
511586d6bb
commit
245008a43c
18 changed files with 234 additions and 568 deletions
|
|
@ -1,10 +1,9 @@
|
|||
import { describe, it, expect } from "vitest"
|
||||
import {
|
||||
type GlobalCommand,
|
||||
type GlobalCommandAction,
|
||||
GLOBAL_COMMANDS,
|
||||
getGlobalCommand,
|
||||
getGlobalCommandsForAutocomplete,
|
||||
type GlobalCommand,
|
||||
type GlobalCommandAction,
|
||||
} from "../globalCommands.js"
|
||||
|
||||
describe("globalCommands", () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { describe, it, expect, beforeEach } from "vitest"
|
||||
import { useCLIStore } from "../ui/store.js"
|
||||
|
||||
describe("useCLIStore", () => {
|
||||
|
|
@ -43,8 +42,8 @@ describe("useCLIStore", () => {
|
|||
const store = useCLIStore.getState()
|
||||
store.addMessage({ id: "1", role: "user", content: "test" })
|
||||
store.setTaskHistory([{ id: "task1", task: "test", workspace: "/test", ts: Date.now() }])
|
||||
store.setAvailableModes([{ slug: "code", name: "Code" }])
|
||||
store.setAllSlashCommands([{ name: "test", source: "global" as const }])
|
||||
store.setAvailableModes([{ key: "code", slug: "code", name: "Code" }])
|
||||
store.setAllSlashCommands([{ key: "test", name: "test", source: "global" as const }])
|
||||
store.setIsResumingTask(true)
|
||||
store.setLoading(true)
|
||||
store.setHasStartedTask(true)
|
||||
|
|
@ -115,8 +114,8 @@ describe("useCLIStore", () => {
|
|||
|
||||
it("should PRESERVE availableModes", () => {
|
||||
const modes = [
|
||||
{ slug: "code", name: "Code", description: "Code mode" },
|
||||
{ slug: "architect", name: "Architect", description: "Architect mode" },
|
||||
{ key: "code", slug: "code", name: "Code", description: "Code mode" },
|
||||
{ key: "architect", slug: "architect", name: "Architect", description: "Architect mode" },
|
||||
]
|
||||
useCLIStore.getState().setAvailableModes(modes)
|
||||
|
||||
|
|
@ -127,8 +126,8 @@ describe("useCLIStore", () => {
|
|||
|
||||
it("should PRESERVE allSlashCommands", () => {
|
||||
const commands = [
|
||||
{ name: "new", description: "New task", source: "global" as const },
|
||||
{ name: "help", description: "Get help", source: "built-in" as const },
|
||||
{ key: "new", name: "new", description: "New task", source: "global" as const },
|
||||
{ key: "help", name: "help", description: "Get help", source: "built-in" as const },
|
||||
]
|
||||
useCLIStore.getState().setAllSlashCommands(commands)
|
||||
|
||||
|
|
@ -139,8 +138,8 @@ describe("useCLIStore", () => {
|
|||
|
||||
it("should PRESERVE fileSearchResults", () => {
|
||||
const results = [
|
||||
{ path: "file1.ts", type: "file" as const },
|
||||
{ path: "file2.ts", type: "file" as const },
|
||||
{ key: "file1", path: "file1.ts", type: "file" as const },
|
||||
{ key: "file2", path: "file2.ts", type: "file" as const },
|
||||
]
|
||||
useCLIStore.getState().setFileSearchResults(results)
|
||||
|
||||
|
|
@ -184,8 +183,8 @@ describe("useCLIStore", () => {
|
|||
|
||||
// Step 1: Initial state with task history and modes from webviewDidLaunch
|
||||
store().setTaskHistory([{ id: "task1", task: "Previous task", workspace: "/test", ts: Date.now() }])
|
||||
store().setAvailableModes([{ slug: "code", name: "Code" }])
|
||||
store().setAllSlashCommands([{ name: "new", source: "global" as const }])
|
||||
store().setAvailableModes([{ key: "code", slug: "code", name: "Code" }])
|
||||
store().setAllSlashCommands([{ key: "new", name: "new", source: "global" as const }])
|
||||
|
||||
// Step 2: User starts a new task
|
||||
store().setHasStartedTask(true)
|
||||
|
|
@ -255,7 +254,7 @@ describe("useCLIStore", () => {
|
|||
// Set up both task-specific and global state
|
||||
store().addMessage({ id: "1", role: "user", content: "test" })
|
||||
store().setTaskHistory([{ id: "t1", task: "task", workspace: "/", ts: Date.now() }])
|
||||
store().setAvailableModes([{ slug: "code", name: "Code" }])
|
||||
store().setAvailableModes([{ key: "code", slug: "code", name: "Code" }])
|
||||
|
||||
// Use resetForTaskSwitch
|
||||
store().resetForTaskSwitch()
|
||||
|
|
|
|||
|
|
@ -4,21 +4,38 @@ import { useState, useEffect, useCallback, useRef, useMemo } from "react"
|
|||
import { EventEmitter } from "events"
|
||||
import { randomUUID } from "crypto"
|
||||
|
||||
import type { ClineMessage, TodoItem, WebviewMessage } from "@roo-code/types"
|
||||
// Import only message-utils to avoid custom-tools dependencies (execa/child_process)
|
||||
import type { ExtensionMessage, ClineMessage, ClineAsk, ClineSay, TodoItem, WebviewMessage } from "@roo-code/types"
|
||||
import { consolidateTokenUsage, consolidateApiRequests, consolidateCommands } from "@roo-code/core/message-utils"
|
||||
|
||||
import { FOLLOWUP_TIMEOUT_SECONDS } from "../constants.js"
|
||||
import { getGlobalCommand, getGlobalCommandsForAutocomplete } from "../globalCommands.js"
|
||||
import { toolInspectorLog, clearToolInspectorLog } from "../utils/toolInspectorLogger.js"
|
||||
import { arePathsEqual } from "../utils/pathUtils.js"
|
||||
import { getContextWindow } from "../utils/getContextWindow.js"
|
||||
|
||||
import type { AppProps, TUIMessage, PendingAsk, View, ToolData } from "./types.js"
|
||||
|
||||
import * as theme from "./utils/theme.js"
|
||||
import { matchesGlobalSequence } from "./utils/globalInputSequences.js"
|
||||
|
||||
import { useCLIStore } from "./store.js"
|
||||
import { getContextWindow } from "../utils/getContextWindow.js"
|
||||
|
||||
import { TerminalSizeProvider, useTerminalSize } from "./hooks/TerminalSizeContext.js"
|
||||
import { useToast } from "./hooks/useToast.js"
|
||||
|
||||
import Header from "./components/Header.js"
|
||||
import ChatHistoryItem from "./components/ChatHistoryItem.js"
|
||||
import LoadingText from "./components/LoadingText.js"
|
||||
import ToastDisplay from "./components/ToastDisplay.js"
|
||||
import TodoDisplay from "./components/TodoDisplay.js"
|
||||
import { useToast } from "./hooks/useToast.js"
|
||||
import {
|
||||
type AutocompleteInputHandle,
|
||||
type AutocompletePickerState,
|
||||
type AutocompleteTrigger,
|
||||
type HistoryResult,
|
||||
type FileResult,
|
||||
type SlashCommandResult,
|
||||
type ModeResult,
|
||||
AutocompleteInput,
|
||||
PickerSelect,
|
||||
createFileTrigger,
|
||||
|
|
@ -30,41 +47,12 @@ import {
|
|||
toSlashCommandResult,
|
||||
toModeResult,
|
||||
toHistoryResult,
|
||||
type AutocompleteInputHandle,
|
||||
type AutocompletePickerState,
|
||||
type AutocompleteTrigger,
|
||||
type FileResult,
|
||||
type SlashCommandResult as SlashCommandItem,
|
||||
type ModeResult as ModeItem,
|
||||
type HistoryResult,
|
||||
} from "./components/autocomplete/index.js"
|
||||
import { ScrollArea, useScrollToBottom } from "./components/ScrollArea.js"
|
||||
import ScrollIndicator from "./components/ScrollIndicator.js"
|
||||
import { TerminalSizeProvider, useTerminalSize } from "./hooks/TerminalSizeContext.js"
|
||||
import * as theme from "./utils/theme.js"
|
||||
import { matchesGlobalSequence } from "./utils/globalInputSequences.js"
|
||||
import { FOLLOWUP_TIMEOUT_SECONDS } from "../constants.js"
|
||||
import type {
|
||||
AppProps,
|
||||
TUIMessage,
|
||||
PendingAsk,
|
||||
SayType,
|
||||
AskType,
|
||||
View,
|
||||
FileSearchResult,
|
||||
SlashCommandResult,
|
||||
ModeResult,
|
||||
TaskHistoryItem,
|
||||
ToolData,
|
||||
} from "./types.js"
|
||||
import { getGlobalCommand, getGlobalCommandsForAutocomplete } from "../globalCommands.js"
|
||||
|
||||
// Layout constants
|
||||
const PICKER_HEIGHT = 10 // Max height for picker when open
|
||||
const PICKER_HEIGHT = 10
|
||||
|
||||
/**
|
||||
* Interface for the extension host that the TUI interacts with
|
||||
*/
|
||||
interface ExtensionHostInterface extends EventEmitter {
|
||||
activate(): Promise<void>
|
||||
runTask(prompt: string): Promise<void>
|
||||
|
|
@ -73,7 +61,7 @@ interface ExtensionHostInterface extends EventEmitter {
|
|||
}
|
||||
|
||||
export interface TUIAppProps extends AppProps {
|
||||
/** Extension host factory - allows dependency injection for testing */
|
||||
/** Extension host factory - allows dependency injection for testing. */
|
||||
createExtensionHost: (options: ExtensionHostOptions) => ExtensionHostInterface
|
||||
}
|
||||
|
||||
|
|
@ -600,13 +588,14 @@ function AppInner({
|
|||
|
||||
// Map extension say messages to TUI messages
|
||||
const handleSayMessage = useCallback(
|
||||
(ts: number, say: SayType, text: string, partial: boolean) => {
|
||||
(ts: number, say: ClineSay, text: string, partial: boolean) => {
|
||||
const messageId = ts.toString()
|
||||
const isResuming = useCLIStore.getState().isResumingTask
|
||||
|
||||
if (say === "checkpoint_saved") {
|
||||
return
|
||||
}
|
||||
|
||||
if (say === "api_req_started" && !verbose) {
|
||||
return
|
||||
}
|
||||
|
|
@ -639,65 +628,60 @@ function AppInner({
|
|||
toolName = "execute_command"
|
||||
toolDisplayName = "bash"
|
||||
toolDisplayOutput = text
|
||||
// Create toolData for command output, including the pending command if available
|
||||
const trackedCommand = pendingCommandRef.current
|
||||
toolInspectorLog("say:command_output", { ts, trackedCommand, outputLength: text?.length })
|
||||
toolData = {
|
||||
tool: "execute_command",
|
||||
command: trackedCommand || undefined,
|
||||
output: text,
|
||||
}
|
||||
// Clear the pending command after using it
|
||||
toolData = { tool: "execute_command", command: trackedCommand || undefined, output: text }
|
||||
pendingCommandRef.current = null
|
||||
} else if (say === "tool") {
|
||||
role = "tool"
|
||||
try {
|
||||
const toolInfo = JSON.parse(text)
|
||||
// } else if (say === "tool") {
|
||||
// role = "tool"
|
||||
|
||||
// Log tool payload for inspection
|
||||
toolInspectorLog("say:tool", {
|
||||
ts,
|
||||
rawText: text,
|
||||
parsedToolInfo: toolInfo,
|
||||
partial,
|
||||
})
|
||||
// try {
|
||||
// const toolInfo = JSON.parse(text)
|
||||
|
||||
toolName = toolInfo.tool
|
||||
toolDisplayName = toolInfo.tool
|
||||
toolDisplayOutput = formatToolOutput(toolInfo)
|
||||
// Extract structured toolData for rich rendering
|
||||
toolData = extractToolData(toolInfo)
|
||||
// // Log tool payload for inspection
|
||||
// toolInspectorLog("say:tool", {
|
||||
// ts,
|
||||
// rawText: text,
|
||||
// parsedToolInfo: toolInfo,
|
||||
// partial,
|
||||
// })
|
||||
|
||||
// Special handling for update_todo_list tool
|
||||
if (toolName === "update_todo_list" || toolName === "updateTodoList") {
|
||||
const todos = parseTodosFromToolInfo(toolInfo)
|
||||
if (todos && todos.length > 0) {
|
||||
// Capture previous todos before updating
|
||||
const prevTodos = [...currentTodos]
|
||||
setTodos(todos)
|
||||
// toolName = toolInfo.tool
|
||||
// toolDisplayName = toolInfo.tool
|
||||
// toolDisplayOutput = formatToolOutput(toolInfo)
|
||||
// // Extract structured toolData for rich rendering
|
||||
// toolData = extractToolData(toolInfo)
|
||||
|
||||
seenMessageIds.current.add(messageId)
|
||||
// // Special handling for update_todo_list tool
|
||||
// if (toolName === "update_todo_list" || toolName === "updateTodoList") {
|
||||
// const todos = parseTodosFromToolInfo(toolInfo)
|
||||
// if (todos && todos.length > 0) {
|
||||
// // Capture previous todos before updating
|
||||
// const prevTodos = [...currentTodos]
|
||||
// setTodos(todos)
|
||||
|
||||
addMessage({
|
||||
id: messageId,
|
||||
role: "tool",
|
||||
content: text || "",
|
||||
toolName,
|
||||
toolDisplayName,
|
||||
toolDisplayOutput,
|
||||
partial,
|
||||
originalType: say,
|
||||
todos,
|
||||
previousTodos: prevTodos,
|
||||
toolData,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
toolDisplayOutput = text
|
||||
}
|
||||
} else if (say === "reasoning" || say === "thinking") {
|
||||
// seenMessageIds.current.add(messageId)
|
||||
|
||||
// addMessage({
|
||||
// id: messageId,
|
||||
// role: "tool",
|
||||
// content: text || "",
|
||||
// toolName,
|
||||
// toolDisplayName,
|
||||
// toolDisplayOutput,
|
||||
// partial,
|
||||
// originalType: say,
|
||||
// todos,
|
||||
// previousTodos: prevTodos,
|
||||
// toolData,
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
// } catch {
|
||||
// toolDisplayOutput = text
|
||||
// }
|
||||
} else if (say === "reasoning") {
|
||||
role = "thinking"
|
||||
}
|
||||
|
||||
|
|
@ -720,7 +704,7 @@ function AppInner({
|
|||
|
||||
// Handle extension ask messages
|
||||
const handleAskMessage = useCallback(
|
||||
(ts: number, ask: AskType, text: string, partial: boolean) => {
|
||||
(ts: number, ask: ClineAsk, text: string, partial: boolean) => {
|
||||
const messageId = ts.toString()
|
||||
|
||||
if (partial) {
|
||||
|
|
@ -912,34 +896,38 @@ function AppInner({
|
|||
|
||||
// Handle extension messages
|
||||
const handleExtensionMessage = useCallback(
|
||||
(message: unknown) => {
|
||||
const msg = message as Record<string, unknown>
|
||||
|
||||
(msg: ExtensionMessage) => {
|
||||
if (msg.type === "state") {
|
||||
const state = msg.state as Record<string, unknown>
|
||||
if (!state) return
|
||||
const state = msg.state
|
||||
|
||||
if (!state) {
|
||||
return
|
||||
}
|
||||
|
||||
// Extract and update current mode from state.
|
||||
const newMode = state.mode
|
||||
|
||||
// Extract and update current mode from state
|
||||
const newMode = state.mode as string | undefined
|
||||
if (newMode) {
|
||||
setCurrentMode(newMode)
|
||||
}
|
||||
|
||||
// Extract and update task history from state
|
||||
const newTaskHistory = state.taskHistory as TaskHistoryItem[] | undefined
|
||||
// Extract and update task history from state.
|
||||
const newTaskHistory = state.taskHistory
|
||||
|
||||
if (newTaskHistory && Array.isArray(newTaskHistory)) {
|
||||
setTaskHistory(newTaskHistory)
|
||||
}
|
||||
|
||||
const clineMessages = state.clineMessages as Array<Record<string, unknown>> | undefined
|
||||
const clineMessages = state.clineMessages
|
||||
|
||||
if (clineMessages) {
|
||||
for (const clineMsg of clineMessages) {
|
||||
const ts = clineMsg.ts as number
|
||||
const type = clineMsg.type as string
|
||||
const say = clineMsg.say as SayType | undefined
|
||||
const ask = clineMsg.ask as AskType | undefined
|
||||
const text = (clineMsg.text as string) || ""
|
||||
const partial = (clineMsg.partial as boolean) || false
|
||||
const ts = clineMsg.ts
|
||||
const type = clineMsg.type
|
||||
const say = clineMsg.say
|
||||
const ask = clineMsg.ask
|
||||
const text = clineMsg.text || ""
|
||||
const partial = clineMsg.partial || false
|
||||
|
||||
if (type === "say" && say) {
|
||||
handleSayMessage(ts, say, text, partial)
|
||||
|
|
@ -948,12 +936,13 @@ function AppInner({
|
|||
}
|
||||
}
|
||||
|
||||
// Compute token usage metrics from clineMessages
|
||||
// Skip first message (task prompt) as per webview UI pattern
|
||||
// Compute token usage metrics from clineMessages.
|
||||
// Skip first message (task prompt) as per webview UI pattern.
|
||||
if (clineMessages.length > 1) {
|
||||
const processed = consolidateApiRequests(
|
||||
consolidateCommands(clineMessages.slice(1) as ClineMessage[]),
|
||||
)
|
||||
|
||||
const metrics = consolidateTokenUsage(processed)
|
||||
setTokenUsage(metrics)
|
||||
}
|
||||
|
|
@ -965,15 +954,18 @@ function AppInner({
|
|||
useCLIStore.getState().setIsResumingTask(false)
|
||||
}
|
||||
} else if (msg.type === "messageUpdated") {
|
||||
const clineMessage = msg.clineMessage as Record<string, unknown>
|
||||
if (!clineMessage) return
|
||||
const clineMessage = msg.clineMessage
|
||||
|
||||
const ts = clineMessage.ts as number
|
||||
const type = clineMessage.type as string
|
||||
const say = clineMessage.say as SayType | undefined
|
||||
const ask = clineMessage.ask as AskType | undefined
|
||||
const text = (clineMessage.text as string) || ""
|
||||
const partial = (clineMessage.partial as boolean) || false
|
||||
if (!clineMessage) {
|
||||
return
|
||||
}
|
||||
|
||||
const ts = clineMessage.ts
|
||||
const type = clineMessage.type
|
||||
const say = clineMessage.say
|
||||
const ask = clineMessage.ask
|
||||
const text = clineMessage.text || ""
|
||||
const partial = clineMessage.partial || false
|
||||
|
||||
if (type === "say" && say) {
|
||||
handleSayMessage(ts, say, text, partial)
|
||||
|
|
@ -981,47 +973,14 @@ function AppInner({
|
|||
handleAskMessage(ts, ask, text, partial)
|
||||
}
|
||||
} else if (msg.type === "fileSearchResults") {
|
||||
const results = (msg.results as FileSearchResult[]) || []
|
||||
setFileSearchResults(results)
|
||||
setFileSearchResults((msg.results as FileResult[]) || [])
|
||||
} else if (msg.type === "commands") {
|
||||
const commands =
|
||||
(msg.commands as Array<{
|
||||
name: string
|
||||
description?: string
|
||||
argumentHint?: string
|
||||
source: "global" | "project" | "built-in"
|
||||
}>) || []
|
||||
const slashCommands: SlashCommandResult[] = commands.map((cmd) => ({
|
||||
name: cmd.name,
|
||||
description: cmd.description,
|
||||
argumentHint: cmd.argumentHint,
|
||||
source: cmd.source,
|
||||
}))
|
||||
setAllSlashCommands(slashCommands)
|
||||
setAllSlashCommands((msg.commands as SlashCommandResult[]) || [])
|
||||
} else if (msg.type === "modes") {
|
||||
const modes =
|
||||
(msg.modes as Array<{
|
||||
slug: string
|
||||
name: string
|
||||
description?: string
|
||||
}>) || []
|
||||
const modeResults: ModeResult[] = modes.map((mode) => ({
|
||||
slug: mode.slug,
|
||||
name: mode.name,
|
||||
description: mode.description,
|
||||
}))
|
||||
setAvailableModes(modeResults)
|
||||
setAvailableModes((msg.modes as ModeResult[]) || [])
|
||||
} else if (msg.type === "routerModels") {
|
||||
// Handle router models for context window lookup
|
||||
const models = msg.models as Record<string, Record<string, { contextWindow?: number }>> | undefined
|
||||
if (models) {
|
||||
setRouterModels(models)
|
||||
}
|
||||
} else if (msg.type === "apiConfiguration") {
|
||||
// Handle API configuration for model identification
|
||||
const config = msg.configuration as unknown
|
||||
if (config) {
|
||||
setApiConfiguration(config as import("@roo-code/types").ProviderSettings)
|
||||
if (msg.routerModels) {
|
||||
setRouterModels(msg.routerModels)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1272,7 +1231,7 @@ function AppInner({
|
|||
(item: any) => {
|
||||
// Check if this is a mode selection
|
||||
if (pickerState.activeTrigger?.id === "mode" && item && typeof item === "object" && "slug" in item) {
|
||||
const modeItem = item as ModeItem
|
||||
const modeItem = item as ModeResult
|
||||
|
||||
// Send mode change message to extension
|
||||
if (hostRef.current) {
|
||||
|
|
@ -1402,7 +1361,7 @@ function AppInner({
|
|||
return pickerState.activeTrigger.renderItem
|
||||
}
|
||||
// Default render
|
||||
return (item: FileResult | SlashCommandItem, isSelected: boolean) => (
|
||||
return (item: FileResult | SlashCommandResult, isSelected: boolean) => (
|
||||
<Box paddingLeft={2}>
|
||||
<Text color={isSelected ? "cyan" : undefined}>{item.key}</Text>
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { render } from "ink-testing-library"
|
||||
import { describe, it, expect } from "vitest"
|
||||
|
||||
import type { Toast } from "../../hooks/useToast.js"
|
||||
import ToastDisplay from "../ToastDisplay.js"
|
||||
|
|
|
|||
|
|
@ -28,39 +28,14 @@
|
|||
*/
|
||||
|
||||
// Main components
|
||||
export { AutocompleteInput, type AutocompleteInputProps, type AutocompleteInputHandle } from "./AutocompleteInput.js"
|
||||
export { PickerSelect, type PickerSelectProps } from "./PickerSelect.js"
|
||||
export { type AutocompleteInputProps, type AutocompleteInputHandle, AutocompleteInput } from "./AutocompleteInput.js"
|
||||
export { type PickerSelectProps, PickerSelect } from "./PickerSelect.js"
|
||||
|
||||
// Hook
|
||||
export { useAutocompletePicker } from "./useAutocompletePicker.js"
|
||||
|
||||
// Types
|
||||
export type {
|
||||
AutocompleteItem,
|
||||
AutocompleteTrigger,
|
||||
AutocompletePickerState,
|
||||
AutocompletePickerActions,
|
||||
TriggerDetectionResult,
|
||||
} from "./types.js"
|
||||
export * from "./types.js"
|
||||
|
||||
// Triggers
|
||||
export {
|
||||
createFileTrigger,
|
||||
toFileResult,
|
||||
type FileResult,
|
||||
type FileTriggerConfig,
|
||||
createSlashCommandTrigger,
|
||||
toSlashCommandResult,
|
||||
type SlashCommandResult,
|
||||
type SlashCommandTriggerConfig,
|
||||
createModeTrigger,
|
||||
toModeResult,
|
||||
type ModeResult,
|
||||
type ModeTriggerConfig,
|
||||
createHelpTrigger,
|
||||
type HelpShortcutResult,
|
||||
createHistoryTrigger,
|
||||
toHistoryResult,
|
||||
type HistoryResult,
|
||||
type HistoryTriggerConfig,
|
||||
} from "./triggers/index.js"
|
||||
export * from "./triggers/index.js"
|
||||
|
|
|
|||
|
|
@ -1,178 +0,0 @@
|
|||
import { render } from "ink-testing-library"
|
||||
import { describe, it, expect } from "vitest"
|
||||
|
||||
import { createFileTrigger, toFileResult } from "./FileTrigger.js"
|
||||
|
||||
describe("FileTrigger", () => {
|
||||
describe("createFileTrigger", () => {
|
||||
it("should detect @ trigger", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const result = trigger.detectTrigger("@fil")
|
||||
expect(result).toEqual({ query: "fil", triggerIndex: 0 })
|
||||
})
|
||||
|
||||
it("should detect @ trigger in middle of line", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const result = trigger.detectTrigger("some text @fil")
|
||||
expect(result).toEqual({ query: "fil", triggerIndex: 10 })
|
||||
})
|
||||
|
||||
it("should detect @ even without text after it", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const result = trigger.detectTrigger("@")
|
||||
expect(result).toEqual({ query: "", triggerIndex: 0 })
|
||||
})
|
||||
|
||||
it("should not detect @ followed by space", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const result = trigger.detectTrigger("@ ")
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it("should close picker when query contains space", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const result = trigger.detectTrigger("@file name")
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it("should generate correct replacement text for files", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const item = toFileResult({ path: "src/index.ts", type: "file" })
|
||||
const lineText = "Check @ind"
|
||||
const replacement = trigger.getReplacementText(item, lineText, 6)
|
||||
|
||||
expect(replacement).toBe("Check @/src/index.ts ")
|
||||
})
|
||||
|
||||
it("should generate correct replacement text for folders", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const item = toFileResult({ path: "src/components", type: "folder" })
|
||||
const lineText = "@comp"
|
||||
const replacement = trigger.getReplacementText(item, lineText, 0)
|
||||
|
||||
expect(replacement).toBe("@/src/components ")
|
||||
})
|
||||
|
||||
it("should preserve full path in replacement text", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const item = toFileResult({
|
||||
path: "apps/cli/src/ui/components/autocomplete/PickerSelect.tsx",
|
||||
type: "file",
|
||||
})
|
||||
const lineText = "Fix @Pick"
|
||||
const replacement = trigger.getReplacementText(item, lineText, 4)
|
||||
|
||||
// Verify the full path is included without truncation
|
||||
expect(replacement).toBe("Fix @/apps/cli/src/ui/components/autocomplete/PickerSelect.tsx ")
|
||||
// Verify last character 'x' is present
|
||||
expect(replacement).toContain("PickerSelect.tsx ")
|
||||
expect(replacement.trim().endsWith(".tsx")).toBe(true)
|
||||
})
|
||||
|
||||
it("should render file items correctly", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const item = toFileResult({ path: "src/index.ts", type: "file" })
|
||||
const { lastFrame } = render(trigger.renderItem(item, false) as React.ReactElement)
|
||||
|
||||
// Verify the path is present in the rendered output
|
||||
expect(lastFrame()).toContain("src/index.ts")
|
||||
})
|
||||
|
||||
it("should render folder items correctly", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const item = toFileResult({ path: "src/components", type: "folder" })
|
||||
const { lastFrame } = render(trigger.renderItem(item, false) as React.ReactElement)
|
||||
|
||||
// Verify the path is present in the rendered output
|
||||
expect(lastFrame()).toContain("src/components")
|
||||
})
|
||||
|
||||
it("should render full path without truncation in UI", () => {
|
||||
const trigger = createFileTrigger({
|
||||
onSearch: () => {},
|
||||
getResults: () => [],
|
||||
})
|
||||
|
||||
const item = toFileResult({
|
||||
path: "apps/cli/src/ui/components/autocomplete/PickerSelect.tsx",
|
||||
type: "file",
|
||||
})
|
||||
const { lastFrame } = render(trigger.renderItem(item, false) as React.ReactElement)
|
||||
|
||||
const output = lastFrame()
|
||||
// Verify the full path is rendered without truncation
|
||||
expect(output).toContain("PickerSelect.tsx")
|
||||
// Verify the last character 'x' is present
|
||||
expect(output).toContain("x")
|
||||
// Verify no truncation occurred
|
||||
expect(output).not.toMatch(/PickerSelect\.ts[^x]/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("toFileResult", () => {
|
||||
it("should convert file search result to FileResult", () => {
|
||||
const result = toFileResult({ path: "src/index.ts", type: "file" })
|
||||
|
||||
expect(result).toEqual({
|
||||
key: "src/index.ts",
|
||||
path: "src/index.ts",
|
||||
type: "file",
|
||||
})
|
||||
})
|
||||
|
||||
it("should preserve label", () => {
|
||||
const result = toFileResult({
|
||||
path: "src/index.ts",
|
||||
type: "file",
|
||||
label: "Main entry",
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
key: "src/index.ts",
|
||||
path: "src/index.ts",
|
||||
type: "file",
|
||||
label: "Main entry",
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -4,16 +4,9 @@ import Fuzzysort from "fuzzysort"
|
|||
import { Icon } from "../../Icon.js"
|
||||
import type { AutocompleteTrigger, AutocompleteItem, TriggerDetectionResult } from "../types.js"
|
||||
|
||||
/**
|
||||
* File search result type.
|
||||
* Extends AutocompleteItem with file-specific properties.
|
||||
*/
|
||||
export interface FileResult extends AutocompleteItem {
|
||||
/** File or folder path */
|
||||
path: string
|
||||
/** Whether this is a file or folder */
|
||||
type: "file" | "folder"
|
||||
/** Optional display label */
|
||||
label?: string
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,34 +3,15 @@ import fuzzysort from "fuzzysort"
|
|||
|
||||
import type { AutocompleteTrigger, AutocompleteItem, TriggerDetectionResult } from "../types.js"
|
||||
|
||||
/**
|
||||
* Mode result type.
|
||||
* Extends AutocompleteItem with mode-specific properties.
|
||||
*/
|
||||
export interface ModeResult extends AutocompleteItem {
|
||||
/** Mode slug (e.g., "code", "architect") */
|
||||
slug: string
|
||||
/** Mode display name */
|
||||
name: string
|
||||
/** Optional description of the mode */
|
||||
description?: string
|
||||
/** Optional icon for the mode */
|
||||
icon?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for creating a mode trigger
|
||||
*/
|
||||
export interface ModeTriggerConfig {
|
||||
/**
|
||||
* Get all available modes for filtering.
|
||||
* Modes are filtered locally using fuzzy search.
|
||||
*/
|
||||
getModes: () => ModeResult[]
|
||||
/**
|
||||
* Maximum number of results to show.
|
||||
* @default 20
|
||||
*/
|
||||
maxResults?: number
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +95,7 @@ export function createModeTrigger(config: ModeTriggerConfig): AutocompleteTrigge
|
|||
}
|
||||
|
||||
/**
|
||||
* Convert external mode data to ModeResult.
|
||||
* Convert external mode data to ModeTriggerResult.
|
||||
* Use this to adapt modes from the store to the trigger's expected type.
|
||||
*/
|
||||
export function toModeResult(mode: { slug: string; name: string; description?: string; icon?: string }): ModeResult {
|
||||
|
|
|
|||
|
|
@ -2,37 +2,19 @@ import { Box, Text } from "ink"
|
|||
import fuzzysort from "fuzzysort"
|
||||
|
||||
import type { AutocompleteTrigger, AutocompleteItem, TriggerDetectionResult } from "../types.js"
|
||||
import { GlobalCommandAction } from "../../../../globalCommands.js"
|
||||
|
||||
/**
|
||||
* Slash command result type.
|
||||
* Extends AutocompleteItem with command-specific properties.
|
||||
*/
|
||||
export interface SlashCommandResult extends AutocompleteItem {
|
||||
/** Command name (without the leading /) */
|
||||
name: string
|
||||
/** Optional description of what the command does */
|
||||
description?: string
|
||||
/** Optional hint about command arguments */
|
||||
argumentHint?: string
|
||||
/** Source of the command */
|
||||
source: "global" | "project" | "built-in"
|
||||
/** Action to trigger for CLI global commands (only present for action commands) */
|
||||
action?: string
|
||||
/** Action to trigger for CLI global commands (e.g., clearTask for /new) */
|
||||
action?: GlobalCommandAction
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for creating a slash command trigger
|
||||
*/
|
||||
export interface SlashCommandTriggerConfig {
|
||||
/**
|
||||
* Get all available commands for filtering.
|
||||
* Commands are filtered locally using fuzzy search.
|
||||
*/
|
||||
getCommands: () => SlashCommandResult[]
|
||||
/**
|
||||
* Maximum number of results to show.
|
||||
* @default 20
|
||||
*/
|
||||
maxResults?: number
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
import { describe, it, expect, vi } from "vitest"
|
||||
import {
|
||||
createFileTrigger,
|
||||
toFileResult,
|
||||
type FileResult,
|
||||
} from "../../ui/components/autocomplete/triggers/FileTrigger.js"
|
||||
import { render } from "ink-testing-library"
|
||||
|
||||
import { createFileTrigger, toFileResult, type FileResult } from "../FileTrigger.js"
|
||||
|
||||
describe("FileTrigger", () => {
|
||||
describe("toFileResult", () => {
|
||||
|
|
@ -46,6 +43,11 @@ describe("FileTrigger", () => {
|
|||
})
|
||||
})
|
||||
|
||||
it("should detect @ trigger at start of line", () => {
|
||||
const result = trigger.detectTrigger("@fil")
|
||||
expect(result).toEqual({ query: "fil", triggerIndex: 0 })
|
||||
})
|
||||
|
||||
it("should return null when no @ present", () => {
|
||||
const result = trigger.detectTrigger("hello world")
|
||||
|
||||
|
|
@ -58,6 +60,11 @@ describe("FileTrigger", () => {
|
|||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it("should return null when @ followed by space", () => {
|
||||
const result = trigger.detectTrigger("@ ")
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it("should detect @ trigger even with empty query", () => {
|
||||
const result = trigger.detectTrigger("hello @")
|
||||
|
||||
|
|
@ -67,6 +74,11 @@ describe("FileTrigger", () => {
|
|||
})
|
||||
})
|
||||
|
||||
it("should detect @ even without text after it", () => {
|
||||
const result = trigger.detectTrigger("@")
|
||||
expect(result).toEqual({ query: "", triggerIndex: 0 })
|
||||
})
|
||||
|
||||
it("should find last @ in line", () => {
|
||||
const result = trigger.detectTrigger("email@test.com @file")
|
||||
|
||||
|
|
@ -95,6 +107,29 @@ describe("FileTrigger", () => {
|
|||
|
||||
expect(result).toBe("check @/config.json ")
|
||||
})
|
||||
|
||||
it("should generate correct replacement text for folders", () => {
|
||||
const item = toFileResult({ path: "src/components", type: "folder" })
|
||||
const lineText = "@comp"
|
||||
const replacement = trigger.getReplacementText(item, lineText, 0)
|
||||
|
||||
expect(replacement).toBe("@/src/components ")
|
||||
})
|
||||
|
||||
it("should preserve full path in replacement text", () => {
|
||||
const item = toFileResult({
|
||||
path: "apps/cli/src/ui/components/autocomplete/PickerSelect.tsx",
|
||||
type: "file",
|
||||
})
|
||||
const lineText = "Fix @Pick"
|
||||
const replacement = trigger.getReplacementText(item, lineText, 4)
|
||||
|
||||
// Verify the full path is included without truncation
|
||||
expect(replacement).toBe("Fix @/apps/cli/src/ui/components/autocomplete/PickerSelect.tsx ")
|
||||
// Verify last character 'x' is present
|
||||
expect(replacement).toContain("PickerSelect.tsx ")
|
||||
expect(replacement.trim().endsWith(".tsx")).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("search", () => {
|
||||
|
|
@ -194,4 +229,42 @@ describe("FileTrigger", () => {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("renderItem", () => {
|
||||
const onSearch = vi.fn()
|
||||
const getResults = (): FileResult[] => []
|
||||
const trigger = createFileTrigger({ onSearch, getResults })
|
||||
|
||||
it("should render file items correctly", () => {
|
||||
const item = toFileResult({ path: "src/index.ts", type: "file" })
|
||||
const { lastFrame } = render(trigger.renderItem(item, false) as React.ReactElement)
|
||||
|
||||
// Verify the path is present in the rendered output
|
||||
expect(lastFrame()).toContain("src/index.ts")
|
||||
})
|
||||
|
||||
it("should render folder items correctly", () => {
|
||||
const item = toFileResult({ path: "src/components", type: "folder" })
|
||||
const { lastFrame } = render(trigger.renderItem(item, false) as React.ReactElement)
|
||||
|
||||
// Verify the path is present in the rendered output
|
||||
expect(lastFrame()).toContain("src/components")
|
||||
})
|
||||
|
||||
it("should render full path without truncation in UI", () => {
|
||||
const item = toFileResult({
|
||||
path: "apps/cli/src/ui/components/autocomplete/PickerSelect.tsx",
|
||||
type: "file",
|
||||
})
|
||||
const { lastFrame } = render(trigger.renderItem(item, false) as React.ReactElement)
|
||||
|
||||
const output = lastFrame()
|
||||
// Verify the full path is rendered without truncation
|
||||
expect(output).toContain("PickerSelect.tsx")
|
||||
// Verify the last character 'x' is present
|
||||
expect(output).toContain("x")
|
||||
// Verify no truncation occurred
|
||||
expect(output).not.toMatch(/PickerSelect\.ts[^x]/)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import { render } from "ink-testing-library"
|
||||
import { describe, it, expect } from "vitest"
|
||||
|
||||
import { createHelpTrigger, type HelpShortcutResult } from "./HelpTrigger.js"
|
||||
import { createHelpTrigger, type HelpShortcutResult } from "../HelpTrigger.js"
|
||||
|
||||
describe("HelpTrigger", () => {
|
||||
describe("createHelpTrigger", () => {
|
||||
|
|
@ -1,9 +1,7 @@
|
|||
import { render } from "ink-testing-library"
|
||||
import { describe, it, expect, vi } from "vitest"
|
||||
|
||||
import { createHistoryTrigger, toHistoryResult, type HistoryResult } from "./HistoryTrigger.js"
|
||||
import { createHistoryTrigger, toHistoryResult, type HistoryResult } from "../HistoryTrigger.js"
|
||||
|
||||
// Sample history items for testing
|
||||
const mockHistoryItems: HistoryResult[] = [
|
||||
{
|
||||
key: "task-1",
|
||||
|
|
@ -1,9 +1,4 @@
|
|||
import { describe, it, expect } from "vitest"
|
||||
import {
|
||||
createModeTrigger,
|
||||
toModeResult,
|
||||
type ModeResult,
|
||||
} from "../../ui/components/autocomplete/triggers/ModeTrigger.js"
|
||||
import { type ModeResult, createModeTrigger, toModeResult } from "../ModeTrigger.js"
|
||||
|
||||
describe("ModeTrigger", () => {
|
||||
const testModes: ModeResult[] = [
|
||||
|
|
@ -1,9 +1,4 @@
|
|||
import { describe, it, expect, vi } from "vitest"
|
||||
import {
|
||||
createSlashCommandTrigger,
|
||||
toSlashCommandResult,
|
||||
type SlashCommandResult,
|
||||
} from "../../ui/components/autocomplete/triggers/SlashCommandTrigger.js"
|
||||
import { type SlashCommandResult, createSlashCommandTrigger, toSlashCommandResult } from "../SlashCommandTrigger.js"
|
||||
|
||||
describe("SlashCommandTrigger", () => {
|
||||
describe("toSlashCommandResult", () => {
|
||||
|
|
@ -1,23 +1,19 @@
|
|||
/**
|
||||
* Autocomplete triggers for different trigger patterns.
|
||||
*/
|
||||
|
||||
export { createFileTrigger, toFileResult, type FileResult, type FileTriggerConfig } from "./FileTrigger.js"
|
||||
export { type FileResult, type FileTriggerConfig, createFileTrigger, toFileResult } from "./FileTrigger.js"
|
||||
|
||||
export {
|
||||
createSlashCommandTrigger,
|
||||
toSlashCommandResult,
|
||||
type SlashCommandResult,
|
||||
type SlashCommandTriggerConfig,
|
||||
createSlashCommandTrigger,
|
||||
toSlashCommandResult,
|
||||
} from "./SlashCommandTrigger.js"
|
||||
|
||||
export { createModeTrigger, toModeResult, type ModeResult, type ModeTriggerConfig } from "./ModeTrigger.js"
|
||||
export { type ModeResult, type ModeTriggerConfig, createModeTrigger, toModeResult } from "./ModeTrigger.js"
|
||||
|
||||
export { createHelpTrigger, type HelpShortcutResult } from "./HelpTrigger.js"
|
||||
export { type HelpShortcutResult, createHelpTrigger } from "./HelpTrigger.js"
|
||||
|
||||
export {
|
||||
createHistoryTrigger,
|
||||
toHistoryResult,
|
||||
type HistoryResult,
|
||||
type HistoryTriggerConfig,
|
||||
createHistoryTrigger,
|
||||
toHistoryResult,
|
||||
} from "./HistoryTrigger.js"
|
||||
|
|
|
|||
|
|
@ -6,24 +6,8 @@ export { default as Header } from "./components/Header.js"
|
|||
export { default as ChatHistoryItem } from "./components/ChatHistoryItem.js"
|
||||
export { default as LoadingText } from "./components/LoadingText.js"
|
||||
|
||||
// Autocomplete system
|
||||
export {
|
||||
AutocompleteInput,
|
||||
PickerSelect,
|
||||
useAutocompletePicker,
|
||||
createFileTrigger,
|
||||
createSlashCommandTrigger,
|
||||
toFileResult,
|
||||
toSlashCommandResult,
|
||||
type AutocompleteInputProps,
|
||||
type AutocompleteInputHandle,
|
||||
type AutocompleteItem,
|
||||
type AutocompleteTrigger,
|
||||
type AutocompletePickerState,
|
||||
type PickerSelectProps,
|
||||
type FileResult,
|
||||
type SlashCommandResult as AutocompleteSlashCommandResult,
|
||||
} from "./components/autocomplete/index.js"
|
||||
// Autocomplete
|
||||
export * from "./components/autocomplete/index.js"
|
||||
|
||||
// Hooks
|
||||
export { useInputHistory } from "./hooks/useInputHistory.js"
|
||||
|
|
@ -36,4 +20,4 @@ export { useCLIStore } from "./store.js"
|
|||
export * as theme from "./utils/theme.js"
|
||||
|
||||
// Types
|
||||
export type { TUIMessage, PendingAsk, SayType, AskType, AppProps, MessageRole, View } from "./types.js"
|
||||
export * from "./types.js"
|
||||
|
|
|
|||
|
|
@ -2,14 +2,8 @@ import { create } from "zustand"
|
|||
|
||||
import type { TokenUsage, ProviderSettings, TodoItem } from "@roo-code/types"
|
||||
|
||||
import type {
|
||||
TUIMessage,
|
||||
PendingAsk,
|
||||
FileSearchResult,
|
||||
SlashCommandResult,
|
||||
ModeResult,
|
||||
TaskHistoryItem,
|
||||
} from "./types.js"
|
||||
import type { TUIMessage, PendingAsk, TaskHistoryItem } from "./types.js"
|
||||
import type { FileResult, SlashCommandResult, ModeResult } from "./components/autocomplete/index.js"
|
||||
|
||||
/**
|
||||
* RouterModels type for context window lookup.
|
||||
|
|
@ -40,7 +34,7 @@ interface CLIState {
|
|||
isResumingTask: boolean
|
||||
|
||||
// Autocomplete data (from API/extension)
|
||||
fileSearchResults: FileSearchResult[]
|
||||
fileSearchResults: FileResult[]
|
||||
allSlashCommands: SlashCommandResult[]
|
||||
availableModes: ModeResult[]
|
||||
|
||||
|
|
@ -83,7 +77,7 @@ interface CLIActions {
|
|||
setIsResumingTask: (isResuming: boolean) => void
|
||||
|
||||
// Autocomplete data actions
|
||||
setFileSearchResults: (results: FileSearchResult[]) => void
|
||||
setFileSearchResults: (results: FileResult[]) => void
|
||||
setAllSlashCommands: (commands: SlashCommandResult[]) => void
|
||||
setAvailableModes: (modes: ModeResult[]) => void
|
||||
|
||||
|
|
@ -210,9 +204,5 @@ export const useCLIStore = create<CLIState & CLIActions>((set) => ({
|
|||
setTokenUsage: (usage) => set({ tokenUsage: usage }),
|
||||
setRouterModels: (models) => set({ routerModels: models }),
|
||||
setApiConfiguration: (config) => set({ apiConfiguration: config }),
|
||||
setTodos: (todos) =>
|
||||
set((state) => ({
|
||||
previousTodos: state.currentTodos,
|
||||
currentTodos: todos,
|
||||
})),
|
||||
setTodos: (todos) => set((state) => ({ previousTodos: state.currentTodos, currentTodos: todos })),
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -1,45 +1,7 @@
|
|||
import type { ClineAsk, ClineSay, TodoItem } from "@roo-code/types"
|
||||
|
||||
import type { GlobalCommandAction } from "../globalCommands.js"
|
||||
|
||||
// Re-export TodoItem for convenience
|
||||
export type { TodoItem }
|
||||
|
||||
export type MessageRole = "system" | "user" | "assistant" | "tool" | "thinking"
|
||||
|
||||
export type AskType = Extract<
|
||||
ClineAsk,
|
||||
| "followup"
|
||||
| "command"
|
||||
| "command_output"
|
||||
| "tool"
|
||||
| "browser_action_launch"
|
||||
| "use_mcp_server"
|
||||
| "api_req_failed"
|
||||
| "resume_task"
|
||||
| "resume_completed_task"
|
||||
| "completion_result"
|
||||
>
|
||||
|
||||
export type SayType =
|
||||
| Extract<
|
||||
ClineSay,
|
||||
| "text"
|
||||
| "reasoning"
|
||||
| "command_output"
|
||||
| "completion_result"
|
||||
| "error"
|
||||
| "api_req_started"
|
||||
| "user_feedback"
|
||||
| "checkpoint_saved"
|
||||
>
|
||||
| "thinking"
|
||||
| "tool"
|
||||
|
||||
/**
|
||||
* Structured tool data for rich rendering
|
||||
* Extracted from tool JSON payloads for tool-specific layouts
|
||||
*/
|
||||
export interface ToolData {
|
||||
/** Tool identifier (e.g., "readFile", "appliedDiff", "searchFiles") */
|
||||
tool: string
|
||||
|
|
@ -130,7 +92,7 @@ export interface TUIMessage {
|
|||
toolDisplayOutput?: string
|
||||
hasPendingToolCalls?: boolean
|
||||
partial?: boolean
|
||||
originalType?: SayType | AskType
|
||||
originalType?: ClineAsk | ClineSay
|
||||
/** TODO items for update_todo_list tool messages */
|
||||
todos?: TodoItem[]
|
||||
/** Previous TODO items for diff display */
|
||||
|
|
@ -141,7 +103,7 @@ export interface TUIMessage {
|
|||
|
||||
export interface PendingAsk {
|
||||
id: string
|
||||
type: AskType
|
||||
type: ClineAsk
|
||||
content: string
|
||||
suggestions?: Array<{ answer: string; mode?: string | null }>
|
||||
}
|
||||
|
|
@ -159,55 +121,20 @@ export interface AppProps {
|
|||
debug: boolean
|
||||
exitOnComplete: boolean
|
||||
reasoningEffort?: string
|
||||
/** Run in ephemeral mode - no state persists after this session */
|
||||
ephemeral?: boolean
|
||||
version: string
|
||||
}
|
||||
|
||||
export type View = "UserInput" | "AgentResponse" | "ToolUse" | "Default"
|
||||
|
||||
export interface FileSearchResult {
|
||||
path: string
|
||||
type: "file" | "folder"
|
||||
label?: string
|
||||
}
|
||||
|
||||
export interface SlashCommandResult {
|
||||
name: string
|
||||
description?: string
|
||||
argumentHint?: string
|
||||
source: "global" | "project" | "built-in"
|
||||
/** Action to trigger for CLI global commands (e.g., clearTask for /new) */
|
||||
action?: GlobalCommandAction
|
||||
}
|
||||
|
||||
export interface ModeResult {
|
||||
slug: string
|
||||
name: string
|
||||
description?: string
|
||||
icon?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Task history item for the CLI.
|
||||
* Subset of HistoryItem from @roo-code/types with fields needed for display and resumption.
|
||||
*/
|
||||
export interface TaskHistoryItem {
|
||||
/** Unique task ID */
|
||||
id: string
|
||||
/** Task prompt/description */
|
||||
task: string
|
||||
/** Timestamp when task was created */
|
||||
ts: number
|
||||
/** Total cost of the task */
|
||||
totalCost?: number
|
||||
/** Workspace path where task was run */
|
||||
workspace?: string
|
||||
/** Mode the task was run in */
|
||||
mode?: string
|
||||
/** Task status */
|
||||
status?: "active" | "completed" | "delegated"
|
||||
/** Tokens consumed */
|
||||
tokensIn?: number
|
||||
tokensOut?: number
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue