Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
111 commits
Select commit Hold shift + click to select a range
505da75
fix: Remove redundant notification in tool call info printing
May 12, 2026
cc3f6a5
Rename ObservationManager to ObservationService for consistency
May 13, 2026
e97ab06
fix: Remove notification on tool call and add to Finished tool
May 13, 2026
62db409
refactor: Make notification calls async and use asyncio subprocess
May 13, 2026
a1aeacc
chore: Add debug logging for notifications
May 13, 2026
fd4e411
cli-21: fixed merge conflicts
May 13, 2026
46a056d
Add stop parameter for background commands to context_manager
May 14, 2026
4695e8e
fix
May 14, 2026
7db69eb
fix: Add notification for user input in get_input
May 14, 2026
620a3c5
fix: Add notification when user input is required
May 14, 2026
2c73055
feat: Make linting interruptible
May 15, 2026
59bccdf
fix: Make run and test commands interruptible
May 15, 2026
780e498
merge conflicts
May 15, 2026
990c27a
feat: Make LLM streaming response interruptible
May 15, 2026
6caa11b
fix: Correct indentation in show_send_output_stream
May 15, 2026
b90874e
fix: Add TUI notifications for user input and confirmations
May 15, 2026
d294596
Sub Agents, first (but really like the 12th) draft
May 17, 2026
93d0293
Use Enhanced Map true by default
May 17, 2026
fd73c63
In ReadRange tool, on unbounded responses, return file stubs or sampl…
May 17, 2026
f42ca8f
Update duplicate tool call message
May 17, 2026
991605d
Update finished tool output
May 17, 2026
3a7463a
Allow sub agents to be sent input while primary coder is running
May 17, 2026
baac47e
Rename `Dispatch` to `Delegate`
May 17, 2026
59c2b6b
Add bash tree sitter tags from #5132
May 17, 2026
1871588
Increase warn number of tokens
May 17, 2026
e035435
Allow sub agent classes to allow different tools and mcp servers from…
May 18, 2026
5525127
Make sure we apply changes to visible container for TUI
May 18, 2026
758d9cd
Make sure we set agent-model for sub agents appropriately
May 18, 2026
30ca34f
Add proper message blocks for sub agent coders
May 18, 2026
6852e50
Update documentation
May 18, 2026
f8d65b6
Re-use start_generate_task() for consistent TUI control for sub agent…
May 18, 2026
fbcc18b
Add finished tool detail to sub agent reminder messages
May 18, 2026
210f127
Update delegate tool and sub agent base prompt
May 18, 2026
30af695
Add invoke summary to main agent
May 18, 2026
d703941
Update documentation
May 18, 2026
b4b58a3
Fix ReadRange bug
May 18, 2026
06c9b91
Update release pipeline to allow for releasing release candidates
May 18, 2026
20f9c40
Fix tests
May 18, 2026
5e5d5ba
Remove requirements-tui.in
May 18, 2026
af6b4b4
Update sub agents documentation
May 18, 2026
3629d0a
fix: Add notification cooldown to prevent spam
May 18, 2026
4596232
refactor: Make agent mode interruptions more robust
May 18, 2026
b7274d6
(no commit message provided)
May 18, 2026
3ab8700
(no commit message provided)
May 18, 2026
c42112e
(no commit message provided)
May 18, 2026
b10a04f
cli-22: fixed black errors
May 18, 2026
1a9fba0
(no commit message provided)
May 18, 2026
e7eeac0
(no commit message provided)
May 18, 2026
5907eee
(no commit message provided)
May 18, 2026
7e2674f
(no commit message provided)
May 19, 2026
d45f096
cli-26: switch agent shortcuts
May 19, 2026
ff7a4df
(no commit message provided)
May 19, 2026
a383202
Merge pull request #516 from szmania/cli-22-robust-interruption
dwash96 May 19, 2026
c63054f
Merge pull request #517 from szmania/cli-21-fix-notifications-agent-mode
dwash96 May 19, 2026
1d4cb4b
fix: Update keybindings for agent navigation
May 19, 2026
d538368
feat: Add /switch-agent command with tab completion
May 19, 2026
5a44906
Use alt+shift+left/right/up for sub agent sqitching for better cross …
May 19, 2026
cfae0eb
fix: Disable Local MCP server when switching from AgentCoder
May 19, 2026
9a1eb87
Spawn agent to tell you your actual key command for agent switching
May 19, 2026
a47da74
fix: Resolve circular import in switch_agent command
May 19, 2026
aec45d6
feat: Add SwitchAgentCommand to command registry
May 19, 2026
78c06ac
fix: Resolve circular import in switch_agent command
May 19, 2026
63a21f4
fix: Improve agent switching robustness in TUI
May 19, 2026
4f95e10
feat: Add tag to ConversationService add_message call
May 19, 2026
3883a52
refactor: Improve agent switching logic and completions
May 19, 2026
ded0451
fix: Ensure agent switching updates UI on main thread
May 19, 2026
8c745f3
refactor: Remove unnecessary call_from_thread in TUI
May 19, 2026
a18de88
refactor: Move AgentService import to top of on_input_area_submit
May 19, 2026
af0c214
cli-23: auto compaction disabled when exit or /clear
May 19, 2026
1b8b81c
test: add tests for switch_agent command
May 19, 2026
4bdefce
Fix tests
May 19, 2026
34e9cde
Clear interruptions before starting new message, make sure directory_…
May 19, 2026
27e916f
feat(agent): Add /switch-agent command and Ctrl+Shift shortcuts
May 19, 2026
9318603
(no commit message provided)
May 19, 2026
a57834d
(no commit message provided)
May 19, 2026
5404d22
cli-26: removed wrong documetnation
May 19, 2026
c2459fb
Aggregate all cost and token usage stats across sub agents
May 19, 2026
4e54d0d
Merge pull request #518 from szmania/cli-27-local-mcp-tools-remain-on…
dwash96 May 19, 2026
71f6145
Change string f-string interpolation for CI/CD tests
May 19, 2026
efe9699
FIx tests
May 19, 2026
8740a04
Add allow_nested_delegation so sub agents can trigger others if desired
May 19, 2026
2193a12
Preserve TUI ref in child coder classes
May 19, 2026
d920fce
FIx grammar
May 19, 2026
3e0fbb0
Release interrupt tasks in coroutine generator, fix windows linter test
May 19, 2026
4af9ad9
Revert release.yml change
May 19, 2026
1dea3f6
feat: Add UUID prefix to duplicate agent names in TUI
May 19, 2026
39b12ed
test: add tests for compaction skipping in coder.generate
May 19, 2026
2ecfca7
fix: Remove unused asyncio import in test_compaction.py
May 19, 2026
093999e
feat: Add UUID prefix matching for agent switching
May 19, 2026
5fb1a45
cli-23: fix isort
May 19, 2026
bd8b569
feat: Implement UUID prefix for agent switching
May 19, 2026
cb599ce
refactor: Improve agent name resolution in switch-agent command
May 19, 2026
499f563
feat: Improve agent switching display and completions
May 19, 2026
c7b3ab1
refactor: Always include UUID prefix for sub-agent completions
May 19, 2026
68b2340
feat: Always show UUID prefix for sub-agents in UI
May 19, 2026
a00a2b7
cli-26: fixed black
May 19, 2026
2d9b2ce
fix: Remove unused name_counts variable in input_container.py
May 19, 2026
51f73ac
chore: Remove unused import 'Counter' from input_container.py
May 19, 2026
f90e62d
Allow models to make mistakes with special markers
May 20, 2026
d6af00c
Convert charachters to tokens (roughly) for command pagination
May 20, 2026
077d30c
Merge pull request #520 from szmania/cli-23-remove-auto-compaction-on…
dwash96 May 20, 2026
7e63a67
Skip compaction on reset as well
May 20, 2026
83c7e1e
Merge pull request #519 from szmania/CLI-26-subagent-windows-shortcuts
dwash96 May 20, 2026
ca49121
edit_allowed to default to true unless mistakes are made
May 20, 2026
eb9cb29
Only show short name if multiple instances of the name exist
May 20, 2026
ffaf8dc
Preserve necessary system makers in build chain for cross platform de…
May 20, 2026
d1ca418
General fixes to running commands on windows:
May 20, 2026
2aca176
Add available sub agents to announcements
May 20, 2026
7cd9c0c
Quote hello world in test with double quotes
May 20, 2026
10b5393
Pre-commit update
May 20, 2026
4c4565a
Differences in echo semantics between local and runners for what echo…
May 20, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_dispatch:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]*.[0-9]*.[0-9]*'

jobs:
publish_cecli:
Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ LLMs are a part of our lives from here on out so join us in learning about and c
* [MCP Configuration](https://github.com/dwash96/cecli/blob/main/cecli/website/docs/config/mcp.md)
* [TUI Configuration](https://github.com/dwash96/cecli/blob/main/cecli/website/docs/config/tui.md)
* [Skills](https://github.com/dwash96/cecli/blob/main/cecli/website/docs/config/skills.md)
* [Subagents](https://github.com/dwash96/cecli/blob/main/cecli/website/docs/config/subagents.md)
* [Session Management](https://github.com/dwash96/cecli/blob/main/cecli/website/docs/sessions.md)
* [Hooks](https://github.com/dwash96/cecli/blob/main/cecli/website/docs/config/hooks.md)
* [Workspaces](https://github.com/dwash96/cecli/blob/main/cecli/website/docs/config/workspaces.md)
Expand Down Expand Up @@ -142,7 +143,7 @@ The current priorities are to improve core capabilities and user experience of t
* [ ] Build an explicit workflow and local tooling for internal discovery mechanisms

4. **Context Delivery** - [Discussion](https://github.com/dwash96/cecli/issues/47)
* [ ] Use workflow for internal discovery to better target file snippets needed for specific tasks
* [x] Use workflow for internal discovery to better target file snippets needed for specific tasks (ExploreCode and ReadRange)
* [x] Add support for partial files and code snippets in model completion messages
* [x] Update message request structure for optimal caching

Expand All @@ -161,12 +162,12 @@ The current priorities are to improve core capabilities and user experience of t
* [x] Add a dynamic tool discovery tool to allow the system to have only the tools it needs in context

7. **Sub Agents**
* [ ] Add `/fork` and `/rejoin` commands to manually manage parts of the conversation history
* [x] Add `/invoke-agent` command to manually branch a sub agent and return a summary to the main context
* [x] Add an instance-able view of the conversation system so sub agents get their own context and workspaces
* [x] Modify coder classes to have discrete identifiers for themselves/management utilities for them to have their own slices of the world
* [x] Refactor global files like todo lists to live inside instance folders to avoid state conflicts
* [ ] Add a `spawn` tool that launches a sub agent as a background command that the parent model waits for to finish
* [ ] Add visibility into active sub agent calls in TUI
* [x] Add a `Delegate` tool that launches a sub agent as a background command that the parent model waits for to finish
* [x] Add visibility into active sub agent calls in TUI

8. **Hooks**
* [x] Add hooks base class for user defined python hooks with an execute method with type and priority settings
Expand All @@ -180,7 +181,7 @@ The current priorities are to improve core capabilities and user experience of t
* [x] Update internal file diff representation to support hashline propagation

10. **Dynamic Context Management**
* [ ] Update compaction to use observational memory sub agent calls to generate decision records that are used as the compaction basis
* [x] Update compaction to use observational memory sub agent calls to generate decision records that are used as the compaction basis
* [ ] Persist decision records to disk for sessions with some settings for managing lifetimes of such persistence
* [ ] Integrate RLM to extract information from decision records on disk and other definable notes
* [ ] Add a "describe" tool that launches a sub agent workflow that populates an RLM call's context with:
Expand Down
2 changes: 1 addition & 1 deletion cecli/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ def get_parser(default_config_files, git_root):
"--use-enhanced-map",
action="store_true",
help="Use enhanced Repo Map that takes into account imports (default: False)",
default=False,
default=True,
)

##########
Expand Down
2 changes: 2 additions & 0 deletions cecli/coders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .hashline_coder import HashLineCoder
from .help_coder import HelpCoder
from .patch_coder import PatchCoder
from .sub_agent_coder import SubAgentCoder
from .udiff_coder import UnifiedDiffCoder
from .udiff_simple import UnifiedDiffSimpleCoder
from .wholefile_coder import WholeFileCoder
Expand All @@ -37,4 +38,5 @@
AgentCoder,
CopyPasteCoder,
HashLineCoder,
SubAgentCoder,
]
128 changes: 119 additions & 9 deletions cecli/coders/agent_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import base64
import json
import locale
import logging
import os
import platform
import random
Expand All @@ -14,6 +15,7 @@
from cecli import utils
from cecli.change_tracker import ChangeTracker
from cecli.helpers import nested, responses
from cecli.helpers.agents.service import AgentService
from cecli.helpers.background_commands import BackgroundCommandManager
from cecli.helpers.conversation import ConversationService, MessageTag
from cecli.helpers.similarity import (
Expand All @@ -33,13 +35,16 @@

from cecli.helpers.coroutines import interruptible # isort:skip

logger = logging.getLogger(__name__)


class AgentCoder(Coder):
"""Mode where the LLM autonomously manages which files are in context."""

edit_format = "agent"
prompt_format = "agent"
context_management_enabled = True

hashlines = True
stop_on_empty = False

Expand Down Expand Up @@ -71,7 +76,7 @@ def __init__(self, *args, **kwargs):
"edittext",
"undochange",
}
self.edit_allowed = False
self.edit_allowed = True
self.max_tool_calls = 10000
self.large_file_token_threshold = 8192
self.skills_manager = None
Expand All @@ -92,11 +97,32 @@ def __init__(self, *args, **kwargs):
self.skip_cli_confirmations = False
self.agent_finished = False
self.agent_config = self._get_agent_config()
self.max_sub_agents = self.agent_config.get("max_sub_agents", 3)
self.sub_agent_paths = self.agent_config.get("subagent_paths", [])
self._setup_agent()

AgentService.build_registry(self.sub_agent_paths)
ToolRegistry.build_registry(agent_config=self.agent_config)

self.loaded_custom_tools = ToolRegistry.loaded_custom_tools
super().__init__(*args, **kwargs)

def post_init(self):
super().post_init()
# Populate per-instance tool and server filters from config
self.registered_tools["included"] = set(
map(str.lower, self.agent_config.get("tools_includelist", []))
)
self.registered_tools["excluded"] = set(
map(str.lower, self.agent_config.get("tools_excludelist", []))
)
self.registered_servers["included"] = set(
map(str.lower, self.agent_config.get("servers_includelist", []))
)
self.registered_servers["excluded"] = set(
map(str.lower, self.agent_config.get("servers_excludelist", []))
)

def _setup_agent(self):
os.makedirs(".cecli/temp", exist_ok=True)

Expand Down Expand Up @@ -128,6 +154,7 @@ def _get_agent_config(self):
)
config["command_timeout"] = nested.getter(config, "command_timeout", 30)
config["hot_reload"] = nested.getter(config, "hot_reload", False)
config["allow_nested_delegation"] = nested.getter(config, "allow_nested_delegation", False)

config["tools_paths"] = nested.getter(config, ["tools_paths", "tool_paths"], [])
config["tools_includelist"] = nested.getter(
Expand All @@ -137,6 +164,12 @@ def _get_agent_config(self):
config, ["tools_excludelist", "tools_blacklist"], []
)

config["servers_includelist"] = nested.getter(
config, ["servers_includelist", "servers_whitelist"], []
)
config["servers_excludelist"] = nested.getter(
config, ["servers_excludelist", "servers_blacklist"], []
)
config["include_context_blocks"] = set(
nested.getter(
config,
Expand All @@ -148,6 +181,7 @@ def _get_agent_config(self):
# "git_status",
# "symbol_outline",
"todo_list",
"sub_agents",
"skills",
},
)
Expand Down Expand Up @@ -212,6 +246,12 @@ def show_announcements(self):
joined_skills = ", ".join(skills_list)
self.io.tool_output(f"Available Skills: {joined_skills}")

registry = AgentService.get_registry()
if registry:
names = sorted(registry.keys())
joined_names = ", ".join(names)
self.io.tool_output(f"Available Subagents: {joined_names}")

def get_local_tool_schemas(self):
"""Returns the JSON schemas for all local tools using the tool registry."""
schemas = []
Expand Down Expand Up @@ -317,6 +357,7 @@ def _calculate_context_block_tokens(self, force=False):
"git_status",
"symbol_outline",
"skills",
"sub_agents",
"loaded_skills",
]
for block_type in block_types:
Expand Down Expand Up @@ -352,6 +393,10 @@ def _generate_context_block(self, block_name):
content = self.get_skills_context()
elif block_name == "loaded_skills":
content = self.get_skills_content()
elif block_name == "sub_agents" and (
not self.parent_uuid or self.agent_config.get("allow_nested_delegation", False)
):
content = self.get_sub_agents_context()
if content is not None:
self.context_blocks_cache[block_name] = content
return content
Expand Down Expand Up @@ -460,7 +505,20 @@ def format_chat_chunks(self):
ConversationService.get_chunks(self).add_file_list_reminder()

# Add system messages (including examples and reminder)
ConversationService.get_chunks(self).add_system_messages()
# For sub-agents, use their specific system prompt via AgentService lookup
# For primary agents, use the default system messages flow
needs_system_prompts = True
if hasattr(self, "parent_uuid") and self.parent_uuid:
service = AgentService.get_instance(self)
info = service.sub_agents.get(self.uuid)
if info:
config = AgentService.get_registry().get(info.name)
if config and config.prompt and config.prompt.strip():
ConversationService.get_chunks(self).add_system_message(config.prompt)
needs_system_prompts = False

if needs_system_prompts:
ConversationService.get_chunks(self).add_system_messages()

# Add static context blocks (priority 50 - between SYSTEM and EXAMPLES)
ConversationService.get_chunks(self).add_static_context_blocks()
Expand Down Expand Up @@ -745,7 +803,13 @@ async def gather_and_await():

if self.auto_lint and used_write_tool:
edited = list(self.files_edited_by_tools)
lint_errors = self.lint_edited(edited, show_output=False)
lint_coro = self.lint_edited(edited, show_output=False)
lint_errors, interrupted = await self.coroutines.interruptible(
lint_coro, self.interrupt_event
)
if interrupted:
raise KeyboardInterrupt("Interrupted during linting")

self.lint_outcome = not lint_errors

if lint_errors:
Expand Down Expand Up @@ -808,6 +872,7 @@ async def reply_completed(self):
# 1. Handle Tool Execution Follow-up (Reflection)
if self.agent_finished:
self.tool_usage_history = []
self.tool_call_vectors = []
self.reflected_message = None
if self.files_edited_by_tools:
_ = await self.auto_commit(self.files_edited_by_tools)
Expand Down Expand Up @@ -847,7 +912,12 @@ async def reply_completed(self):
" its outputs are no longer necessary"
)
self.io.tool_output(waiting_msg)
await asyncio.sleep(command_timeout / 2)
sleep_coro = asyncio.sleep(command_timeout / 2)
_res, interrupted = await self.coroutines.interruptible(
sleep_coro, self.interrupt_event
)
if interrupted:
raise KeyboardInterrupt("Interrupted while waiting for background commands")
return True

# Check for recently finished commands that need reflection
Expand All @@ -860,11 +930,15 @@ async def reply_completed(self):
self.tool_usage_history = []
return True

if content and not tool_calls_found and self.num_reflections < self.max_reflections:
self.reflected_message = (
"Continue with your task. If you have completed it, call the `Finished` tool."
)
return True
# 4. If we have called no tools (e.g. the first message)
# Allow early exiting
# If a model forgets a tool call, replay the request instead of stopping early
if self.tool_call_vectors:
if content and not tool_calls_found and self.num_reflections < self.max_reflections:
self.reflected_message = (
"Continue with your task. If you have completed it, call the `Finished` tool."
)
return True

if tool_calls_found and self.num_reflections < self.max_reflections:
self.tool_call_count = 0
Expand Down Expand Up @@ -1384,6 +1458,42 @@ def get_skills_content(self):
self.io.tool_error(f"Error generating skills content context: {str(e)}")
return None

def get_sub_agents_context(self):
"""
Generate a context block for registered sub-agents.
Only shown for primary coders (no parent_uuid).

Returns:
Formatted context block string with sub-agent names and descriptions,
or None if no sub-agents are registered or if called from a sub-agent.
"""
if not self.use_enhanced_context:
return None
if hasattr(self, "parent_uuid") and self.parent_uuid:
return None
try:
registry = AgentService.get_registry()
if not registry:
return None

result = '<context name="sub_agents" from="agent">\n'
result += "## Available Sub-Agents\n\n"
result += f"Found {len(registry)} registered sub-agent(s):\n\n"

for name, config in sorted(registry.items()):
result += f"**{name}**:\n"
desc = config.metadata.get("description", "")
if desc:
result += f"{desc}\n"
result += "\n"

result += "Use the `Delegate` tool with the sub-agent name to delegate tasks.\n"
result += "</context>"
return result
except Exception as e:
self.io.tool_error(f"Error generating sub-agents context: {str(e)}")
return None

def get_background_command_output(self):
"""
Get background command output to append after the main message.
Expand Down
Loading
Loading