quantalogic 0.33.4__py3-none-any.whl → 0.40.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. quantalogic/__init__.py +0 -4
  2. quantalogic/agent.py +603 -362
  3. quantalogic/agent_config.py +260 -28
  4. quantalogic/agent_factory.py +43 -17
  5. quantalogic/coding_agent.py +20 -12
  6. quantalogic/config.py +7 -4
  7. quantalogic/console_print_events.py +4 -8
  8. quantalogic/console_print_token.py +2 -2
  9. quantalogic/docs_cli.py +15 -10
  10. quantalogic/event_emitter.py +258 -83
  11. quantalogic/flow/__init__.py +23 -0
  12. quantalogic/flow/flow.py +595 -0
  13. quantalogic/flow/flow_extractor.py +672 -0
  14. quantalogic/flow/flow_generator.py +89 -0
  15. quantalogic/flow/flow_manager.py +407 -0
  16. quantalogic/flow/flow_manager_schema.py +169 -0
  17. quantalogic/flow/flow_yaml.md +419 -0
  18. quantalogic/generative_model.py +109 -77
  19. quantalogic/get_model_info.py +6 -6
  20. quantalogic/interactive_text_editor.py +100 -73
  21. quantalogic/main.py +36 -23
  22. quantalogic/model_info_list.py +12 -0
  23. quantalogic/model_info_litellm.py +14 -14
  24. quantalogic/prompts.py +2 -1
  25. quantalogic/{llm.py → quantlitellm.py} +29 -39
  26. quantalogic/search_agent.py +4 -4
  27. quantalogic/server/models.py +4 -1
  28. quantalogic/task_file_reader.py +5 -5
  29. quantalogic/task_runner.py +21 -20
  30. quantalogic/tool_manager.py +10 -21
  31. quantalogic/tools/__init__.py +98 -68
  32. quantalogic/tools/composio/composio.py +416 -0
  33. quantalogic/tools/{generate_database_report_tool.py → database/generate_database_report_tool.py} +4 -9
  34. quantalogic/tools/database/sql_query_tool_advanced.py +261 -0
  35. quantalogic/tools/document_tools/markdown_to_docx_tool.py +620 -0
  36. quantalogic/tools/document_tools/markdown_to_epub_tool.py +438 -0
  37. quantalogic/tools/document_tools/markdown_to_html_tool.py +362 -0
  38. quantalogic/tools/document_tools/markdown_to_ipynb_tool.py +319 -0
  39. quantalogic/tools/document_tools/markdown_to_latex_tool.py +420 -0
  40. quantalogic/tools/document_tools/markdown_to_pdf_tool.py +623 -0
  41. quantalogic/tools/document_tools/markdown_to_pptx_tool.py +319 -0
  42. quantalogic/tools/duckduckgo_search_tool.py +2 -4
  43. quantalogic/tools/finance/alpha_vantage_tool.py +440 -0
  44. quantalogic/tools/finance/ccxt_tool.py +373 -0
  45. quantalogic/tools/finance/finance_llm_tool.py +387 -0
  46. quantalogic/tools/finance/google_finance.py +192 -0
  47. quantalogic/tools/finance/market_intelligence_tool.py +520 -0
  48. quantalogic/tools/finance/technical_analysis_tool.py +491 -0
  49. quantalogic/tools/finance/tradingview_tool.py +336 -0
  50. quantalogic/tools/finance/yahoo_finance.py +236 -0
  51. quantalogic/tools/git/bitbucket_clone_repo_tool.py +181 -0
  52. quantalogic/tools/git/bitbucket_operations_tool.py +326 -0
  53. quantalogic/tools/git/clone_repo_tool.py +189 -0
  54. quantalogic/tools/git/git_operations_tool.py +532 -0
  55. quantalogic/tools/google_packages/google_news_tool.py +480 -0
  56. quantalogic/tools/grep_app_tool.py +123 -186
  57. quantalogic/tools/{dalle_e.py → image_generation/dalle_e.py} +37 -27
  58. quantalogic/tools/jinja_tool.py +6 -10
  59. quantalogic/tools/language_handlers/__init__.py +22 -9
  60. quantalogic/tools/list_directory_tool.py +131 -42
  61. quantalogic/tools/llm_tool.py +45 -15
  62. quantalogic/tools/llm_vision_tool.py +59 -7
  63. quantalogic/tools/markitdown_tool.py +17 -5
  64. quantalogic/tools/nasa_packages/models.py +47 -0
  65. quantalogic/tools/nasa_packages/nasa_apod_tool.py +232 -0
  66. quantalogic/tools/nasa_packages/nasa_neows_tool.py +147 -0
  67. quantalogic/tools/nasa_packages/services.py +82 -0
  68. quantalogic/tools/presentation_tools/presentation_llm_tool.py +396 -0
  69. quantalogic/tools/product_hunt/product_hunt_tool.py +258 -0
  70. quantalogic/tools/product_hunt/services.py +63 -0
  71. quantalogic/tools/rag_tool/__init__.py +48 -0
  72. quantalogic/tools/rag_tool/document_metadata.py +15 -0
  73. quantalogic/tools/rag_tool/query_response.py +20 -0
  74. quantalogic/tools/rag_tool/rag_tool.py +566 -0
  75. quantalogic/tools/rag_tool/rag_tool_beta.py +264 -0
  76. quantalogic/tools/read_html_tool.py +24 -38
  77. quantalogic/tools/replace_in_file_tool.py +10 -10
  78. quantalogic/tools/safe_python_interpreter_tool.py +10 -24
  79. quantalogic/tools/search_definition_names.py +2 -2
  80. quantalogic/tools/sequence_tool.py +14 -23
  81. quantalogic/tools/sql_query_tool.py +17 -19
  82. quantalogic/tools/tool.py +39 -15
  83. quantalogic/tools/unified_diff_tool.py +1 -1
  84. quantalogic/tools/utilities/csv_processor_tool.py +234 -0
  85. quantalogic/tools/utilities/download_file_tool.py +179 -0
  86. quantalogic/tools/utilities/mermaid_validator_tool.py +661 -0
  87. quantalogic/tools/utils/__init__.py +1 -4
  88. quantalogic/tools/utils/create_sample_database.py +24 -38
  89. quantalogic/tools/utils/generate_database_report.py +74 -82
  90. quantalogic/tools/wikipedia_search_tool.py +17 -21
  91. quantalogic/utils/ask_user_validation.py +1 -1
  92. quantalogic/utils/async_utils.py +35 -0
  93. quantalogic/utils/check_version.py +3 -5
  94. quantalogic/utils/get_all_models.py +2 -1
  95. quantalogic/utils/git_ls.py +21 -7
  96. quantalogic/utils/lm_studio_model_info.py +9 -7
  97. quantalogic/utils/python_interpreter.py +113 -43
  98. quantalogic/utils/xml_utility.py +178 -0
  99. quantalogic/version_check.py +1 -1
  100. quantalogic/welcome_message.py +7 -7
  101. quantalogic/xml_parser.py +0 -1
  102. {quantalogic-0.33.4.dist-info → quantalogic-0.40.0.dist-info}/METADATA +44 -1
  103. quantalogic-0.40.0.dist-info/RECORD +148 -0
  104. quantalogic-0.33.4.dist-info/RECORD +0 -102
  105. {quantalogic-0.33.4.dist-info → quantalogic-0.40.0.dist-info}/LICENSE +0 -0
  106. {quantalogic-0.33.4.dist-info → quantalogic-0.40.0.dist-info}/WHEEL +0 -0
  107. {quantalogic-0.33.4.dist-info → quantalogic-0.40.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,532 @@
1
+ """Tool for performing Git operations like creating branches and making commits."""
2
+
3
+ import os
4
+ import re
5
+ from typing import ClassVar, Dict
6
+ from urllib.parse import urlparse
7
+
8
+ from git import Repo
9
+ from git.exc import GitCommandError
10
+ from loguru import logger
11
+ from pydantic import Field, validator
12
+
13
+ from quantalogic.tools.tool import Tool, ToolArgument
14
+
15
+
16
+ class GitOperationsTool(Tool):
17
+ """Tool for Git operations including branch creation and commits.
18
+
19
+ This tool provides a simple interface for common Git operations like creating branches,
20
+ making commits, pushing changes, and more. It handles both public and private repositories
21
+ through token-based authentication.
22
+
23
+ Examples:
24
+ Create a new branch and switch to it:
25
+ ```python
26
+ tool = GitOperationsTool(auth_token="your_github_token")
27
+ tool.execute(
28
+ repo_path="/path/to/repo",
29
+ operation="create_branch",
30
+ branch_name="feature/new-feature"
31
+ )
32
+ ```
33
+
34
+ Make a commit with specific files:
35
+ ```python
36
+ tool.execute(
37
+ repo_path="/path/to/repo",
38
+ operation="commit",
39
+ commit_message="Add new feature implementation",
40
+ files_to_commit="file1.py,file2.py"
41
+ )
42
+ ```
43
+
44
+ Push changes to remote:
45
+ ```python
46
+ tool.execute(
47
+ repo_path="/path/to/repo",
48
+ operation="push"
49
+ )
50
+ ```
51
+
52
+ Pull latest changes:
53
+ ```python
54
+ tool.execute(
55
+ repo_path="/path/to/repo",
56
+ operation="pull"
57
+ )
58
+ ```
59
+
60
+ Switch to existing branch:
61
+ ```python
62
+ tool.execute(
63
+ repo_path="/path/to/repo",
64
+ operation="checkout",
65
+ branch_name="main"
66
+ )
67
+ ```
68
+ """
69
+
70
+ name: str = "git_operations_tool"
71
+ description: str = (
72
+ "Performs Git operations on a repository including creating branches, "
73
+ "making commits, pushing changes, pulling updates, and checking out branches. "
74
+ "Automatically handles authentication for private repositories using the provided token."
75
+ )
76
+ need_validation: bool = False
77
+ auth_token: str = Field(default=None, description="Authentication token for private repositories")
78
+ provider_urls: ClassVar[Dict[str, str]] = {
79
+ "github.com": "https://github.com",
80
+ "gitlab.com": "https://gitlab.com",
81
+ "bitbucket.org": "https://bitbucket.org",
82
+ "dev.azure.com": "https://dev.azure.com"
83
+ }
84
+
85
+ def __init__(self, auth_token: str = None, **data):
86
+ """Initialize the tool with an optional auth token.
87
+
88
+ Args:
89
+ auth_token: Authentication token for private repositories
90
+ **data: Additional tool configuration data
91
+ """
92
+ super().__init__(**data)
93
+ self.auth_token = auth_token
94
+
95
+ @validator('auth_token')
96
+ def validate_auth_token(cls, v):
97
+ """Validate the authentication token format.
98
+
99
+ Args:
100
+ v: The token value to validate
101
+
102
+ Returns:
103
+ The validated token
104
+
105
+ Raises:
106
+ ValueError: If the token format is invalid
107
+ """
108
+ if v is not None:
109
+ if not isinstance(v, str):
110
+ raise ValueError("Authentication token must be a string")
111
+ if len(v.strip()) < 8:
112
+ raise ValueError("Authentication token seems too short")
113
+ if not re.match(r'^[a-zA-Z0-9_\-]+$', v):
114
+ raise ValueError("Authentication token contains invalid characters")
115
+ return v
116
+
117
+ def _get_provider_from_url(self, url: str) -> str:
118
+ """Determine the Git provider from the repository URL.
119
+
120
+ Args:
121
+ url: Repository URL
122
+
123
+ Returns:
124
+ str: Provider name or 'unknown'
125
+ """
126
+ try:
127
+ parsed = urlparse(url)
128
+ domain = parsed.netloc.lower()
129
+ for provider, base_url in self.provider_urls.items():
130
+ if provider in domain:
131
+ return provider
132
+ return 'unknown'
133
+ except Exception:
134
+ return 'unknown'
135
+
136
+ def _setup_auth_for_remote(self, repo: Repo) -> tuple[str, bool]:
137
+ """Setup authentication for remote operations if needed.
138
+
139
+ Args:
140
+ repo: Git repository instance
141
+
142
+ Returns:
143
+ tuple: Original remote URL and whether URL was modified
144
+
145
+ Raises:
146
+ ValueError: If remote URL is invalid or authentication setup fails
147
+ """
148
+ try:
149
+ original_url = repo.remote().url
150
+ if not original_url:
151
+ raise ValueError("No remote URL found in repository")
152
+
153
+ # Only modify HTTPS URLs
154
+ if not self.auth_token or not original_url.startswith('https://'):
155
+ return original_url, False
156
+
157
+ provider = self._get_provider_from_url(original_url)
158
+ if provider == 'unknown':
159
+ logger.warning(f"Unknown Git provider for URL: {original_url}")
160
+
161
+ # Remove any existing credentials from URL
162
+ url_parts = original_url.split('@')
163
+ if len(url_parts) > 1:
164
+ base_url = url_parts[-1]
165
+ new_url = f"https://{self.auth_token}@{base_url}"
166
+ else:
167
+ new_url = original_url.replace("https://", f"https://{self.auth_token}@")
168
+
169
+ repo.remote().set_url(new_url)
170
+ logger.debug("Successfully configured authentication for remote operations")
171
+ return original_url, True
172
+
173
+ except Exception as e:
174
+ raise ValueError(f"Failed to setup authentication: {str(e)}")
175
+
176
+ arguments: list = [
177
+ ToolArgument(
178
+ name="repo_path",
179
+ arg_type="string",
180
+ description=(
181
+ "The local path to the Git repository. This should be an absolute path to "
182
+ "an existing Git repository on your system.\n"
183
+ "Examples:\n"
184
+ "- '/home/user/projects/my-repo'\n"
185
+ "- '/path/to/project'\n"
186
+ "- './current/directory/repo'"
187
+ ),
188
+ required=True,
189
+ example="/path/to/repo",
190
+ ),
191
+ ToolArgument(
192
+ name="operation",
193
+ arg_type="string",
194
+ description=(
195
+ "Git operation to perform. Available operations:\n"
196
+ "- 'create_branch': Create and checkout a new branch\n"
197
+ "- 'commit': Create a new commit with specified files\n"
198
+ "- 'push': Push local changes to remote repository\n"
199
+ "- 'pull': Pull latest changes from remote repository\n"
200
+ "- 'checkout': Switch to an existing branch\n\n"
201
+ "Usage examples:\n"
202
+ "- operation='create_branch' + branch_name='feature/new-feature'\n"
203
+ "- operation='commit' + commit_message='Add new feature' + files_to_commit='file1.py,file2.py'\n"
204
+ "- operation='push' + branch_name='feature/new-feature' (pushes specified branch)\n"
205
+ "- operation='checkout' + branch_name='main'"
206
+ ),
207
+ required=True,
208
+ example="create_branch",
209
+ ),
210
+ ToolArgument(
211
+ name="branch_name",
212
+ arg_type="string",
213
+ description=(
214
+ "Name of the branch to create or switch to. Required for 'create_branch' and 'checkout' operations.\n"
215
+ "Branch naming conventions:\n"
216
+ "- feature/[feature-name] for new features\n"
217
+ "- bugfix/[bug-name] for bug fixes\n"
218
+ "- hotfix/[fix-name] for urgent fixes\n"
219
+ "- release/[version] for release branches\n\n"
220
+ "Examples:\n"
221
+ "- 'feature/user-authentication'\n"
222
+ "- 'bugfix/login-error'\n"
223
+ "- 'main' or 'master' for main branch\n"
224
+ "- 'develop' for development branch"
225
+ ),
226
+ required=False,
227
+ example="feature/new-feature",
228
+ ),
229
+ ToolArgument(
230
+ name="commit_message",
231
+ arg_type="string",
232
+ description=(
233
+ "Commit message when operation is 'commit'. If not provided, a default message will be generated.\n"
234
+ "Commit message guidelines:\n"
235
+ "- Start with a verb (Add, Fix, Update, Refactor, etc.)\n"
236
+ "- Keep it concise but descriptive\n"
237
+ "- Include ticket/issue number if applicable\n\n"
238
+ "Examples:\n"
239
+ "- 'Add user authentication feature'\n"
240
+ "- 'Fix login validation bug #123'\n"
241
+ "- 'Update README with API documentation'\n"
242
+ "- 'Refactor database connection logic'"
243
+ ),
244
+ required=False,
245
+ example="Add new feature implementation",
246
+ ),
247
+ ToolArgument(
248
+ name="files_to_commit",
249
+ arg_type="string",
250
+ description=(
251
+ "Comma-separated list of files to commit, or '.' for all changes. Used with 'commit' operation.\n"
252
+ "File specification:\n"
253
+ "- Use '.' to commit all changes\n"
254
+ "- Use relative paths from repo root\n"
255
+ "- Separate multiple files with commas\n"
256
+ "- Supports wildcards (*.py, *.js)\n\n"
257
+ "Examples:\n"
258
+ "- '.' (all changes)\n"
259
+ "- 'src/main.py,tests/test_main.py'\n"
260
+ "- 'docs/*.md'\n"
261
+ "- 'feature/auth/*.py,feature/auth/*.js'"
262
+ ),
263
+ required=False,
264
+ example="file1.py,file2.py",
265
+ default=".",
266
+ ),
267
+ ]
268
+
269
+ def execute(
270
+ self,
271
+ repo_path: str,
272
+ operation: str,
273
+ branch_name: str = None,
274
+ commit_message: str = None,
275
+ files_to_commit: str = ".",
276
+ ) -> str:
277
+ """Executes the specified Git operation.
278
+
279
+ Args:
280
+ repo_path: Path to the local Git repository
281
+ operation: Git operation to perform
282
+ branch_name: Name of the branch (for create_branch/checkout)
283
+ commit_message: Commit message (for commit)
284
+ files_to_commit: Files to commit (for commit)
285
+
286
+ Returns:
287
+ str: Result message
288
+
289
+ Raises:
290
+ GitCommandError: If there's an error during Git operations
291
+ ValueError: If the parameters are invalid
292
+ """
293
+ try:
294
+ if not os.path.exists(repo_path):
295
+ raise ValueError(f"Repository path does not exist: {repo_path}")
296
+ if not os.path.isdir(os.path.join(repo_path, '.git')):
297
+ raise ValueError(f"Not a valid Git repository: {repo_path}")
298
+
299
+ repo = Repo(repo_path)
300
+
301
+ # Validate remote configuration for operations that need it
302
+ if operation in ['push', 'pull']:
303
+ if not repo.remotes:
304
+ raise ValueError("Repository has no configured remotes")
305
+ remote = repo.remote()
306
+ if not remote.url:
307
+ raise ValueError("Remote URL is not configured")
308
+
309
+ if operation == "create_branch":
310
+ if not branch_name:
311
+ raise ValueError("branch_name is required for create_branch operation")
312
+
313
+ # Check if branch already exists
314
+ if branch_name in repo.heads:
315
+ raise ValueError(f"Branch '{branch_name}' already exists")
316
+
317
+ # Create and checkout new branch
318
+ current = repo.create_head(branch_name)
319
+ current.checkout()
320
+ logger.info(f"Created and checked out branch: {branch_name}")
321
+ return f"Successfully created and checked out branch: {branch_name}"
322
+
323
+ elif operation == "commit":
324
+ # Validate repository state
325
+ if repo.is_dirty(untracked_files=True):
326
+ # Handle default commit behavior
327
+ if files_to_commit == ".":
328
+ # Stage all changes
329
+ repo.git.add(A=True)
330
+ else:
331
+ # Stage specific files
332
+ for file in files_to_commit.split(","):
333
+ file = file.strip()
334
+ file_path = os.path.join(repo_path, file)
335
+ if os.path.exists(file_path):
336
+ repo.git.add(file)
337
+ else:
338
+ logger.warning(f"File not found: {file}")
339
+
340
+ # Get list of staged files
341
+ staged_files = repo.index.diff("HEAD")
342
+ if not staged_files:
343
+ return "No changes to commit"
344
+
345
+ # Generate default commit message if none provided
346
+ if not commit_message:
347
+ status = repo.git.status('--porcelain')
348
+ changes = self._analyze_changes(status)
349
+ commit_message = self._generate_commit_message(changes)
350
+
351
+ # Create commit
352
+ commit = repo.index.commit(commit_message)
353
+ logger.info(f"Created commit: {commit.hexsha[:8]}")
354
+ return f"Successfully created commit: {commit.hexsha[:8]}\n{commit_message}"
355
+ else:
356
+ return "No changes to commit"
357
+
358
+ elif operation in ["push", "pull"]:
359
+ # Setup authentication if needed
360
+ original_url, url_modified = self._setup_auth_for_remote(repo)
361
+
362
+ try:
363
+ if operation == "push":
364
+ current_branch = repo.active_branch
365
+ try:
366
+ # Try to push with current configuration
367
+ repo.remote().push(current_branch)
368
+ except GitCommandError as e:
369
+ if "no upstream branch" in str(e).lower():
370
+ # Set upstream and push
371
+ repo.git.push('--set-upstream', 'origin', current_branch.name)
372
+ else:
373
+ raise
374
+ logger.info(f"Pushed changes to remote repository on branch: {current_branch.name}")
375
+ result = f"Successfully pushed changes to remote repository on branch: {current_branch.name}"
376
+ else: # pull
377
+ repo.remote().pull()
378
+ logger.info("Pulled latest changes from remote repository")
379
+ result = "Successfully pulled latest changes from remote repository"
380
+ finally:
381
+ # Reset URL if it was modified
382
+ if url_modified:
383
+ repo.remote().set_url(original_url)
384
+
385
+ return result
386
+
387
+ elif operation == "checkout":
388
+ if not branch_name:
389
+ raise ValueError("branch_name is required for checkout operation")
390
+
391
+ # Check if branch exists
392
+ if branch_name in repo.heads:
393
+ # Check if there are uncommitted changes
394
+ if repo.is_dirty():
395
+ raise ValueError("Cannot checkout branch: You have uncommitted changes")
396
+ # Checkout existing branch
397
+ repo.heads[branch_name].checkout()
398
+ logger.info(f"Checked out existing branch: {branch_name}")
399
+ return f"Successfully checked out branch: {branch_name}"
400
+ else:
401
+ raise ValueError(f"Branch '{branch_name}' does not exist. Use create_branch operation to create a new branch.")
402
+
403
+ else:
404
+ raise ValueError(f"Unsupported operation: {operation}")
405
+
406
+ except GitCommandError as e:
407
+ error_msg = str(e)
408
+ # Remove sensitive information from error message
409
+ if self.auth_token:
410
+ error_msg = error_msg.replace(self.auth_token, "***")
411
+ logger.error(f"Git operation failed: {error_msg}")
412
+ raise GitCommandError("Git operation failed", e.status)
413
+
414
+ except Exception as e:
415
+ logger.error(f"An error occurred during Git operation: {str(e)}")
416
+ raise ValueError(f"An error occurred during Git operation: {str(e)}")
417
+
418
+ def _analyze_changes(self, status: str) -> dict:
419
+ """Analyze Git status output to categorize changes.
420
+
421
+ Args:
422
+ status: Git status porcelain output
423
+
424
+ Returns:
425
+ dict: Categorized changes
426
+ """
427
+ changes = {
428
+ 'added': [],
429
+ 'modified': [],
430
+ 'deleted': []
431
+ }
432
+
433
+ for line in status.split('\n'):
434
+ if line:
435
+ status_code = line[:2]
436
+ file_path = line[3:]
437
+ if status_code.startswith('A'):
438
+ changes['added'].append(file_path)
439
+ elif status_code.startswith('M'):
440
+ changes['modified'].append(file_path)
441
+ elif status_code.startswith('D'):
442
+ changes['deleted'].append(file_path)
443
+
444
+ return changes
445
+
446
+ def _generate_commit_message(self, changes: dict) -> str:
447
+ """Generate a descriptive commit message from changes.
448
+
449
+ Args:
450
+ changes: Dictionary of categorized changes
451
+
452
+ Returns:
453
+ str: Generated commit message
454
+ """
455
+ message_parts = []
456
+ if changes['added']:
457
+ message_parts.append(f"Add {len(changes['added'])} file(s)")
458
+ if changes['modified']:
459
+ message_parts.append(f"Update {len(changes['modified'])} file(s)")
460
+ if changes['deleted']:
461
+ message_parts.append(f"Remove {len(changes['deleted'])} file(s)")
462
+
463
+ commit_message = " & ".join(message_parts)
464
+
465
+ # Add file details
466
+ details = []
467
+ if changes['added']:
468
+ details.append("\nAdded files:\n- " + "\n- ".join(changes['added']))
469
+ if changes['modified']:
470
+ details.append("\nModified files:\n- " + "\n- ".join(changes['modified']))
471
+ if changes['deleted']:
472
+ details.append("\nDeleted files:\n- " + "\n- ".join(changes['deleted']))
473
+
474
+ return commit_message + "".join(details)
475
+
476
+
477
+ if __name__ == "__main__":
478
+ # Example usage of the GitOperationsTool
479
+ def run_example(repo_path: str):
480
+ """Run example Git operations using the tool."""
481
+ tool = GitOperationsTool(auth_token="your_token_here")
482
+
483
+ try:
484
+ # 1. Create and switch to a new feature branch
485
+ logger.info("Creating new feature branch...")
486
+ tool.execute(
487
+ repo_path=repo_path,
488
+ operation="create_branch",
489
+ branch_name="feature/example-feature"
490
+ )
491
+
492
+ # 2. Make some changes and commit them
493
+ logger.info("Creating a commit...")
494
+ tool.execute(
495
+ repo_path=repo_path,
496
+ operation="commit",
497
+ commit_message="Add new example feature",
498
+ files_to_commit="." # Commit all changes
499
+ )
500
+
501
+ # 3. Push changes to remote
502
+ logger.info("Pushing changes to remote...")
503
+ tool.execute(
504
+ repo_path=repo_path,
505
+ operation="push"
506
+ )
507
+
508
+ # 4. Switch back to main branch
509
+ logger.info("Switching back to main branch...")
510
+ tool.execute(
511
+ repo_path=repo_path,
512
+ operation="checkout",
513
+ branch_name="main"
514
+ )
515
+
516
+ # 5. Pull latest changes
517
+ logger.info("Pulling latest changes...")
518
+ tool.execute(
519
+ repo_path=repo_path,
520
+ operation="pull"
521
+ )
522
+
523
+ except Exception as e:
524
+ logger.error(f"Example failed: {str(e)}")
525
+ raise
526
+
527
+ # To run the example, uncomment and modify the path:
528
+ # run_example("/path/to/your/repo")
529
+
530
+ # Print tool documentation
531
+ tool = GitOperationsTool(auth_token="your_token_here")
532
+ print(tool.to_markdown())