Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
868fa5e
feat: add OutputContainer with markdown rendering and cost tracking
1broseidon Dec 8, 2025
a595267
feat: add experimental tui
1broseidon Dec 9, 2025
c8092c5
feat: tui loading title
1broseidon Dec 9, 2025
041067e
fix: defer confirmation handling to resolve Windows event loop race c…
tgbender Dec 12, 2025
4d11c68
Merge remote-tracking branch 'broseidon-tui/experimental-tui' into tu…
dwash96 Dec 12, 2025
e24605c
Allow messages exceeding context to be auto approved
dwash96 Dec 12, 2025
553d5f8
Respect parallel input
dwash96 Dec 13, 2025
d8f479f
Round 1: Styling Updates
dwash96 Dec 13, 2025
dafcd8b
TUI Changes:
dwash96 Dec 14, 2025
9c3742c
Update agent mode system prompts
dwash96 Dec 14, 2025
8ec5c54
FIle formatting
dwash96 Dec 14, 2025
f3239ee
Add print output capturing
dwash96 Dec 14, 2025
07e8152
Add spinner suffixes back to TUI mode, friendlier scrolling during ou…
dwash96 Dec 14, 2025
f7fa82d
TUI Updates:
dwash96 Dec 14, 2025
58c2086
TUI Updates: Stop generation task wIth escape
dwash96 Dec 14, 2025
871b556
TUI Updates:
dwash96 Dec 15, 2025
6cdb5b0
TUI Updates: Always auto complete last contiguous block
dwash96 Dec 15, 2025
42e3b06
TUI Updates: Reduce aliases to 2 since I still like "ce.cli"
dwash96 Dec 15, 2025
b25450b
TUI Updates: Add active file list inside common input container
dwash96 Dec 15, 2025
9d2974d
TUI Updates:
dwash96 Dec 15, 2025
fb9e343
TUI Updates: Make clear commands behave intuitively
dwash96 Dec 15, 2025
568eac3
Add configuration and documentation
dwash96 Dec 15, 2025
cb823d3
Update package imports so textual exists in the core app, remove gui …
dwash96 Dec 15, 2025
0d96a70
Bump Version
dwash96 Dec 15, 2025
32f8879
Merge branch 'tui-experiment' into v0.90.0
dwash96 Dec 15, 2025
55c769c
TUI Update: Placeholder text
dwash96 Dec 15, 2025
772bfba
#256: Advanced model setting with configuration and tag suffixes
dwash96 Dec 15, 2025
98afe8c
Documentation Updates
dwash96 Dec 15, 2025
22a1ec6
TUI, by default since it looks good as heck
dwash96 Dec 15, 2025
ead5d08
#259: Add cost analyzer script
dwash96 Dec 15, 2025
d2a59d7
Merge pull request #258 from tgbender/fix/windows-confirmation-race
dwash96 Dec 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/ubuntu-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,9 @@ jobs:
pytest \
pytest-asyncio \
-r requirements/requirements.in \
-r requirements/requirements-browser.in \
-r requirements/requirements-help.in \
-r requirements/requirements-playwright.in \
".[browser,help,playwright]"
".[help,playwright]"

- name: Run tests
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/windows-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install uv
uv pip install --system pytest pytest-asyncio -r requirements/requirements.in -r requirements/requirements-browser.in -r requirements/requirements-help.in -r requirements/requirements-playwright.in '.[browser,help,playwright]'
uv pip install --system pytest pytest-asyncio -r requirements/requirements.in -r requirements/requirements-help.in -r requirements/requirements-playwright.in '.[help,playwright]'

- name: Run tests
env:
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ LLMs are a part of our lives from here on out so join us in learning about and c

* [Agent Mode](https://github.com/dwash96/aider-ce/blob/main/aider/website/docs/config/agent-mode.md)
* [MCP Configuration](https://github.com/dwash96/aider-ce/blob/main/aider/website/docs/config/mcp.md)
* [Session Management](https://github.com/dwash96/aider-ce/blob/main/aider/website/docs/sessions.md)
* [TUI Configuration](https://github.com/dwash96/aider-ce/blob/main/aider/website/docs/config/tui.md)
* [Skills](https://github.com/dwash96/aider-ce/blob/main/aider/website/docs/config/skills.md)
* [Session Management](https://github.com/dwash96/aider-ce/blob/main/aider/website/docs/sessions.md)
* [Advanced Model Configuration](https://github.com/dwash96/aider-ce/blob/main/aider/website/docs/config/model-aliases.md# advanced-model-settings)
* [Aider Original Documentation (still mostly applies)](https://aider.chat/)

You can see a selection of the enhancements and updates by comparing the help output:
Expand Down Expand Up @@ -75,6 +77,7 @@ preserve-todo-list: true
show-model-warnings: true
use-enhanced-map: true
watch-files: false
tui: true

agent-config:
large_file_token_threshold: 12500
Expand Down Expand Up @@ -138,9 +141,9 @@ The current priorities are to improve core capabilities and user experience of t
* [ ] Add support for partial files and code snippets in model completion messages

5. **TUI Experience** - [Discussion](https://github.com/dwash96/aider-ce/issues/48)
* [ ] Add a full TUI (probably using textual) to have a visual interface competitive with the other coding agent terminal programs
* [x] Add a full TUI (probably using textual) to have a visual interface competitive with the other coding agent terminal programs
* [x] Re-integrate pretty output formatting
* [ ] Implement a response area, a prompt area with current auto completion capabilities, and a helper area for management utility commands
* [x] Implement a response area, a prompt area with current auto completion capabilities, and a helper area for managing utility commands

6. **Agent Mode** - [Discussion](https://github.com/dwash96/aider-ce/issues/111)
* [x] Renaming "navigator mode" to "agent mode" for simplicity
Expand Down
2 changes: 1 addition & 1 deletion aider/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from packaging import version

__version__ = "0.89.7.dev"
__version__ = "0.90.0.dev"
safe_version = __version__

try:
Expand Down
40 changes: 32 additions & 8 deletions aider/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,23 @@ def get_parser(default_config_files, git_root):
metavar="ALIAS:MODEL",
help="Add a model alias (can be used multiple times)",
)
group.add_argument(
"--model-overrides",
metavar="MODEL_OVERRIDES_JSON",
help=(
'Specify model tag overrides directly as JSON/YAML string (e.g., \'{"gpt-4o": {"high":'
' {"temperature": 0.8}}}\')'
),
default=None,
)
group.add_argument(
"--model-overrides-file",
metavar="MODEL_OVERRIDES_FILE",
default=".aider.model.overrides.yml",
help=(
"Specify a file with model tag overrides (e.g., gpt-4o:high -> reasoning_effort: high)"
),
).complete = shtab.FILE
group.add_argument(
"--reasoning-effort",
type=str,
Expand Down Expand Up @@ -218,8 +235,22 @@ def get_parser(default_config_files, git_root):
),
)

########
group = parser.add_argument_group("TUI Settings")
group.add_argument(
"--tui",
action="store_true",
default=False,
help="Launch Textual TUI interface (experimental)",
)
group.add_argument(
"--tui-config",
metavar="TUI_CONFIG_JSON",
help="Specify TUI Mode configuration as a JSON string",
default=None,
)
#########
group = parser.add_argument_group("Agent settings")
group = parser.add_argument_group("Agent Settings")
group.add_argument(
"--agent-config",
metavar="AGENT_CONFIG_JSON",
Expand Down Expand Up @@ -725,13 +756,6 @@ def get_parser(default_config_files, git_root):
" (disables chat mode)"
),
).complete = shtab.FILE
group.add_argument(
"--gui",
"--browser",
action=argparse.BooleanOptionalAction,
help=argparse.SUPPRESS,
default=False,
)
group.add_argument(
"--copy-paste",
action=argparse.BooleanOptionalAction,
Expand Down
14 changes: 6 additions & 8 deletions aider/coders/agent_prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ class AgentPrompts(CoderPrompts):
## Core Directives
- **Role**: Act as an expert software engineer.
- **Act Proactively**: Autonomously use file discovery and context management tools (`ViewFilesAtGlob`, `ViewFilesMatching`, `Ls`, `View`, `Remove`) to gather information and fulfill the user's request. Chain tool calls across multiple turns to continue exploration.
- **Be Decisive**: Do not ask the same question or search for the same term in multiple ways. Trust your initial valid findings.
- **Be Decisive**: Do not ask the same question or search for the same term in multiple ways. Trust that your initial findings are valid.
- **Be Concise**: Keep all responses brief and direct (1-3 sentences). Avoid preamble, postamble, and unnecessary explanations.
- **Confirm Ambiguity**: Before applying complex or ambiguous edits, briefly state your plan and ask for confirmation. For simple, direct edits, proceed without confirmation.
- **Confirm Ambiguity**: Before applying complex or ambiguous edits, briefly state your plan. For simple, direct edits, proceed without confirmation.
</context>

<context name="workflow_and_tool_usage">
## Core Workflow
1. **Plan**: Determine the necessary changes. Use the `UpdateTodoList` tool to manage your plan. Always begin by the todo list.
1. **Plan**: Determine the necessary changes. Use the `UpdateTodoList` tool to manage your plan. Always begin by updating the todo list.
2. **Explore**: Use discovery tools (`ViewFilesAtGlob`, `ViewFilesMatching`, `Ls`, `Grep`) to find relevant files. These tools add files to context as read-only. Use `Grep` first for broad searches to avoid context clutter. Concisely describe your search strategy with the `Thinking` tool.
3. **Think**: Given the contents of your exploration, concisely reason through the edits with the `Thinking` tool that need to be made to accomplish the goal. For complex edits, briefly outline your plan for the user.
4. **Execute**: Use the appropriate editing tool. Remember to use `MakeEditable` on a file before modifying it. Break large edits (those greater than ~100 lines) into multiple smaller steps. Proactively use skills if they are available
Expand All @@ -36,10 +36,7 @@ class AgentPrompts(CoderPrompts):
- **Plan Steps**: Create a todo list at the start of complex tasks to track your progress through multiple exploration rounds.
- **Stay Organized**: Update the todo list as you complete steps every 3-10 tool calls to maintain context across multiple tool calls.

## Code Editing Hierarchy
Your primary method for all modifications is through granular tool calls. Use SEARCH/REPLACE only as a last resort.

### 1. Granular Tools (Always Preferred)
### Editing Tools
Use these for precision and safety.
- **Text/Block Manipulation**: `ReplaceText` (Preferred for the majority of edits), `InsertBlock`, `DeleteBlock`, `ReplaceAll` (use with `dry_run=True` for safety).
- **Line-Based Edits**: `ReplaceLine(s)`, `DeleteLine(s)`, `IndentLines`.
Expand Down Expand Up @@ -78,9 +75,10 @@ class AgentPrompts(CoderPrompts):
system_reminder = """
<context name="critical_reminders">
## Reminders
- Stay on task. Do not pursue goals the user did not ask for.
- Any tool call automatically continues to the next turn. Provide no tool calls in your final answer.
- Use context blocks (directory structure, git status) to orient yourself.
- Remove files you are done with viewing/editing from the context with the `Remove` tool. It is fine to re-add them later
- Remove files from the context when you are done with viewing/editing with the `Remove` tool. It is fine to re-add them later, if they are needed again
- Remove skills if they are not helpful for your current task with `RemoveSkill`

{lazy_prompt}
Expand Down
22 changes: 17 additions & 5 deletions aider/coders/base_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ class Coder:

ok_to_warm_cache = False

# Weak reference to TUI app instance (when running in TUI mode)
tui = None

@classmethod
async def create(
self,
Expand Down Expand Up @@ -546,7 +549,7 @@ def __init__(

def get_announcements(self):
lines = []
lines.append(f"Aider-CE v{__version__}")
lines.append(f"cecli v{__version__}")

# Model
main_model = self.main_model
Expand Down Expand Up @@ -1250,11 +1253,17 @@ async def _run_linear(self, with_message=None, preproc=True):
await self.io.recreate_input()
await self.io.input_task
user_message = self.io.input_task.result()
self.io.tool_output("Processing...\n")

if self.args and not self.args.tui:
self.io.tool_output("Processing...\n")

self.io.output_task = asyncio.create_task(self.generate(user_message, preproc))

await self.io.output_task
self.io.tool_output("Finished.")

if self.args and not self.args.tui:
self.io.tool_output("Finished.")

self.io.ring_bell()
user_message = None
await self.auto_save_session()
Expand Down Expand Up @@ -1351,8 +1360,11 @@ async def input_task(self, preproc):
try:
user_message = self.io.input_task.result()

# Defer to confirmation handler to fix Windows event loop race.
if self.io.confirmation_in_progress:
pass
# Set user message for output task
if not self.io.acknowledge_confirmation():
elif not self.io.acknowledge_confirmation():
if user_message:
self.user_message = user_message
await self.auto_save_session()
Expand Down Expand Up @@ -2129,7 +2141,7 @@ async def check_tokens(self, messages):
" the context limit is exceeded."
)

if not await self.io.confirm_ask("Try to proceed anyway?", explicit_yes_required=True):
if not await self.io.confirm_ask("Try to proceed anyway?"):
return False
return True

Expand Down
59 changes: 42 additions & 17 deletions aider/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,10 @@ def cmd_clear(self, args):
"Clear the chat history"

self._clear_chat_history()

if self.coder.tui and self.coder.tui():
self.coder.tui().action_clear_output()

self.io.tool_output("All chat history cleared.")

def _drop_all_files(self):
Expand Down Expand Up @@ -494,6 +498,10 @@ def cmd_reset(self, args):
"Drop all files and clear the chat history"
self._drop_all_files()
self._clear_chat_history()

if self.coder.tui and self.coder.tui():
self.coder.tui().action_clear_output()

self.io.tool_output("All files dropped and chat history cleared.")

def cmd_tokens(self, args):
Expand Down Expand Up @@ -920,7 +928,7 @@ async def cmd_add(self, args):
if not addable_files:
self.io.tool_output("No files available to add.")
return
selected_files = run_fzf(addable_files, multi=True)
selected_files = run_fzf(addable_files, multi=True, coder=self.coder)
if not selected_files:
return
args = " ".join([self.quote_fname(f) for f in selected_files])
Expand Down Expand Up @@ -1254,19 +1262,29 @@ async def cmd_run(self, args, add_on_nonzero_exit=False):
"Run a shell command and optionally add the output to the chat (alias: !)"
try:
self.cmd_running = True
should_print = True

if self.coder.args.tui:
should_print = False

exit_status, combined_output = await asyncio.to_thread(
run_cmd,
args,
verbose=self.verbose,
error_print=self.io.tool_error,
error_print=self.coder.io.tool_error,
cwd=self.coder.root,
should_print=should_print,
)

self.cmd_running = False

# This print statement, for whatever reason,
# allows the thread to properly yield control of the terminal
# to the main program
print("")
if self.coder.args.tui:
print(combined_output)
else:
# This print statement, for whatever reason,
# allows the thread to properly yield control of the terminal
# to the main program
print("")

if combined_output is None:
return
Expand Down Expand Up @@ -1319,6 +1337,13 @@ async def cmd_exit(self, args):

await asyncio.sleep(0)

# Check if running in TUI mode - use graceful exit to restore terminal
if hasattr(self.io, "request_exit"):
self.io.request_exit()
# Give TUI time to process the exit message
await asyncio.sleep(0.5)
return

try:
if self.coder.args.linear_output:
os._exit(0)
Expand Down Expand Up @@ -1411,16 +1436,16 @@ def cmd_ls(self, args):

files = self.coder.get_all_relative_files()

other_files = []
# other_files = []
chat_files = []
read_only_files = []
read_only_stub_files = []
for file in files:
abs_file_path = self.coder.abs_root_path(file)
if abs_file_path in self.coder.abs_fnames:
chat_files.append(file)
else:
other_files.append(file)
# else:
# other_files.append(file)

# Add read-only files
for abs_file_path in self.coder.abs_read_only_fnames:
Expand All @@ -1432,14 +1457,14 @@ def cmd_ls(self, args):
rel_file_path = self.coder.get_rel_fname(abs_file_path)
read_only_stub_files.append(rel_file_path)

if not chat_files and not other_files and not read_only_files and not read_only_stub_files:
if not chat_files and not read_only_files and not read_only_stub_files:
self.io.tool_output("\nNo files in chat, git repo, or read-only list.")
return

if other_files:
self.io.tool_output("Repo files not in the chat:\n")
for file in other_files:
self.io.tool_output(f" {file}")
# if other_files:
# self.io.tool_output("Repo files not in the chat:\n")
# for file in other_files:
# self.io.tool_output(f" {file}")

# Read-only files:
if read_only_files or read_only_stub_files:
Expand Down Expand Up @@ -1827,7 +1852,7 @@ def cmd_read_only(self, args):
target_mode="read-only",
)
return
selected_files = run_fzf(addable_files, multi=True)
selected_files = run_fzf(addable_files, multi=True, coder=self.coder)
if not selected_files:
# If user didn't select any files, convert all editable files to read-only
self._cmd_read_only_base(
Expand Down Expand Up @@ -1865,7 +1890,7 @@ def cmd_read_only_stub(self, args):
target_mode="read-only (stub)",
)
return
selected_files = run_fzf(addable_files, multi=True)
selected_files = run_fzf(addable_files, multi=True, coder=self.coder)
if not selected_files:
# If user didn't select any files, convert all editable files to read-only stubs
self._cmd_read_only_base(
Expand Down Expand Up @@ -2059,7 +2084,7 @@ def cmd_edit(self, args=""):
def cmd_history_search(self, args):
"Fuzzy search in history and paste it in the prompt"
history_lines = self.io.get_input_history()
selected_lines = run_fzf(history_lines)
selected_lines = run_fzf(history_lines, coder=self.coder)
if selected_lines:
self.io.set_placeholder("".join(selected_lines))

Expand Down
Loading
Loading