-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add git add and commit repo tools #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -113,6 +113,14 @@ bool optional_bool_arg(const ToolCall& cmd, const char* key, bool default_value | |||||||||||||||||||||||||
| return value.get<bool>(); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| std::string require_non_empty_string_arg(const ToolCall& cmd, const char* key, const char* tool_name) { | ||||||||||||||||||||||||||
| const std::string value = require_string_arg(cmd, key, tool_name); | ||||||||||||||||||||||||||
| if (value.empty()) { | ||||||||||||||||||||||||||
| throw std::runtime_error("Argument '" + std::string(key) + "' for " + tool_name + " must not be empty."); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| return value; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| size_t parse_bounded_output_bytes_arg(const ToolCall& cmd, const char* key, size_t default_value) { | ||||||||||||||||||||||||||
| const size_t parsed = optional_size_arg(cmd, key, default_value); | ||||||||||||||||||||||||||
| if (parsed == 0 || parsed > kMaxBuildToolOutputBytes) { | ||||||||||||||||||||||||||
|
|
@@ -228,6 +236,8 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| .name = "read_file_safe", | ||||||||||||||||||||||||||
| .description = "Reads the complete contents of a workspace file.", | ||||||||||||||||||||||||||
| .category = ToolCategory::ReadOnly, | ||||||||||||||||||||||||||
| .mutates_repository_state = false, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = false, | ||||||||||||||||||||||||||
| .requires_approval = false, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"path", { | ||||||||||||||||||||||||||
|
|
@@ -252,6 +262,8 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| .name = "write_file_safe", | ||||||||||||||||||||||||||
| .description = "Writes string content to a workspace file, overwriting existing content.", | ||||||||||||||||||||||||||
| .category = ToolCategory::Mutating, | ||||||||||||||||||||||||||
| .mutates_repository_state = true, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = false, | ||||||||||||||||||||||||||
| .requires_approval = true, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"path", { | ||||||||||||||||||||||||||
|
|
@@ -279,6 +291,8 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| .name = "bash_execute_safe", | ||||||||||||||||||||||||||
| .description = "Executes a bounded bash command inside the workspace.", | ||||||||||||||||||||||||||
| .category = ToolCategory::Execution, | ||||||||||||||||||||||||||
| .mutates_repository_state = true, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = true, | ||||||||||||||||||||||||||
| .requires_approval = true, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"command", { | ||||||||||||||||||||||||||
|
|
@@ -311,6 +325,8 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| .name = "build_project_safe", | ||||||||||||||||||||||||||
| .description = "Runs ./build.sh in debug or release mode with bounded retained output.", | ||||||||||||||||||||||||||
| .category = ToolCategory::Execution, | ||||||||||||||||||||||||||
| .mutates_repository_state = true, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = true, | ||||||||||||||||||||||||||
| .requires_approval = true, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"build_mode", { | ||||||||||||||||||||||||||
|
|
@@ -360,6 +376,8 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| .name = "test_project_safe", | ||||||||||||||||||||||||||
| .description = "Runs ./build.sh test with bounded retained output and a small ctest summary.", | ||||||||||||||||||||||||||
| .category = ToolCategory::Execution, | ||||||||||||||||||||||||||
| .mutates_repository_state = true, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = true, | ||||||||||||||||||||||||||
| .requires_approval = true, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"timeout_ms", { | ||||||||||||||||||||||||||
|
|
@@ -407,6 +425,8 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| .name = "list_files_bounded", | ||||||||||||||||||||||||||
| .description = "Lists workspace files with optional directory and extension filters.", | ||||||||||||||||||||||||||
| .category = ToolCategory::ReadOnly, | ||||||||||||||||||||||||||
| .mutates_repository_state = false, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = false, | ||||||||||||||||||||||||||
| .requires_approval = false, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"directory", { | ||||||||||||||||||||||||||
|
|
@@ -438,6 +458,8 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| .name = "rg_search", | ||||||||||||||||||||||||||
| .description = "Searches the workspace with ripgrep and returns structured matches.", | ||||||||||||||||||||||||||
| .category = ToolCategory::ReadOnly, | ||||||||||||||||||||||||||
| .mutates_repository_state = false, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = false, | ||||||||||||||||||||||||||
| .requires_approval = false, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"query", { | ||||||||||||||||||||||||||
|
|
@@ -473,6 +495,8 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| .name = "git_status", | ||||||||||||||||||||||||||
| .description = "Returns the current git working tree status for the workspace.", | ||||||||||||||||||||||||||
| .category = ToolCategory::ReadOnly, | ||||||||||||||||||||||||||
| .mutates_repository_state = false, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = false, | ||||||||||||||||||||||||||
| .requires_approval = false, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema(nlohmann::json::object()), | ||||||||||||||||||||||||||
| .max_output_bytes = kMaxRepoOutputBytes, | ||||||||||||||||||||||||||
|
|
@@ -481,13 +505,56 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| register_or_throw(®istry, ToolDescriptor{ | ||||||||||||||||||||||||||
| .name = "git_add", | ||||||||||||||||||||||||||
| .description = "Stages explicitly listed git pathspecs into the index.", | ||||||||||||||||||||||||||
| .category = ToolCategory::Mutating, | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Registering Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||
| .mutates_repository_state = true, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = true, | ||||||||||||||||||||||||||
| .requires_approval = true, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"pathspecs", { | ||||||||||||||||||||||||||
| {"type", "array"}, | ||||||||||||||||||||||||||
| {"items", {{"type", "string"}}}, | ||||||||||||||||||||||||||
| {"description", "Explicit git pathspecs to stage. Must contain at least one entry."} | ||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||
| }, {"pathspecs"}), | ||||||||||||||||||||||||||
| .max_output_bytes = kMaxRepoOutputBytes, | ||||||||||||||||||||||||||
| .execute = [](const ToolCall& cmd, const AgentConfig& config, size_t output_limit) { | ||||||||||||||||||||||||||
| const std::vector<std::string> pathspecs = optional_string_array_arg_strict(cmd, "pathspecs"); | ||||||||||||||||||||||||||
| return git_add(config.workspace_abs, pathspecs, output_limit); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| register_or_throw(®istry, ToolDescriptor{ | ||||||||||||||||||||||||||
| .name = "git_commit", | ||||||||||||||||||||||||||
| .description = "Creates a git commit from the currently staged index changes.", | ||||||||||||||||||||||||||
| .category = ToolCategory::Mutating, | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Marking Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||
| .mutates_repository_state = true, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = true, | ||||||||||||||||||||||||||
| .requires_approval = true, | ||||||||||||||||||||||||||
|
Comment on lines
+530
to
+535
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"message", { | ||||||||||||||||||||||||||
| {"type", "string"}, | ||||||||||||||||||||||||||
| {"description", "Commit message for the staged index changes."} | ||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||
| }, {"message"}), | ||||||||||||||||||||||||||
| .max_output_bytes = kMaxRepoOutputBytes, | ||||||||||||||||||||||||||
| .execute = [](const ToolCall& cmd, const AgentConfig& config, size_t output_limit) { | ||||||||||||||||||||||||||
| const std::string message = require_string_arg(cmd, "message", "git_commit"); | ||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency and to fail earlier, you should use the
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency and to perform argument validation at the earliest opportunity, consider using the
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency and to fail earlier, please use the newly introduced
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency and to centralize argument validation, consider using the
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency and to fail early, consider using the new
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For consistency with the newly introduced
Suggested change
|
||||||||||||||||||||||||||
| return git_commit(config.workspace_abs, message, output_limit); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| register_or_throw(®istry, ToolDescriptor{ | ||||||||||||||||||||||||||
| .name = "apply_patch", | ||||||||||||||||||||||||||
| .description = "Applies an exact-text replacement patch to a workspace file. " | ||||||||||||||||||||||||||
| "Supports single mode (old_text + new_text) and batch mode (patches array). " | ||||||||||||||||||||||||||
| "old_text must appear exactly once in the file. " | ||||||||||||||||||||||||||
| "An explicit empty new_text is valid and deletes the matched text.", | ||||||||||||||||||||||||||
| .category = ToolCategory::Mutating, | ||||||||||||||||||||||||||
| .mutates_repository_state = true, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = false, | ||||||||||||||||||||||||||
| .requires_approval = true, | ||||||||||||||||||||||||||
| .json_schema = nlohmann::json{ | ||||||||||||||||||||||||||
| {"type", "object"}, | ||||||||||||||||||||||||||
|
|
@@ -678,6 +745,8 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| "By default shows unstaged changes; set cached=true for staged changes. " | ||||||||||||||||||||||||||
| "Optionally filter by pathspecs and control hunk context with context_lines (default 3).", | ||||||||||||||||||||||||||
| .category = ToolCategory::ReadOnly, | ||||||||||||||||||||||||||
| .mutates_repository_state = false, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = false, | ||||||||||||||||||||||||||
| .requires_approval = false, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"cached", { | ||||||||||||||||||||||||||
|
|
@@ -716,6 +785,8 @@ ToolRegistry build_default_tool_registry() { | |||||||||||||||||||||||||
| "rev is required. Optionally disable patch or stat output, filter by pathspecs, " | ||||||||||||||||||||||||||
| "and control diff context with context_lines (default 3).", | ||||||||||||||||||||||||||
| .category = ToolCategory::ReadOnly, | ||||||||||||||||||||||||||
| .mutates_repository_state = false, | ||||||||||||||||||||||||||
| .can_execute_repo_controlled_code = false, | ||||||||||||||||||||||||||
| .requires_approval = false, | ||||||||||||||||||||||||||
| .json_schema = make_parameters_schema({ | ||||||||||||||||||||||||||
| {"rev", { | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New helper function is defined but never called
Low Severity
require_non_empty_string_argis added in this PR but never called anywhere in the codebase. Thegit_commitexecutor at line 544 usesrequire_string_arginstead, relying on thegit_commit()function inrepo_tools.cppto perform its own empty-message check. This is dead code that adds maintenance burden without providing value.Additional Locations (1)
src/agent_tools.cpp#L543-L544