diff --git a/codemcp/git_message.py b/codemcp/git_message.py index cd3408c..ac3e904 100644 --- a/codemcp/git_message.py +++ b/codemcp/git_message.py @@ -23,16 +23,30 @@ def append_metadata_to_message(message: str, metadata: dict[str, str]) -> str: Returns: The updated commit message with trailers added """ + # Ensure there's a blank line before trailers if the message doesn't end with one + processed_message = message + if message and not message.endswith("\n\n"): + if message.endswith("\n"): + processed_message = message + "\n" + else: + processed_message = message + "\n\n" - return subprocess.check_output( + result = subprocess.check_output( [ "git", "interpret-trailers", *[f"--trailer={k}: {v}" for k, v in metadata.items()], ], - input=message.encode("utf-8"), + input=processed_message.encode("utf-8"), ).decode("utf-8") + # git interpret-trailers adds a trailing newline that we need to handle properly + # Remove one trailing newline if there are multiple + if result.endswith("\n\n"): + result = result[:-1] + + return result + def update_commit_message_with_description( current_commit_message: str, diff --git a/codemcp/main.py b/codemcp/main.py index 028ddbb..af42b9c 100644 --- a/codemcp/main.py +++ b/codemcp/main.py @@ -14,18 +14,18 @@ from starlette.routing import Mount from .mcp import mcp -from .tools.chmod import chmod -from .tools.edit_file import edit_file -from .tools.glob import glob -from .tools.grep import grep -from .tools.init_project import init_project -from .tools.ls import ls -from .tools.mv import mv -from .tools.read_file import read_file -from .tools.rm import rm -from .tools.run_command import run_command -from .tools.think import think -from .tools.write_file import write_file +from .tools.chmod import chmod # noqa: F401 +from .tools.edit_file import edit_file # noqa: F401 +from .tools.glob import glob # noqa: F401 +from .tools.grep import grep # noqa: F401 +from .tools.init_project import init_project # noqa: F401 +from .tools.ls import ls # noqa: F401 +from .tools.mv import mv # noqa: F401 +from .tools.read_file import read_file # noqa: F401 +from .tools.rm import rm # noqa: F401 +from .tools.run_command import run_command # noqa: F401 +from .tools.think import think # noqa: F401 +from .tools.write_file import write_file # noqa: F401 def get_files_respecting_gitignore(dir_path: Path, pattern: str = "**/*") -> List[Path]: diff --git a/codemcp/testing.py b/codemcp/testing.py index e547d99..c3aab5a 100644 --- a/codemcp/testing.py +++ b/codemcp/testing.py @@ -349,7 +349,9 @@ async def call_tool_assert_error( "Session cannot be None when in_process=False" ) # Convert subtool name to lowercase snake case (e.g., ReadFile -> read_file) - subtool_snake_case = ''.join(['_' + c.lower() if c.isupper() else c for c in subtool]).lstrip('_') + subtool_snake_case = "".join( + ["_" + c.lower() if c.isupper() else c for c in subtool] + ).lstrip("_") # Call the subtool directly instead of calling the codemcp tool result = await session.call_tool(subtool_snake_case, kwargs) # type: ignore self.assertTrue(result.isError, result) @@ -405,7 +407,9 @@ async def call_tool_assert_success( else: assert session is not None, "Session cannot be None when in_process=False" # Convert subtool name to lowercase snake case (e.g., ReadFile -> read_file) - subtool_snake_case = ''.join(['_' + c.lower() if c.isupper() else c for c in subtool]).lstrip('_') + subtool_snake_case = "".join( + ["_" + c.lower() if c.isupper() else c for c in subtool] + ).lstrip("_") # Call the subtool directly instead of calling the codemcp tool result = await session.call_tool(subtool_snake_case, kwargs) # type: ignore self.assertFalse(result.isError, result) diff --git a/codemcp/tools/init_project.py b/codemcp/tools/init_project.py index 22d720f..e2fa9a7 100644 --- a/codemcp/tools/init_project.py +++ b/codemcp/tools/init_project.py @@ -8,7 +8,7 @@ import tomli -from ..common import MAX_LINE_LENGTH, MAX_LINES_TO_READ, normalize_file_path +from ..common import normalize_file_path from ..git import get_repository_root, is_git_repository from ..mcp import mcp @@ -295,6 +295,16 @@ async def init_project( This project uses Git commit hashes to track changes across conversations. After each operation that modifies files, the current Git commit hash will be reported. The commit hash represents the current state of the repository. """ + # Add command information if commands are available + if command_help: + system_prompt += ( + f"\n- The run_command tool accepts these commands: {command_help}" + ) + + # Add command documentation if available + if command_docs: + system_prompt += _generate_command_docs(command_docs) + # Combine system prompt, global prompt combined_prompt = system_prompt if project_prompt: diff --git a/codemcp/tools/run_command.py b/codemcp/tools/run_command.py index 0665b55..e4cf91a 100644 --- a/codemcp/tools/run_command.py +++ b/codemcp/tools/run_command.py @@ -5,8 +5,8 @@ from ..code_command import get_command_from_config, run_code_command from ..common import normalize_file_path -from .commit_utils import append_commit_hash from ..mcp import mcp +from .commit_utils import append_commit_hash __all__ = [ "run_command",