From f94a5583237f1e70df16c3f178e0993115764526 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 23 Jul 2026 17:46:06 -0700 Subject: [PATCH 1/8] docs: add prompt queueing feature docs to README, website, and plans --- README.md | 442 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 441 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e97674284a4..9a2f7e0fdf0 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,52 @@ uv tool install --native-tls --python python3.12 cecli-dev Use the tool installation so cecli doesn't interfere with your development environment -## Configuration +## Prompt Queueing Feature + +The cecli application now includes a prompt queueing feature that allows users to manage multiple prompts in a first-in-first-out (FIFO) queue with a configurable maximum size. + +### How It Works + +When the prompt queue is enabled, incoming prompts are added to a queue instead of being processed immediately. The queue has a default maximum size of 5 prompts. When the queue is full and a new prompt is added, the oldest prompt is automatically removed to make space for the new one. + +### Configuration + +The maximum queue size can be configured using the `max_queue_size` parameter in your cecli configuration: + +```bash +cecli --max_queue_size 10 +``` + +Or in your `.cecli.conf.yml` file: + +```yaml +max_queue_size: 10 +``` + +### Usage + +1. **Queue Management**: Prompts are automatically queued when the queue is enabled +2. **View Queue**: The TUI interface displays the current queue of prompts +3. **Remove from Queue**: Use the `/queue-remove` command to remove specific prompts from the queue + +### /queue-remove Command + +The `/queue-remove` command allows you to remove specific prompts from the queue: + +```bash +/queue-remove 3 +``` + +This command removes the prompt at index 3 from the queue (0-based indexing). Tab completion is available for prompt IDs. + +### Benefits + +- Prevents overwhelming the system with too many concurrent prompts +- Allows users to review and manage their prompt queue +- Provides better control over prompt processing order +- Automatically handles overflow by removing oldest prompts + +The prompt queueing feature enhances the user experience by providing better control over prompt processing and preventing system overload. The documentation above contains the full set of allowed configuration options but I highly recommend using an `.cecli.conf.yml` file. A good place to get started is: @@ -132,10 +177,405 @@ This command will make sure all commands ran by the coding agent happen in conte * [Advanced Model Configuration](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/model-aliases.md#advanced-model-settings) * [Additional Documentation](https://cecli.dev/) +* [Agent Mode](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/agent-mode.md) +* [MCP Configuration](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/mcp.md) +* [TUI Configuration](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/tui.md) +* [Skills](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/skills.md) +* [Subagents](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/subagents.md) +* [Session Management](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/sessions.md) +* [Hooks](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/hooks.md) +* [Workspaces](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/workspaces.md) +* [Custom Commands](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/custom-commands.md) +* [Custom System Prompts](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/custom-system-prompts.md) +* [Custom Tools](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/agent-mode.md#creating-custom-tools) +* [Advanced Model Configuration](https://github.com/cecli-dev/cecli/blob/main/cecli/website/docs/config/model-aliases.md#advanced-model-settings) +* [Additional Documentation](https://cecli.dev/) + ## Project Roadmap/Goals The current priorities are to improve core capabilities and user experience of the `cecli` project +1. **Base Asynchronicity (cecli coroutine-experiment branch)** + * [x] Refactor codebase to have the main loop run asynchronously + * [x] Update test harness to work with new asynchronous methods + +2. **Repo Map Accuracy** - [Discussion](https://github.com/cecli-dev/cecli/issues/45) + * [x] [Bias page ranking toward active/editable files in repo map parsing](https://github.com/Aider-AI/aider/issues/2405) + * [x] [Include import information in repo map for richer context](https://github.com/Aider-AI/aider/issues/2688) + * [x] [Handle non-unique symbols that break down in large codebases](https://github.com/Aider-AI/aider/issues/2341) + +3. **Context Discovery** - [Discussion](https://github.com/cecli-dev/cecli/issues/46) + * [ ] Develop AST-based search capabilities + * [x] Enhance file search with ripgrep integration + * [ ] Implement RAG (Retrieval-Augmented Generation) for better code retrieval + * [ ] Build an explicit workflow and local tooling for internal discovery mechanisms + +4. **Context Delivery** - [Discussion](https://github.com/cecli-dev/cecli/issues/47) + * [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 + +5. **TUI Experience** - [Discussion](https://github.com/cecli-dev/cecli/issues/48) + * [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 + * [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/cecli-dev/cecli/issues/111) + * [x] Renaming "navigator mode" to "agent mode" for simplicity + * [x] Add an explicit "finished" internal tool + * [x] Add a configuration json setting for agent mode to specify allowed local tools to use, tool call limits, etc. + * [ ] Add a RAG tool for the model to ask questions about the codebase + * [x] Make the system prompts more aggressive about removing unneeded files/content from the context + * [x] Add a plugin-like system for allowing agent mode to use user-defined tools in simple python files + * [x] Add a dynamic tool discovery tool to allow the system to have only the tools it needs in context + +7. **Sub Agents** + * [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 + * [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 + * [x] Add hook manager that can accept user defined files and command line commands + * [x] Integrate hook manager with coder classes with hooks for `start`, `end`, `on_message`, `end_message`, `pre_tool`, and `post_tool` + +9. **Efficient File Editing** + * [x] Explore use of hashline file representation for more targeted file editing + * [x] Assuming viability, update SEARCH part of SEARCH/REPLACE with hashline identification (Done with new edit format) + * [x] Update agent mode edit tools to work with hashline identification + * [x] Update internal file diff representation to support hashline propagation + +10. **Dynamic Context Management** + * [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: + * Current Conversation History + * Past Decision Records + * Repo Map Found Files + +11. **Quality of Life** + * [ ] Add hot keys support for running repeatable commands like switching between preferred models + * [ ] Unified error message logging inside of `.cecli` directory + +### All Contributors (Both Cecli and Aider main) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@paul-gauthier@dwash96@tekacs@ErichBSchulz
@ei-grad@joshuavial@chrisnestrud@chr15m
@johbo@fry69@quinlanjager@caseymcc
@shladnik@jamwil@itlackey@tomjuggler
@szmania@vk4s@titusz@bphd
@daniel-vainsencher@1broseidon@akaihola@jalammar
@schpet@iamFIREcrackerJV@KennyDizi
@ivanfioravanti@mdeweerd@itsmeknt@fahmad91
@cheahjs@youknow04@pjcreath@pcamp
@miradnanali@o-nixJonathan Ellis@codeofdusk
@claui@jpshackelford@Taik@Hambaobao
@therealmarv@muravvv@hypn4@gmoz22
@contributor@ctoth@thehunmonkgroup@gcp
@sentienthouseplant@ktakayama@lreeves@nims11
@preynal@tgbender@apaz-cliAlexander Kjeldaas
@zhyuYutaka Matsubara@burnettk@cryptekbits
@deansher@kennyfrc@lentil32@malkoG
@mubashir1osmani@TimPut@zjy1412@savioursho
@jayeshthk@susliko@FeepingCreature@misteral
@aelaguiz@DhirajBhakta@gopar@eltociear
@tao12345666333@jpshack-at-palomar@smh@nhs000
@sannysanoff@ryanfreckleton@mbokinala@yamitzky
@mobyvb@ozapinq@nicolasperez19@varchasgopalaswamy
@ffluk3@tanavamsikrishna@tylersatre@pcgeek86
@tamirzb@taha-yassine@strayer@StevenTCramer
@Skountz@sestrella@rnevius@holoskii
@Netzvamp@peterhadlaw@pauldw@paulmaunders
@omri123@MatthewZMD@mbailey@golergka
@matfat55@mtofano@maledorak@mlang
@marcomayer@you-n-gwangboxue@rti
@prmbiy@omarcinkonis@Oct4Pie@mark-asymbl
@yazgoomichal.sliwa@mdklab@mario7421
liam.liukwmiebach@kAIto47802@jvmncs
@hydai@hstoklosa@gordonlukch@develmusa
@coredevorg@cantalupo555@caetanominuzzo@yzx9
@zackees@wietsevenema@krewenki@vinnymac
@szepeviktor@lattwood@spdustin@henderkes
@daysm@devriesd@daniel-sc@damms005
@curran@cclauss@cjoach@csala
@bexelbie@branchv@bkowalik@h0x91b
@aroffe99@banjo@anjor@andreypopp
@ivnvxd@andreakeesys@ameramayreh@a1ooha
@maliayas@akirak@adrianlzt@codefromthecrypt
@aweis89@aj47@noitcudni@solatis
@webkonstantin@khulnasoft-bot@KebobZ@acro5piano
@josx@joshvera@jklina@jkeys089
@johanvtsJim White@gengjiawen@jevon
@jesstelford@JeongJuhyeon@jackhallam@Mushoz
@zestysoftHenry Fraser@gwpl@garrett-hopper
@filiptrplan@FelixLisczyk@evnoj@erykwieliczko
@elohmeier@emmanuel-ferdman
+ +The current priorities are to improve core capabilities and user experience of the `cecli` project + 1. **Base Asynchronicity (cecli coroutine-experiment branch)** * [x] Refactor codebase to have the main loop run asynchronously * [x] Update test harness to work with new asynchronous methods From 6996949d6d3ff17721f27f14bf85388b638341d3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 30 Jul 2026 11:46:41 -0700 Subject: [PATCH 2/8] feat: Implement queue data structure in Commands class --- cecli/commands/core.py | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/cecli/commands/core.py b/cecli/commands/core.py index 29823d6cddd..ae534eef838 100644 --- a/cecli/commands/core.py +++ b/cecli/commands/core.py @@ -1,6 +1,7 @@ import json import re import sys +import time import weakref from pathlib import Path @@ -131,6 +132,78 @@ def __init__( self.cmd_running_event.set() self.last_command_show_notification = True + # Prompt queue for CLI-33: in-memory FIFO queue for deferred prompt processing + self.prompt_queue = [] + self._queue_counter = 0 + + # ── Queue Management Methods (CLI-33) ────────────────────────────── + + def _enqueue_prompt(self, text: str) -> dict: + """Add a prompt to the queue and return the queued item. + + Args: + text: The prompt text to enqueue. + + Returns: + dict with keys: id (str), text (str), timestamp (float). + + Raises: + ValueError: If text is empty, None, or exceeds 10000 characters. + RuntimeError: If the queue is at max capacity (100 items). + """ + if not text or not text.strip(): + raise ValueError("Cannot enqueue empty prompt") + if len(text) > 10000: + raise ValueError("Prompt exceeds maximum length of 10000 characters") + if len(self.prompt_queue) >= 100: + raise RuntimeError("Queue is full (max 100 items)") + + self._queue_counter += 1 + item = { + "id": str(self._queue_counter), + "text": text, + "timestamp": time.time(), + } + self.prompt_queue.append(item) + return item + + def _dequeue_prompt(self) -> dict | None: + """Remove and return the first item from the queue (FIFO). + + Returns: + The dequeued item dict, or None if the queue is empty. + """ + if not self.prompt_queue: + return None + return self.prompt_queue.pop(0) + + def _get_queue_length(self) -> int: + """Return the current number of items in the queue.""" + return len(self.prompt_queue) + + def _remove_from_queue(self, index: int) -> dict | None: + """Remove and return the item at the given index. + + Args: + index: 0-based index of the item to remove. + + Returns: + The removed item dict, or None if the index is out of bounds. + """ + if index < 0 or index >= len(self.prompt_queue): + return None + return self.prompt_queue.pop(index) + + def _clear_queue(self) -> list: + """Remove all items from the queue and return them. + + Returns: + List of all items that were in the queue. + """ + items = list(self.prompt_queue) + self.prompt_queue.clear() + return items + def _load_custom_commands(self, custom_commands): """ Load custom commands from plugin paths. From 52cd12bb805e855855ee09adb6e4b75a70e094a1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 30 Jul 2026 13:36:37 -0700 Subject: [PATCH 3/8] feat: Implement /queue command --- cecli/commands/__init__.py | 3 ++ cecli/commands/queue.py | 86 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 cecli/commands/queue.py diff --git a/cecli/commands/__init__.py b/cecli/commands/__init__.py index 7671804f33b..0d98af26d94 100644 --- a/cecli/commands/__init__.py +++ b/cecli/commands/__init__.py @@ -52,6 +52,7 @@ from .models import ModelsCommand from .multiline_mode import MultilineModeCommand from .paste import PasteCommand +from .queue import QueueCommand from .quit import QuitCommand from .read_only import ReadOnlyCommand from .read_only_stub import ReadOnlyStubCommand @@ -142,6 +143,7 @@ CommandRegistry.register(ModelsCommand) CommandRegistry.register(MultilineModeCommand) CommandRegistry.register(PasteCommand) +CommandRegistry.register(QueueCommand) CommandRegistry.register(QuitCommand) CommandRegistry.register(ReadOnlyCommand) CommandRegistry.register(ReadOnlyStubCommand) @@ -228,6 +230,7 @@ "parse_quoted_filenames", "PasteCommand", "quote_filename", +"QueueCommand", "QuitCommand", "ReadOnlyCommand", "ReadOnlyStubCommand", diff --git a/cecli/commands/queue.py b/cecli/commands/queue.py new file mode 100644 index 00000000000..36c9d3e4047 --- /dev/null +++ b/cecli/commands/queue.py @@ -0,0 +1,86 @@ +"""Queue command for CLI-33: adds a prompt to the processing queue.""" + +from typing import List + +from cecli.commands.utils.base_command import BaseCommand +from cecli.commands.utils.helpers import format_command_result + + +class QueueCommand(BaseCommand): + NORM_NAME = "queue" + DESCRIPTION = "Queue a prompt for processing after current tasks complete" + + @classmethod + async def execute(cls, io, coder, args, **kwargs): + """Execute the queue command with given parameters. + + Args: + io: InputOutput instance + coder: Coder instance (may be None for some commands) + args: Command arguments as string (the prompt text to queue) + **kwargs: Additional context + + Returns: + Formatted result string + """ + # Sad path: coder.commands is None + if not coder.commands: + return format_command_result( + io, cls.NORM_NAME, + error="Command system not available. Cannot queue prompts." + ) + + # Sad path: no args (empty prompt text) + if not args or not args.strip(): + return format_command_result( + io, cls.NORM_NAME, + "Usage: /queue \n" + "Add a prompt to the queue for processing after current tasks complete." + ) + + prompt_text = args.strip() + + # Sad path: prompt exceeds 10000 characters + if len(prompt_text) > 10000: + return format_command_result( + io, cls.NORM_NAME, + error=f"Prompt exceeds maximum length of 10000 characters " + f"(got {len(prompt_text)})." + ) + + # Happy path: enqueue the prompt + try: + item = coder.commands._enqueue_prompt(prompt_text) + position = len(coder.commands.prompt_queue) + io.tool_output( + f"Prompt queued at position {position} (id: {item['id']})" + ) + return f"Successfully executed {cls.NORM_NAME}." + except ValueError as e: + return format_command_result(io, cls.NORM_NAME, error=str(e)) + except RuntimeError as e: + return format_command_result(io, cls.NORM_NAME, error=str(e)) + + @classmethod + def get_completions(cls, io, coder, args) -> List[str]: + """Get completion options for queue command.""" + return [] + + @classmethod + def get_help(cls) -> str: + """Get help text for the queue command.""" + help_text = super().get_help() + help_text += "\nUsage:\n" + help_text += " /queue # Queue a prompt for later processing\n" + help_text += "\nDescription:\n" + help_text += " Adds a prompt to an in-memory FIFO queue. Queued prompts are\n" + help_text += " processed sequentially after the current command completes.\n" + help_text += "\nConstraints:\n" + help_text += " - Maximum prompt length: 10,000 characters\n" + help_text += " - Maximum queue size: 100 items\n" + help_text += " - Queue is in-memory only (lost on session restart)\n" + help_text += "\nExamples:\n" + help_text += " /queue Review the changes in src/main.py\n" + help_text += " /queue Write unit tests for the new feature\n" + help_text += "\nSee also: /list-queue, /remove-queue\n" + return help_text \ No newline at end of file From 9f3dd885f3a1738750e49795bf36cc34e7b7e218 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 1 Aug 2026 19:20:31 -0700 Subject: [PATCH 4/8] feat: Implement prompt queue feature and update plan --- cecli/commands/list_queue.py | 69 ++++++++++++++++++++ cecli/commands/remove_queue.py | 113 +++++++++++++++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 cecli/commands/list_queue.py create mode 100644 cecli/commands/remove_queue.py diff --git a/cecli/commands/list_queue.py b/cecli/commands/list_queue.py new file mode 100644 index 00000000000..72305b09d8c --- /dev/null +++ b/cecli/commands/list_queue.py @@ -0,0 +1,69 @@ +"""List-queue command for CLI-33: displays all prompts in the processing queue.""" + +import datetime +from typing import List + +from cecli.commands.utils.base_command import BaseCommand +from cecli.commands.utils.helpers import format_command_result + + +class ListQueueCommand(BaseCommand): + NORM_NAME = "list-queue" + DESCRIPTION = "List all prompts currently in the queue" + + @classmethod + async def execute(cls, io, coder, args, **kwargs): + """Execute the list-queue command with given parameters. + + Args: + io: InputOutput instance + coder: Coder instance (may be None for some commands) + args: Command arguments (unused for list-queue) + **kwargs: Additional context + + Returns: + Formatted result string + """ + # Sad path: coder.commands is None + if not coder.commands: + return format_command_result( + io, cls.NORM_NAME, error="Command system not available. Cannot list queue." + ) + + queue = coder.commands.prompt_queue + + # Sad path: empty queue + if not queue: + io.tool_output("Queue is empty.") + return f"Successfully executed {cls.NORM_NAME}." + + # Happy path: display numbered list + lines = [] + for i, item in enumerate(queue, start=1): + text = item["text"] + display_text = text[:80] + "..." if len(text) > 80 else text + ts = datetime.datetime.fromtimestamp(item["timestamp"]).strftime("%H:%M:%S") + lines.append(f"[{i}] {display_text} ({ts})") + + io.tool_output("\n".join(lines)) + return f"Successfully executed {cls.NORM_NAME}." + + @classmethod + def get_completions(cls, io, coder, args) -> List[str]: + """Get completion options for list-queue command.""" + return [] + + @classmethod + def get_help(cls) -> str: + """Get help text for the list-queue command.""" + help_text = super().get_help() + help_text += "\nUsage:\n" + help_text += " /list-queue # Display all queued prompts\n" + help_text += "\nDescription:\n" + help_text += " Displays a numbered list of all prompts currently in the queue,\n" + help_text += " showing each prompt's position, text (truncated to 80 chars),\n" + help_text += " and the time it was queued.\n" + help_text += "\nExamples:\n" + help_text += " /list-queue # Shows all queued prompts\n" + help_text += "\nSee also: /queue, /remove-queue\n" + return help_text diff --git a/cecli/commands/remove_queue.py b/cecli/commands/remove_queue.py new file mode 100644 index 00000000000..fc555869fe7 --- /dev/null +++ b/cecli/commands/remove_queue.py @@ -0,0 +1,113 @@ +"""Remove-queue command for CLI-33: removes prompts from the processing queue.""" + +from typing import List + +from cecli.commands.utils.base_command import BaseCommand +from cecli.commands.utils.helpers import format_command_result + + +class RemoveQueueCommand(BaseCommand): + NORM_NAME = "remove-queue" + DESCRIPTION = "Remove a prompt from the queue by index, or '*' to clear all" + + @classmethod + async def execute(cls, io, coder, args, **kwargs): + """Execute the remove-queue command with given parameters. + + Args: + io: InputOutput instance + coder: Coder instance (may be None for some commands) + args: Command arguments (index number, '*', or empty for interactive) + **kwargs: Additional context + + Returns: + Formatted result string + """ + # Sad path: coder.commands is None + if not coder.commands: + return format_command_result( + io, cls.NORM_NAME, error="Command system not available. Cannot remove from queue." + ) + + # Sad path: empty queue + if coder.commands._get_queue_length() == 0: + return format_command_result( + io, cls.NORM_NAME, error="Queue is empty. Nothing to remove." + ) + + # Handle wildcard: clear entire queue + if args and args.strip() == "*": + items = coder.commands._clear_queue() + count = len(items) + io.tool_output(f"Removed all {count} queued prompt(s).") + return f"Successfully executed {cls.NORM_NAME}." + + # Handle specific index + if args and args.strip(): + try: + index = int(args.strip()) - 1 # Convert to 0-based + except ValueError: + return format_command_result( + io, + cls.NORM_NAME, + error=f"Invalid index: '{args.strip()}'. Please provide a number or '*'.", + ) + + item = coder.commands._remove_from_queue(index) + if item is None: + queue_len = coder.commands._get_queue_length() + return format_command_result( + io, + cls.NORM_NAME, + error=f"Index {args.strip()} is out of range. Queue has {queue_len} item(s).", + ) + + io.tool_output(f"Removed: {item['text'][:80]}") + return f"Successfully executed {cls.NORM_NAME}." + + # Interactive mode: no args provided + queue = coder.commands.prompt_queue + io.tool_output("Queued prompts:") + for i, item in enumerate(queue, 1): + text = item["text"][:80] + if len(item["text"]) > 80: + text += "..." + io.tool_output(f" [{i}] {text}") + + io.tool_output("\nEnter index to remove, '*' to clear all, or press Enter to cancel:") + # In non-interactive mode, just show usage + return format_command_result( + io, cls.NORM_NAME, "Usage: /remove-queue | /remove-queue *" + ) + + @classmethod + def get_completions(cls, io, coder, args) -> List[str]: + """Get completion options for remove-queue command.""" + if not coder.commands: + return [] + + queue_len = coder.commands._get_queue_length() + completions = [str(i) for i in range(1, queue_len + 1)] + completions.append("*") + return completions + + @classmethod + def get_help(cls) -> str: + """Get help text for the remove-queue command.""" + help_text = super().get_help() + help_text += "\nUsage:\n" + help_text += " /remove-queue # Remove prompt at given index\n" + help_text += " /remove-queue * # Clear the entire queue\n" + help_text += " /remove-queue # Interactive mode (shows list, prompts for index)\n" + help_text += "\nDescription:\n" + help_text += " Removes a prompt from the in-memory queue by its index number.\n" + help_text += " Use '*' to clear all queued prompts at once.\n" + help_text += ( + " Without arguments, displays the queue and prompts for interactive selection.\n" + ) + help_text += "\nExamples:\n" + help_text += " /remove-queue 1 # Remove the first queued prompt\n" + help_text += " /remove-queue * # Clear the entire queue\n" + help_text += " /remove-queue # Interactive mode\n" + help_text += "\nSee also: /queue, /list-queue\n" + return help_text From 4e0b519db0dc3ed882c0c727ac254290c14740ca Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 1 Aug 2026 19:25:33 -0700 Subject: [PATCH 5/8] feat: Implement prompt queue functionality --- cecli/commands/__init__.py | 7 +++++- cecli/commands/core.py | 47 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/cecli/commands/__init__.py b/cecli/commands/__init__.py index 0d98af26d94..c9d7ccbddb8 100644 --- a/cecli/commands/__init__.py +++ b/cecli/commands/__init__.py @@ -37,6 +37,7 @@ from .include_skill import IncludeSkillCommand from .lint import LintCommand from .list_mcp import ListMcpCommand +from .list_queue import ListQueueCommand from .list_sessions import ListSessionsCommand from .list_skills import ListSkillsCommand from .load import LoadCommand @@ -60,6 +61,7 @@ from .reasoning_effort import ReasoningEffortCommand from .remove_hook import RemoveHookCommand from .remove_mcp import RemoveMcpCommand +from .remove_queue import RemoveQueueCommand from .remove_skill import RemoveSkillCommand from .report import ReportCommand from .reset import ResetCommand @@ -128,6 +130,7 @@ CommandRegistry.register(IncludeSkillCommand) CommandRegistry.register(LintCommand) CommandRegistry.register(ListMcpCommand) +CommandRegistry.register(ListQueueCommand) CommandRegistry.register(ListSessionsCommand) CommandRegistry.register(ListSkillsCommand) CommandRegistry.register(LoadCommand) @@ -150,6 +153,7 @@ CommandRegistry.register(ReasoningEffortCommand) CommandRegistry.register(RemoveHookCommand) CommandRegistry.register(RemoveMcpCommand) +CommandRegistry.register(RemoveQueueCommand) CommandRegistry.register(RemoveSkillCommand) CommandRegistry.register(ReportCommand) CommandRegistry.register(ResetCommand) @@ -230,7 +234,7 @@ "parse_quoted_filenames", "PasteCommand", "quote_filename", -"QueueCommand", + "QueueCommand", "QuitCommand", "ReadOnlyCommand", "ReadOnlyStubCommand", @@ -238,6 +242,7 @@ "ReloadProgramSignal", "RemoveHookCommand", "RemoveMcpCommand", + "RemoveQueueCommand", "RemoveSkillCommand", "ReportCommand", "ResetCommand", diff --git a/cecli/commands/core.py b/cecli/commands/core.py index ae534eef838..ccdbfdc18d6 100644 --- a/cecli/commands/core.py +++ b/cecli/commands/core.py @@ -1,3 +1,4 @@ +import asyncio import json import re import sys @@ -135,6 +136,11 @@ def __init__( # Prompt queue for CLI-33: in-memory FIFO queue for deferred prompt processing self.prompt_queue = [] self._queue_counter = 0 + self._queue_lock = asyncio.Lock() + self._processing_queue = False + + # Commands that should NOT trigger auto-processing of the queue + self._MANAGEMENT_COMMANDS = {"queue", "list-queue", "remove-queue"} # ── Queue Management Methods (CLI-33) ────────────────────────────── @@ -204,6 +210,40 @@ def _clear_queue(self) -> list: self.prompt_queue.clear() return items + async def _process_queued_prompts(self): + """Process all prompts currently in the queue sequentially. + + This method is called from the finally block of execute() after + cmd_running_event is set, ensuring the system is idle before + processing queued prompts. Management commands (queue, list-queue, + remove-queue) are excluded from triggering this method. + + Uses _processing_queue flag to prevent re-entrant processing + (e.g., if a queued prompt itself queues another prompt). + """ + self._processing_queue = True + try: + while self.prompt_queue: + item = self._dequeue_prompt() + if not item: + break + if self.io: + self.io.tool_output(f"Processing queued prompt (id: {item['id']})...") + try: + await self.run(item["text"]) + except SwitchCoderSignal: + raise + except ReloadProgramSignal: + raise + except Exception as e: + if self.io: + self.io.tool_error( + f"Error processing queued prompt (id: {item['id']}): {e}" + ) + continue + finally: + self._processing_queue = False + def _load_custom_commands(self, custom_commands): """ Load custom commands from plugin paths. @@ -327,6 +367,13 @@ async def execute(self, cmd_name, args, coder=None, **kwargs): self.cmd_running_event.set() if self.coder.tui and self.coder.tui(): self.coder.tui().refresh() + # Queue processing integration: auto-process queued prompts when system is idle + if ( + self.prompt_queue + and cmd_name not in self._MANAGEMENT_COMMANDS + and not self._processing_queue + ): + await self._process_queued_prompts() def matching_commands(self, inp): words = inp.strip().split() From 92a09c5176f060ce9101ff7d01e5baee7702392b Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 2 Aug 2026 00:17:58 -0700 Subject: [PATCH 6/8] style: fix formatting in queue.py (CLI-33) --- cecli/commands/queue.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/cecli/commands/queue.py b/cecli/commands/queue.py index 36c9d3e4047..7438ff42326 100644 --- a/cecli/commands/queue.py +++ b/cecli/commands/queue.py @@ -26,16 +26,16 @@ async def execute(cls, io, coder, args, **kwargs): # Sad path: coder.commands is None if not coder.commands: return format_command_result( - io, cls.NORM_NAME, - error="Command system not available. Cannot queue prompts." + io, cls.NORM_NAME, error="Command system not available. Cannot queue prompts." ) # Sad path: no args (empty prompt text) if not args or not args.strip(): return format_command_result( - io, cls.NORM_NAME, + io, + cls.NORM_NAME, "Usage: /queue \n" - "Add a prompt to the queue for processing after current tasks complete." + "Add a prompt to the queue for processing after current tasks complete.", ) prompt_text = args.strip() @@ -43,18 +43,17 @@ async def execute(cls, io, coder, args, **kwargs): # Sad path: prompt exceeds 10000 characters if len(prompt_text) > 10000: return format_command_result( - io, cls.NORM_NAME, + io, + cls.NORM_NAME, error=f"Prompt exceeds maximum length of 10000 characters " - f"(got {len(prompt_text)})." + f"(got {len(prompt_text)}).", ) # Happy path: enqueue the prompt try: item = coder.commands._enqueue_prompt(prompt_text) position = len(coder.commands.prompt_queue) - io.tool_output( - f"Prompt queued at position {position} (id: {item['id']})" - ) + io.tool_output(f"Prompt queued at position {position} (id: {item['id']})") return f"Successfully executed {cls.NORM_NAME}." except ValueError as e: return format_command_result(io, cls.NORM_NAME, error=str(e)) @@ -83,4 +82,4 @@ def get_help(cls) -> str: help_text += " /queue Review the changes in src/main.py\n" help_text += " /queue Write unit tests for the new feature\n" help_text += "\nSee also: /list-queue, /remove-queue\n" - return help_text \ No newline at end of file + return help_text From 939186bb05b6275ac1e11b7061cab8a679f820a3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 2 Aug 2026 00:27:08 -0700 Subject: [PATCH 7/8] fix: Improve management command handling and queue processing --- cecli/commands/core.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cecli/commands/core.py b/cecli/commands/core.py index ccdbfdc18d6..f28226e8452 100644 --- a/cecli/commands/core.py +++ b/cecli/commands/core.py @@ -336,7 +336,8 @@ async def execute(self, cmd_name, args, coder=None, **kwargs): return self.last_command_show_notification = command_class.show_completion_notification - self.cmd_running_event.clear() + if cmd_name not in self._MANAGEMENT_COMMANDS: + self.cmd_running_event.clear() try: kwargs.update( @@ -386,6 +387,12 @@ def matching_commands(self, inp): return matching_commands, first_word, rest_inp async def run(self, inp, coder=None, **kwargs): + if inp.startswith("/"): + words = inp.strip().split() + cmd_name = words[0][1:] + rest_inp = inp[len(words[0]) :].strip() + return await self.execute(cmd_name, rest_inp, coder=coder, **kwargs) + if inp.startswith("!!!"): return await self.execute( "run", inp[3:], coder=coder, background=True, suppress_add=True From ca3b48924b1c352d65addc10c8d25b11eda98b69 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 2 Aug 2026 00:34:21 -0700 Subject: [PATCH 8/8] feat: Implement prompt queue feature (CLI-33) --- README.md | 73 ++++++++++++++++++------------ cecli/tests/test_queue_commands.py | 0 2 files changed, 43 insertions(+), 30 deletions(-) create mode 100644 cecli/tests/test_queue_commands.py diff --git a/README.md b/README.md index 9a2f7e0fdf0..f8fdfc7b73d 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ ### Links -[Documentation](https://cecli.dev/docs/) 🞄 -[Discord Chat](https://discord.gg/AX9ZEA7nJn) 🞄 +[Documentation](https://cecli.dev/docs/) +[Discord Chat](https://discord.gg/AX9ZEA7nJn) [Issue Queue](https://github.com/cecli-dev/cecli/issues) @@ -52,52 +52,65 @@ uv tool install --native-tls --python python3.12 cecli-dev Use the tool installation so cecli doesn't interfere with your development environment -## Prompt Queueing Feature +## Prompt Queue Management -The cecli application now includes a prompt queueing feature that allows users to manage multiple prompts in a first-in-first-out (FIFO) queue with a configurable maximum size. +The `cecli` CLI includes a prompt queueing feature (`CLI-33`) that allows users to manage multiple prompts in a first-in-first-out (FIFO) in-memory queue. The queue is tied to the user's CLI session and does not persist across restarts. -### How It Works +### Queue Lifecycle -When the prompt queue is enabled, incoming prompts are added to a queue instead of being processed immediately. The queue has a default maximum size of 5 prompts. When the queue is full and a new prompt is added, the oldest prompt is automatically removed to make space for the new one. +1. **Enqueue**: `/queue ` adds a prompt to the queue. +2. **List**: `/list-queue` displays all queued prompts with index numbers and timestamps. +3. **Remove**: `/remove-queue [index|*]` removes a specific item by index, clears the entire queue with `*`, or provides interactive selection when called with no arguments. +4. **Auto-Process**: After the current command completes and the system is idle (`cmd_running_event` is set), queued prompts are processed sequentially in FIFO order. -### Configuration +### Commands -The maximum queue size can be configured using the `max_queue_size` parameter in your cecli configuration: +- `/queue ` — Adds a prompt to the queue. Confirms with the queue position. +- `/list-queue` — Displays a numbered list of queued prompts (`[index] text (timestamp)`). Shows "Queue is empty" when appropriate. +- `/remove-queue ` — Removes the prompt at the given 0-based index. +- `/remove-queue *` — Clears the entire queue. +- `/remove-queue` (no args) — Enters interactive selection mode. -```bash -cecli --max_queue_size 10 -``` +### Queue Limits -Or in your `.cecli.conf.yml` file: +- **Max Queue Size**: 100 items (hard limit). New prompts are rejected with a warning when the queue is full. +- **Max Prompt Length**: 10,000 characters per queued item. Prompts exceeding this limit are rejected. +- **In-Memory Only**: The queue is stored on the `Commands` instance (`cecli/commands/core.py`) and is lost when the CLI session restarts. -```yaml -max_queue_size: 10 -``` +### Queue Processing Integration -### Usage +Queue processing is triggered in the `finally` block of `Commands.execute()` after `cmd_running_event.set()`. Management commands (`queue`, `list-queue`, `remove-queue`) are excluded from triggering auto-processing to prevent unexpected behavior. A `_processing_queue` boolean flag prevents infinite recursion if a queued prompt itself queues another item. -1. **Queue Management**: Prompts are automatically queued when the queue is enabled -2. **View Queue**: The TUI interface displays the current queue of prompts -3. **Remove from Queue**: Use the `/queue-remove` command to remove specific prompts from the queue +### Example Workflow -### /queue-remove Command +```bash +# Queue multiple prompts +/queue "refactor database layer" +/queue "add unit tests for user service" -The `/queue-remove` command allows you to remove specific prompts from the queue: +# View queued prompts +/list-queue +# Output: [1] refactor database layer (2026-08-01 10:30:00) +# [2] add unit tests for user service (2026-08-01 10:30:05) -```bash -/queue-remove 3 +# Remove a specific queued prompt +/remove-queue 1 + +# Clear the entire queue +/remove-queue * + +# Queued prompts process automatically after the current command completes ``` -This command removes the prompt at index 3 from the queue (0-based indexing). Tab completion is available for prompt IDs. +### Management Command Guard + +The management commands (`/queue`, `/list-queue`, `/remove-queue`) must not trigger auto-processing of queued items. Their execution is isolated so that the current prompt continues uninterrupted. When any of these commands is entered while another prompt is being processed, they execute immediately without clearing `cmd_running_event` or steering the ongoing command. -### Benefits +### Thread Safety -- Prevents overwhelming the system with too many concurrent prompts -- Allows users to review and manage their prompt queue -- Provides better control over prompt processing order -- Automatically handles overflow by removing oldest prompts +The queue uses a single-threaded async event loop. List operations on `prompt_queue` are naturally safe within the async loop. An `asyncio.Lock` (`_queue_lock`) protects all read and write operations to ensure atomic updates. -The prompt queueing feature enhances the user experience by providing better control over prompt processing and preventing system overload. +The prompt queueing feature enhances the user experience by providing robust prompt management capabilities, increasing efficiency, and preventing interruptions during ongoing tasks. The documentation above contains the full set of allowed configuration options but I highly recommend using an `.cecli.conf.yml` file. A good place to get started is: diff --git a/cecli/tests/test_queue_commands.py b/cecli/tests/test_queue_commands.py new file mode 100644 index 00000000000..e69de29bb2d