diff --git a/cecli/__init__.py b/cecli/__init__.py index 745ede1fdbb..e805aeaf69e 100644 --- a/cecli/__init__.py +++ b/cecli/__init__.py @@ -1,6 +1,6 @@ from packaging import version -__version__ = "0.96.7.dev" +__version__ = "0.96.8.dev" safe_version = __version__ try: diff --git a/cecli/coders/agent_coder.py b/cecli/coders/agent_coder.py index b02be54c101..c0d7a9bbe52 100644 --- a/cecli/coders/agent_coder.py +++ b/cecli/coders/agent_coder.py @@ -41,6 +41,7 @@ class AgentCoder(Coder): edit_format = "agent" prompt_format = "agent" + context_management_enabled = True def __init__(self, *args, **kwargs): self.recently_removed = {} @@ -71,7 +72,6 @@ def __init__(self, *args, **kwargs): } self.max_tool_calls = 10000 self.large_file_token_threshold = 8192 - self.context_management_enabled = True self.skills_manager = None self.change_tracker = ChangeTracker() self.args = kwargs.get("args") diff --git a/cecli/coders/base_coder.py b/cecli/coders/base_coder.py index 8dfca4db0d8..ae7791207d8 100755 --- a/cecli/coders/base_coder.py +++ b/cecli/coders/base_coder.py @@ -262,6 +262,7 @@ async def create( # Transfer TUI app weak reference res.tui = from_coder.tui + res.context_management_enabled = from_coder.context_management_enabled await res.initialize_mcp_tools() @@ -2095,6 +2096,10 @@ async def send_message(self, inp): self.io.llm_started() if inp: + # Make sure current coder actually has control of conversation system + ConversationChunks.initialize_conversation_system(self) + self.format_chat_chunks() + # Always add user message to conversation manager ConversationManager.add_message( message_dict=dict(role="user", content=inp), diff --git a/cecli/commands/context_management.py b/cecli/commands/context_management.py index c7efa2ab606..137344efe2e 100644 --- a/cecli/commands/context_management.py +++ b/cecli/commands/context_management.py @@ -2,6 +2,11 @@ from cecli.commands.utils.base_command import BaseCommand from cecli.commands.utils.helpers import format_command_result +from cecli.helpers.conversation import ( + ConversationFiles, + ConversationManager, + MessageTag, +) class ContextManagementCommand(BaseCommand): @@ -19,6 +24,10 @@ async def execute(cls, io, coder, args, **kwargs): # Toggle the setting coder.context_management_enabled = not coder.context_management_enabled + ConversationManager.clear_tag(MessageTag.READONLY_FILES) + ConversationManager.clear_tag(MessageTag.EDIT_FILES) + ConversationManager.clear_tag(MessageTag.CHAT_FILES) + ConversationFiles.clear_file_cache() # Report the new state if coder.context_management_enabled: diff --git a/cecli/helpers/conversation/files.py b/cecli/helpers/conversation/files.py index a96e7fef86e..c8a4c35fe40 100644 --- a/cecli/helpers/conversation/files.py +++ b/cecli/helpers/conversation/files.py @@ -93,7 +93,7 @@ def get_file_content( fname: str, generate_stub: bool = False, context_management_enabled: bool = False, - large_file_token_threshold: int = 1000, + large_file_token_threshold: int = 8192, ) -> Optional[str]: """ Get file content with optional stub generation for large files. @@ -111,7 +111,6 @@ def get_file_content( File content, stub for large files, or None if file cannot be read """ abs_fname = os.path.abspath(fname) - # First, ensure file is in cache (read-through cache) if abs_fname not in cls._file_contents_original: cls.add_file(fname) @@ -129,14 +128,14 @@ def get_file_content( if not context_management_enabled: return content + coder = cls.get_coder() + # Check if file is large - content_length = len(content) + content_length = coder.main_model.token_count(content) if content_length <= large_file_token_threshold: return content - # File is large, generate stub - coder = cls.get_coder() # Use RepoMap to generate file stub return RepoMap.get_file_stub(fname, coder.io, line_numbers=True) @@ -181,6 +180,7 @@ def generate_diff(cls, fname: str) -> Optional[str]: # Read current content using coder.io.read_text() coder = cls.get_coder() + rel_fname = coder.get_rel_fname(fname) try: current_content = coder.io.read_text(abs_fname) except Exception: @@ -199,8 +199,8 @@ def generate_diff(cls, fname: str) -> Optional[str]: diff_lines = difflib.unified_diff( snapshot_content.splitlines(), current_content.splitlines(), - fromfile=f"{abs_fname} (snapshot)", - tofile=f"{abs_fname} (current)", + fromfile=f"{rel_fname} (snapshot)", + tofile=f"{rel_fname} (current)", lineterm="", n=3, ) @@ -224,20 +224,25 @@ def update_file_diff(cls, fname: str) -> Optional[str]: Returns: Diff string or None if no changes """ + coder = cls.get_coder() diff = cls.generate_diff(fname) + if diff: # Store diff abs_fname = os.path.abspath(fname) cls._file_diffs[abs_fname] = diff + rel_fname = fname + + if coder: + rel_fname = coder.get_rel_fname(fname) + # Add diff message to conversation diff_message = { "role": "user", - "content": f"File {fname} has changed:\n\n{diff}", + "content": f"File {rel_fname} has changed:\n\n{diff}", } - # Determine tag based on file type - coder = cls.get_coder() if coder and hasattr(coder, "abs_fnames"): tag = ( MessageTag.EDIT_FILES diff --git a/cecli/helpers/conversation/integration.py b/cecli/helpers/conversation/integration.py index 6602a54c927..40451a0b753 100644 --- a/cecli/helpers/conversation/integration.py +++ b/cecli/helpers/conversation/integration.py @@ -373,9 +373,11 @@ def add_readonly_files_messages(cls, coder) -> List[Dict[str, Any]]: content = ConversationFiles.get_file_stub(fname) if content: # Add user message with file path as hash_key + rel_fname = coder.get_rel_fname(fname) + user_msg = { "role": "user", - "content": f"File Contents {fname}:\n\n{content}", + "content": f"File Contents {rel_fname}:\n\n{content}", } ConversationManager.add_message( message_dict=user_msg, @@ -423,7 +425,6 @@ def add_readonly_files_messages(cls, coder) -> List[Dict[str, Any]]: message_dict=assistant_msg, tag=MessageTag.READONLY_FILES, hash_key=("image_assistant", fname), - force=True, ) return messages @@ -469,16 +470,18 @@ def add_chat_files_messages(cls, coder) -> Dict[str, Any]: if not content: continue + rel_fname = coder.get_rel_fname(fname) + # Create user message user_msg = { "role": "user", - "content": f"File Contents {fname}:\n\n{content}", + "content": f"File Contents {rel_fname}:\n\n{content}", } # Create assistant message assistant_msg = { "role": "assistant", - "content": "Ok, I will view and/or modify this file as is necessary.", + "content": "Ok, I will modify this file as is necessary.", } # Determine tag based on editability @@ -526,7 +529,6 @@ def add_chat_files_messages(cls, coder) -> Dict[str, Any]: message_dict=assistant_msg, tag=MessageTag.CHAT_FILES, hash_key=("image_assistant", fname), - force=True, ) return result