Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
981b118
Bump Version
Feb 14, 2026
90ce1c2
Be explicit about using IndentText to fix indentation errors
Feb 14, 2026
278813b
Add basic hashline helper (will probably grow as time goes on)
Feb 14, 2026
5b8224d
Add ability to set agent model in config and add in-chat command
gopar Feb 16, 2026
42fa0c7
Update benchmark file to prevent context leakage
Feb 18, 2026
0b5ebe7
Hash line coder (promising!) and convert agent coder to use hashline …
Feb 19, 2026
79c9df1
Allow models with colons to have settings overrides, and make sure sy…
Feb 19, 2026
c467b73
#439: Fix test command output on successful command execution
Feb 19, 2026
27f4ad7
#443: Reset file cache structures, remove diffs on /clear
Feb 19, 2026
e8dd02a
Merge pull request #444 from gopar/allow-choosing-agent-model-and-con…
dwash96 Feb 19, 2026
36873cf
Allow approximate hashline range matching
Feb 19, 2026
7ab7445
#447: Tweak responses to work with TUI
Feb 19, 2026
d8af631
Fix README config example, website updates
Feb 19, 2026
122c6e0
Prevent coder classes from overwriting and duplicating one another's …
Feb 20, 2026
a09c96f
Also format example messages!
Feb 20, 2026
c640b90
Operate on a copy of the example messages
Feb 20, 2026
ac7a721
De-duplicate multiple hashline operations starting on same line in re…
Feb 20, 2026
0d4cdea
Add more forgiving processing to hashline edit format
Feb 22, 2026
7106d26
#449: Ergonomic command updates:
Feb 22, 2026
c994722
Benchmark Harness Updates
Feb 22, 2026
9863a5f
#450: Show contents of commands in terminal, fix from 441 should hand…
Feb 22, 2026
460b2ad
#204: By default, give the user the space to veto the architect if th…
Feb 22, 2026
dc2fb3a
#426: Reduce output as /help set itself up
Feb 22, 2026
f991122
Fix architect coder tests
Feb 22, 2026
8c4e5ce
Update yes_always_commands tests
Feb 22, 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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Why `cecli`?

`cecli` (probably pronounced like "Cecily", aka `aider-ce`) is a community-driven fork of the [Aider](https://aider.chat/) AI pair programming tool.
`cecli` (probably pronounced like "Cecily", aka `aider-ce`) is a community-driven fork of the [Aider](https://cecli.dev/) AI pair programming tool.
Aider is a fantastic piece of software with a wonderful community but it has been painfully slow in receiving updates in the quickly evolving AI tooling space.

We aim to foster an open, collaborative ecosystem where new features, experiments, and improvements can be developed and shared rapidly. We believe in genuine FOSS principles and actively welcome contributors of all skill levels.
Expand All @@ -25,7 +25,7 @@ LLMs are a part of our lives from here on out so join us in learning about and c
* [Custom System Prompts](https://github.com/dwash96/cecli/blob/main/cecli/website/docs/config/custom-system-prompts.md)
* [Custom Tools](https://github.com/dwash96/cecli/blob/main/cecli/website/docs/config/agent-mode.md#creating-custom-tools)
* [Advanced Model Configuration](https://github.com/dwash96/cecli/blob/main/cecli/website/docs/config/model-aliases.md#advanced-model-settings)
* [Aider Original Documentation (still mostly applies)](https://aider.chat/)
* [Aider Original Documentation (still mostly applies)](https://cecli.dev/)

You can see a selection of the enhancements and updates by comparing the help output:

Expand Down Expand Up @@ -74,7 +74,7 @@ enable-context-compaction: true
context-compaction-max-tokens: 0.8
env-file: .cecli.env
show-model-warnings: true
use-enhanced-map: true.p
use-enhanced-map: true
watch-files: false
tui: true

Expand Down
17 changes: 16 additions & 1 deletion benchmark/benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -873,7 +873,9 @@ async def run_test_real(

from cecli import models
from cecli.coders import Coder
from cecli.helpers.conversation import ConversationFiles, ConversationManager
from cecli.io import InputOutput
from cecli.main import SwitchCoderSignal

if not os.path.isdir(testdir):
if dry:
Expand Down Expand Up @@ -1053,6 +1055,7 @@ async def run_test_real(
use_git=True,
auto_commits=False,
dirty_commits=False,
auto_lint=False,
stream=False,
verbose=verbose,
# auto_lint=False, # disabled for code-in-json experiments
Expand Down Expand Up @@ -1088,6 +1091,15 @@ async def run_test_real(

dur = 0
test_outcomes = []

ConversationManager.initialize(
coder,
reset=True,
reformat=True,
)

ConversationFiles.reset()

for i in range(tries):
start = time.time()

Expand All @@ -1103,7 +1115,10 @@ async def run_test_real(

await coder.apply_updates()
else:
response = await coder.run(with_message=instructions, preproc=False)
try:
response = await coder.run(with_message=instructions, preproc=False)
except SwitchCoderSignal:
pass

dur += time.time() - start

Expand Down
18 changes: 9 additions & 9 deletions benchmark/primary_variations.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
set -e # Exit on error

# Default values
BASE_NAME="primary-variation"
EDIT_FORMAT="diff"
BASE_NAME="cecli-base-hashline-9"
EDIT_FORMAT="hashline"
MAP_TOKENS="512"
THREADS="1"
HASH_RE="^4"
NUM_TESTS="16"
LANGUAGES="javascript,python,rust,go,java"
HASH_RE="^[15]"
NUM_TESTS="32"
EXERCISES_DIR="polyglot-benchmark"
OUTPUT_DIR="tmp.benchmarks"
SLEEP_BETWEEN=30 # Seconds to sleep between runs
Expand All @@ -20,13 +21,11 @@ SLEEP_BETWEEN=30 # Seconds to sleep between runs
# "openrouter/minimax/minimax-m2.1"
# "openrouter/qwen/qwen3-vl-235b-a22b-thinking"
MODELS=(
# "openrouter/deepseek/deepseek-v3.2"
"openrouter/google/gemini-3-flash-preview"
"openrouter/deepseek/deepseek-v3.2-exp"
# "openrouter/moonshotai/kimi-k2.5"
# "openrouter/minimax/minimax-m2.1"
# "openrouter/minimax/minimax-m2.1"
# "openrouter/qwen/qwen3-vl-235b-a22b-thinking"
# "openrouter/openai/gpt-oss-120b"
"openrouter/openai/gpt-5.2"
# "openrouter/openai/gpt-5.2"
# "openrouter/google/gemini-3-flash-preview"
# "openrouter/google/gemini-3-pro-preview"
# "openrouter/anthropic/claude-haiku-4.5"
Expand Down Expand Up @@ -118,6 +117,7 @@ run_benchmark() {
--threads "$THREADS" \
--hash-re "$HASH_RE" \
--num-tests "$NUM_TESTS" \
--languages "$LANGUAGES" \
--exercises-dir "$EXERCISES_DIR"

echo "Benchmark completed: $run_name"
Expand Down
2 changes: 1 addition & 1 deletion cecli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from packaging import version

__version__ = "0.97.0.dev"
__version__ = "0.97.1.dev"
safe_version = __version__

try:
Expand Down
29 changes: 27 additions & 2 deletions cecli/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ def get_parser(default_config_files, git_root):
const="agent",
help="Use agent edit format for the main chat (autonomous file management)",
)
group.add_argument(
"--hashline",
action="store_const",
dest="edit_format",
const="hashline",
help="Use hashline edit format for the main chat",
)
group.add_argument(
"--auto-accept-architect",
action=argparse.BooleanOptionalAction,
Expand Down Expand Up @@ -247,6 +254,12 @@ def get_parser(default_config_files, git_root):
" If unspecified, defaults to the model's max_chat_history_tokens."
),
)
group.add_argument(
"--file-diffs",
action=argparse.BooleanOptionalAction,
default=True,
help="Whether to store file diffs in context or reload files (default: True)",
)
group.add_argument(
"--retries",
metavar="RETRIES_JSON",
Expand Down Expand Up @@ -297,6 +310,12 @@ def get_parser(default_config_files, git_root):
help="Specify Agent Mode configuration as a JSON string",
default=None,
)
group.add_argument(
"--agent-model",
metavar="AGENT_MODEL",
default=None,
help="Specify the model to use for Agent mode (default depends on --model)",
)
group.add_argument(
"--auto-save",
action=argparse.BooleanOptionalAction,
Expand Down Expand Up @@ -1111,15 +1130,21 @@ def main():
shell = sys.argv[2]
if shell not in shtab.SUPPORTED_SHELLS:
print(f"Error: Unsupported shell '{shell}'.", file=sys.stderr)
print(f"Supported shells are: {', '.join(shtab.SUPPORTED_SHELLS)}", file=sys.stderr)
print(
f"Supported shells are: {', '.join(shtab.SUPPORTED_SHELLS)}",
file=sys.stderr,
)
sys.exit(1)
parser = get_parser([], None)
parser.prog = "cecli" # Set the program name on the parser
print(shtab.complete(parser, shell=shell))
else:
print("Error: Please specify a shell for completion.", file=sys.stderr)
print(f"Usage: python {sys.argv[0]} completion <shell_name>", file=sys.stderr)
print(f"Supported shells are: {', '.join(shtab.SUPPORTED_SHELLS)}", file=sys.stderr)
print(
f"Supported shells are: {', '.join(shtab.SUPPORTED_SHELLS)}",
file=sys.stderr,
)
sys.exit(1)
else:
# Default to YAML for any other unrecognized argument, or if 'yaml' was explicitly passed
Expand Down
2 changes: 2 additions & 0 deletions cecli/coders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .editor_diff_fenced_coder import EditorDiffFencedCoder
from .editor_editblock_coder import EditorEditBlockCoder
from .editor_whole_coder import EditorWholeFileCoder
from .hashline_coder import HashLineCoder
from .help_coder import HelpCoder
from .patch_coder import PatchCoder
from .udiff_coder import UnifiedDiffCoder
Expand All @@ -35,4 +36,5 @@
ContextCoder,
AgentCoder,
CopyPasteCoder,
HashLineCoder,
]
11 changes: 9 additions & 2 deletions cecli/coders/agent_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class AgentCoder(Coder):
edit_format = "agent"
prompt_format = "agent"
context_management_enabled = True
hashlines = True

def __init__(self, *args, **kwargs):
self.recently_removed = {}
Expand All @@ -56,6 +57,7 @@ def __init__(self, *args, **kwargs):
"grep",
"listchanges",
"shownumberedcontext",
"thinking",
}
self.write_tools = {
"command",
Expand Down Expand Up @@ -497,6 +499,9 @@ def format_chat_chunks(self):
# Use parent's implementation which may use conversation system if flag is enabled
return super().format_chat_chunks()

# Choose appropriate fence based on file content
self.choose_fence()

ConversationChunks.initialize_conversation_system(self)
# Decrement mark_for_delete values before adding new messages
ConversationManager.decrement_mark_for_delete()
Expand Down Expand Up @@ -679,7 +684,7 @@ async def process_tool_calls(self, tool_call_response):
"""
Track tool usage before calling the base implementation.
"""
self.agent_finished = False

await self.auto_save_session()
self.last_round_tools = []
if self.partial_response_tool_calls:
Expand Down Expand Up @@ -940,7 +945,7 @@ def _generate_tool_context(self, repetitive_tools):
for i, tool in enumerate(recent_history, 1):
context_parts.append(f"{i}. {tool}")
context_parts.append("\n\n")
if repetitive_tools:
if repetitive_tools and len(self.tool_usage_history) >= 8:
context_parts.append("""**Instruction:**
You have used the following tool(s) repeatedly:""")
context_parts.append("### DO NOT USE THE FOLLOWING TOOLS/FUNCTIONS")
Expand Down Expand Up @@ -1054,6 +1059,8 @@ async def preproc_user_input(self, inp):
inp = await super().preproc_user_input(inp)
if inp and not inp.startswith('<context name="user_input" from="agent">'):
inp = f'<context name="user_input" from="agent">\n{inp}\n</context>'

self.agent_finished = False
return inp

def get_directory_structure(self):
Expand Down
13 changes: 10 additions & 3 deletions cecli/coders/architect_coder.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,20 @@ async def reply_completed(self):
return

tweak_responses = getattr(self.args, "tweak_responses", False)
confirmation = await self.io.confirm_ask("Edit the files?", allow_tweak=tweak_responses)
confirmation = await self.io.confirm_ask(
"Edit the files?",
allow_tweak=tweak_responses,
explicit_yes_required=not self.auto_accept_architect,
)

if not self.auto_accept_architect and not confirmation:
if not confirmation:
return

if confirmation == "tweak":
content = self.io.edit_in_editor(content)
if self.tui and self.tui():
content = self.tui().get_response_from_editor(content)
else:
content = self.io.edit_in_editor(content)

await asyncio.sleep(0.1)

Expand Down
Loading
Loading