alita-sdk 0.3.462__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 (261) hide show
  1. alita_sdk/cli/agent/__init__.py +5 -0
  2. alita_sdk/cli/agent/default.py +258 -0
  3. alita_sdk/cli/agent_executor.py +15 -3
  4. alita_sdk/cli/agent_loader.py +56 -8
  5. alita_sdk/cli/agent_ui.py +93 -31
  6. alita_sdk/cli/agents.py +2274 -230
  7. alita_sdk/cli/callbacks.py +96 -25
  8. alita_sdk/cli/cli.py +10 -1
  9. alita_sdk/cli/config.py +162 -9
  10. alita_sdk/cli/context/__init__.py +30 -0
  11. alita_sdk/cli/context/cleanup.py +198 -0
  12. alita_sdk/cli/context/manager.py +731 -0
  13. alita_sdk/cli/context/message.py +285 -0
  14. alita_sdk/cli/context/strategies.py +289 -0
  15. alita_sdk/cli/context/token_estimation.py +127 -0
  16. alita_sdk/cli/input_handler.py +419 -0
  17. alita_sdk/cli/inventory.py +1073 -0
  18. alita_sdk/cli/testcases/__init__.py +94 -0
  19. alita_sdk/cli/testcases/data_generation.py +119 -0
  20. alita_sdk/cli/testcases/discovery.py +96 -0
  21. alita_sdk/cli/testcases/executor.py +84 -0
  22. alita_sdk/cli/testcases/logger.py +85 -0
  23. alita_sdk/cli/testcases/parser.py +172 -0
  24. alita_sdk/cli/testcases/prompts.py +91 -0
  25. alita_sdk/cli/testcases/reporting.py +125 -0
  26. alita_sdk/cli/testcases/setup.py +108 -0
  27. alita_sdk/cli/testcases/test_runner.py +282 -0
  28. alita_sdk/cli/testcases/utils.py +39 -0
  29. alita_sdk/cli/testcases/validation.py +90 -0
  30. alita_sdk/cli/testcases/workflow.py +196 -0
  31. alita_sdk/cli/toolkit.py +14 -17
  32. alita_sdk/cli/toolkit_loader.py +35 -5
  33. alita_sdk/cli/tools/__init__.py +36 -2
  34. alita_sdk/cli/tools/approval.py +224 -0
  35. alita_sdk/cli/tools/filesystem.py +910 -64
  36. alita_sdk/cli/tools/planning.py +389 -0
  37. alita_sdk/cli/tools/terminal.py +414 -0
  38. alita_sdk/community/__init__.py +72 -12
  39. alita_sdk/community/inventory/__init__.py +236 -0
  40. alita_sdk/community/inventory/config.py +257 -0
  41. alita_sdk/community/inventory/enrichment.py +2137 -0
  42. alita_sdk/community/inventory/extractors.py +1469 -0
  43. alita_sdk/community/inventory/ingestion.py +3172 -0
  44. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  45. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  46. alita_sdk/community/inventory/parsers/base.py +295 -0
  47. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  48. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  49. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  50. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  51. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  52. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  53. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  54. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  55. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  56. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  57. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  58. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  59. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  60. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  61. alita_sdk/community/inventory/patterns/loader.py +348 -0
  62. alita_sdk/community/inventory/patterns/registry.py +198 -0
  63. alita_sdk/community/inventory/presets.py +535 -0
  64. alita_sdk/community/inventory/retrieval.py +1403 -0
  65. alita_sdk/community/inventory/toolkit.py +173 -0
  66. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  67. alita_sdk/community/inventory/visualize.py +1370 -0
  68. alita_sdk/configurations/__init__.py +1 -1
  69. alita_sdk/configurations/ado.py +141 -20
  70. alita_sdk/configurations/bitbucket.py +0 -3
  71. alita_sdk/configurations/confluence.py +76 -42
  72. alita_sdk/configurations/figma.py +76 -0
  73. alita_sdk/configurations/gitlab.py +17 -5
  74. alita_sdk/configurations/openapi.py +329 -0
  75. alita_sdk/configurations/qtest.py +72 -1
  76. alita_sdk/configurations/report_portal.py +96 -0
  77. alita_sdk/configurations/sharepoint.py +148 -0
  78. alita_sdk/configurations/testio.py +83 -0
  79. alita_sdk/runtime/clients/artifact.py +3 -3
  80. alita_sdk/runtime/clients/client.py +353 -48
  81. alita_sdk/runtime/clients/sandbox_client.py +0 -21
  82. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  83. alita_sdk/runtime/langchain/assistant.py +123 -26
  84. alita_sdk/runtime/langchain/constants.py +642 -1
  85. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  86. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  87. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +6 -3
  88. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
  89. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  90. alita_sdk/runtime/langchain/document_loaders/constants.py +12 -7
  91. alita_sdk/runtime/langchain/langraph_agent.py +279 -73
  92. alita_sdk/runtime/langchain/utils.py +82 -15
  93. alita_sdk/runtime/llms/preloaded.py +2 -6
  94. alita_sdk/runtime/skills/__init__.py +91 -0
  95. alita_sdk/runtime/skills/callbacks.py +498 -0
  96. alita_sdk/runtime/skills/discovery.py +540 -0
  97. alita_sdk/runtime/skills/executor.py +610 -0
  98. alita_sdk/runtime/skills/input_builder.py +371 -0
  99. alita_sdk/runtime/skills/models.py +330 -0
  100. alita_sdk/runtime/skills/registry.py +355 -0
  101. alita_sdk/runtime/skills/skill_runner.py +330 -0
  102. alita_sdk/runtime/toolkits/__init__.py +7 -0
  103. alita_sdk/runtime/toolkits/application.py +21 -9
  104. alita_sdk/runtime/toolkits/artifact.py +15 -5
  105. alita_sdk/runtime/toolkits/datasource.py +13 -6
  106. alita_sdk/runtime/toolkits/mcp.py +139 -251
  107. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  108. alita_sdk/runtime/toolkits/planning.py +178 -0
  109. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  110. alita_sdk/runtime/toolkits/subgraph.py +251 -6
  111. alita_sdk/runtime/toolkits/tools.py +238 -32
  112. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  113. alita_sdk/runtime/tools/__init__.py +3 -1
  114. alita_sdk/runtime/tools/application.py +20 -6
  115. alita_sdk/runtime/tools/artifact.py +511 -28
  116. alita_sdk/runtime/tools/data_analysis.py +183 -0
  117. alita_sdk/runtime/tools/function.py +43 -15
  118. alita_sdk/runtime/tools/image_generation.py +50 -44
  119. alita_sdk/runtime/tools/llm.py +852 -67
  120. alita_sdk/runtime/tools/loop.py +3 -1
  121. alita_sdk/runtime/tools/loop_output.py +3 -1
  122. alita_sdk/runtime/tools/mcp_remote_tool.py +25 -10
  123. alita_sdk/runtime/tools/mcp_server_tool.py +7 -6
  124. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  125. alita_sdk/runtime/tools/planning/models.py +246 -0
  126. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  127. alita_sdk/runtime/tools/router.py +2 -4
  128. alita_sdk/runtime/tools/sandbox.py +9 -6
  129. alita_sdk/runtime/tools/skill_router.py +776 -0
  130. alita_sdk/runtime/tools/tool.py +3 -1
  131. alita_sdk/runtime/tools/vectorstore.py +7 -2
  132. alita_sdk/runtime/tools/vectorstore_base.py +51 -11
  133. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  134. alita_sdk/runtime/utils/constants.py +5 -1
  135. alita_sdk/runtime/utils/mcp_client.py +492 -0
  136. alita_sdk/runtime/utils/mcp_oauth.py +202 -5
  137. alita_sdk/runtime/utils/mcp_sse_client.py +36 -7
  138. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  139. alita_sdk/runtime/utils/serialization.py +155 -0
  140. alita_sdk/runtime/utils/streamlit.py +6 -10
  141. alita_sdk/runtime/utils/toolkit_utils.py +16 -5
  142. alita_sdk/runtime/utils/utils.py +36 -0
  143. alita_sdk/tools/__init__.py +113 -29
  144. alita_sdk/tools/ado/repos/__init__.py +51 -33
  145. alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
  146. alita_sdk/tools/ado/test_plan/__init__.py +25 -9
  147. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  148. alita_sdk/tools/ado/utils.py +1 -18
  149. alita_sdk/tools/ado/wiki/__init__.py +25 -8
  150. alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
  151. alita_sdk/tools/ado/work_item/__init__.py +26 -9
  152. alita_sdk/tools/ado/work_item/ado_wrapper.py +56 -3
  153. alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
  154. alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
  155. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  156. alita_sdk/tools/azure_ai/search/__init__.py +11 -8
  157. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  158. alita_sdk/tools/base/tool.py +5 -1
  159. alita_sdk/tools/base_indexer_toolkit.py +170 -45
  160. alita_sdk/tools/bitbucket/__init__.py +17 -12
  161. alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
  162. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
  163. alita_sdk/tools/browser/__init__.py +5 -4
  164. alita_sdk/tools/carrier/__init__.py +5 -6
  165. alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
  166. alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
  167. alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
  168. alita_sdk/tools/chunkers/__init__.py +3 -1
  169. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  170. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  171. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  172. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  173. alita_sdk/tools/cloud/aws/__init__.py +10 -7
  174. alita_sdk/tools/cloud/azure/__init__.py +10 -7
  175. alita_sdk/tools/cloud/gcp/__init__.py +10 -7
  176. alita_sdk/tools/cloud/k8s/__init__.py +10 -7
  177. alita_sdk/tools/code/linter/__init__.py +10 -8
  178. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  179. alita_sdk/tools/code/sonar/__init__.py +10 -7
  180. alita_sdk/tools/code_indexer_toolkit.py +73 -23
  181. alita_sdk/tools/confluence/__init__.py +21 -15
  182. alita_sdk/tools/confluence/api_wrapper.py +78 -23
  183. alita_sdk/tools/confluence/loader.py +4 -2
  184. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  185. alita_sdk/tools/elastic/__init__.py +11 -8
  186. alita_sdk/tools/elitea_base.py +493 -30
  187. alita_sdk/tools/figma/__init__.py +58 -11
  188. alita_sdk/tools/figma/api_wrapper.py +1235 -143
  189. alita_sdk/tools/figma/figma_client.py +73 -0
  190. alita_sdk/tools/figma/toon_tools.py +2748 -0
  191. alita_sdk/tools/github/__init__.py +13 -14
  192. alita_sdk/tools/github/github_client.py +224 -100
  193. alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
  194. alita_sdk/tools/github/schemas.py +14 -5
  195. alita_sdk/tools/github/tool.py +5 -1
  196. alita_sdk/tools/github/tool_prompts.py +9 -22
  197. alita_sdk/tools/gitlab/__init__.py +15 -11
  198. alita_sdk/tools/gitlab/api_wrapper.py +207 -41
  199. alita_sdk/tools/gitlab_org/__init__.py +10 -8
  200. alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
  201. alita_sdk/tools/google/bigquery/__init__.py +13 -12
  202. alita_sdk/tools/google/bigquery/tool.py +5 -1
  203. alita_sdk/tools/google_places/__init__.py +10 -8
  204. alita_sdk/tools/google_places/api_wrapper.py +1 -1
  205. alita_sdk/tools/jira/__init__.py +17 -11
  206. alita_sdk/tools/jira/api_wrapper.py +91 -40
  207. alita_sdk/tools/keycloak/__init__.py +11 -8
  208. alita_sdk/tools/localgit/__init__.py +9 -3
  209. alita_sdk/tools/localgit/local_git.py +62 -54
  210. alita_sdk/tools/localgit/tool.py +5 -1
  211. alita_sdk/tools/memory/__init__.py +11 -3
  212. alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
  213. alita_sdk/tools/ocr/__init__.py +11 -8
  214. alita_sdk/tools/openapi/__init__.py +490 -114
  215. alita_sdk/tools/openapi/api_wrapper.py +1368 -0
  216. alita_sdk/tools/openapi/tool.py +20 -0
  217. alita_sdk/tools/pandas/__init__.py +20 -12
  218. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  219. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  220. alita_sdk/tools/postman/__init__.py +11 -11
  221. alita_sdk/tools/pptx/__init__.py +10 -9
  222. alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
  223. alita_sdk/tools/qtest/__init__.py +30 -10
  224. alita_sdk/tools/qtest/api_wrapper.py +430 -13
  225. alita_sdk/tools/rally/__init__.py +10 -8
  226. alita_sdk/tools/rally/api_wrapper.py +1 -1
  227. alita_sdk/tools/report_portal/__init__.py +12 -9
  228. alita_sdk/tools/salesforce/__init__.py +10 -9
  229. alita_sdk/tools/servicenow/__init__.py +17 -14
  230. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  231. alita_sdk/tools/sharepoint/__init__.py +10 -8
  232. alita_sdk/tools/sharepoint/api_wrapper.py +4 -4
  233. alita_sdk/tools/slack/__init__.py +10 -8
  234. alita_sdk/tools/slack/api_wrapper.py +2 -2
  235. alita_sdk/tools/sql/__init__.py +11 -9
  236. alita_sdk/tools/testio/__init__.py +10 -8
  237. alita_sdk/tools/testrail/__init__.py +11 -8
  238. alita_sdk/tools/testrail/api_wrapper.py +1 -1
  239. alita_sdk/tools/utils/__init__.py +9 -4
  240. alita_sdk/tools/utils/content_parser.py +77 -3
  241. alita_sdk/tools/utils/text_operations.py +410 -0
  242. alita_sdk/tools/utils/tool_prompts.py +79 -0
  243. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +17 -13
  244. alita_sdk/tools/xray/__init__.py +12 -9
  245. alita_sdk/tools/yagmail/__init__.py +9 -3
  246. alita_sdk/tools/zephyr/__init__.py +9 -7
  247. alita_sdk/tools/zephyr_enterprise/__init__.py +11 -8
  248. alita_sdk/tools/zephyr_essential/__init__.py +10 -8
  249. alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
  250. alita_sdk/tools/zephyr_essential/client.py +2 -2
  251. alita_sdk/tools/zephyr_scale/__init__.py +11 -9
  252. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  253. alita_sdk/tools/zephyr_squad/__init__.py +10 -8
  254. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +147 -7
  255. alita_sdk-0.3.627.dist-info/RECORD +468 -0
  256. alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
  257. alita_sdk-0.3.462.dist-info/RECORD +0 -384
  258. alita_sdk-0.3.462.dist-info/entry_points.txt +0 -2
  259. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
  260. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
  261. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,768 @@
1
+ """
2
+ Kotlin Parser - Regex-based parser for Kotlin source files.
3
+
4
+ Extracts symbols and relationships from .kt and .kts files using
5
+ comprehensive regex patterns. Supports Kotlin-specific features like
6
+ data classes, sealed classes, companion objects, extension functions,
7
+ and coroutines.
8
+ """
9
+
10
+ import re
11
+ from typing import Dict, List, Optional, Set, Tuple, Union, Any
12
+ from pathlib import Path
13
+ from concurrent.futures import ThreadPoolExecutor
14
+ import logging
15
+
16
+ from .base import (
17
+ BaseParser,
18
+ ParseResult,
19
+ Symbol,
20
+ Relationship,
21
+ RelationshipType,
22
+ SymbolType,
23
+ Scope,
24
+ Position,
25
+ Range,
26
+ parser_registry,
27
+ )
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ # Comprehensive Kotlin regex patterns
33
+ PATTERNS = {
34
+ # Package declaration
35
+ 'package': re.compile(
36
+ r'^\s*package\s+([\w.]+)',
37
+ re.MULTILINE
38
+ ),
39
+
40
+ # Import patterns
41
+ 'import': re.compile(
42
+ r'^\s*import\s+([\w.]+)(?:\s+as\s+(\w+))?',
43
+ re.MULTILINE
44
+ ),
45
+ 'import_wildcard': re.compile(
46
+ r'^\s*import\s+([\w.]+)\.\*',
47
+ re.MULTILINE
48
+ ),
49
+
50
+ # Class declarations
51
+ 'class': re.compile(
52
+ r'^\s*(?:(public|private|protected|internal)\s+)?'
53
+ r'(?:(abstract|open|final|sealed|data|enum|annotation|inner|value)\s+)*'
54
+ r'class\s+(\w+)'
55
+ r'(?:<[^>]+>)?' # Generic parameters
56
+ r'(?:\s*\([^)]*\))?' # Primary constructor
57
+ r'(?:\s*:\s*([^{]+))?', # Inheritance
58
+ re.MULTILINE
59
+ ),
60
+
61
+ # Interface declarations
62
+ 'interface': re.compile(
63
+ r'^\s*(?:(public|private|protected|internal)\s+)?'
64
+ r'(?:(fun)\s+)?' # fun interface
65
+ r'interface\s+(\w+)'
66
+ r'(?:<[^>]+>)?' # Generic parameters
67
+ r'(?:\s*:\s*([^{]+))?', # Extended interfaces
68
+ re.MULTILINE
69
+ ),
70
+
71
+ # Object declarations (singleton, companion)
72
+ 'object': re.compile(
73
+ r'^\s*(?:(public|private|protected|internal)\s+)?'
74
+ r'(?:(companion)\s+)?'
75
+ r'object\s+(\w+)?'
76
+ r'(?:\s*:\s*([^{]+))?',
77
+ re.MULTILINE
78
+ ),
79
+
80
+ # Function declarations
81
+ 'function': re.compile(
82
+ r'^\s*(?:(public|private|protected|internal|override|open|final|abstract|inline|suspend|tailrec|operator|infix|external)\s+)*'
83
+ r'fun\s+'
84
+ r'(?:<[^>]+>\s+)?' # Generic parameters
85
+ r'(?:(\w+)\.)?' # Extension receiver
86
+ r'(\w+)' # Function name
87
+ r'\s*\([^)]*\)' # Parameters
88
+ r'(?:\s*:\s*([^\n{=]+))?', # Return type
89
+ re.MULTILINE
90
+ ),
91
+
92
+ # Property declarations
93
+ 'property': re.compile(
94
+ r'^\s*(?:(public|private|protected|internal|override|open|final|abstract|lateinit|const)\s+)*'
95
+ r'(val|var)\s+'
96
+ r'(?:(\w+)\.)?' # Extension receiver
97
+ r'(\w+)' # Property name
98
+ r'(?:\s*:\s*([^\n=]+))?' # Type
99
+ r'(?:\s*(?:=|by)\s*([^\n]+))?', # Initializer or delegate
100
+ re.MULTILINE
101
+ ),
102
+
103
+ # Type alias
104
+ 'typealias': re.compile(
105
+ r'^\s*(?:(public|private|protected|internal)\s+)?'
106
+ r'typealias\s+(\w+)'
107
+ r'(?:<[^>]+>)?' # Generic parameters
108
+ r'\s*=\s*([^\n]+)',
109
+ re.MULTILINE
110
+ ),
111
+
112
+ # Annotations
113
+ 'annotation_use': re.compile(
114
+ r'@(\w+)(?:\([^)]*\))?',
115
+ re.MULTILINE
116
+ ),
117
+
118
+ # Function calls
119
+ 'function_call': re.compile(
120
+ r'(?:^|[^\w.])(\w+)\s*\(',
121
+ re.MULTILINE
122
+ ),
123
+
124
+ # Method calls (with receiver)
125
+ 'method_call': re.compile(
126
+ r'\.(\w+)\s*\(',
127
+ re.MULTILINE
128
+ ),
129
+
130
+ # Constructor calls
131
+ 'constructor_call': re.compile(
132
+ r'(?:^|[^\w])([A-Z]\w*)\s*\(',
133
+ re.MULTILINE
134
+ ),
135
+
136
+ # Delegation (by keyword)
137
+ 'delegation': re.compile(
138
+ r'(?:class|interface)\s+\w+[^{]*:\s*[^{]*\s+by\s+(\w+)',
139
+ re.MULTILINE
140
+ ),
141
+
142
+ # Coroutine patterns
143
+ 'coroutine_launch': re.compile(
144
+ r'(?:launch|async|runBlocking|withContext)\s*(?:\([^)]*\))?\s*\{',
145
+ re.MULTILINE
146
+ ),
147
+
148
+ # Generic type usage
149
+ 'generic_usage': re.compile(
150
+ r'<\s*([A-Z]\w*)\s*(?:,\s*[A-Z]\w*)*\s*>',
151
+ re.MULTILINE
152
+ ),
153
+
154
+ # DSL patterns (common Kotlin builders)
155
+ 'dsl_builder': re.compile(
156
+ r'(\w+)\s*\{\s*\n',
157
+ re.MULTILINE
158
+ ),
159
+
160
+ # Destructuring
161
+ 'destructuring': re.compile(
162
+ r'\(\s*(\w+(?:\s*,\s*\w+)*)\s*\)\s*=',
163
+ re.MULTILINE
164
+ ),
165
+
166
+ # KDoc patterns
167
+ 'kdoc': re.compile(
168
+ r'/\*\*\s*([\s\S]*?)\s*\*/',
169
+ re.MULTILINE
170
+ ),
171
+ 'kdoc_reference': re.compile(
172
+ r'\[(\w+(?:\.\w+)*)\]',
173
+ re.MULTILINE
174
+ ),
175
+ }
176
+
177
+
178
+ class KotlinParser(BaseParser):
179
+ """Kotlin source code parser using regex patterns."""
180
+
181
+ # Global symbol registry for cross-file resolution
182
+ _global_symbols: Dict[str, Set[str]] = {}
183
+ _symbol_to_file: Dict[str, str] = {}
184
+
185
+ def __init__(self):
186
+ super().__init__("kotlin")
187
+
188
+ def _get_supported_extensions(self) -> Set[str]:
189
+ return {'.kt', '.kts'}
190
+
191
+ def _make_range(self, content: str, start_offset: int, end_line: int) -> Range:
192
+ """Create a Range object from content and offset."""
193
+ start_line = content[:start_offset].count('\n') + 1
194
+ # Find column (chars since last newline)
195
+ last_newline = content.rfind('\n', 0, start_offset)
196
+ start_col = start_offset - last_newline - 1 if last_newline >= 0 else start_offset
197
+ return Range(
198
+ start=Position(line=start_line, column=start_col),
199
+ end=Position(line=end_line, column=0)
200
+ )
201
+
202
+ def _make_symbol(
203
+ self,
204
+ name: str,
205
+ symbol_type: SymbolType,
206
+ content: str,
207
+ file_path: str,
208
+ start_offset: int,
209
+ end_line: int,
210
+ scope: Scope = Scope.GLOBAL,
211
+ parent: Optional[str] = None,
212
+ full_name: Optional[str] = None,
213
+ docstring: Optional[str] = None,
214
+ visibility: Optional[str] = None,
215
+ is_static: bool = False,
216
+ is_async: bool = False,
217
+ return_type: Optional[str] = None,
218
+ metadata: Optional[Dict[str, Any]] = None
219
+ ) -> Symbol:
220
+ """Create a Symbol with correct fields."""
221
+ return Symbol(
222
+ name=name,
223
+ symbol_type=symbol_type,
224
+ scope=scope,
225
+ range=self._make_range(content, start_offset, end_line),
226
+ file_path=file_path,
227
+ parent_symbol=parent,
228
+ full_name=full_name,
229
+ visibility=visibility,
230
+ is_static=is_static,
231
+ is_async=is_async,
232
+ docstring=docstring,
233
+ return_type=return_type,
234
+ metadata=metadata or {}
235
+ )
236
+
237
+ def _make_relationship(
238
+ self,
239
+ source: str,
240
+ target: str,
241
+ rel_type: RelationshipType,
242
+ file_path: str,
243
+ content: str,
244
+ offset: int,
245
+ metadata: Optional[Dict[str, Any]] = None
246
+ ) -> Relationship:
247
+ """Create a Relationship with correct fields."""
248
+ return Relationship(
249
+ source_symbol=source,
250
+ target_symbol=target,
251
+ relationship_type=rel_type,
252
+ source_file=file_path,
253
+ source_range=self._make_range(content, offset, content[:offset].count('\n') + 1),
254
+ metadata=metadata or {}
255
+ )
256
+
257
+ def parse_file(self, file_path: Union[str, Path], content: Optional[str] = None) -> ParseResult:
258
+ """Parse a Kotlin file and extract symbols and relationships."""
259
+ import time
260
+ start_time = time.time()
261
+ file_path = str(file_path)
262
+
263
+ if content is None:
264
+ try:
265
+ with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
266
+ content = f.read()
267
+ except Exception as e:
268
+ return ParseResult(
269
+ file_path=file_path,
270
+ language=self.language,
271
+ symbols=[],
272
+ relationships=[],
273
+ errors=[str(e)]
274
+ )
275
+
276
+ symbols = self._extract_symbols(content, file_path)
277
+ relationships = self._extract_relationships(content, file_path, symbols)
278
+
279
+ return ParseResult(
280
+ file_path=file_path,
281
+ language=self.language,
282
+ symbols=symbols,
283
+ relationships=relationships,
284
+ parse_time=time.time() - start_time
285
+ )
286
+
287
+ def _extract_symbols(self, content: str, file_path: str) -> List[Symbol]:
288
+ """Extract all symbols from Kotlin source code."""
289
+ symbols = []
290
+ package_name = self._extract_package(content)
291
+
292
+ # Extract classes
293
+ for match in PATTERNS['class'].finditer(content):
294
+ visibility, modifiers, name, inheritance = match.groups()
295
+ end_line = self._find_block_end(content, match.end())
296
+
297
+ # Determine symbol type based on modifiers
298
+ symbol_type = SymbolType.CLASS
299
+ metadata = {'visibility': visibility or 'public'}
300
+
301
+ if modifiers:
302
+ metadata['modifiers'] = modifiers.strip()
303
+ if 'enum' in modifiers:
304
+ symbol_type = SymbolType.ENUM
305
+ elif 'data' in modifiers:
306
+ metadata['is_data_class'] = True
307
+ elif 'sealed' in modifiers:
308
+ metadata['is_sealed'] = True
309
+
310
+ qualified_name = f"{package_name}.{name}" if package_name else name
311
+ docstring = self._find_preceding_kdoc(content, match.start())
312
+
313
+ symbols.append(self._make_symbol(
314
+ name=name,
315
+ symbol_type=symbol_type,
316
+ content=content,
317
+ file_path=file_path,
318
+ start_offset=match.start(),
319
+ end_line=end_line,
320
+ scope=Scope.GLOBAL,
321
+ full_name=qualified_name,
322
+ docstring=docstring,
323
+ visibility=visibility or 'public',
324
+ metadata=metadata
325
+ ))
326
+
327
+ # Extract interfaces
328
+ for match in PATTERNS['interface'].finditer(content):
329
+ visibility, fun_modifier, name, extends = match.groups()
330
+ end_line = self._find_block_end(content, match.end())
331
+
332
+ metadata = {'visibility': visibility or 'public'}
333
+ if fun_modifier:
334
+ metadata['is_fun_interface'] = True
335
+
336
+ qualified_name = f"{package_name}.{name}" if package_name else name
337
+ docstring = self._find_preceding_kdoc(content, match.start())
338
+
339
+ symbols.append(self._make_symbol(
340
+ name=name,
341
+ symbol_type=SymbolType.INTERFACE,
342
+ content=content,
343
+ file_path=file_path,
344
+ start_offset=match.start(),
345
+ end_line=end_line,
346
+ scope=Scope.GLOBAL,
347
+ full_name=qualified_name,
348
+ docstring=docstring,
349
+ visibility=visibility or 'public',
350
+ metadata=metadata
351
+ ))
352
+
353
+ # Extract objects
354
+ for match in PATTERNS['object'].finditer(content):
355
+ visibility, companion, name, implements = match.groups()
356
+ if not name and not companion:
357
+ continue # Skip anonymous objects in expressions
358
+
359
+ obj_name = name if name else 'Companion'
360
+ end_line = self._find_block_end(content, match.end())
361
+
362
+ metadata = {'visibility': visibility or 'public'}
363
+ if companion:
364
+ metadata['is_companion'] = True
365
+
366
+ qualified_name = f"{package_name}.{obj_name}" if package_name else obj_name
367
+ docstring = self._find_preceding_kdoc(content, match.start())
368
+
369
+ symbols.append(self._make_symbol(
370
+ name=obj_name,
371
+ symbol_type=SymbolType.CLASS,
372
+ content=content,
373
+ file_path=file_path,
374
+ start_offset=match.start(),
375
+ end_line=end_line,
376
+ scope=Scope.GLOBAL,
377
+ full_name=qualified_name,
378
+ docstring=docstring,
379
+ visibility=visibility or 'public',
380
+ metadata=metadata
381
+ ))
382
+
383
+ # Extract functions
384
+ for match in PATTERNS['function'].finditer(content):
385
+ modifiers, receiver, name, return_type = match.groups()
386
+ end_line = self._find_function_end(content, match.end())
387
+
388
+ metadata = {}
389
+ is_async = False
390
+ if modifiers:
391
+ modifier_list = modifiers.strip().split()
392
+ metadata['modifiers'] = modifier_list
393
+ if 'suspend' in modifier_list:
394
+ is_async = True
395
+ metadata['is_suspend'] = True
396
+ if 'inline' in modifier_list:
397
+ metadata['is_inline'] = True
398
+
399
+ if receiver:
400
+ metadata['extension_receiver'] = receiver
401
+
402
+ qualified_name = f"{package_name}.{name}" if package_name else name
403
+ docstring = self._find_preceding_kdoc(content, match.start())
404
+
405
+ symbols.append(self._make_symbol(
406
+ name=name,
407
+ symbol_type=SymbolType.FUNCTION,
408
+ content=content,
409
+ file_path=file_path,
410
+ start_offset=match.start(),
411
+ end_line=end_line,
412
+ scope=Scope.GLOBAL,
413
+ full_name=qualified_name,
414
+ docstring=docstring,
415
+ is_async=is_async,
416
+ return_type=return_type.strip() if return_type else None,
417
+ metadata=metadata
418
+ ))
419
+
420
+ # Extract properties
421
+ for match in PATTERNS['property'].finditer(content):
422
+ modifiers, val_var, receiver, name, type_decl, initializer = match.groups()
423
+ line = content[:match.start()].count('\n') + 1
424
+
425
+ metadata = {'mutable': val_var == 'var'}
426
+ if modifiers:
427
+ metadata['modifiers'] = modifiers.strip().split()
428
+ if receiver:
429
+ metadata['extension_receiver'] = receiver
430
+ if type_decl:
431
+ metadata['type'] = type_decl.strip()
432
+
433
+ qualified_name = f"{package_name}.{name}" if package_name else name
434
+
435
+ symbols.append(self._make_symbol(
436
+ name=name,
437
+ symbol_type=SymbolType.VARIABLE,
438
+ content=content,
439
+ file_path=file_path,
440
+ start_offset=match.start(),
441
+ end_line=line,
442
+ scope=Scope.GLOBAL,
443
+ full_name=qualified_name,
444
+ metadata=metadata
445
+ ))
446
+
447
+ # Extract type aliases
448
+ for match in PATTERNS['typealias'].finditer(content):
449
+ visibility, name, aliased_type = match.groups()
450
+ line = content[:match.start()].count('\n') + 1
451
+
452
+ qualified_name = f"{package_name}.{name}" if package_name else name
453
+
454
+ symbols.append(self._make_symbol(
455
+ name=name,
456
+ symbol_type=SymbolType.TYPE_ALIAS,
457
+ content=content,
458
+ file_path=file_path,
459
+ start_offset=match.start(),
460
+ end_line=line,
461
+ scope=Scope.GLOBAL,
462
+ full_name=qualified_name,
463
+ visibility=visibility or 'public',
464
+ metadata={'aliased_type': aliased_type.strip()}
465
+ ))
466
+
467
+ return symbols
468
+
469
+ def _extract_relationships(
470
+ self,
471
+ content: str,
472
+ file_path: str,
473
+ symbols: List[Symbol]
474
+ ) -> List[Relationship]:
475
+ """Extract relationships from Kotlin source code."""
476
+ relationships = []
477
+ current_scope = Path(file_path).stem
478
+
479
+ # Extract imports
480
+ for match in PATTERNS['import'].finditer(content):
481
+ import_path, alias = match.groups()
482
+
483
+ relationships.append(self._make_relationship(
484
+ source=current_scope,
485
+ target=import_path,
486
+ rel_type=RelationshipType.IMPORTS,
487
+ file_path=file_path,
488
+ content=content,
489
+ offset=match.start(),
490
+ metadata={'alias': alias} if alias else None
491
+ ))
492
+
493
+ # Extract wildcard imports
494
+ for match in PATTERNS['import_wildcard'].finditer(content):
495
+ import_path = match.group(1)
496
+
497
+ relationships.append(self._make_relationship(
498
+ source=current_scope,
499
+ target=import_path,
500
+ rel_type=RelationshipType.IMPORTS,
501
+ file_path=file_path,
502
+ content=content,
503
+ offset=match.start(),
504
+ metadata={'wildcard': True}
505
+ ))
506
+
507
+ # Extract inheritance relationships
508
+ for match in PATTERNS['class'].finditer(content):
509
+ _, _, class_name, inheritance = match.groups()
510
+ if inheritance:
511
+ parents = self._parse_inheritance(inheritance)
512
+ for parent, rel_type in parents:
513
+ relationships.append(self._make_relationship(
514
+ source=class_name,
515
+ target=parent,
516
+ rel_type=rel_type,
517
+ file_path=file_path,
518
+ content=content,
519
+ offset=match.start()
520
+ ))
521
+
522
+ # Extract interface extension
523
+ for match in PATTERNS['interface'].finditer(content):
524
+ _, _, iface_name, extends = match.groups()
525
+ if extends:
526
+ for parent in self._parse_type_list(extends):
527
+ relationships.append(self._make_relationship(
528
+ source=iface_name,
529
+ target=parent,
530
+ rel_type=RelationshipType.INHERITANCE,
531
+ file_path=file_path,
532
+ content=content,
533
+ offset=match.start()
534
+ ))
535
+
536
+ # Extract object implementations
537
+ for match in PATTERNS['object'].finditer(content):
538
+ _, companion, obj_name, implements = match.groups()
539
+ if implements and obj_name:
540
+ for parent in self._parse_type_list(implements):
541
+ relationships.append(self._make_relationship(
542
+ source=obj_name,
543
+ target=parent,
544
+ rel_type=RelationshipType.IMPLEMENTATION,
545
+ file_path=file_path,
546
+ content=content,
547
+ offset=match.start()
548
+ ))
549
+
550
+ # Extract delegation relationships
551
+ for match in PATTERNS['delegation'].finditer(content):
552
+ delegate = match.group(1)
553
+
554
+ relationships.append(self._make_relationship(
555
+ source=current_scope,
556
+ target=delegate,
557
+ rel_type=RelationshipType.COMPOSITION,
558
+ file_path=file_path,
559
+ content=content,
560
+ offset=match.start(),
561
+ metadata={'delegation': True}
562
+ ))
563
+
564
+ # Extract annotation usage
565
+ for match in PATTERNS['annotation_use'].finditer(content):
566
+ annotation = match.group(1)
567
+
568
+ # Skip common built-in annotations
569
+ if annotation not in {'Override', 'Deprecated', 'Suppress', 'JvmStatic',
570
+ 'JvmField', 'JvmOverloads', 'JvmName', 'Throws',
571
+ 'Nullable', 'NotNull', 'Test', 'Before', 'After'}:
572
+ relationships.append(self._make_relationship(
573
+ source=current_scope,
574
+ target=annotation,
575
+ rel_type=RelationshipType.DECORATES,
576
+ file_path=file_path,
577
+ content=content,
578
+ offset=match.start()
579
+ ))
580
+
581
+ # Extract function calls
582
+ symbol_names = {s.name for s in symbols}
583
+ for match in PATTERNS['function_call'].finditer(content):
584
+ func_name = match.group(1)
585
+
586
+ # Skip keywords and common functions
587
+ if func_name not in symbol_names and func_name not in {
588
+ 'if', 'when', 'for', 'while', 'try', 'catch', 'finally',
589
+ 'return', 'throw', 'break', 'continue', 'print', 'println',
590
+ 'listOf', 'mapOf', 'setOf', 'arrayOf', 'mutableListOf',
591
+ 'mutableMapOf', 'mutableSetOf', 'lazy', 'require', 'check',
592
+ 'assert', 'error', 'TODO', 'also', 'apply', 'let', 'run', 'with'
593
+ }:
594
+ relationships.append(self._make_relationship(
595
+ source=current_scope,
596
+ target=func_name,
597
+ rel_type=RelationshipType.CALLS,
598
+ file_path=file_path,
599
+ content=content,
600
+ offset=match.start()
601
+ ))
602
+
603
+ # Extract constructor calls
604
+ for match in PATTERNS['constructor_call'].finditer(content):
605
+ class_name = match.group(1)
606
+
607
+ if class_name not in symbol_names:
608
+ relationships.append(self._make_relationship(
609
+ source=current_scope,
610
+ target=class_name,
611
+ rel_type=RelationshipType.USES,
612
+ file_path=file_path,
613
+ content=content,
614
+ offset=match.start()
615
+ ))
616
+
617
+ return relationships
618
+
619
+ def _extract_package(self, content: str) -> Optional[str]:
620
+ """Extract package name from content."""
621
+ match = PATTERNS['package'].search(content)
622
+ return match.group(1) if match else None
623
+
624
+ def _parse_inheritance(self, inheritance: str) -> List[Tuple[str, RelationshipType]]:
625
+ """Parse inheritance clause to extract parent types."""
626
+ results = []
627
+
628
+ # Remove generic parameters for simpler parsing
629
+ clean = re.sub(r'<[^>]*>', '', inheritance)
630
+
631
+ # Split by comma
632
+ for part in clean.split(','):
633
+ part = part.strip()
634
+ if not part:
635
+ continue
636
+
637
+ # Remove constructor call parentheses
638
+ part = re.sub(r'\([^)]*\)', '', part).strip()
639
+
640
+ # Get the type name
641
+ type_name = part.split()[0] if part else None
642
+ if type_name:
643
+ # Heuristic: if it has parentheses in original, it's a class
644
+ if '(' in inheritance and type_name in inheritance.split('(')[0]:
645
+ results.append((type_name, RelationshipType.INHERITANCE))
646
+ else:
647
+ # Could be either, default to implementation for interfaces
648
+ results.append((type_name, RelationshipType.IMPLEMENTATION))
649
+
650
+ return results
651
+
652
+ def _parse_type_list(self, type_list: str) -> List[str]:
653
+ """Parse a comma-separated list of types."""
654
+ # Remove generic parameters
655
+ clean = re.sub(r'<[^>]*>', '', type_list)
656
+
657
+ types = []
658
+ for part in clean.split(','):
659
+ part = part.strip()
660
+ if part:
661
+ # Get just the type name
662
+ type_name = part.split()[0]
663
+ type_name = re.sub(r'\([^)]*\)', '', type_name).strip()
664
+ if type_name:
665
+ types.append(type_name)
666
+
667
+ return types
668
+
669
+ def _find_preceding_kdoc(self, content: str, position: int) -> Optional[str]:
670
+ """Find KDoc comment preceding a position."""
671
+ # Look for KDoc before the position
672
+ before = content[:position]
673
+ match = re.search(r'/\*\*\s*([\s\S]*?)\s*\*/\s*$', before)
674
+
675
+ if match:
676
+ doc = match.group(1)
677
+ # Clean up the doc
678
+ lines = doc.split('\n')
679
+ cleaned = []
680
+ for line in lines:
681
+ line = re.sub(r'^\s*\*\s?', '', line)
682
+ cleaned.append(line.strip())
683
+ return '\n'.join(cleaned).strip()
684
+
685
+ return None
686
+
687
+ def _find_block_end(self, content: str, start: int) -> int:
688
+ """Find the end line of a code block."""
689
+ brace_count = 0
690
+ in_block = False
691
+
692
+ for i, char in enumerate(content[start:], start):
693
+ if char == '{':
694
+ brace_count += 1
695
+ in_block = True
696
+ elif char == '}':
697
+ brace_count -= 1
698
+ if in_block and brace_count == 0:
699
+ return content[:i].count('\n') + 1
700
+
701
+ return content[:start].count('\n') + 1
702
+
703
+ def _find_function_end(self, content: str, start: int) -> int:
704
+ """Find the end line of a function."""
705
+ # Check for expression body (=) vs block body ({)
706
+ rest = content[start:start + 100]
707
+
708
+ if '=' in rest.split('\n')[0] and '{' not in rest.split('\n')[0]:
709
+ # Expression body - find newline
710
+ newline_pos = content.find('\n', start)
711
+ return content[:newline_pos].count('\n') + 1 if newline_pos != -1 else content[:start].count('\n') + 1
712
+ else:
713
+ return self._find_block_end(content, start)
714
+
715
+ def parse_multiple_files(
716
+ self,
717
+ files: List[Tuple[str, Optional[str]]],
718
+ resolve_cross_file: bool = True,
719
+ max_workers: int = 4
720
+ ) -> Dict[str, ParseResult]:
721
+ """
722
+ Parse multiple Kotlin files with optional cross-file resolution.
723
+
724
+ Args:
725
+ files: List of (file_path, content) tuples
726
+ resolve_cross_file: Whether to resolve cross-file references
727
+ max_workers: Maximum number of parallel workers
728
+
729
+ Returns:
730
+ Dict mapping file paths to ParseResult objects
731
+ """
732
+ results = {}
733
+
734
+ def parse_single(file_info: Tuple[str, Optional[str]]) -> Tuple[str, Optional[ParseResult]]:
735
+ file_path, content = file_info
736
+ if content is None:
737
+ try:
738
+ with open(file_path, 'r', encoding='utf-8') as f:
739
+ content = f.read()
740
+ except Exception as e:
741
+ logger.warning(f"Failed to read {file_path}: {e}")
742
+ return file_path, None
743
+
744
+ try:
745
+ result = self.parse(content, file_path)
746
+ return file_path, result
747
+ except Exception as e:
748
+ logger.warning(f"Failed to parse {file_path}: {e}")
749
+ return file_path, None
750
+
751
+ # Parse files in parallel
752
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
753
+ for file_path, result in executor.map(parse_single, files):
754
+ if result:
755
+ results[file_path] = result
756
+
757
+ # Register symbols for cross-file resolution
758
+ if resolve_cross_file:
759
+ for symbol in result.symbols:
760
+ self._symbol_to_file[symbol.name] = file_path
761
+ if symbol.qualified_name:
762
+ self._symbol_to_file[symbol.qualified_name] = file_path
763
+
764
+ return results
765
+
766
+
767
+ # Register the parser
768
+ parser_registry.register_parser(KotlinParser())