janito 2.24.0__py3-none-any.whl → 2.25.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.
- janito/cli/chat_mode/session.py +2 -2
- janito/cli/cli_commands/list_plugins.py +32 -0
- janito/cli/core/getters.py +2 -0
- janito/cli/main_cli.py +6 -2
- janito/cli/single_shot_mode/handler.py +2 -2
- janito/config_manager.py +8 -0
- janito/exceptions.py +19 -1
- janito/plugins/base.py +53 -2
- janito/plugins/config.py +87 -0
- janito/plugins/discovery.py +32 -0
- janito/plugins/manager.py +56 -2
- janito/tools/adapters/local/adapter.py +8 -26
- janito/tools/adapters/local/ask_user.py +1 -1
- janito/tools/adapters/local/copy_file.py +3 -1
- janito/tools/adapters/local/create_directory.py +2 -2
- janito/tools/adapters/local/create_file.py +8 -4
- janito/tools/adapters/local/fetch_url.py +25 -22
- janito/tools/adapters/local/find_files.py +3 -2
- janito/tools/adapters/local/get_file_outline/core.py +3 -1
- janito/tools/adapters/local/get_file_outline/search_outline.py +1 -1
- janito/tools/adapters/local/move_file.py +3 -2
- janito/tools/adapters/local/open_html_in_browser.py +1 -1
- janito/tools/adapters/local/open_url.py +1 -1
- janito/tools/adapters/local/python_file_run.py +2 -0
- janito/tools/adapters/local/read_chart.py +61 -54
- janito/tools/adapters/local/read_files.py +4 -3
- janito/tools/adapters/local/remove_directory.py +2 -0
- janito/tools/adapters/local/remove_file.py +3 -3
- janito/tools/adapters/local/run_powershell_command.py +1 -0
- janito/tools/adapters/local/search_text/core.py +3 -2
- janito/tools/adapters/local/validate_file_syntax/core.py +3 -1
- janito/tools/adapters/local/view_file.py +3 -1
- janito/tools/loop_protection_decorator.py +64 -25
- janito/tools/path_utils.py +39 -0
- janito/tools/tools_adapter.py +68 -22
- {janito-2.24.0.dist-info → janito-2.25.0.dist-info}/METADATA +1 -1
- {janito-2.24.0.dist-info → janito-2.25.0.dist-info}/RECORD +46 -39
- janito-2.25.0.dist-info/top_level.txt +2 -0
- janito-coder/janito_coder/__init__.py +9 -0
- janito-coder/janito_coder/plugins/__init__.py +27 -0
- janito-coder/janito_coder/plugins/code_navigator.py +618 -0
- janito-coder/janito_coder/plugins/git_analyzer.py +273 -0
- janito-coder/pyproject.toml +347 -0
- janito-2.24.0.dist-info/top_level.txt +0 -1
- {janito-2.24.0.dist-info → janito-2.25.0.dist-info}/WHEEL +0 -0
- {janito-2.24.0.dist-info → janito-2.25.0.dist-info}/entry_points.txt +0 -0
- {janito-2.24.0.dist-info → janito-2.25.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,618 @@
|
|
1
|
+
"""
|
2
|
+
Code navigation and analysis plugin using tree-sitter.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
import re
|
7
|
+
from typing import Dict, List, Optional, Any
|
8
|
+
from pathlib import Path
|
9
|
+
|
10
|
+
try:
|
11
|
+
from tree_sitter import Language, Parser
|
12
|
+
from tree_sitter_languages import get_language, get_parser
|
13
|
+
|
14
|
+
TREESITTER_AVAILABLE = True
|
15
|
+
except ImportError:
|
16
|
+
TREESITTER_AVAILABLE = False
|
17
|
+
|
18
|
+
from janito.plugins.base import Plugin, PluginMetadata
|
19
|
+
from janito.tools.tool_base import ToolBase, ToolPermissions
|
20
|
+
|
21
|
+
|
22
|
+
class FindDefinitionTool(ToolBase):
|
23
|
+
"""Tool to find function/class definitions in code."""
|
24
|
+
|
25
|
+
tool_name = "find_definition"
|
26
|
+
permissions = ToolPermissions(read=True, write=False, execute=True)
|
27
|
+
|
28
|
+
def run(self, symbol: str, path: str = ".", language: str = None) -> str:
|
29
|
+
"""
|
30
|
+
Find definitions of functions, classes, or variables.
|
31
|
+
|
32
|
+
Args:
|
33
|
+
symbol: Name of the symbol to find
|
34
|
+
path: Directory or file to search in
|
35
|
+
language: Specific language to search (python, javascript, etc.)
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
Definition locations as string
|
39
|
+
"""
|
40
|
+
if not TREESITTER_AVAILABLE:
|
41
|
+
return "Tree-sitter not available. Please install janito-coder with tree-sitter support."
|
42
|
+
|
43
|
+
try:
|
44
|
+
results = []
|
45
|
+
search_path = Path(path)
|
46
|
+
|
47
|
+
if search_path.is_file():
|
48
|
+
files = [search_path]
|
49
|
+
else:
|
50
|
+
# Find all source files
|
51
|
+
extensions = {
|
52
|
+
"python": [".py"],
|
53
|
+
"javascript": [".js", ".jsx"],
|
54
|
+
"typescript": [".ts", ".tsx"],
|
55
|
+
"java": [".java"],
|
56
|
+
"c": [".c", ".h"],
|
57
|
+
"cpp": [".cpp", ".cc", ".cxx", ".hpp"],
|
58
|
+
"rust": [".rs"],
|
59
|
+
"go": [".go"],
|
60
|
+
"ruby": [".rb"],
|
61
|
+
"php": [".php"],
|
62
|
+
"swift": [".swift"],
|
63
|
+
"kotlin": [".kt"],
|
64
|
+
"scala": [".scala"],
|
65
|
+
"csharp": [".cs"],
|
66
|
+
}
|
67
|
+
|
68
|
+
if language:
|
69
|
+
extensions_to_search = extensions.get(language.lower(), [])
|
70
|
+
else:
|
71
|
+
extensions_to_search = [
|
72
|
+
ext for exts in extensions.values() for ext in exts
|
73
|
+
]
|
74
|
+
|
75
|
+
files = []
|
76
|
+
for ext in extensions_to_search:
|
77
|
+
files.extend(search_path.rglob(f"*{ext}"))
|
78
|
+
|
79
|
+
for file_path in files:
|
80
|
+
if file_path.is_file():
|
81
|
+
definitions = self._find_definitions_in_file(file_path, symbol)
|
82
|
+
results.extend(definitions)
|
83
|
+
|
84
|
+
if not results:
|
85
|
+
return f"No definitions found for '{symbol}'"
|
86
|
+
|
87
|
+
output = [f"Found {len(results)} definitions for '{symbol}':"]
|
88
|
+
for result in results:
|
89
|
+
output.append(f" {result}")
|
90
|
+
|
91
|
+
return "\n".join(output)
|
92
|
+
|
93
|
+
except Exception as e:
|
94
|
+
return f"Error finding definitions: {str(e)}"
|
95
|
+
|
96
|
+
def _find_definitions_in_file(self, file_path: Path, symbol: str) -> List[str]:
|
97
|
+
"""Find definitions in a single file."""
|
98
|
+
results = []
|
99
|
+
|
100
|
+
try:
|
101
|
+
# Determine language from file extension
|
102
|
+
ext = file_path.suffix.lower()
|
103
|
+
language_map = {
|
104
|
+
".py": "python",
|
105
|
+
".js": "javascript",
|
106
|
+
".jsx": "javascript",
|
107
|
+
".ts": "typescript",
|
108
|
+
".tsx": "typescript",
|
109
|
+
".java": "java",
|
110
|
+
".c": "c",
|
111
|
+
".h": "c",
|
112
|
+
".cpp": "cpp",
|
113
|
+
".cc": "cpp",
|
114
|
+
".cxx": "cpp",
|
115
|
+
".hpp": "cpp",
|
116
|
+
".rs": "rust",
|
117
|
+
".go": "go",
|
118
|
+
".rb": "ruby",
|
119
|
+
".php": "php",
|
120
|
+
".swift": "swift",
|
121
|
+
".kt": "kotlin",
|
122
|
+
".scala": "scala",
|
123
|
+
".cs": "csharp",
|
124
|
+
}
|
125
|
+
|
126
|
+
language_name = language_map.get(ext)
|
127
|
+
if not language_name:
|
128
|
+
return results
|
129
|
+
|
130
|
+
# Get parser for the language
|
131
|
+
try:
|
132
|
+
parser = get_parser(language_name)
|
133
|
+
language = get_language(language_name)
|
134
|
+
except:
|
135
|
+
return results
|
136
|
+
|
137
|
+
# Parse the file
|
138
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
139
|
+
source_code = f.read()
|
140
|
+
|
141
|
+
tree = parser.parse(bytes(source_code, "utf8"))
|
142
|
+
|
143
|
+
# Find definitions based on language
|
144
|
+
if language_name == "python":
|
145
|
+
results.extend(
|
146
|
+
self._find_python_definitions(tree, source_code, symbol, file_path)
|
147
|
+
)
|
148
|
+
elif language_name in ["javascript", "typescript"]:
|
149
|
+
results.extend(
|
150
|
+
self._find_js_definitions(tree, source_code, symbol, file_path)
|
151
|
+
)
|
152
|
+
elif language_name == "java":
|
153
|
+
results.extend(
|
154
|
+
self._find_java_definitions(tree, source_code, symbol, file_path)
|
155
|
+
)
|
156
|
+
elif language_name in ["c", "cpp"]:
|
157
|
+
results.extend(
|
158
|
+
self._find_cpp_definitions(tree, source_code, symbol, file_path)
|
159
|
+
)
|
160
|
+
elif language_name == "rust":
|
161
|
+
results.extend(
|
162
|
+
self._find_rust_definitions(tree, source_code, symbol, file_path)
|
163
|
+
)
|
164
|
+
elif language_name == "go":
|
165
|
+
results.extend(
|
166
|
+
self._find_go_definitions(tree, source_code, symbol, file_path)
|
167
|
+
)
|
168
|
+
|
169
|
+
except Exception:
|
170
|
+
pass # Skip files that can't be parsed
|
171
|
+
|
172
|
+
return results
|
173
|
+
|
174
|
+
def _find_python_definitions(
|
175
|
+
self, tree, source_code: str, symbol: str, file_path: Path
|
176
|
+
) -> List[str]:
|
177
|
+
"""Find Python function/class definitions."""
|
178
|
+
results = []
|
179
|
+
lines = source_code.split("\n")
|
180
|
+
|
181
|
+
def find_nodes(node, type_name):
|
182
|
+
if node.type in ["function_definition", "class_definition"]:
|
183
|
+
for child in node.children:
|
184
|
+
if (
|
185
|
+
child.type == "identifier"
|
186
|
+
and child.text.decode("utf8") == symbol
|
187
|
+
):
|
188
|
+
start_line = node.start_point[0] + 1
|
189
|
+
results.append(
|
190
|
+
f"{file_path}:{start_line}: {node.type} '{symbol}'"
|
191
|
+
)
|
192
|
+
break
|
193
|
+
|
194
|
+
for child in node.children:
|
195
|
+
find_nodes(child, type_name)
|
196
|
+
|
197
|
+
find_nodes(tree.root_node, symbol)
|
198
|
+
return results
|
199
|
+
|
200
|
+
def _find_js_definitions(
|
201
|
+
self, tree, source_code: str, symbol: str, file_path: Path
|
202
|
+
) -> List[str]:
|
203
|
+
"""Find JavaScript/TypeScript function/class definitions."""
|
204
|
+
results = []
|
205
|
+
|
206
|
+
def find_nodes(node, type_name):
|
207
|
+
if node.type in [
|
208
|
+
"function_declaration",
|
209
|
+
"class_declaration",
|
210
|
+
"method_definition",
|
211
|
+
]:
|
212
|
+
for child in node.children:
|
213
|
+
if (
|
214
|
+
child.type == "identifier"
|
215
|
+
and child.text.decode("utf8") == symbol
|
216
|
+
):
|
217
|
+
start_line = node.start_point[0] + 1
|
218
|
+
results.append(
|
219
|
+
f"{file_path}:{start_line}: {node.type} '{symbol}'"
|
220
|
+
)
|
221
|
+
break
|
222
|
+
|
223
|
+
for child in node.children:
|
224
|
+
find_nodes(child, type_name)
|
225
|
+
|
226
|
+
find_nodes(tree.root_node, symbol)
|
227
|
+
return results
|
228
|
+
|
229
|
+
def _find_java_definitions(
|
230
|
+
self, tree, source_code: str, symbol: str, file_path: Path
|
231
|
+
) -> List[str]:
|
232
|
+
"""Find Java class/method definitions."""
|
233
|
+
results = []
|
234
|
+
|
235
|
+
def find_nodes(node, type_name):
|
236
|
+
if node.type in ["class_declaration", "method_declaration"]:
|
237
|
+
for child in node.children:
|
238
|
+
if (
|
239
|
+
child.type == "identifier"
|
240
|
+
and child.text.decode("utf8") == symbol
|
241
|
+
):
|
242
|
+
start_line = node.start_point[0] + 1
|
243
|
+
results.append(
|
244
|
+
f"{file_path}:{start_line}: {node.type} '{symbol}'"
|
245
|
+
)
|
246
|
+
break
|
247
|
+
|
248
|
+
for child in node.children:
|
249
|
+
find_nodes(child, type_name)
|
250
|
+
|
251
|
+
find_nodes(tree.root_node, symbol)
|
252
|
+
return results
|
253
|
+
|
254
|
+
def _find_cpp_definitions(
|
255
|
+
self, tree, source_code: str, symbol: str, file_path: Path
|
256
|
+
) -> List[str]:
|
257
|
+
"""Find C++ function/class definitions."""
|
258
|
+
results = []
|
259
|
+
|
260
|
+
def find_nodes(node, type_name):
|
261
|
+
if node.type in [
|
262
|
+
"function_definition",
|
263
|
+
"class_specifier",
|
264
|
+
"struct_specifier",
|
265
|
+
]:
|
266
|
+
for child in node.children:
|
267
|
+
if (
|
268
|
+
child.type == "identifier"
|
269
|
+
and child.text.decode("utf8") == symbol
|
270
|
+
):
|
271
|
+
start_line = node.start_point[0] + 1
|
272
|
+
results.append(
|
273
|
+
f"{file_path}:{start_line}: {node.type} '{symbol}'"
|
274
|
+
)
|
275
|
+
break
|
276
|
+
|
277
|
+
for child in node.children:
|
278
|
+
find_nodes(child, type_name)
|
279
|
+
|
280
|
+
find_nodes(tree.root_node, symbol)
|
281
|
+
return results
|
282
|
+
|
283
|
+
def _find_rust_definitions(
|
284
|
+
self, tree, source_code: str, symbol: str, file_path: Path
|
285
|
+
) -> List[str]:
|
286
|
+
"""Find Rust function/struct definitions."""
|
287
|
+
results = []
|
288
|
+
|
289
|
+
def find_nodes(node, type_name):
|
290
|
+
if node.type in ["function_item", "struct_item", "enum_item", "trait_item"]:
|
291
|
+
for child in node.children:
|
292
|
+
if (
|
293
|
+
child.type == "identifier"
|
294
|
+
and child.text.decode("utf8") == symbol
|
295
|
+
):
|
296
|
+
start_line = node.start_point[0] + 1
|
297
|
+
results.append(
|
298
|
+
f"{file_path}:{start_line}: {node.type} '{symbol}'"
|
299
|
+
)
|
300
|
+
break
|
301
|
+
|
302
|
+
for child in node.children:
|
303
|
+
find_nodes(child, type_name)
|
304
|
+
|
305
|
+
find_nodes(tree.root_node, symbol)
|
306
|
+
return results
|
307
|
+
|
308
|
+
def _find_go_definitions(
|
309
|
+
self, tree, source_code: str, symbol: str, file_path: Path
|
310
|
+
) -> List[str]:
|
311
|
+
"""Find Go function/type definitions."""
|
312
|
+
results = []
|
313
|
+
|
314
|
+
def find_nodes(node, type_name):
|
315
|
+
if node.type in ["function_declaration", "type_declaration"]:
|
316
|
+
for child in node.children:
|
317
|
+
if (
|
318
|
+
child.type == "identifier"
|
319
|
+
and child.text.decode("utf8") == symbol
|
320
|
+
):
|
321
|
+
start_line = node.start_point[0] + 1
|
322
|
+
results.append(
|
323
|
+
f"{file_path}:{start_line}: {node.type} '{symbol}'"
|
324
|
+
)
|
325
|
+
break
|
326
|
+
|
327
|
+
for child in node.children:
|
328
|
+
find_nodes(child, type_name)
|
329
|
+
|
330
|
+
find_nodes(tree.root_node, symbol)
|
331
|
+
return results
|
332
|
+
|
333
|
+
|
334
|
+
class FindReferencesTool(ToolBase):
|
335
|
+
"""Tool to find references to symbols in code."""
|
336
|
+
|
337
|
+
tool_name = "find_references"
|
338
|
+
permissions = ToolPermissions(read=True, write=False, execute=True)
|
339
|
+
|
340
|
+
def run(self, symbol: str, path: str = ".", language: str = None) -> str:
|
341
|
+
"""
|
342
|
+
Find all references to a symbol.
|
343
|
+
|
344
|
+
Args:
|
345
|
+
symbol: Name of the symbol to find references for
|
346
|
+
path: Directory or file to search in
|
347
|
+
language: Specific language to search
|
348
|
+
|
349
|
+
Returns:
|
350
|
+
Reference locations as string
|
351
|
+
"""
|
352
|
+
if not TREESITTER_AVAILABLE:
|
353
|
+
return "Tree-sitter not available. Please install janito-coder with tree-sitter support."
|
354
|
+
|
355
|
+
try:
|
356
|
+
results = []
|
357
|
+
search_path = Path(path)
|
358
|
+
|
359
|
+
if search_path.is_file():
|
360
|
+
files = [search_path]
|
361
|
+
else:
|
362
|
+
# Find all source files
|
363
|
+
extensions = {
|
364
|
+
"python": [".py"],
|
365
|
+
"javascript": [".js", ".jsx"],
|
366
|
+
"typescript": [".ts", ".tsx"],
|
367
|
+
"java": [".java"],
|
368
|
+
"c": [".c", ".h"],
|
369
|
+
"cpp": [".cpp", ".cc", ".cxx", ".hpp"],
|
370
|
+
"rust": [".rs"],
|
371
|
+
"go": [".go"],
|
372
|
+
"ruby": [".rb"],
|
373
|
+
"php": [".php"],
|
374
|
+
"swift": [".swift"],
|
375
|
+
"kotlin": [".kt"],
|
376
|
+
"scala": [".scala"],
|
377
|
+
"csharp": [".cs"],
|
378
|
+
}
|
379
|
+
|
380
|
+
if language:
|
381
|
+
extensions_to_search = extensions.get(language.lower(), [])
|
382
|
+
else:
|
383
|
+
extensions_to_search = [
|
384
|
+
ext for exts in extensions.values() for ext in exts
|
385
|
+
]
|
386
|
+
|
387
|
+
files = []
|
388
|
+
for ext in extensions_to_search:
|
389
|
+
files.extend(search_path.rglob(f"*{ext}"))
|
390
|
+
|
391
|
+
for file_path in files:
|
392
|
+
if file_path.is_file():
|
393
|
+
references = self._find_references_in_file(file_path, symbol)
|
394
|
+
results.extend(references)
|
395
|
+
|
396
|
+
if not results:
|
397
|
+
return f"No references found for '{symbol}'"
|
398
|
+
|
399
|
+
output = [f"Found {len(results)} references to '{symbol}':"]
|
400
|
+
for result in results:
|
401
|
+
output.append(f" {result}")
|
402
|
+
|
403
|
+
return "\n".join(output)
|
404
|
+
|
405
|
+
except Exception as e:
|
406
|
+
return f"Error finding references: {str(e)}"
|
407
|
+
|
408
|
+
def _find_references_in_file(self, file_path: Path, symbol: str) -> List[str]:
|
409
|
+
"""Find references in a single file."""
|
410
|
+
results = []
|
411
|
+
|
412
|
+
try:
|
413
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
414
|
+
content = f.read()
|
415
|
+
|
416
|
+
# Simple regex-based reference finding
|
417
|
+
# This is a basic implementation - a more sophisticated approach
|
418
|
+
# would use tree-sitter queries
|
419
|
+
lines = content.split("\n")
|
420
|
+
|
421
|
+
for i, line in enumerate(lines, 1):
|
422
|
+
# Skip comments and strings (basic filtering)
|
423
|
+
stripped_line = line.strip()
|
424
|
+
if (
|
425
|
+
stripped_line.startswith("#")
|
426
|
+
or stripped_line.startswith("//")
|
427
|
+
or stripped_line.startswith("/*")
|
428
|
+
or stripped_line.startswith("*")
|
429
|
+
):
|
430
|
+
continue
|
431
|
+
|
432
|
+
# Find symbol usage (basic pattern matching)
|
433
|
+
pattern = r"\b" + re.escape(symbol) + r"\b"
|
434
|
+
matches = re.finditer(pattern, line)
|
435
|
+
|
436
|
+
for match in matches:
|
437
|
+
# Get context around the match
|
438
|
+
start = max(0, match.start() - 20)
|
439
|
+
end = min(len(line), match.end() + 20)
|
440
|
+
context = line[start:end].strip()
|
441
|
+
|
442
|
+
results.append(f"{file_path}:{i}: {context}")
|
443
|
+
|
444
|
+
except Exception:
|
445
|
+
pass # Skip files that can't be read
|
446
|
+
|
447
|
+
return results
|
448
|
+
|
449
|
+
|
450
|
+
class CodeStructureTool(ToolBase):
|
451
|
+
"""Tool to analyze code structure and complexity."""
|
452
|
+
|
453
|
+
tool_name = "code_structure"
|
454
|
+
permissions = ToolPermissions(read=True, write=False, execute=True)
|
455
|
+
|
456
|
+
def run(self, path: str = ".", language: str = None) -> str:
|
457
|
+
"""
|
458
|
+
Analyze code structure and provide overview.
|
459
|
+
|
460
|
+
Args:
|
461
|
+
path: Directory or file to analyze
|
462
|
+
language: Specific language to analyze
|
463
|
+
|
464
|
+
Returns:
|
465
|
+
Code structure analysis as string
|
466
|
+
"""
|
467
|
+
try:
|
468
|
+
search_path = Path(path)
|
469
|
+
|
470
|
+
if search_path.is_file():
|
471
|
+
files = [search_path]
|
472
|
+
else:
|
473
|
+
# Find all source files
|
474
|
+
extensions = {
|
475
|
+
"python": [".py"],
|
476
|
+
"javascript": [".js", ".jsx"],
|
477
|
+
"typescript": [".ts", ".tsx"],
|
478
|
+
"java": [".java"],
|
479
|
+
"c": [".c", ".h"],
|
480
|
+
"cpp": [".cpp", ".cc", ".cxx", ".hpp"],
|
481
|
+
"rust": [".rs"],
|
482
|
+
"go": [".go"],
|
483
|
+
"ruby": [".rb"],
|
484
|
+
"php": [".php"],
|
485
|
+
"swift": [".swift"],
|
486
|
+
"kotlin": [".kt"],
|
487
|
+
"scala": [".scala"],
|
488
|
+
"csharp": [".cs"],
|
489
|
+
}
|
490
|
+
|
491
|
+
if language:
|
492
|
+
extensions_to_search = extensions.get(language.lower(), [])
|
493
|
+
else:
|
494
|
+
extensions_to_search = [
|
495
|
+
ext for exts in extensions.values() for ext in exts
|
496
|
+
]
|
497
|
+
|
498
|
+
files = []
|
499
|
+
for ext in extensions_to_search:
|
500
|
+
files.extend(search_path.rglob(f"*{ext}"))
|
501
|
+
|
502
|
+
analysis = {
|
503
|
+
"total_files": len(files),
|
504
|
+
"languages": {},
|
505
|
+
"total_lines": 0,
|
506
|
+
"total_size": 0,
|
507
|
+
"file_details": [],
|
508
|
+
}
|
509
|
+
|
510
|
+
for file_path in files:
|
511
|
+
if file_path.is_file():
|
512
|
+
try:
|
513
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
514
|
+
content = f.read()
|
515
|
+
|
516
|
+
lines = len(content.split("\n"))
|
517
|
+
size = file_path.stat().st_size
|
518
|
+
|
519
|
+
ext = file_path.suffix.lower()
|
520
|
+
language_name = {
|
521
|
+
".py": "Python",
|
522
|
+
".js": "JavaScript",
|
523
|
+
".jsx": "JavaScript",
|
524
|
+
".ts": "TypeScript",
|
525
|
+
".tsx": "TypeScript",
|
526
|
+
".java": "Java",
|
527
|
+
".c": "C",
|
528
|
+
".h": "C",
|
529
|
+
".cpp": "C++",
|
530
|
+
".cc": "C++",
|
531
|
+
".cxx": "C++",
|
532
|
+
".hpp": "C++",
|
533
|
+
".rs": "Rust",
|
534
|
+
".go": "Go",
|
535
|
+
".rb": "Ruby",
|
536
|
+
".php": "PHP",
|
537
|
+
".swift": "Swift",
|
538
|
+
".kt": "Kotlin",
|
539
|
+
".scala": "Scala",
|
540
|
+
".cs": "C#",
|
541
|
+
}.get(ext, "Unknown")
|
542
|
+
|
543
|
+
analysis["languages"][language_name] = (
|
544
|
+
analysis["languages"].get(language_name, 0) + 1
|
545
|
+
)
|
546
|
+
analysis["total_lines"] += lines
|
547
|
+
analysis["total_size"] += size
|
548
|
+
|
549
|
+
analysis["file_details"].append(
|
550
|
+
{
|
551
|
+
"path": str(file_path),
|
552
|
+
"language": language_name,
|
553
|
+
"lines": lines,
|
554
|
+
"size": size,
|
555
|
+
}
|
556
|
+
)
|
557
|
+
|
558
|
+
except Exception:
|
559
|
+
continue
|
560
|
+
|
561
|
+
# Generate report
|
562
|
+
output = [
|
563
|
+
"Code Structure Analysis",
|
564
|
+
"=" * 30,
|
565
|
+
f"Total files: {analysis['total_files']}",
|
566
|
+
f"Total lines: {analysis['total_lines']:,}",
|
567
|
+
f"Total size: {analysis['total_size']:,} bytes",
|
568
|
+
"",
|
569
|
+
"Languages:",
|
570
|
+
]
|
571
|
+
|
572
|
+
for lang, count in sorted(
|
573
|
+
analysis["languages"].items(), key=lambda x: x[1], reverse=True
|
574
|
+
):
|
575
|
+
output.append(f" {lang}: {count} files")
|
576
|
+
|
577
|
+
output.extend(["", "Largest files:"])
|
578
|
+
|
579
|
+
# Sort by size and show top 10
|
580
|
+
sorted_files = sorted(
|
581
|
+
analysis["file_details"], key=lambda x: x["size"], reverse=True
|
582
|
+
)[:10]
|
583
|
+
for file_info in sorted_files:
|
584
|
+
output.append(
|
585
|
+
f" {file_info['path']}: {file_info['lines']} lines, {file_info['size']} bytes"
|
586
|
+
)
|
587
|
+
|
588
|
+
return "\n".join(output)
|
589
|
+
|
590
|
+
except Exception as e:
|
591
|
+
return f"Error analyzing code structure: {str(e)}"
|
592
|
+
|
593
|
+
|
594
|
+
class CodeNavigatorPlugin(Plugin):
|
595
|
+
"""Plugin providing code navigation and analysis tools."""
|
596
|
+
|
597
|
+
def get_metadata(self) -> PluginMetadata:
|
598
|
+
return PluginMetadata(
|
599
|
+
name="code_navigator",
|
600
|
+
version="1.0.0",
|
601
|
+
description="Code navigation and static analysis tools",
|
602
|
+
author="Janito Coder Team",
|
603
|
+
license="MIT",
|
604
|
+
homepage="https://github.com/ikignosis/janito-coder",
|
605
|
+
)
|
606
|
+
|
607
|
+
def get_tools(self):
|
608
|
+
return [FindDefinitionTool, FindReferencesTool, CodeStructureTool]
|
609
|
+
|
610
|
+
def initialize(self):
|
611
|
+
print("Code Navigator plugin initialized!")
|
612
|
+
|
613
|
+
def cleanup(self):
|
614
|
+
print("Code Navigator plugin cleaned up!")
|
615
|
+
|
616
|
+
|
617
|
+
# This makes the plugin discoverable
|
618
|
+
PLUGIN_CLASS = CodeNavigatorPlugin
|