alita-sdk 0.3.379__py3-none-any.whl → 0.3.627__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 (278) hide show
  1. alita_sdk/cli/__init__.py +10 -0
  2. alita_sdk/cli/__main__.py +17 -0
  3. alita_sdk/cli/agent/__init__.py +5 -0
  4. alita_sdk/cli/agent/default.py +258 -0
  5. alita_sdk/cli/agent_executor.py +156 -0
  6. alita_sdk/cli/agent_loader.py +245 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3113 -0
  9. alita_sdk/cli/callbacks.py +647 -0
  10. alita_sdk/cli/cli.py +168 -0
  11. alita_sdk/cli/config.py +306 -0
  12. alita_sdk/cli/context/__init__.py +30 -0
  13. alita_sdk/cli/context/cleanup.py +198 -0
  14. alita_sdk/cli/context/manager.py +731 -0
  15. alita_sdk/cli/context/message.py +285 -0
  16. alita_sdk/cli/context/strategies.py +289 -0
  17. alita_sdk/cli/context/token_estimation.py +127 -0
  18. alita_sdk/cli/formatting.py +182 -0
  19. alita_sdk/cli/input_handler.py +419 -0
  20. alita_sdk/cli/inventory.py +1073 -0
  21. alita_sdk/cli/mcp_loader.py +315 -0
  22. alita_sdk/cli/testcases/__init__.py +94 -0
  23. alita_sdk/cli/testcases/data_generation.py +119 -0
  24. alita_sdk/cli/testcases/discovery.py +96 -0
  25. alita_sdk/cli/testcases/executor.py +84 -0
  26. alita_sdk/cli/testcases/logger.py +85 -0
  27. alita_sdk/cli/testcases/parser.py +172 -0
  28. alita_sdk/cli/testcases/prompts.py +91 -0
  29. alita_sdk/cli/testcases/reporting.py +125 -0
  30. alita_sdk/cli/testcases/setup.py +108 -0
  31. alita_sdk/cli/testcases/test_runner.py +282 -0
  32. alita_sdk/cli/testcases/utils.py +39 -0
  33. alita_sdk/cli/testcases/validation.py +90 -0
  34. alita_sdk/cli/testcases/workflow.py +196 -0
  35. alita_sdk/cli/toolkit.py +327 -0
  36. alita_sdk/cli/toolkit_loader.py +85 -0
  37. alita_sdk/cli/tools/__init__.py +43 -0
  38. alita_sdk/cli/tools/approval.py +224 -0
  39. alita_sdk/cli/tools/filesystem.py +1751 -0
  40. alita_sdk/cli/tools/planning.py +389 -0
  41. alita_sdk/cli/tools/terminal.py +414 -0
  42. alita_sdk/community/__init__.py +72 -12
  43. alita_sdk/community/inventory/__init__.py +236 -0
  44. alita_sdk/community/inventory/config.py +257 -0
  45. alita_sdk/community/inventory/enrichment.py +2137 -0
  46. alita_sdk/community/inventory/extractors.py +1469 -0
  47. alita_sdk/community/inventory/ingestion.py +3172 -0
  48. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  49. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  50. alita_sdk/community/inventory/parsers/base.py +295 -0
  51. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  52. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  53. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  54. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  55. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  56. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  57. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  58. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  59. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  60. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  61. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  62. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  63. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  64. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  65. alita_sdk/community/inventory/patterns/loader.py +348 -0
  66. alita_sdk/community/inventory/patterns/registry.py +198 -0
  67. alita_sdk/community/inventory/presets.py +535 -0
  68. alita_sdk/community/inventory/retrieval.py +1403 -0
  69. alita_sdk/community/inventory/toolkit.py +173 -0
  70. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  71. alita_sdk/community/inventory/visualize.py +1370 -0
  72. alita_sdk/configurations/__init__.py +1 -1
  73. alita_sdk/configurations/ado.py +141 -20
  74. alita_sdk/configurations/bitbucket.py +94 -2
  75. alita_sdk/configurations/confluence.py +130 -1
  76. alita_sdk/configurations/figma.py +76 -0
  77. alita_sdk/configurations/gitlab.py +91 -0
  78. alita_sdk/configurations/jira.py +103 -0
  79. alita_sdk/configurations/openapi.py +329 -0
  80. alita_sdk/configurations/qtest.py +72 -1
  81. alita_sdk/configurations/report_portal.py +96 -0
  82. alita_sdk/configurations/sharepoint.py +148 -0
  83. alita_sdk/configurations/testio.py +83 -0
  84. alita_sdk/configurations/testrail.py +88 -0
  85. alita_sdk/configurations/xray.py +93 -0
  86. alita_sdk/configurations/zephyr_enterprise.py +93 -0
  87. alita_sdk/configurations/zephyr_essential.py +75 -0
  88. alita_sdk/runtime/clients/artifact.py +3 -3
  89. alita_sdk/runtime/clients/client.py +388 -46
  90. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  91. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  92. alita_sdk/runtime/clients/sandbox_client.py +8 -21
  93. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  94. alita_sdk/runtime/langchain/assistant.py +157 -39
  95. alita_sdk/runtime/langchain/constants.py +647 -1
  96. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  97. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  98. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  99. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -4
  100. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
  101. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  102. alita_sdk/runtime/langchain/document_loaders/constants.py +40 -19
  103. alita_sdk/runtime/langchain/langraph_agent.py +405 -84
  104. alita_sdk/runtime/langchain/utils.py +106 -7
  105. alita_sdk/runtime/llms/preloaded.py +2 -6
  106. alita_sdk/runtime/models/mcp_models.py +61 -0
  107. alita_sdk/runtime/skills/__init__.py +91 -0
  108. alita_sdk/runtime/skills/callbacks.py +498 -0
  109. alita_sdk/runtime/skills/discovery.py +540 -0
  110. alita_sdk/runtime/skills/executor.py +610 -0
  111. alita_sdk/runtime/skills/input_builder.py +371 -0
  112. alita_sdk/runtime/skills/models.py +330 -0
  113. alita_sdk/runtime/skills/registry.py +355 -0
  114. alita_sdk/runtime/skills/skill_runner.py +330 -0
  115. alita_sdk/runtime/toolkits/__init__.py +31 -0
  116. alita_sdk/runtime/toolkits/application.py +29 -10
  117. alita_sdk/runtime/toolkits/artifact.py +20 -11
  118. alita_sdk/runtime/toolkits/datasource.py +13 -6
  119. alita_sdk/runtime/toolkits/mcp.py +783 -0
  120. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  121. alita_sdk/runtime/toolkits/planning.py +178 -0
  122. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  123. alita_sdk/runtime/toolkits/subgraph.py +251 -6
  124. alita_sdk/runtime/toolkits/tools.py +356 -69
  125. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  126. alita_sdk/runtime/tools/__init__.py +10 -3
  127. alita_sdk/runtime/tools/application.py +27 -6
  128. alita_sdk/runtime/tools/artifact.py +511 -28
  129. alita_sdk/runtime/tools/data_analysis.py +183 -0
  130. alita_sdk/runtime/tools/function.py +67 -35
  131. alita_sdk/runtime/tools/graph.py +10 -4
  132. alita_sdk/runtime/tools/image_generation.py +148 -46
  133. alita_sdk/runtime/tools/llm.py +1003 -128
  134. alita_sdk/runtime/tools/loop.py +3 -1
  135. alita_sdk/runtime/tools/loop_output.py +3 -1
  136. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  137. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  138. alita_sdk/runtime/tools/mcp_server_tool.py +8 -5
  139. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  140. alita_sdk/runtime/tools/planning/models.py +246 -0
  141. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  142. alita_sdk/runtime/tools/router.py +2 -4
  143. alita_sdk/runtime/tools/sandbox.py +65 -48
  144. alita_sdk/runtime/tools/skill_router.py +776 -0
  145. alita_sdk/runtime/tools/tool.py +3 -1
  146. alita_sdk/runtime/tools/vectorstore.py +9 -3
  147. alita_sdk/runtime/tools/vectorstore_base.py +70 -14
  148. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  149. alita_sdk/runtime/utils/constants.py +5 -1
  150. alita_sdk/runtime/utils/mcp_client.py +492 -0
  151. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  152. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  153. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  154. alita_sdk/runtime/utils/serialization.py +155 -0
  155. alita_sdk/runtime/utils/streamlit.py +40 -13
  156. alita_sdk/runtime/utils/toolkit_utils.py +30 -9
  157. alita_sdk/runtime/utils/utils.py +36 -0
  158. alita_sdk/tools/__init__.py +134 -35
  159. alita_sdk/tools/ado/repos/__init__.py +51 -32
  160. alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
  161. alita_sdk/tools/ado/test_plan/__init__.py +25 -9
  162. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  163. alita_sdk/tools/ado/utils.py +1 -18
  164. alita_sdk/tools/ado/wiki/__init__.py +25 -12
  165. alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
  166. alita_sdk/tools/ado/work_item/__init__.py +26 -13
  167. alita_sdk/tools/ado/work_item/ado_wrapper.py +73 -11
  168. alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
  169. alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
  170. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  171. alita_sdk/tools/azure_ai/search/__init__.py +11 -8
  172. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  173. alita_sdk/tools/base/tool.py +5 -1
  174. alita_sdk/tools/base_indexer_toolkit.py +271 -84
  175. alita_sdk/tools/bitbucket/__init__.py +17 -11
  176. alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
  177. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
  178. alita_sdk/tools/browser/__init__.py +5 -4
  179. alita_sdk/tools/carrier/__init__.py +5 -6
  180. alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
  181. alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
  182. alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
  183. alita_sdk/tools/chunkers/__init__.py +3 -1
  184. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  185. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  186. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  187. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  188. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  189. alita_sdk/tools/cloud/aws/__init__.py +10 -7
  190. alita_sdk/tools/cloud/azure/__init__.py +10 -7
  191. alita_sdk/tools/cloud/gcp/__init__.py +10 -7
  192. alita_sdk/tools/cloud/k8s/__init__.py +10 -7
  193. alita_sdk/tools/code/linter/__init__.py +10 -8
  194. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  195. alita_sdk/tools/code/sonar/__init__.py +11 -8
  196. alita_sdk/tools/code_indexer_toolkit.py +82 -22
  197. alita_sdk/tools/confluence/__init__.py +22 -16
  198. alita_sdk/tools/confluence/api_wrapper.py +107 -30
  199. alita_sdk/tools/confluence/loader.py +14 -2
  200. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  201. alita_sdk/tools/elastic/__init__.py +11 -8
  202. alita_sdk/tools/elitea_base.py +493 -30
  203. alita_sdk/tools/figma/__init__.py +58 -11
  204. alita_sdk/tools/figma/api_wrapper.py +1235 -143
  205. alita_sdk/tools/figma/figma_client.py +73 -0
  206. alita_sdk/tools/figma/toon_tools.py +2748 -0
  207. alita_sdk/tools/github/__init__.py +14 -15
  208. alita_sdk/tools/github/github_client.py +224 -100
  209. alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
  210. alita_sdk/tools/github/schemas.py +14 -5
  211. alita_sdk/tools/github/tool.py +5 -1
  212. alita_sdk/tools/github/tool_prompts.py +9 -22
  213. alita_sdk/tools/gitlab/__init__.py +16 -11
  214. alita_sdk/tools/gitlab/api_wrapper.py +218 -48
  215. alita_sdk/tools/gitlab_org/__init__.py +10 -9
  216. alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
  217. alita_sdk/tools/google/bigquery/__init__.py +13 -12
  218. alita_sdk/tools/google/bigquery/tool.py +5 -1
  219. alita_sdk/tools/google_places/__init__.py +11 -8
  220. alita_sdk/tools/google_places/api_wrapper.py +1 -1
  221. alita_sdk/tools/jira/__init__.py +17 -10
  222. alita_sdk/tools/jira/api_wrapper.py +92 -41
  223. alita_sdk/tools/keycloak/__init__.py +11 -8
  224. alita_sdk/tools/localgit/__init__.py +9 -3
  225. alita_sdk/tools/localgit/local_git.py +62 -54
  226. alita_sdk/tools/localgit/tool.py +5 -1
  227. alita_sdk/tools/memory/__init__.py +12 -4
  228. alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
  229. alita_sdk/tools/ocr/__init__.py +11 -8
  230. alita_sdk/tools/openapi/__init__.py +491 -106
  231. alita_sdk/tools/openapi/api_wrapper.py +1368 -0
  232. alita_sdk/tools/openapi/tool.py +20 -0
  233. alita_sdk/tools/pandas/__init__.py +20 -12
  234. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  235. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  236. alita_sdk/tools/postman/__init__.py +10 -9
  237. alita_sdk/tools/pptx/__init__.py +11 -10
  238. alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
  239. alita_sdk/tools/qtest/__init__.py +31 -11
  240. alita_sdk/tools/qtest/api_wrapper.py +2135 -86
  241. alita_sdk/tools/rally/__init__.py +10 -9
  242. alita_sdk/tools/rally/api_wrapper.py +1 -1
  243. alita_sdk/tools/report_portal/__init__.py +12 -8
  244. alita_sdk/tools/salesforce/__init__.py +10 -8
  245. alita_sdk/tools/servicenow/__init__.py +17 -15
  246. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  247. alita_sdk/tools/sharepoint/__init__.py +10 -7
  248. alita_sdk/tools/sharepoint/api_wrapper.py +129 -38
  249. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  250. alita_sdk/tools/sharepoint/utils.py +8 -2
  251. alita_sdk/tools/slack/__init__.py +10 -7
  252. alita_sdk/tools/slack/api_wrapper.py +2 -2
  253. alita_sdk/tools/sql/__init__.py +12 -9
  254. alita_sdk/tools/testio/__init__.py +10 -7
  255. alita_sdk/tools/testrail/__init__.py +11 -10
  256. alita_sdk/tools/testrail/api_wrapper.py +1 -1
  257. alita_sdk/tools/utils/__init__.py +9 -4
  258. alita_sdk/tools/utils/content_parser.py +103 -18
  259. alita_sdk/tools/utils/text_operations.py +410 -0
  260. alita_sdk/tools/utils/tool_prompts.py +79 -0
  261. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +30 -13
  262. alita_sdk/tools/xray/__init__.py +13 -9
  263. alita_sdk/tools/yagmail/__init__.py +9 -3
  264. alita_sdk/tools/zephyr/__init__.py +10 -7
  265. alita_sdk/tools/zephyr_enterprise/__init__.py +11 -7
  266. alita_sdk/tools/zephyr_essential/__init__.py +10 -7
  267. alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
  268. alita_sdk/tools/zephyr_essential/client.py +2 -2
  269. alita_sdk/tools/zephyr_scale/__init__.py +11 -8
  270. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  271. alita_sdk/tools/zephyr_squad/__init__.py +10 -7
  272. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +154 -8
  273. alita_sdk-0.3.627.dist-info/RECORD +468 -0
  274. alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
  275. alita_sdk-0.3.379.dist-info/RECORD +0 -360
  276. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
  277. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
  278. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,327 @@
1
+ """
2
+ Toolkit testing commands for Alita CLI.
3
+
4
+ Provides commands to list, inspect, and test toolkits directly from the command line.
5
+ """
6
+
7
+ import click
8
+ import json
9
+ import logging
10
+ from typing import Optional, Dict, Any
11
+
12
+ from .cli import get_client
13
+ from .toolkit_loader import load_toolkit_config
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ @click.group()
19
+ def toolkit():
20
+ """Toolkit testing commands."""
21
+ pass
22
+
23
+
24
+ @toolkit.command('list')
25
+ @click.option('--failed', is_flag=True, help='Show failed imports')
26
+ @click.pass_context
27
+ def toolkit_list(ctx, failed: bool):
28
+ """List all available toolkits."""
29
+ formatter = ctx.obj['formatter']
30
+
31
+ try:
32
+ # Import toolkit registry
33
+ from alita_sdk.tools import AVAILABLE_TOOLS, AVAILABLE_TOOLKITS, FAILED_IMPORTS
34
+
35
+ if failed:
36
+ # Show failed imports
37
+ if FAILED_IMPORTS:
38
+ click.echo("\nFailed toolkit imports:\n")
39
+ for name, error in FAILED_IMPORTS.items():
40
+ click.echo(f" - {name}: {error}")
41
+ click.echo(f"\nTotal failed: {len(FAILED_IMPORTS)}")
42
+ else:
43
+ click.echo("\nNo failed imports")
44
+ return
45
+
46
+ # Build toolkit list
47
+ toolkits = []
48
+ for name, toolkit_dict in AVAILABLE_TOOLS.items():
49
+ toolkit_class_name = None
50
+ if 'toolkit_class' in toolkit_dict:
51
+ toolkit_class_name = toolkit_dict['toolkit_class'].__name__
52
+
53
+ toolkits.append({
54
+ 'name': name,
55
+ 'class_name': toolkit_class_name,
56
+ 'has_get_tools': 'get_tools' in toolkit_dict
57
+ })
58
+
59
+ # Format and display
60
+ output = formatter.format_toolkit_list(toolkits)
61
+ click.echo(output)
62
+
63
+ except Exception as e:
64
+ logger.exception("Failed to list toolkits")
65
+ click.echo(formatter.format_error(str(e)), err=True)
66
+ raise click.Abort()
67
+
68
+
69
+ @toolkit.command('schema')
70
+ @click.argument('toolkit_name')
71
+ @click.pass_context
72
+ def toolkit_schema(ctx, toolkit_name: str):
73
+ """
74
+ Show configuration schema for a toolkit.
75
+
76
+ TOOLKIT_NAME: Name of the toolkit (e.g., 'jira', 'github', 'confluence')
77
+ """
78
+ formatter = ctx.obj['formatter']
79
+
80
+ try:
81
+ # Import toolkit registry
82
+ from alita_sdk.tools import AVAILABLE_TOOLKITS
83
+
84
+ # Find toolkit class
85
+ toolkit_class = None
86
+ for name, cls in AVAILABLE_TOOLKITS.items():
87
+ if name.lower().replace('toolkit', '').replace('alita', '').strip() == toolkit_name.lower():
88
+ toolkit_class = cls
89
+ break
90
+
91
+ if not toolkit_class:
92
+ # Try direct match
93
+ for name, cls in AVAILABLE_TOOLKITS.items():
94
+ if toolkit_name.lower() in name.lower():
95
+ toolkit_class = cls
96
+ break
97
+
98
+ if not toolkit_class:
99
+ available = [name.lower().replace('toolkit', '').replace('alita', '').strip()
100
+ for name in AVAILABLE_TOOLKITS.keys()]
101
+ raise click.ClickException(
102
+ f"Toolkit '{toolkit_name}' not found.\n"
103
+ f"Available toolkits: {', '.join(sorted(set(available)))}"
104
+ )
105
+
106
+ # Get schema
107
+ schema_model = toolkit_class.toolkit_config_schema()
108
+ schema = schema_model.model_json_schema()
109
+
110
+ # Format and display
111
+ if formatter.__class__.__name__ == 'JSONFormatter':
112
+ output = formatter.format_toolkit_schema(toolkit_name, schema)
113
+ else:
114
+ output = formatter.format_toolkit_schema(toolkit_name, schema)
115
+
116
+ click.echo(output)
117
+
118
+ except click.ClickException:
119
+ raise
120
+ except Exception as e:
121
+ logger.exception(f"Failed to get schema for toolkit '{toolkit_name}'")
122
+ click.echo(formatter.format_error(str(e)), err=True)
123
+ raise click.Abort()
124
+
125
+
126
+ @toolkit.command('test')
127
+ @click.argument('toolkit_type')
128
+ @click.option('--tool', required=True, help='Tool name to execute')
129
+ @click.option('--config', 'config_file', type=click.File('r'),
130
+ help='Toolkit configuration JSON file')
131
+ @click.option('--params', type=click.File('r'),
132
+ help='Tool parameters JSON file')
133
+ @click.option('--param', multiple=True,
134
+ help='Tool parameter as key=value (can be used multiple times)')
135
+ @click.option('--llm-model', default='gpt-4o-mini',
136
+ help='LLM model to use (default: gpt-4o-mini)')
137
+ @click.option('--temperature', default=0.1, type=float,
138
+ help='LLM temperature (default: 0.1)')
139
+ @click.option('--max-tokens', default=1000, type=int,
140
+ help='LLM max tokens (default: 1000)')
141
+ @click.pass_context
142
+ def toolkit_test(ctx, toolkit_type: str, tool: str, config_file, params,
143
+ param, llm_model: str, temperature: float, max_tokens: int):
144
+ """Test a specific tool from a toolkit.
145
+
146
+ TOOLKIT_TYPE: Type of toolkit (e.g., 'jira', 'github', 'confluence')
147
+
148
+ \b
149
+ Examples:
150
+ alita toolkit test jira --tool get_issue --config jira.json --params params.json
151
+ alita toolkit test jira --tool get_issue --config jira.json --param issue_key=PROJ-123
152
+ alita -o json toolkit test github --tool get_issue --config github.json
153
+ """
154
+ formatter = ctx.obj['formatter']
155
+ client = get_client(ctx)
156
+
157
+ try:
158
+ # Load toolkit configuration
159
+ toolkit_config = {}
160
+ if config_file:
161
+ toolkit_config = load_toolkit_config(config_file.name)
162
+ logger.debug(f"Loaded toolkit config from {config_file.name}")
163
+
164
+ # Add the tool to selected_tools in the config
165
+ if 'selected_tools' not in toolkit_config:
166
+ toolkit_config['selected_tools'] = []
167
+ if tool not in toolkit_config['selected_tools']:
168
+ toolkit_config['selected_tools'].append(tool)
169
+
170
+ # Load tool parameters
171
+ tool_params = {}
172
+ if params:
173
+ tool_params = json.load(params)
174
+ logger.debug(f"Loaded tool params from {params.name}")
175
+
176
+ # Parse inline parameters
177
+ if param:
178
+ for param_pair in param:
179
+ if '=' not in param_pair:
180
+ raise click.ClickException(
181
+ f"Invalid parameter format: '{param_pair}'. "
182
+ "Use --param key=value"
183
+ )
184
+ key, value = param_pair.split('=', 1)
185
+
186
+ # Try to parse as JSON for complex values
187
+ try:
188
+ tool_params[key] = json.loads(value)
189
+ except json.JSONDecodeError:
190
+ tool_params[key] = value
191
+
192
+ logger.debug(f"Parsed {len(param)} inline parameters")
193
+
194
+ # Prepare full toolkit configuration
195
+ full_config = {
196
+ 'toolkit_name': toolkit_type,
197
+ 'type': toolkit_type,
198
+ 'settings': toolkit_config
199
+ }
200
+
201
+ # LLM configuration
202
+ llm_config = {
203
+ 'temperature': temperature,
204
+ 'max_tokens': max_tokens,
205
+ }
206
+
207
+ # Execute test
208
+ logger.info(f"Testing tool '{tool}' from toolkit '{toolkit_type}'")
209
+ result = client.test_toolkit_tool(
210
+ toolkit_config=full_config,
211
+ tool_name=tool,
212
+ tool_params=tool_params,
213
+ llm_model=llm_model,
214
+ llm_config=llm_config
215
+ )
216
+
217
+ # Format and display result
218
+ output = formatter.format_toolkit_result(result)
219
+ click.echo(output)
220
+
221
+ # Exit with error code if test failed
222
+ if not result.get('success', False):
223
+ raise click.Abort()
224
+
225
+ except click.ClickException:
226
+ raise
227
+ except Exception as e:
228
+ logger.exception("Failed to execute toolkit test")
229
+ click.echo(formatter.format_error(str(e)), err=True)
230
+ raise click.Abort()
231
+
232
+
233
+ @toolkit.command('tools')
234
+ @click.argument('toolkit_type')
235
+ @click.option('--config', 'config_file', type=click.File('r'),
236
+ help='Toolkit configuration JSON file (required for some toolkits)')
237
+ @click.pass_context
238
+ def toolkit_tools(ctx, toolkit_type: str, config_file):
239
+ """
240
+ List available tools for a specific toolkit.
241
+
242
+ TOOLKIT_TYPE: Type of toolkit (e.g., 'jira', 'github', 'confluence')
243
+
244
+ Some toolkits require configuration to determine available tools.
245
+ Use --config to provide configuration if needed.
246
+ """
247
+ formatter = ctx.obj['formatter']
248
+ client = get_client(ctx)
249
+
250
+ try:
251
+ # Load toolkit configuration if provided
252
+ toolkit_config = {}
253
+ if config_file:
254
+ toolkit_config = load_toolkit_config(config_file.name)
255
+
256
+ # Import and instantiate toolkit
257
+ from alita_sdk.tools import AVAILABLE_TOOLS
258
+
259
+ if toolkit_type not in AVAILABLE_TOOLS:
260
+ raise click.ClickException(
261
+ f"Toolkit '{toolkit_type}' not found. "
262
+ f"Use 'alita-cli toolkit list' to see available toolkits."
263
+ )
264
+
265
+ toolkit_entry = AVAILABLE_TOOLS[toolkit_type]
266
+
267
+ if 'toolkit_class' not in toolkit_entry:
268
+ raise click.ClickException(
269
+ f"Toolkit '{toolkit_type}' does not support tool listing"
270
+ )
271
+
272
+ # Get toolkit class and instantiate
273
+ toolkit_class = toolkit_entry['toolkit_class']
274
+
275
+ # Create minimal configuration
276
+ full_config = {
277
+ 'toolkit_name': toolkit_type,
278
+ 'type': toolkit_type,
279
+ 'settings': toolkit_config
280
+ }
281
+
282
+ # Try to get available tools via API wrapper
283
+ try:
284
+ # Instantiate API wrapper if possible
285
+ api_wrapper_class = None
286
+
287
+ # Find API wrapper class by inspecting toolkit
288
+ import inspect
289
+ for name, obj in inspect.getmembers(toolkit_class):
290
+ if inspect.isclass(obj) and 'ApiWrapper' in obj.__name__:
291
+ api_wrapper_class = obj
292
+ break
293
+
294
+ if api_wrapper_class:
295
+ try:
296
+ api_wrapper = api_wrapper_class(**toolkit_config)
297
+ available_tools = api_wrapper.get_available_tools()
298
+
299
+ # Format tools list
300
+ click.echo(f"\nAvailable tools for {toolkit_type}:\n")
301
+ for tool_info in available_tools:
302
+ tool_name = tool_info.get('name', 'unknown')
303
+ description = tool_info.get('description', '')
304
+ click.echo(f" - {tool_name}")
305
+ if description:
306
+ click.echo(f" {description}")
307
+
308
+ click.echo(f"\nTotal: {len(available_tools)} tools")
309
+ return
310
+
311
+ except Exception as e:
312
+ logger.debug(f"Could not instantiate API wrapper: {e}")
313
+
314
+ # Fallback: show general info
315
+ click.echo(f"\n{toolkit_type} toolkit is available")
316
+ click.echo("Use 'alita-cli toolkit schema {toolkit_type}' to see configuration options")
317
+
318
+ except Exception as e:
319
+ logger.exception("Failed to list tools")
320
+ raise click.ClickException(str(e))
321
+
322
+ except click.ClickException:
323
+ raise
324
+ except Exception as e:
325
+ logger.exception("Failed to list toolkit tools")
326
+ click.echo(formatter.format_error(str(e)), err=True)
327
+ raise click.Abort()
@@ -0,0 +1,85 @@
1
+ """
2
+ Toolkit configuration loading and management.
3
+
4
+ Handles loading toolkit configurations from JSON/YAML files.
5
+ """
6
+
7
+ import json
8
+ import yaml
9
+ from pathlib import Path
10
+ from typing import Dict, Any, List
11
+
12
+ from .config import substitute_env_vars
13
+
14
+
15
+ # All available tools in the inventory toolkit
16
+ INVENTORY_TOOLS = [
17
+ "search_graph",
18
+ "get_entity",
19
+ "get_entity_content",
20
+ "impact_analysis",
21
+ "get_related_entities",
22
+ "get_stats",
23
+ "get_citations",
24
+ "list_entities_by_type",
25
+ "list_entities_by_layer",
26
+ ]
27
+
28
+
29
+ def load_toolkit_config(file_path: str) -> Dict[str, Any]:
30
+ """Load toolkit configuration from JSON or YAML file with env var substitution."""
31
+ path = Path(file_path)
32
+
33
+ if not path.exists():
34
+ raise FileNotFoundError(f"Toolkit configuration not found: {file_path}")
35
+
36
+ with open(path) as f:
37
+ content = f.read()
38
+
39
+ # Apply environment variable substitution
40
+ content = substitute_env_vars(content)
41
+
42
+ # Parse based on file extension
43
+ if path.suffix in ['.yaml', '.yml']:
44
+ return yaml.safe_load(content)
45
+ else:
46
+ return json.loads(content)
47
+
48
+
49
+ def load_toolkit_configs(agent_def: Dict[str, Any], toolkit_config_paths: tuple) -> List[Dict[str, Any]]:
50
+ """Load all toolkit configurations from agent definition and CLI options.
51
+
52
+ Args:
53
+ agent_def: Agent definition dictionary
54
+ toolkit_config_paths: Tuple of file paths or dict configs from CLI options
55
+
56
+ Returns:
57
+ List of toolkit configuration dictionaries
58
+ """
59
+ toolkit_configs = []
60
+
61
+ # Load from agent definition if present
62
+ if 'toolkit_configs' in agent_def:
63
+ for tk_config in agent_def['toolkit_configs']:
64
+ if isinstance(tk_config, dict):
65
+ if 'file' in tk_config:
66
+ config = load_toolkit_config(tk_config['file'])
67
+ toolkit_configs.append(config)
68
+ elif 'config' in tk_config:
69
+ toolkit_configs.append(tk_config['config'])
70
+ elif 'type' in tk_config:
71
+ # Direct toolkit config (e.g., {'type': 'inventory', ...})
72
+ toolkit_configs.append(tk_config)
73
+
74
+ # Load from CLI options - can be file paths (str) or dict configs
75
+ if toolkit_config_paths:
76
+ for config_item in toolkit_config_paths:
77
+ if isinstance(config_item, dict):
78
+ # Direct config dict (e.g., from /inventory command)
79
+ toolkit_configs.append(config_item)
80
+ elif isinstance(config_item, str):
81
+ # File path
82
+ config = load_toolkit_config(config_item)
83
+ toolkit_configs.append(config)
84
+
85
+ return toolkit_configs
@@ -0,0 +1,43 @@
1
+ """
2
+ CLI tools package.
3
+
4
+ Contains specialized tools for CLI agents.
5
+ """
6
+
7
+ from .filesystem import get_filesystem_tools, FilesystemApiWrapper
8
+ from .terminal import get_terminal_tools, create_default_blocked_patterns_file
9
+ from .planning import (
10
+ get_planning_tools,
11
+ PlanState,
12
+ list_sessions,
13
+ generate_session_id,
14
+ create_session_memory,
15
+ save_session_metadata,
16
+ load_session_metadata,
17
+ update_session_metadata,
18
+ get_session_dir,
19
+ to_portable_path,
20
+ from_portable_path,
21
+ )
22
+ from .approval import create_approval_wrapper, ApprovalToolWrapper, prompt_approval
23
+
24
+ __all__ = [
25
+ 'get_filesystem_tools',
26
+ 'FilesystemApiWrapper',
27
+ 'get_terminal_tools',
28
+ 'create_default_blocked_patterns_file',
29
+ 'get_planning_tools',
30
+ 'PlanState',
31
+ 'list_sessions',
32
+ 'generate_session_id',
33
+ 'create_session_memory',
34
+ 'save_session_metadata',
35
+ 'load_session_metadata',
36
+ 'update_session_metadata',
37
+ 'get_session_dir',
38
+ 'to_portable_path',
39
+ 'from_portable_path',
40
+ 'create_approval_wrapper',
41
+ 'ApprovalToolWrapper',
42
+ 'prompt_approval',
43
+ ]
@@ -0,0 +1,224 @@
1
+ """
2
+ Tool approval wrapper for CLI.
3
+
4
+ Wraps tools to require user approval before execution based on approval mode.
5
+ Modes:
6
+ - 'always': Always require approval for each tool call
7
+ - 'auto': No approval required (automatic execution)
8
+ - 'yolo': No approval and no safety checks (use with caution)
9
+ """
10
+
11
+ import functools
12
+ from typing import Any, Callable, Dict, List, Optional, Set
13
+ from rich.console import Console
14
+ from rich.panel import Panel
15
+ from rich.table import Table
16
+ from rich import box
17
+ from rich.text import Text
18
+ import json
19
+
20
+ from langchain_core.tools import BaseTool, StructuredTool, Tool
21
+
22
+ console = Console()
23
+
24
+ # Tools that always require approval in 'always' mode (dangerous built-in operations)
25
+ # These are the built-in CLI tools that modify the filesystem or execute commands
26
+ DANGEROUS_TOOLS = {
27
+ 'terminal_run_command', # Shell command execution
28
+ 'write_file',
29
+ 'create_file',
30
+ 'delete_file',
31
+ 'move_file',
32
+ 'copy_file',
33
+ 'create_directory',
34
+ }
35
+
36
+ # Note: Tools NOT in DANGEROUS_TOOLS are auto-approved, including:
37
+ # - Read-only filesystem tools (read_file, list_directory, etc.)
38
+ # - User-added toolkit tools (via --toolkit_config or /add_toolkit)
39
+ # - MCP server tools (via --mcp or /add_mcp)
40
+ #
41
+ # The assumption is that users explicitly add toolkits they trust.
42
+ # Only built-in tools that can modify files or run commands require approval.
43
+
44
+
45
+ def prompt_approval(tool_name: str, tool_args: Dict[str, Any], approval_mode: str) -> bool:
46
+ """
47
+ Prompt user for approval before executing a tool.
48
+
49
+ Args:
50
+ tool_name: Name of the tool to execute
51
+ tool_args: Arguments to pass to the tool
52
+ approval_mode: Current approval mode ('always', 'auto', 'yolo')
53
+
54
+ Returns:
55
+ True if approved, False if rejected
56
+ """
57
+ # Auto mode - always approve
58
+ if approval_mode == 'auto':
59
+ return True
60
+
61
+ # Yolo mode - always approve, no questions asked
62
+ if approval_mode == 'yolo':
63
+ return True
64
+
65
+ # Always mode - only prompt for dangerous built-in tools
66
+ # User-added toolkits and MCP tools are auto-approved (user explicitly added them)
67
+ if tool_name not in DANGEROUS_TOOLS:
68
+ return True
69
+
70
+ # Create approval prompt panel for dangerous tools
71
+ console.print()
72
+
73
+ # Build args display
74
+ args_content = []
75
+ for key, value in tool_args.items():
76
+ if isinstance(value, str) and len(value) > 100:
77
+ display_value = value[:100] + "..."
78
+ elif isinstance(value, (dict, list)):
79
+ try:
80
+ formatted = json.dumps(value, indent=2)
81
+ if len(formatted) > 200:
82
+ formatted = formatted[:200] + "..."
83
+ display_value = formatted
84
+ except:
85
+ display_value = str(value)[:100]
86
+ else:
87
+ display_value = str(value)
88
+ args_content.append(f" [cyan]{key}[/cyan]: {display_value}")
89
+
90
+ args_text = "\n".join(args_content) if args_content else " (no arguments)"
91
+
92
+ # All tools reaching here are dangerous (in DANGEROUS_TOOLS)
93
+ icon = "⚠️"
94
+ border_style = "yellow"
95
+ title_style = "bold yellow"
96
+
97
+ panel = Panel(
98
+ Text.from_markup(f"[bold]{tool_name}[/bold]\n\n[dim]Arguments:[/dim]\n{args_text}"),
99
+ title=f"[{title_style}]{icon} Approve Tool Call?[/{title_style}]",
100
+ title_align="left",
101
+ subtitle="[dim][y]es / [n]o / [a]uto-approve / [q]uit[/dim]",
102
+ subtitle_align="right",
103
+ border_style=border_style,
104
+ box=box.ROUNDED,
105
+ padding=(1, 2),
106
+ )
107
+ console.print(panel)
108
+
109
+ # Get user input
110
+ while True:
111
+ try:
112
+ response = input("→ ").strip().lower()
113
+ except (KeyboardInterrupt, EOFError):
114
+ console.print("\n[yellow]Cancelled[/yellow]")
115
+ return False
116
+
117
+ if response in ('y', 'yes', ''):
118
+ console.print("[green]✓ Approved[/green]")
119
+ return True
120
+ elif response in ('n', 'no'):
121
+ console.print("[red]✗ Rejected[/red]")
122
+ return False
123
+ elif response in ('a', 'auto'):
124
+ console.print("[cyan]→ Switching to auto mode for this session[/cyan]")
125
+ # Signal to switch to auto mode
126
+ return 'switch_auto'
127
+ elif response in ('q', 'quit'):
128
+ console.print("[yellow]Quitting...[/yellow]")
129
+ raise KeyboardInterrupt()
130
+ else:
131
+ console.print("[dim]Please enter y/n/a/q[/dim]")
132
+
133
+
134
+ class ApprovalToolWrapper:
135
+ """
136
+ Wrapper that adds approval prompts to tools based on approval mode.
137
+
138
+ This wrapper intercepts tool calls and prompts for user approval
139
+ before executing dangerous operations.
140
+ """
141
+
142
+ def __init__(self, approval_mode_ref: List[str]):
143
+ """
144
+ Initialize the approval wrapper.
145
+
146
+ Args:
147
+ approval_mode_ref: A mutable list containing the current approval mode.
148
+ Using a list allows the mode to be changed externally.
149
+ access as approval_mode_ref[0]
150
+ """
151
+ self.approval_mode_ref = approval_mode_ref
152
+
153
+ @property
154
+ def approval_mode(self) -> str:
155
+ """Get current approval mode."""
156
+ return self.approval_mode_ref[0] if self.approval_mode_ref else 'always'
157
+
158
+ def wrap_tool(self, tool: BaseTool) -> BaseTool:
159
+ """
160
+ Wrap a tool to add approval prompts.
161
+
162
+ Args:
163
+ tool: The tool to wrap
164
+
165
+ Returns:
166
+ Wrapped tool with approval logic
167
+ """
168
+ original_func = tool.func if hasattr(tool, 'func') else tool._run
169
+ tool_name = tool.name
170
+ wrapper_self = self
171
+
172
+ @functools.wraps(original_func)
173
+ def wrapped_func(*args, **kwargs):
174
+ # Get approval
175
+ approval = prompt_approval(tool_name, kwargs, wrapper_self.approval_mode)
176
+
177
+ if approval == 'switch_auto':
178
+ # Switch to auto mode
179
+ wrapper_self.approval_mode_ref[0] = 'auto'
180
+ console.print("[cyan]Mode switched to 'auto' - tools will auto-approve[/cyan]")
181
+ elif not approval:
182
+ return f"Tool execution rejected by user"
183
+
184
+ # Execute the tool
185
+ return original_func(*args, **kwargs)
186
+
187
+ # Create new tool with wrapped function
188
+ if isinstance(tool, StructuredTool):
189
+ return StructuredTool(
190
+ name=tool.name,
191
+ description=tool.description,
192
+ func=wrapped_func,
193
+ args_schema=tool.args_schema,
194
+ return_direct=tool.return_direct,
195
+ )
196
+ else:
197
+ # Clone the tool with wrapped function
198
+ tool.func = wrapped_func
199
+ return tool
200
+
201
+ def wrap_tools(self, tools: List[BaseTool]) -> List[BaseTool]:
202
+ """
203
+ Wrap multiple tools with approval logic.
204
+
205
+ Args:
206
+ tools: List of tools to wrap
207
+
208
+ Returns:
209
+ List of wrapped tools
210
+ """
211
+ return [self.wrap_tool(tool) for tool in tools]
212
+
213
+
214
+ def create_approval_wrapper(approval_mode_ref: List[str]) -> ApprovalToolWrapper:
215
+ """
216
+ Create an approval wrapper with a reference to the current mode.
217
+
218
+ Args:
219
+ approval_mode_ref: Mutable list containing current approval mode [0]
220
+
221
+ Returns:
222
+ ApprovalToolWrapper instance
223
+ """
224
+ return ApprovalToolWrapper(approval_mode_ref)