From 19a9146570439c9f7542c57123673ba48123a701 Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 16:04:15 +0100 Subject: [PATCH 01/16] preserved repo as private attribute lazuly laded in codetide --- codetide/__init__.py | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/codetide/__init__.py b/codetide/__init__.py index 3b5f2fc..22f6077 100644 --- a/codetide/__init__.py +++ b/codetide/__init__.py @@ -10,7 +10,7 @@ from codetide.parsers import BaseParser from codetide import parsers -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator from typing import Optional, List, Tuple, Union, Dict from datetime import datetime, timezone from pathlib import Path @@ -27,6 +27,11 @@ class CodeTide(BaseModel): codebase :CodeBase = Field(default_factory=CodeBase) files :Dict[Path, datetime]= Field(default_factory=dict) _instantiated_parsers :Dict[str, BaseParser] = {} + _repo :pygit2.Repository = None + + model_config = ConfigDict( + arbitrary_types_allowed=True + ) @field_validator("rootpath", mode="after") @classmethod @@ -94,6 +99,34 @@ def relative_filepaths(self)->List[str]: def cached_ids(self)->List[str]: return self.codebase.unique_ids+self.relative_filepaths + @property + def repo(self)->Optional[pygit2.Repository]: + """ + Lazily initializes and returns the Git repository for the given root path. + + If the repository has not yet been loaded (`self._repo is None`), this + property attempts to open a `pygit2.Repository` at `self.rootpath`. + If the root path does not exist or is not a directory, an error is logged + and `None` is returned. If the repository's working directory differs + from `self.rootpath`, the root path is updated to match the repository's + actual working directory. + + Returns: + Optional[pygit2.Repository]: The initialized Git repository if + successful, otherwise `None`. + """ + if self._repo is None: + + if not self.rootpath.exists() or not self.rootpath.is_dir(): + logger.error(f"Root path does not exist or is not a directory: {self.rootpath}") + return None + + self._repo = pygit2.Repository(self.rootpath) + if not Path(self._repo.workdir) == self.rootpath: + self.rootpath = Path(self._repo.workdir) + + return self._repo + async def _reset(self): self = await self.from_path(self.rootpath) @@ -296,9 +329,7 @@ def _find_code_files(self, languages: Optional[List[str]] = None) -> List[Path]: try: # Try to open the repository - repo = pygit2.Repository(self.rootpath) - if not Path(repo.workdir) == self.rootpath: - self.rootpath = Path(repo.workdir) + repo = self.repo # Get the repository's index (staging area) index = repo.index From b7837f284fa7ee0d5b62ad3bff48c4fd1fc990bf Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 20:18:06 +0100 Subject: [PATCH 02/16] fixed imports --- codetide/agents/tide/ui/app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/codetide/agents/tide/ui/app.py b/codetide/agents/tide/ui/app.py index ac9acb3..96e3266 100644 --- a/codetide/agents/tide/ui/app.py +++ b/codetide/agents/tide/ui/app.py @@ -27,7 +27,8 @@ "Install it with: pip install codetide[agents-ui]" ) from e -from codetide.agents.tide.defaults import DEFAULT_AGENT_TIDE_LLM_CONFIG_PATH, AICORE_CONFIG_EXAMPLE, EXCEPTION_MESSAGE, MISSING_CONFIG_MESSAGE +from codetide.agents.tide.ui.defaults import AICORE_CONFIG_EXAMPLE, EXCEPTION_MESSAGE, MISSING_CONFIG_MESSAGE +from codetide.agents.tide.defaults import DEFAULT_AGENT_TIDE_LLM_CONFIG_PATH from codetide.core.defaults import DEFAULT_ENCODING from codetide.agents.data_layer import init_db import argparse From 32c168a08ad9d4a4ea998567322d21526a3c3183 Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 20:36:54 +0100 Subject: [PATCH 03/16] updated md files --- README.md | 2 +- codetide/agents/tide/ui/chainlit.md | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f80ed26..c0df3bd 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ This will start a web server for the AgentTide UI. Follow the on-screen instruct **Usage Tips:** If you know the exact code context, specify identifiers directly in your request (e.g., `module.submodule.file_withoutextension.object`). -You can request a plan, edit steps, and proceed step-by-step—see the [chainlit.md](codetide/agents/tide/ui/chainlit.md) for full details and advanced workflows! +You can use the `plan` command to generate a step-by-step implementation plan for your request, review and edit the plan, and then proceed step-by-step. The `commit` command allows you to review and finalize changes before they are applied. See the [chainlit.md](codetide/agents/tide/ui/chainlit.md) for full details and advanced workflows, including the latest specifications for these commands! --- diff --git a/codetide/agents/tide/ui/chainlit.md b/codetide/agents/tide/ui/chainlit.md index 9619f3b..ed856bb 100644 --- a/codetide/agents/tide/ui/chainlit.md +++ b/codetide/agents/tide/ui/chainlit.md @@ -152,7 +152,7 @@ Type `/command` (replace `command` with the actual command name) in the text inp ### Available Commands 1. **/test** - - **Description:** Request test implmentations for a specific element. + - **Description:** Request test implementations for a specific element. - **Usage:** `/test` add coverage for apply_patch tool. @@ -162,11 +162,16 @@ Type `/command` (replace `command` with the actual command name) in the text inp `/review` codetide/agents/tide/ui/app.py 3. **/commit** - - **Description:** Generate a concise, production-ready commit message for the most recent patch or set of changes. + - **Description:** Commit changed files. This command will stage and commit all recent changes, generating a conventional commit message. - **Usage:** `/commit` the changes we just made -You can use these commands at any time to guide Agent Tide's workflow, request reviews, or generate commit messages. More commands may be added in the future—refer to this section for updates. +4. **/plan** + - **Description:** Create a step-by-step task plan for your request. This command will instruct Agent Tide to decompose your request into actionable steps, which you can review and edit before execution. + - **Usage:** + `/plan` add a new authentication system to the project + +You can use these commands at any time to guide Agent Tide's workflow, request reviews, generate commit messages, or create implementation plans. More commands may be added in the future—refer to this section for updates. --- From 62ebf877c1ddb6160fd3f8bd6877b05d64a1a743 Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 22:53:16 +0100 Subject: [PATCH 04/16] added support to inspect codetide retrieved context from ui --- codetide/agents/tide/agent.py | 8 +++- codetide/agents/tide/ui/app.py | 51 +++++++++++++++++++++-- codetide/mcp/tools/patch_code/__init__.py | 4 +- 3 files changed, 55 insertions(+), 8 deletions(-) diff --git a/codetide/agents/tide/agent.py b/codetide/agents/tide/agent.py index 967bc8e..8b58664 100644 --- a/codetide/agents/tide/agent.py +++ b/codetide/agents/tide/agent.py @@ -23,7 +23,7 @@ from prompt_toolkit import PromptSession from pydantic import BaseModel, Field, model_validator from typing_extensions import Self -from typing import List, Optional +from typing import List, Optional, Set from datetime import date from pathlib import Path from ulid import ulid @@ -44,6 +44,8 @@ class AgentTide(BaseModel): history :Optional[list]=None steps :Optional[Steps]=None session_id :str=Field(default_factory=ulid) + _last_code_identifers :Optional[Set[str]]=set() + _last_code_context :Optional[str] = None @model_validator(mode="after") def pass_custom_logger_fn(self)->Self: @@ -85,7 +87,7 @@ async def agent_loop(self, codeIdentifiers :Optional[List[str]]=None): codeContext = None if codeIdentifiers: - autocomplete = AutoComplete(self.tide.cached_ids) + autocomplete = AutoComplete(self.tide.cached_ids) # Validate each code identifier validatedCodeIdentifiers = [] for codeId in codeIdentifiers: @@ -96,7 +98,9 @@ async def agent_loop(self, codeIdentifiers :Optional[List[str]]=None): elif result.get("matching_identifiers"): validatedCodeIdentifiers.append(result.get("matching_identifiers")[0]) + self._last_code_identifers = set(validatedCodeIdentifiers) codeContext = self.tide.get(validatedCodeIdentifiers, as_string=True) + self._last_code_context = codeContext response = await self.llm.acomplete( self.history, diff --git a/codetide/agents/tide/ui/app.py b/codetide/agents/tide/ui/app.py index 96e3266..b7c40af 100644 --- a/codetide/agents/tide/ui/app.py +++ b/codetide/agents/tide/ui/app.py @@ -31,6 +31,7 @@ from codetide.agents.tide.defaults import DEFAULT_AGENT_TIDE_LLM_CONFIG_PATH from codetide.core.defaults import DEFAULT_ENCODING from codetide.agents.data_layer import init_db +from ulid import ulid import argparse import getpass import asyncio @@ -140,7 +141,6 @@ async def on_chat_resume(thread: ThreadDict): async def loadAgentTideUi()->AgentTideUi: agent_tide_ui: AgentTideUi = cl.user_session.get("AgentTideUi") - print(f"{agent_tide_ui=}") if agent_tide_ui is None: try: agent_tide_ui = AgentTideUi( @@ -233,6 +233,33 @@ async def on_stop_steps(action :cl.Action): # await cl.send_window_message("Current Steps have beed discarded") +@cl.action_callback("inspect_code_context") +async def on_inspect_context(action :cl.Action): + agent_tide_ui: AgentTideUi = cl.user_session.get("AgentTideUi") + + inspect_msg = cl.Message( + content="", + author="Agent Tide", + elements= [ + cl.Text( + name="CodeTIde Retrieved Identifiers", + content=f"""```json{json.dumps(list(agent_tide_ui.agent_tide._last_code_identifers), indent=4)}\n```""" + ) + ] + ) + agent_tide_ui.agent_tide._last_code_identifers = None + + if agent_tide_ui.agent_tide._last_code_context: + inspect_msg.elements.append( + cl.File( + name=f"codetide_context_{ulid()}.txt", + content=agent_tide_ui.agent_tide._last_code_context + ) + ) + agent_tide_ui.agent_tide._last_code_context = None + + await inspect_msg.send() + @cl.on_message async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None): @@ -249,7 +276,6 @@ async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None await agent_tide_ui.add_to_history(message.content) msg = cl.Message(content="", author="Agent Tide") - async with cl.Step("ApplyPatch", type="tool") as diff_step: await diff_step.remove() @@ -269,7 +295,14 @@ async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None start_wrapper="\n```shell\n", end_wrapper="\n```\n", target_step=msg - ) + ), + MarkerConfig( + begin_marker="*** Begin Commit", + end_marker="*** End Commit", + start_wrapper="\n```shell\n", + end_wrapper="\n```\n", + target_step=msg + ), ], global_fallback_msg=msg ) @@ -290,7 +323,7 @@ async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None msg.actions = [ cl.Action( name="stop_steps", - tooltip="stop", + tooltip="Stop", icon="octagon-x", payload={"msg_id": msg.id} ), @@ -302,6 +335,16 @@ async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None ) ] + if agent_tide_ui.agent_tide._last_code_identifers: + msg.actions.append( + cl.Action( + name="inspect_code_context", + tooltip="Inspect CodeContext", + icon= "telescope", + payload={"msg_id": msg.id} + ) + ) + # # Send the final message await msg.send() diff --git a/codetide/mcp/tools/patch_code/__init__.py b/codetide/mcp/tools/patch_code/__init__.py index ccc70ef..c9a623c 100644 --- a/codetide/mcp/tools/patch_code/__init__.py +++ b/codetide/mcp/tools/patch_code/__init__.py @@ -109,7 +109,7 @@ def process_patch( write_fn: Callable[[str, str], None], remove_fn: Callable[[str], None], exists_fn: Callable[[str], bool] -) -> str: +) -> List[str]: """The main entrypoint function to process a patch from text to filesystem.""" if not os.path.exists(patch_path): raise DiffError("Patch path {patch_path} does not exist.") @@ -136,7 +136,7 @@ def process_patch( apply_commit(commit, write_fn, remove_fn, exists_fn) remove_fn(patch_path) - return "Patch applied successfully." + return paths_needed # --------------------------------------------------------------------------- # # Default FS wrappers From 3845316afb8260f4c06fbfb46c31d73b4cb2fc22 Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 22:54:47 +0100 Subject: [PATCH 05/16] added utils to parse commit blocks --- codetide/agents/tide/utils.py | 21 +++++++++++++++++++++ codetide/mcp/tools/apply_patch.py | 6 +++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/codetide/agents/tide/utils.py b/codetide/agents/tide/utils.py index e002da7..7797337 100644 --- a/codetide/agents/tide/utils.py +++ b/codetide/agents/tide/utils.py @@ -53,6 +53,27 @@ def parse_patch_blocks(text: str, multiple: bool = True) -> Union[str, List[str] return matches if multiple else matches[0] +def parse_commit_blocks(text: str, multiple: bool = True) -> Union[str, List[str], None]: + """ + Extract content between *** Begin Patch and *** End Patch markers (inclusive), + ensuring that both markers are at zero indentation (start of line, no leading spaces). + + Args: + text: Full input text containing one or more patch blocks. + multiple: If True, return a list of all patch blocks. If False, return the first match. + + Returns: + A string (single patch), list of strings (multiple patches), or None if not found. + """ + + pattern = r"(?m)^(\*\*\* Begin Commit[\s\S]*?^\*\*\* End Commit)$" + matches = re.findall(pattern, text) + + if not matches: + return None + + return matches if multiple else matches[0] + def parse_steps_markdown(md: str): steps = [] diff --git a/codetide/mcp/tools/apply_patch.py b/codetide/mcp/tools/apply_patch.py index 977c183..1bb015f 100644 --- a/codetide/mcp/tools/apply_patch.py +++ b/codetide/mcp/tools/apply_patch.py @@ -173,8 +173,12 @@ def run(): patch_path = f"./storage/{ulid()}.txt" writeFile(patch_text, patch_path) try: - result = process_patch(patch_path, open_file, write_file, remove_file, file_exists) + paths_changed = process_patch(patch_path, open_file, write_file, remove_file, file_exists) _ = await initCodeTide() + if paths_changed: + result = "Patch applied successfully." + else: + result = None except DiffError as exc: result = f"Error applying patch:\n{exc}" From b8627c6d0b1f4878ebc699f01c5802a5a086ee93 Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 22:56:00 +0100 Subject: [PATCH 06/16] updated commit cmd to commit changed files --- codetide/agents/tide/agent.py | 120 +++++++++++++++++++++-- codetide/agents/tide/ui/agent_tide_ui.py | 11 ++- 2 files changed, 120 insertions(+), 11 deletions(-) diff --git a/codetide/agents/tide/agent.py b/codetide/agents/tide/agent.py index 8b58664..1be064d 100644 --- a/codetide/agents/tide/agent.py +++ b/codetide/agents/tide/agent.py @@ -5,9 +5,9 @@ from ...autocomplete import AutoComplete from .models import Steps from .prompts import ( - AGENT_TIDE_SYSTEM_PROMPT, GET_CODE_IDENTIFIERS_SYSTEM_PROMPT, STEPS_SYSTEM_PROMPT, WRITE_PATCH_SYSTEM_PROMPT + AGENT_TIDE_SYSTEM_PROMPT, GET_CODE_IDENTIFIERS_SYSTEM_PROMPT, STAGED_DIFFS_TEMPLATE, STEPS_SYSTEM_PROMPT, WRITE_PATCH_SYSTEM_PROMPT ) -from .utils import parse_patch_blocks, parse_steps_markdown, trim_to_patch_section +from .utils import parse_commit_blocks, parse_patch_blocks, parse_steps_markdown, trim_to_patch_section from .consts import AGENT_TIDE_ASCII_ART try: @@ -29,6 +29,7 @@ from ulid import ulid import aiofiles import asyncio +import pygit2 import os async def custom_logger_fn(message :str, session_id :str, filepath :str): @@ -44,6 +45,8 @@ class AgentTide(BaseModel): history :Optional[list]=None steps :Optional[Steps]=None session_id :str=Field(default_factory=ulid) + changed_paths :List[str]=Field(default_factory=list) + _skip_context_retrieval :bool=False _last_code_identifers :Optional[Set[str]]=set() _last_code_context :Optional[str] = None @@ -76,7 +79,7 @@ async def agent_loop(self, codeIdentifiers :Optional[List[str]]=None): include_types=True ) - if codeIdentifiers is None: + if codeIdentifiers is None and not self._skip_context_retrieval: codeIdentifiers = await self.llm.acomplete( self.history, system_prompt=[GET_CODE_IDENTIFIERS_SYSTEM_PROMPT.format(DATE=TODAY)], @@ -114,7 +117,12 @@ async def agent_loop(self, codeIdentifiers :Optional[List[str]]=None): await trim_to_patch_section(self.patch_path) if os.path.exists(self.patch_path): - process_patch(self.patch_path, open_file, write_file, remove_file, file_exists) + changed_paths = process_patch(self.patch_path, open_file, write_file, remove_file, file_exists) + self.changed_paths.extend(changed_paths) + + commitMessage = parse_commit_blocks(response, multiple=False) + if commitMessage: + await self.commit(commitMessage) steps = parse_steps_markdown(response) if steps: @@ -128,6 +136,102 @@ async def agent_loop(self, codeIdentifiers :Optional[List[str]]=None): self.history.append(response) + @staticmethod + async def get_git_diff_staged_simple(directory: str) -> str: + """ + Simple async function to get git diff --staged output + """ + # Validate directory exists + if not Path(directory).is_dir(): + raise FileNotFoundError(f"Directory not found: {directory}") + + process = await asyncio.create_subprocess_exec( + 'git', 'diff', '--staged', + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + cwd=directory + ) + + stdout, stderr = await process.communicate() + + if process.returncode != 0: + raise Exception(f"Git command failed: {stderr.decode().strip()}") + + return stdout.decode() + + async def _stage(self)->str: + index = self.tide.repo.index + for path in self.changed_paths: + index.add(path) + + staged_diff = await self.get_git_diff_staged_simple(self.tide.rootpath) + staged_diff = staged_diff.strip() + return staged_diff if staged_diff else "No files were staged. Nothing to commit. Tell the user to request some changes so there is something to commit" + + async def prepare_commit(self)->str: + staged_diff = await self._stage() + self.changed_paths = [] + self._skip_context_retrieval = True + return STAGED_DIFFS_TEMPLATE.format(diffs=staged_diff) + + async def commit(self, message :str): + """ + Commit all staged files in a git repository with the given message. + + Args: + repo_path (str): Path to the git repository + message (str): Commit message + author_name (str, optional): Author name. If None, uses repo config + author_email (str, optional): Author email. If None, uses repo config + + Returns: + pygit2.Commit: The created commit object, or None if no changes to commit + + Raises: + ValueError: If no files are staged for commit + Exception: For other git-related errors + """ + try: + # Open the repository + repo = self.repo + + # Get author and committer information + config = repo.config + author_name = config.get('user.name', 'Unknown Author') + author_email = config.get('user.email', 'unknown@example.com') + + author = pygit2.Signature(author_name, author_email) + committer = author # Typically same as author + + # Get the current tree from the index + tree = repo.index.write_tree() + + # Get the parent commit (current HEAD) + parents = [repo.head.target] if repo.head else [] + + # Create the commit + commit_oid = repo.create_commit( + 'HEAD', # Reference to update + author, + committer, + message, + tree, + parents + ) + + # Clear the staging area after successful commit + repo.index.write() + + return repo[commit_oid] + + except pygit2.GitError as e: + raise Exception(f"Git error: {e}") + except KeyError as e: + raise Exception(f"Configuration error: {e}") + + finally: + self._skip_context_retrieval = False + async def run(self, max_tokens: int = 48000): if self.history is None: self.history = [] @@ -175,7 +279,11 @@ def _(event): finally: _logger.logger.info("Exited by user. Goodbye!") - async def _handle_commands(self, command :str): + async def _handle_commands(self, command :str) -> str: # TODO add logic here to handlle git command, i.e stage files, write commit messages and checkout # expand to support new branches - pass \ No newline at end of file + context = "" + if command == "commit": + context = await self.prepare_commit() + + return context diff --git a/codetide/agents/tide/ui/agent_tide_ui.py b/codetide/agents/tide/ui/agent_tide_ui.py index 00afceb..1d1ee70 100644 --- a/codetide/agents/tide/ui/agent_tide_ui.py +++ b/codetide/agents/tide/ui/agent_tide_ui.py @@ -9,7 +9,7 @@ "Install it with: pip install codetide[agents-ui]" ) from e -from codetide.agents.tide.prompts import CMD_CODE_REVIEW_PROMPT, CMD_COMMIT_PROMPT, CMD_WRITE_TESTS_PROMPT +from codetide.agents.tide.prompts import CMD_CODE_REVIEW_PROMPT, CMD_COMMIT_PROMPT, CMD_TRIGGER_PLANNING_STEPS, CMD_WRITE_TESTS_PROMPT from codetide.agents.tide.defaults import DEFAULT_AGENT_TIDE_LLM_CONFIG_PATH from codetide.agents.tide.ui.defaults import PLACEHOLDER_LLM_CONFIG from codetide.agents.tide.agent import AgentTide @@ -39,6 +39,7 @@ def __init__(self, project_path: Path = Path("./"), history :Optional[list]=None self.history = [] if history is None else history self.current_step :Optional[int] = None self.commands_prompts = { + "plan": CMD_TRIGGER_PLANNING_STEPS, "review": CMD_CODE_REVIEW_PROMPT, "test": CMD_WRITE_TESTS_PROMPT, "commit": CMD_COMMIT_PROMPT @@ -48,7 +49,8 @@ def __init__(self, project_path: Path = Path("./"), history :Optional[list]=None commands = [ {"id": "review", "icon": "search-check", "description": "Review file(s) or object(s)"}, {"id": "test", "icon": "flask-conical", "description": "Test file(s) or object(s)"}, - {"id": "commit", "icon": "git-commit", "description": "Generate commit message"}, + {"id": "commit", "icon": "git-commit", "description": "Commit changed files"}, + {"id": "plan", "icon": "notepad-text-dashed", "description": "Create a step-by-step task plan"} ] async def load(self): @@ -127,6 +129,5 @@ def settings(self): ] async def get_command_prompt(self, command :str)->Optional[str]: - await self.agent_tide._handle_commands(command) - - return self.commands_prompts.get(command) + context = await self.agent_tide._handle_commands(command) + return f"{self.commands_prompts.get(command)} {context}" From 41dc19a30d9bbe1c0b803eda6f5c5c139da9de91 Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 22:56:28 +0100 Subject: [PATCH 07/16] updated prompt --- codetide/agents/tide/prompts.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/codetide/agents/tide/prompts.py b/codetide/agents/tide/prompts.py index c7cb122..5167b24 100644 --- a/codetide/agents/tide/prompts.py +++ b/codetide/agents/tide/prompts.py @@ -258,6 +258,8 @@ FINAL CHECKLIST BEFORE PATCHING: +0. Ensure you have all the required context to generate the patch, if you feel like something is missing ask the user for clarification: + - it is preferable to ask for clarification instead of halucinating a patch without enough context. 1. Validate that every line you edit exists exactly as-is in the original context 2. Ensure one patch block per file, using multiple @@ hunks as needed 3. Include no formatting, layout, or interpretation changes @@ -274,9 +276,7 @@ Your job is to take a user request, analyze any provided code context (including repository structure / repo_tree identifiers), and decompose the work into the minimal set of concrete implementation steps needed to fully satisfy the request. If the requirement is simple, output a single step; if it’s complex, decompose it into multiple ordered steps. You must build upon, refine, or correct any existing code context rather than ignoring it. -If the user provides feedback on prior steps, update the current steps to reflect that feedback. If the user responds “all is good” or equivalent, do not repeat the steps—reply exactly with: - - +If the user provides feedback on prior steps, update the current steps to reflect that feedback. If the user responds “all is good” or equivalent, do not repeat the steps - ask the user if he wants you to start implementing them one by one in sequence. Important Note: If the user's request already contains a complete step, is direct enough to be solved without additional decomposition, or does not require implementation planning at all (e.g., general questions, documentation requests, commit messages), you may skip the multi-step planning and execution mode entirely. @@ -320,16 +320,16 @@ 10. **Succinctness of Format:** Strictly adhere to the step formatting with separators (`---`) and the beginning/end markers. Do not add extraneous numbering or narrative outside the prescribed structure. -When the user confirms everything is correct and no further planning is needed, respond only with: - - - --- `repo_tree` {REPO_TREE} """ +CMD_TRIGGER_PLANNING_STEPS = """ + +""" + CMD_WRITE_TESTS_PROMPT = """ Analyze the provided code and write comprehensive tests. Ensure high coverage by including unit, integration, and end-to-end tests that address edge cases and follow best practices. @@ -343,4 +343,23 @@ CMD_COMMIT_PROMPT = """ Generate a conventional commit message that summarizes the work done since the previous commit. The message should have a clear subject line and a body explaining the problem solved and the implementation approach. + +Important Instructions: + +Place the commit message inside exactly this format: +*** Begin Commit +[commit message] +*** End Commit + +You may include additional comments about the changes made outside of this block + +If no diffs for staged files are provided in the context, reply that there's nothing to commit + +The commit message should follow conventional commit format with a clear type/scope prefix +""" + +STAGED_DIFFS_TEMPLATE = """ +** The following diffs are currently staged and will be commited once you generate an appropriate description:** + +{diffs} """ \ No newline at end of file From 0ffd0e03946e77e194049f5d3fd9ace1adddef6d Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 22:59:57 +0100 Subject: [PATCH 08/16] updated hf_demo to latest spec --- examples/hf_demo_space/app.py | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/examples/hf_demo_space/app.py b/examples/hf_demo_space/app.py index 24161c4..304abd6 100644 --- a/examples/hf_demo_space/app.py +++ b/examples/hf_demo_space/app.py @@ -248,6 +248,33 @@ async def on_checkout_commit_push(action :cl.Action): session_id = cl.user_session.get("session_id") await commit_and_push_changes(DEFAULT_SESSIONS_WORKSPACE / session_id) +@cl.action_callback("inspect_code_context") +async def on_inspect_context(action :cl.Action): + agent_tide_ui: AgentTideUi = cl.user_session.get("AgentTideUi") + + inspect_msg = cl.Message( + content="", + author="Agent Tide", + elements= [ + cl.Text( + name="CodeTIde Retrieved Identifiers", + content=f"""```json{json.dumps(list(agent_tide_ui.agent_tide._last_code_identifers), indent=4)}\n```""" + ) + ] + ) + agent_tide_ui.agent_tide._last_code_identifers = None + + if agent_tide_ui.agent_tide._last_code_context: + inspect_msg.elements.append( + cl.File( + name=f"codetide_context_{ulid()}.txt", + content=agent_tide_ui.agent_tide._last_code_context + ) + ) + agent_tide_ui.agent_tide._last_code_context = None + + await inspect_msg.send() + @cl.on_message async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None): agent_tide_ui = await loadAgentTideUi() @@ -270,6 +297,13 @@ async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None # Initialize the stream processor stream_processor = StreamProcessor( marker_configs=[ + MarkerConfig( + begin_marker="*** Begin Commit", + end_marker="*** End Commit", + start_wrapper="\n```shell\n", + end_wrapper="\n```\n", + target_step=msg + ), MarkerConfig( begin_marker="*** Begin Patch", end_marker="*** End Patch", @@ -316,6 +350,16 @@ async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None ) ] + if agent_tide_ui.agent_tide._last_code_identifers: + msg.actions.append( + cl.Action( + name="inspect_code_context", + tooltip="Inspect CodeContext", + icon= "telescope", + payload={"msg_id": msg.id} + ) + ) + msg.actions.append( cl.Action( name="checkout_commit_push", From 8ae07923a5599d914642ba53600dd900d5359b9c Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 23:16:48 +0100 Subject: [PATCH 09/16] added checkout and push funcitons based on pygit2 --- examples/hf_demo_space/git_utils.py | 67 ++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/examples/hf_demo_space/git_utils.py b/examples/hf_demo_space/git_utils.py index 20967de..f6c04cb 100644 --- a/examples/hf_demo_space/git_utils.py +++ b/examples/hf_demo_space/git_utils.py @@ -1,7 +1,8 @@ -import asyncio from pathlib import Path -import re import subprocess +import pygit2 +import asyncio +import re GIT_URL_PATTERN = re.compile( @@ -115,3 +116,65 @@ async def commit_and_push_changes(repo_path: Path, branch_name: str = None, comm raise ValueError(f"Timeout during git operation in {repo_path}") except subprocess.CalledProcessError as e: raise ValueError(f"Git operation failed in {repo_path}. Error: {e.stderr}") from e + +def push_new_branch(repo :pygit2.Repository, branch_name :str, remote_name :str='origin'): + """ + Push a new branch to remote origin (equivalent to 'git push origin branch_name') + + Args: + repo (pygit2.Repository): Repo Obj + branch_name (str): Name of the branch to push + remote_name (str): Name of the remote (default: 'origin') + + Returns: + bool: True if push was successful, False otherwise + """ + + # Get the remote + remote = repo.remotes[remote_name] + + # Create refspec for pushing new branch + # Format: local_branch:remote_branch (this publishes the new branch) + refspec = f'refs/heads/{branch_name}:refs/heads/{branch_name}' + + # Push to remote + result = remote.push([refspec]) + + # Check if push was successful (no error message means success) + return not result.error_message + +def checkout_new_branch(repo :pygit2.Repository, new_branch_name :str, start_point=None): + """ + Create and checkout a new branch from the current HEAD or specified start point. + + Args: + repo_path (str): Path to the git repository + new_branch_name (str): Name of the new branch to create and checkout + start_point (pygit2.Oid or Reference, optional): Commit or reference to start from. + If None, uses current HEAD. + + Returns: + pygit2.Reference: The newly created branch reference + + Raises: + ValueError: If branch already exists or invalid start point + Exception: For other git-related errors + """ + + # Check if branch already exists + if new_branch_name in repo.branches.local: + raise ValueError(f"Branch '{new_branch_name}' already exists") + + # Get the start point commit (default to HEAD) + if start_point is None: + if repo.head_is_detached: + raise ValueError("HEAD is detached, please specify a start point") + start_point = repo.head.target + + # Create the new branch + new_branch = repo.branches.local.create(new_branch_name, repo[start_point]) + + # Checkout the new branch + repo.checkout(new_branch, strategy=pygit2.GIT_CHECKOUT_SAFE) + + return new_branch \ No newline at end of file From 426853ee10aa0432b9923cd9143b248b69fa1a42 Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 23:18:18 +0100 Subject: [PATCH 10/16] fixed wrong import --- examples/hf_demo_space/git_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/hf_demo_space/git_utils.py b/examples/hf_demo_space/git_utils.py index f6c04cb..0ad4686 100644 --- a/examples/hf_demo_space/git_utils.py +++ b/examples/hf_demo_space/git_utils.py @@ -1,7 +1,8 @@ from pathlib import Path +from ulid import ulid import subprocess -import pygit2 import asyncio +import pygit2 import re @@ -48,8 +49,7 @@ async def commit_and_push_changes(repo_path: Path, branch_name: str = None, comm try: # Create new branch with Agent Tide + ULID name if not provided if not branch_name: - import ulid - branch_name = f"agent-tide-{ulid.new()}" + branch_name = f"agent-tide-{ulid()}" # Create and checkout new branch process = await asyncio.create_subprocess_exec( From cee78904d6ffd8c68e801e7dafbae203893be7db Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Sun, 24 Aug 2025 23:19:01 +0100 Subject: [PATCH 11/16] integrated new git functions into deom app --- examples/hf_demo_space/app.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/hf_demo_space/app.py b/examples/hf_demo_space/app.py index 304abd6..f66047f 100644 --- a/examples/hf_demo_space/app.py +++ b/examples/hf_demo_space/app.py @@ -18,7 +18,7 @@ from aicore.config import Config from aicore.llm import Llm -from git_utils import commit_and_push_changes, validate_git_url +from git_utils import push_new_branch, validate_git_url, checkout_new_branch from chainlit.cli import run_chainlit from typing import Optional from pathlib import Path @@ -90,6 +90,8 @@ async def validate_llm_config_hf(agent_tide_ui: AgentTideUi): async def loadAgentTideUi()->AgentTideUi: agent_tide_ui: AgentTideUi = cl.user_session.get("AgentTideUi") session_id = cl.user_session.get("session_id") + new_branch_name = f"agent-tide-{ulid()}" + cl.user_session.set("current_branch_name", new_branch_name) if agent_tide_ui is None: await clone_repo(session_id) @@ -101,6 +103,7 @@ async def loadAgentTideUi()->AgentTideUi: session_id=session_id ) await agent_tide_ui.load() + checkout_new_branch(agent_tide_ui.agent_tide.tide.repo, new_branch_name=new_branch_name) except FileNotFoundError: ... @@ -245,8 +248,11 @@ async def on_stop_steps(action :cl.Action): @cl.action_callback("checkout_commit_push") async def on_checkout_commit_push(action :cl.Action): - session_id = cl.user_session.get("session_id") - await commit_and_push_changes(DEFAULT_SESSIONS_WORKSPACE / session_id) + agent_tide_ui: AgentTideUi = cl.user_session.get("AgentTideUi") + await agent_tide_ui.agent_tide.prepare_commit() + agent_tide_ui.agent_tide.commit("AgentTide - add all and push") + + push_new_branch(agent_tide_ui.agent_tide.tide.repo, branch_name=cl.user_session.get("current_branch_name")) @cl.action_callback("inspect_code_context") async def on_inspect_context(action :cl.Action): From 5c920c29a56d69d0d0281cb625cb9d05d75d7a7a Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Mon, 25 Aug 2025 22:50:42 +0100 Subject: [PATCH 12/16] added default_encoding --- codetide/agents/tide/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/codetide/agents/tide/utils.py b/codetide/agents/tide/utils.py index 7797337..73a7172 100644 --- a/codetide/agents/tide/utils.py +++ b/codetide/agents/tide/utils.py @@ -1,3 +1,4 @@ +from codetide.core.defaults import DEFAULT_ENCODING from typing import List, Union import aiofiles @@ -12,7 +13,7 @@ async def trim_to_patch_section(filename): if not os.path.exists(filename): return - async with aiofiles.open(filename, 'r') as f: + async with aiofiles.open(filename, 'r', encoding=DEFAULT_ENCODING) as f: async for line in f: if '*** Begin Patch' in line: capturing = True @@ -24,7 +25,7 @@ async def trim_to_patch_section(filename): lines_to_keep.append(line) if lines_to_keep: # Write back only the lines we want to keep - async with aiofiles.open(filename, 'w') as f: + async with aiofiles.open(filename, 'w', encoding=DEFAULT_ENCODING) as f: await f.writelines(lines_to_keep) else: # Otherwise, delete the file try: From 681188565fcb89091b18e28562053a1aaa7c7216 Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Mon, 25 Aug 2025 22:51:11 +0100 Subject: [PATCH 13/16] removed async from commit --- codetide/agents/tide/agent.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/codetide/agents/tide/agent.py b/codetide/agents/tide/agent.py index 1be064d..e04d45a 100644 --- a/codetide/agents/tide/agent.py +++ b/codetide/agents/tide/agent.py @@ -122,7 +122,7 @@ async def agent_loop(self, codeIdentifiers :Optional[List[str]]=None): commitMessage = parse_commit_blocks(response, multiple=False) if commitMessage: - await self.commit(commitMessage) + self.commit(commitMessage) steps = parse_steps_markdown(response) if steps: @@ -174,7 +174,7 @@ async def prepare_commit(self)->str: self._skip_context_retrieval = True return STAGED_DIFFS_TEMPLATE.format(diffs=staged_diff) - async def commit(self, message :str): + def commit(self, message :str): """ Commit all staged files in a git repository with the given message. From f0054346fc4c59285b163456206366649b63c9c0 Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Mon, 25 Aug 2025 22:55:01 +0100 Subject: [PATCH 14/16] updted on_insert_context action --- codetide/agents/tide/ui/app.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/codetide/agents/tide/ui/app.py b/codetide/agents/tide/ui/app.py index b7c40af..16a71ef 100644 --- a/codetide/agents/tide/ui/app.py +++ b/codetide/agents/tide/ui/app.py @@ -237,13 +237,15 @@ async def on_stop_steps(action :cl.Action): async def on_inspect_context(action :cl.Action): agent_tide_ui: AgentTideUi = cl.user_session.get("AgentTideUi") + await action.remove() + inspect_msg = cl.Message( content="", author="Agent Tide", elements= [ cl.Text( name="CodeTIde Retrieved Identifiers", - content=f"""```json{json.dumps(list(agent_tide_ui.agent_tide._last_code_identifers), indent=4)}\n```""" + content=f"""```json\n{json.dumps(list(agent_tide_ui.agent_tide._last_code_identifers), indent=4)}\n```""" ) ] ) @@ -432,3 +434,5 @@ def main(): # TODO pre release, create hf orchestrator that launches temp dir, clones repo there and stores api config there # TODO or just deactivate pre data persistance for hf release # TODO need to test project path is working as expected... + + # TODO need to revisit logic From f9b76e39b1edcb8344f4b97217a353cff423a7db Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Mon, 25 Aug 2025 23:18:05 +0100 Subject: [PATCH 15/16] updated hf_demo_space --- examples/hf_demo_space/app.py | 218 ++++++++++++++++++---------------- 1 file changed, 117 insertions(+), 101 deletions(-) diff --git a/examples/hf_demo_space/app.py b/examples/hf_demo_space/app.py index f66047f..35d63a5 100644 --- a/examples/hf_demo_space/app.py +++ b/examples/hf_demo_space/app.py @@ -15,8 +15,8 @@ from aicore.const import STREAM_END_TOKEN, STREAM_START_TOKEN from aicore.models import AuthenticationError, ModelError +from aicore.llm import Llm, LlmConfig from aicore.config import Config -from aicore.llm import Llm from git_utils import push_new_branch, validate_git_url, checkout_new_branch from chainlit.cli import run_chainlit @@ -34,94 +34,21 @@ DEFAULT_SESSIONS_WORKSPACE = Path(os.getcwd()) / "sessions" -async def validate_llm_config_hf(agent_tide_ui: AgentTideUi): - exception = True - session_id = cl.user_session.get("session_id") - while exception: - try: - agent_tide_ui.agent_tide.llm.provider.validate_config(force_check_against_provider=True) - exception = None - - except (AuthenticationError, ModelError) as e: - exception = e - await cl.Message( - content=MISSING_CONFIG_MESSAGE.format( - agent_tide_config_path="because-we-dont-actually-store-it-it-only-exists-while-this-session-is-alive", - config_file="config-file-in-yaml", - example_config=AICORE_CONFIG_EXAMPLE - ), - elements=[ - cl.File( - name="config.yml", - display="inline", - content=AICORE_CONFIG_EXAMPLE, - size="small" - ), - ] - ).send() - - _config_files = None - while _config_files is None: - _config_files = await cl.AskFileMessage( - content=EXCEPTION_MESSAGE.format(exception=json.dumps(exception.__dict__, indent=4)), - accept=[".yml", ".yaml"], - timeout=3600 - ).send() - - if _config_files: - _config_file = _config_files[0] - - try: - with open(_config_file.path, "r", encoding=DEFAULT_ENCODING) as _file: - config_raw = _file.read() - config_dict = yaml.safe_load(config_raw) - config = Config(**config_dict) - - agent_tide_ui.agent_tide.llm = Llm.from_config(config.llm) - agent_tide_ui.agent_tide.llm.provider.session_id = agent_tide_ui.agent_tide.session_id - - session_dir_path = DEFAULT_SESSIONS_WORKSPACE / session_id - if not os.path.exists(session_dir_path): - os.makedirs(session_dir_path, exist_ok=True) - - except Exception as e: - exception = e - -async def loadAgentTideUi()->AgentTideUi: - agent_tide_ui: AgentTideUi = cl.user_session.get("AgentTideUi") - session_id = cl.user_session.get("session_id") - new_branch_name = f"agent-tide-{ulid()}" - cl.user_session.set("current_branch_name", new_branch_name) - if agent_tide_ui is None: - await clone_repo(session_id) - - try: - agent_tide_ui = AgentTideUi( - DEFAULT_SESSIONS_WORKSPACE / session_id, - history=cl.user_session.get("chat_history"), - llm_config=cl.user_session.get("settings"), - session_id=session_id - ) - await agent_tide_ui.load() - checkout_new_branch(agent_tide_ui.agent_tide.tide.repo, new_branch_name=new_branch_name) - - except FileNotFoundError: - ... - - await validate_llm_config_hf(agent_tide_ui) - - cl.user_session.set("AgentTideUi", agent_tide_ui) - - return agent_tide_ui +@cl.on_chat_start +async def start_chatr(): + ### create new dir to clone repo + ### and yeah force agentTide llm_config to be zreo + session_id = ulid() + cl.user_session.set("session_id", session_id) + await cl.context.emitter.set_commands(AgentTideUi.commands) + cl.user_session.set("chat_history", []) -async def clone_repo(session_id): - # TODO ask user actions to get PAT and attempt to clone git repo contents exception = True - while exception: try: user_message = await cl.AskUserMessage( - content="Provide a valid github url to give AgentTide some context!" + content="Provide a valid github url to give AgentTide some context!", + timeout=3600 ).send() url = user_message.get("output") await validate_git_url(url) @@ -130,14 +57,12 @@ async def clone_repo(session_id): await cl.Message(f"Invalid url found, please provide only the url, if it is a private repo you can inlucde a PAT in the url: {e}").send() exception = e - logger.info(f"executing cmd git clone --no-checkout {url} {DEFAULT_SESSIONS_WORKSPACE / session_id}") - process = await asyncio.create_subprocess_exec( "git", "clone", url, str(DEFAULT_SESSIONS_WORKSPACE / session_id), stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) - + try: stdout, stderr = await asyncio.wait_for(process.communicate(), timeout=300) except asyncio.TimeoutError: @@ -150,14 +75,101 @@ async def clone_repo(session_id): logger.info(f"finished cloning to {DEFAULT_SESSIONS_WORKSPACE / session_id}") + res = await cl.AskActionMessage( + content="Select the LLM to power Agent Tide! You can use one of the following free options or configure your own via api key! We recommend `gpt-4.1` or `sonnet-4` for ultimate performance (don't worry we are not logging api keys, you can check the code yourself)! Bear in mind that free alternatives can be context and rate limited.", + actions=[ + cl.Action(name="kimi-k2", payload={"model": "moonshotai/kimi-k2:free"}, label="kimi-k2"), + cl.Action(name="qwen3-coder", payload={"model": "qwen/qwen3-coder:free"}, label="qwen3-coder"), + cl.Action(name="gpt-oss-20b", payload={"model": "openai/gpt-oss-20b:free"}, label="gpt-oss-20b"), + cl.Action(name="custom", payload={"model": "custom"}, label="bring your model") + ], + ).send() + -@cl.on_chat_start -async def start_chatr(): - ### create new dir to clone repo - ### and yeah force agentTide llm_config to be zreo - cl.user_session.set("session_id", ulid()) - await cl.context.emitter.set_commands(AgentTideUi.commands) - cl.user_session.set("chat_history", []) + if res and res.get("payload").get("model") != "custom": + llm_config = LlmConfig( + provider="openrouter", + model=res.get("payload").get("model"), + api_key=os.getenv("OPENROUTER_API_KEY"), + max_tokens=int(os.getenv("OPENROUTER_MAX_TOKENS")) + ) + agent_tide_ui = AgentTideUi( + DEFAULT_SESSIONS_WORKSPACE / session_id, + history=cl.user_session.get("chat_history"), + llm_config=llm_config, + session_id=session_id + ) + await agent_tide_ui.load() + + elif res.get("payload").get("model") == "custom": + agent_tide_ui = AgentTideUi( + DEFAULT_SESSIONS_WORKSPACE / session_id, + history=cl.user_session.get("chat_history"), + llm_config=cl.user_session.get("settings"), + session_id=session_id + ) + await agent_tide_ui.load() + + exception = True + + while exception: + try: + agent_tide_ui.agent_tide.llm.provider.validate_config(force_check_against_provider=True) + exception = None + + except (AuthenticationError, ModelError) as e: + exception = e + await cl.Message( + content=MISSING_CONFIG_MESSAGE.format( + agent_tide_config_path="because-we-dont-actually-store-it-it-only-exists-while-this-session-is-alive", + config_file="config-file-in-yaml", + example_config=AICORE_CONFIG_EXAMPLE + ), + elements=[ + cl.File( + name="config.yml", + display="inline", + content=AICORE_CONFIG_EXAMPLE, + size="small" + ), + ] + ).send() + + _config_files = None + while _config_files is None: + _config_files = await cl.AskFileMessage( + content=EXCEPTION_MESSAGE.format(exception=json.dumps(exception.__dict__, indent=4)), + accept=[".yml", ".yaml"], + timeout=3600 + ).send() + + if _config_files: + _config_file = _config_files[0] + + try: + with open(_config_file.path, "r", encoding=DEFAULT_ENCODING) as _file: + config_raw = _file.read() + config_dict = yaml.safe_load(config_raw) + config = Config(**config_dict) + + agent_tide_ui.agent_tide.llm = Llm.from_config(config.llm) + agent_tide_ui.agent_tide.llm.provider.session_id = agent_tide_ui.agent_tide.session_id + + session_dir_path = DEFAULT_SESSIONS_WORKSPACE / session_id + if not os.path.exists(session_dir_path): + os.makedirs(session_dir_path, exist_ok=True) + + except Exception as e: + exception = e + + await cl.Message( + content="Hi, I'm Tide! Nice to meet you." + ).send() + + new_branch_name = f"agent-tide-{ulid()}" + cl.user_session.set("current_branch_name", new_branch_name) + checkout_new_branch(agent_tide_ui.agent_tide.tide.repo, new_branch_name=new_branch_name) + cl.user_session.set("AgentTideUi", agent_tide_ui) @cl.set_starters async def set_starters(): @@ -258,13 +270,15 @@ async def on_checkout_commit_push(action :cl.Action): async def on_inspect_context(action :cl.Action): agent_tide_ui: AgentTideUi = cl.user_session.get("AgentTideUi") + await action.remove() + inspect_msg = cl.Message( content="", author="Agent Tide", elements= [ cl.Text( name="CodeTIde Retrieved Identifiers", - content=f"""```json{json.dumps(list(agent_tide_ui.agent_tide._last_code_identifers), indent=4)}\n```""" + content=f"""```json\n{json.dumps(list(agent_tide_ui.agent_tide._last_code_identifers), indent=4)}\n```""" ) ] ) @@ -283,7 +297,7 @@ async def on_inspect_context(action :cl.Action): @cl.on_message async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None): - agent_tide_ui = await loadAgentTideUi() + agent_tide_ui = cl.user_session.get("AgentTideUi") chat_history = cl.user_session.get("chat_history") @@ -368,11 +382,11 @@ async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None msg.actions.append( cl.Action( - name="checkout_commit_push", - tooltip="A new branch will be created and the changes made so far will be commited and pushed to the upstream repository", - icon="circle-fading-arrow-up", - payload={"msg_id": msg.id} - ) + name="checkout_commit_push", + tooltip="A new branch will be created and the changes made so far will be commited and pushed to the upstream repository", + icon="circle-fading-arrow-up", + payload={"msg_id": msg.id} + ) ) # # Send the final message await msg.send() @@ -381,6 +395,8 @@ async def agent_loop(message: cl.Message, codeIdentifiers: Optional[list] = None await agent_tide_ui.add_to_history(msg.content) if __name__ == "__main__": + from dotenv import load_dotenv + load_dotenv() # TODO add button button to ckeckout and push run_chainlit(os.path.abspath(__file__)) \ No newline at end of file From c2c8aedea33a382a6209eeb52b7ed8ab9425d33c Mon Sep 17 00:00:00 2001 From: BrunoV21 Date: Mon, 25 Aug 2025 23:25:44 +0100 Subject: [PATCH 16/16] added agenttide logo --- examples/hf_demo_space/app.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/hf_demo_space/app.py b/examples/hf_demo_space/app.py index 35d63a5..bc206b6 100644 --- a/examples/hf_demo_space/app.py +++ b/examples/hf_demo_space/app.py @@ -36,13 +36,21 @@ @cl.on_chat_start async def start_chatr(): - ### create new dir to clone repo - ### and yeah force agentTide llm_config to be zreo session_id = ulid() cl.user_session.set("session_id", session_id) await cl.context.emitter.set_commands(AgentTideUi.commands) cl.user_session.set("chat_history", []) + await cl.Message( + content="", + elements=[ + cl.Image( + path=os.getenv("AGENT_TIDE_LOGO_PATH"), + size="large" + ) + ] + ).send() + exception = True while exception: try: @@ -163,9 +171,9 @@ async def start_chatr(): exception = e await cl.Message( - content="Hi, I'm Tide! Nice to meet you." + content="Hi, I'm Tide... Nice to meet you!" ).send() - + new_branch_name = f"agent-tide-{ulid()}" cl.user_session.set("current_branch_name", new_branch_name) checkout_new_branch(agent_tide_ui.agent_tide.tide.repo, new_branch_name=new_branch_name)