Skip to content

Commit 97a2aa5

Browse files
committed
WIP
1 parent e972b68 commit 97a2aa5

File tree

4 files changed

+112
-14
lines changed

4 files changed

+112
-14
lines changed

docs/TUI-ISSUES.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
Based on my analysis of the application's state management (`src/agent/state.ts`) and overall architecture, I can identify several potential issues, missing features, and areas for improvement in the Terminal User Interface (TUI). While I cannot see the specific React code for the UI components, the state is the source of truth that dictates the UI's capabilities.
2+
3+
Here is my breakdown.
4+
5+
### 1. Potential UI Issues That Need to Be Fixed
6+
7+
These are fundamental user experience problems that likely exist due to the current agent logic.
8+
9+
1. **No Response Streaming:** The `_runAgentLogic` function awaits the full response from the LLM before updating the history. This means the user sees a "thinking" indicator for a potentially long time and then gets a large block of text all at once. For a chat application, this feels slow and unresponsive. The UI is not showing progress.
10+
2. **No Interactive Confirmation for Dangerous Tools:** The agent executes tools like `bash` (arbitrary command execution) and `edit` (file modification/overwriting) immediately upon the LLM's decision. This is a significant safety risk. The user is given no opportunity to review and approve potentially destructive operations before they happen.
11+
3. **Lack of Granular Status Updates:** The `mode` state is limited to `"idle"`, `"thinking"`, or `"running-tool"`. If the LLM decides to run three tools in sequence, the user will only see a generic "running-tool" status. They won't know which tool is currently active (`readFile`, `bash`, etc.) or what the progress is. This lack of feedback can be confusing.
12+
4. **Unstructured Tool Output:** Tool calls and their results are likely rendered as plain text or stringified JSON in the history. This makes it difficult to distinguish a tool's operations from the AI's conversational text and can be hard to read, especially for complex outputs.
13+
14+
### 2. Suggestions for UI/UX Improvements
15+
16+
These are suggestions to enhance the existing UI, making it more polished and user-friendly.
17+
18+
1. **Implement a Collapsible, Rich Display for Tool Calls:**
19+
- Each tool call in the history should be a distinct, structured component.
20+
- It should clearly show the tool name and arguments (e.g., `edit(path: 'src/main.ts', ...)`).
21+
- The component should be collapsible, hiding the (potentially long) tool output by default to keep the chat history clean.
22+
- For the `edit` tool, render its output as a colorized diff (`+` for additions, `-` for deletions) instead of just new content.
23+
24+
2. **Enhance the Footer/Status Bar:**
25+
- Use the `mode` state to display more descriptive text: "Tobi is thinking...", "Running `bash`...", "Waiting for confirmation...".
26+
- Display the current Git branch (`branchName` from the store) prominently.
27+
- Show dynamic keyboard shortcuts relevant to the current context (e.g., `Y/N` for confirmation prompts).
28+
29+
3. **Improve the User Input Component:**
30+
- Add support for multi-line input (`Shift+Enter` for a new line) to allow users to paste code blocks or write more complex prompts easily.
31+
- Consider basic autocomplete for file paths when the user is typing arguments for tools like `readFile` or `edit`.
32+
33+
4. **Add Syntax Highlighting:**
34+
- All code blocks, whether from the user, the assistant, or tool results, should have syntax highlighting to improve readability. There are several libraries that can achieve this in an Ink TUI.
35+
36+
### 3. Missing TUI Features (Compared to Similar Tools)
37+
38+
These are larger features commonly found in modern AI assistants that are absent from Tobi's current design.
39+
40+
1. **Interactive User Confirmation Workflow:** This is the most critical missing feature. The TUI needs a workflow where it can:
41+
- Pause execution when the agent wants to run a tool.
42+
- Display the planned tool call(s) to the user in a clear format.
43+
- Prompt the user for approval (`[Y]es / [N]o / [A]lways / [E]dit`).
44+
- Allow the user to edit the arguments of a tool call before execution.
45+
- The state machine needs a new mode like `awaiting-confirmation` to support this.
46+
47+
2. **Explicit Context Management:** The user currently has no way to see or manage which files the AI "knows about." A key feature of tools like this is the ability to manually add files to the context.
48+
- **Missing UI:** A dedicated panel or command (`/add <file_path>`) to add files to the conversation context. The UI should display a list of these active files.
49+
- **Missing Logic:** The `FileTracker` is for safety, but there's no corresponding "ContextManager" that holds the content of files explicitly added by the user to be included in subsequent LLM prompts.
50+
51+
3. **Session Management:** The application appears to have one continuous history. Users often need to work on multiple, distinct tasks.
52+
- **Missing UI:** Commands or keybindings to save the current session, start a new one, and list/load previous sessions.
53+
54+
4. **"Plan" Visualization:** More advanced agents first generate a step-by-step plan before execution. Tobi follows a simpler one-step-at-a-time loop.
55+
- **Missing Feature:** The ability for the agent to output a plan (e.g., 1. Read `package.json`. 2. Install dependencies with `bash`. 3. Read `src/index.ts`.). The TUI should then display this as a checklist, updating it as the agent completes each step. This gives the user immense insight into the AI's intentions.
56+
57+
5. **In-App Configuration:** To change the model or system prompt, the user must edit the `config.json5` file and restart the app.
58+
- **Missing UI:** Special commands within the TUI to manage configuration on the fly, for example:
59+
- `/model claude-3.5-sonnet` to switch the active LLM.
60+
- `/system "You are an expert in React."` to change the system prompt for the current session.
61+
- `/clear` to clear the current session history. (The `clearHistory` action exists but needs to be exposed).

src/agent/state.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ type AppActions = {
4343
addCommandToHistory: (command: string) => void;
4444
getPreviousCommand: () => string | null;
4545
getNextCommand: () => string | null;
46+
setSystemPrompt: (prompt: string) => void;
47+
setModel: (modelName: string) => void;
4648
};
4749
};
4850

@@ -120,6 +122,24 @@ export const useStore = create<AppState & AppActions>((set, get) => ({
120122
}
121123
return null;
122124
},
125+
setSystemPrompt: (prompt) => {
126+
const { config } = get();
127+
if (config) {
128+
set({ config: { ...config, systemPrompt: prompt } });
129+
}
130+
},
131+
setModel: (modelName) => {
132+
const { config } = get();
133+
if (config) {
134+
const modelExists = config.models.some((m) => m.name === modelName);
135+
if (modelExists) {
136+
set({ config: { ...config, defaultModel: modelName } });
137+
} else {
138+
// Maybe add a user-facing error message later
139+
console.error(`Model "${modelName}" not found in config.`);
140+
}
141+
}
142+
},
123143
startAgent: async (input) => {
124144
get().actions.addCommandToHistory(input);
125145
const newHistory: Message[] = [

src/ui/HistoryItemDisplay.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// src/ui/HistoryItemDisplay.tsx
22
import React from "react";
33
import { Box, Text } from "ink";
4-
import type { AssistantContent, CoreMessage, ToolContent } from "ai";
4+
import type { AssistantContent, ModelMessage, ToolContent } from "ai";
55
import type { Message } from "@/agent/types.js";
66

77
function AssistantMessageContent({ content }: { content: AssistantContent | string }) {
@@ -45,7 +45,7 @@ function ToolMessageContent({ content }: { content: ToolContent }) {
4545
);
4646
}
4747

48-
function UserMessageContent({ content }: { content: CoreMessage["content"] }) {
48+
function UserMessageContent({ content }: { content: ModelMessage["content"] }) {
4949
const userContent = Array.isArray(content)
5050
? content.map((part) => ("text" in part ? part.text : "")).join("")
5151
: content;

src/ui/UserInput.tsx

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,27 @@ import { useShallow } from "zustand/react/shallow";
77

88
export function UserInput() {
99
const [useStateInput, setInputValue] = useState("");
10-
const { startAgent, mode, toggleHelpMenu, clearHistory, getPreviousCommand, getNextCommand } =
11-
useStore(
12-
useShallow((s) => ({
13-
startAgent: s.actions.startAgent,
14-
mode: s.mode,
15-
toggleHelpMenu: s.actions.toggleHelpMenu,
16-
clearHistory: s.actions.clearHistory,
17-
getPreviousCommand: s.actions.getPreviousCommand,
18-
getNextCommand: s.actions.getNextCommand,
19-
})),
20-
);
10+
const {
11+
startAgent,
12+
mode,
13+
toggleHelpMenu,
14+
clearHistory,
15+
getPreviousCommand,
16+
getNextCommand,
17+
setSystemPrompt,
18+
setModel,
19+
} = useStore(
20+
useShallow((s) => ({
21+
startAgent: s.actions.startAgent,
22+
mode: s.mode,
23+
toggleHelpMenu: s.actions.toggleHelpMenu,
24+
clearHistory: s.actions.clearHistory,
25+
getPreviousCommand: s.actions.getPreviousCommand,
26+
getNextCommand: s.actions.getNextCommand,
27+
setSystemPrompt: s.actions.setSystemPrompt,
28+
setModel: s.actions.setModel,
29+
})),
30+
);
2131
const { exit } = useApp();
2232

2333
const isThinking = mode !== "idle";
@@ -42,7 +52,8 @@ export function UserInput() {
4252
const value = useStateInput.trim();
4353
if (value && !isThinking) {
4454
if (value.startsWith("/")) {
45-
const [command] = value.slice(1).split(" ");
55+
const [command, ...args] = value.slice(1).split(" ");
56+
const rest = args.join(" ");
4657
switch (command) {
4758
case "help":
4859
toggleHelpMenu();
@@ -53,6 +64,12 @@ export function UserInput() {
5364
case "quit":
5465
exit();
5566
break;
67+
case "system":
68+
setSystemPrompt(rest);
69+
break;
70+
case "model":
71+
setModel(rest);
72+
break;
5673
default:
5774
startAgent(value);
5875
break;

0 commit comments

Comments
 (0)