siada-cli 0.0.1__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.
- agent_config.yaml +34 -0
- siada/__init__.py +13 -0
- siada/agent_hub/__init__.py +10 -0
- siada/agent_hub/coder/bug_fix_agent.py +398 -0
- siada/agent_hub/coder/code_gen_agent.py +235 -0
- siada/agent_hub/coder/fe_gen_agent.py +37 -0
- siada/agent_hub/coder/issue_review_agent.py +68 -0
- siada/agent_hub/coder/prompt/__init__.py +0 -0
- siada/agent_hub/coder/prompt/base/__init__.py +0 -0
- siada/agent_hub/coder/prompt/base/capabilities.py +18 -0
- siada/agent_hub/coder/prompt/base/non_interactive.py +14 -0
- siada/agent_hub/coder/prompt/base/prompt_builder.py +33 -0
- siada/agent_hub/coder/prompt/base/rules.py +40 -0
- siada/agent_hub/coder/prompt/base/tool_use.py +14 -0
- siada/agent_hub/coder/prompt/bug_prompt/bug_fix_prompt.py +51 -0
- siada/agent_hub/coder/prompt/bug_prompt/rules.py +53 -0
- siada/agent_hub/coder/prompt/code_gen_prompt.py +45 -0
- siada/agent_hub/coder/prompt/fe_gen_prompt.py +45 -0
- siada/agent_hub/coder/prompt/issue_review_prompt.py +121 -0
- siada/agent_hub/coder/tracing/__init__.py +22 -0
- siada/agent_hub/coder/tracing/logger_tracing_processor.py +458 -0
- siada/agent_hub/hooks/__init__.py +0 -0
- siada/agent_hub/hooks/io_hooks.py +30 -0
- siada/agent_hub/siada_agent.py +214 -0
- siada/config/__init__.py +0 -0
- siada/config/config_loader.py +67 -0
- siada/entrypoint/__init__.py +3 -0
- siada/entrypoint/args_parser/__init__.py +0 -0
- siada/entrypoint/args_parser/args.py +195 -0
- siada/entrypoint/interaction/__init__.py +0 -0
- siada/entrypoint/interaction/base_turn.py +107 -0
- siada/entrypoint/interaction/config.py +30 -0
- siada/entrypoint/interaction/controller.py +122 -0
- siada/entrypoint/interaction/nointeractive_controller.py +27 -0
- siada/entrypoint/interaction/turn/interface.py +116 -0
- siada/entrypoint/interaction/turn/models.py +34 -0
- siada/entrypoint/interaction/turn/run_turn.py +641 -0
- siada/entrypoint/interaction/turn_factory.py +35 -0
- siada/entrypoint/interaction/turn_models.py +28 -0
- siada/entrypoint/siadahub.py +413 -0
- siada/foundation/__init__.py +0 -0
- siada/foundation/code_agent_context.py +48 -0
- siada/foundation/config.py +59 -0
- siada/foundation/constants.py +2 -0
- siada/foundation/context.py +76 -0
- siada/foundation/logging.py +341 -0
- siada/foundation/tools/__init__.py +0 -0
- siada/foundation/tools/get_git_diff.py +105 -0
- siada/io/__init__.py +0 -0
- siada/io/banner.py +148 -0
- siada/io/color_settings.py +207 -0
- siada/io/color_utils.py +16 -0
- siada/io/components/__init__.py +0 -0
- siada/io/components/mdstream.py +204 -0
- siada/io/console_printer.py +68 -0
- siada/io/io.py +661 -0
- siada/io/key_bindings.py +129 -0
- siada/io/notification_command.py +40 -0
- siada/io/test_banner.py +64 -0
- siada/models/__init__.py +0 -0
- siada/models/agent.py +47 -0
- siada/models/coder/__init__.py +0 -0
- siada/models/converter.py +27 -0
- siada/models/model_base_config.py +121 -0
- siada/models/model_run_config.py +153 -0
- siada/provider/__init__.py +0 -0
- siada/provider/client_factory.py +112 -0
- siada/provider/llm_client.py +11 -0
- siada/provider/openrouter/__init__.py +0 -0
- siada/provider/openrouter/coverter.py +17 -0
- siada/provider/openrouter/openroute_provider.py +33 -0
- siada/provider/provider_factory.py +53 -0
- siada/queries/tree-sitter-language-pack/README.md +7 -0
- siada/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/c-tags.scm +9 -0
- siada/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- siada/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
- siada/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
- siada/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
- siada/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- siada/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
- siada/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
- siada/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
- siada/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- siada/queries/tree-sitter-language-pack/go-tags.scm +42 -0
- siada/queries/tree-sitter-language-pack/java-tags.scm +20 -0
- siada/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
- siada/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
- siada/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- siada/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
- siada/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- siada/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/python-tags.scm +14 -0
- siada/queries/tree-sitter-language-pack/r-tags.scm +21 -0
- siada/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- siada/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
- siada/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
- siada/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- siada/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
- siada/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- siada/queries/tree-sitter-languages/README.md +23 -0
- siada/queries/tree-sitter-languages/c-tags.scm +9 -0
- siada/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
- siada/queries/tree-sitter-languages/cpp-tags.scm +15 -0
- siada/queries/tree-sitter-languages/dart-tags.scm +91 -0
- siada/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- siada/queries/tree-sitter-languages/elixir-tags.scm +54 -0
- siada/queries/tree-sitter-languages/elm-tags.scm +19 -0
- siada/queries/tree-sitter-languages/go-tags.scm +30 -0
- siada/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- siada/queries/tree-sitter-languages/java-tags.scm +20 -0
- siada/queries/tree-sitter-languages/javascript-tags.scm +88 -0
- siada/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
- siada/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- siada/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
- siada/queries/tree-sitter-languages/php-tags.scm +26 -0
- siada/queries/tree-sitter-languages/python-tags.scm +12 -0
- siada/queries/tree-sitter-languages/ql-tags.scm +26 -0
- siada/queries/tree-sitter-languages/ruby-tags.scm +64 -0
- siada/queries/tree-sitter-languages/rust-tags.scm +60 -0
- siada/queries/tree-sitter-languages/scala-tags.scm +65 -0
- siada/queries/tree-sitter-languages/typescript-tags.scm +41 -0
- siada/services/__init__.py +30 -0
- siada/services/at_file_recommendation_design_document.md +1213 -0
- siada/services/code_context_manager.py +136 -0
- siada/services/enhanced_fix_result_check.py +546 -0
- siada/services/execution_trace_collector.py +207 -0
- siada/services/file_recommendation/__init__.py +136 -0
- siada/services/file_recommendation/core/__init__.py +17 -0
- siada/services/file_recommendation/core/completion_engine.py +351 -0
- siada/services/file_recommendation/core/config.py +62 -0
- siada/services/file_recommendation/core/file_discovery.py +353 -0
- siada/services/file_recommendation/core/suggestion.py +110 -0
- siada/services/file_recommendation/utils/__init__.py +22 -0
- siada/services/file_recommendation/utils/path_utils.py +138 -0
- siada/services/file_recommendation/utils/text_utils.py +253 -0
- siada/services/fix_result_check.py +377 -0
- siada/services/handle_at_command/README.md +247 -0
- siada/services/handle_at_command/__init__.py +115 -0
- siada/services/handle_at_command/exceptions.py +46 -0
- siada/services/handle_at_command/models.py +92 -0
- siada/services/handle_at_command/parser.py +189 -0
- siada/services/handle_at_command/processor.py +321 -0
- siada/services/handle_at_command/resolver.py +318 -0
- siada/services/handle_at_command/utils.py +345 -0
- siada/services/model_info_service.py +103 -0
- siada/services/siada_runner.py +170 -0
- siada/session/__init__.py +29 -0
- siada/session/session_manager.py +66 -0
- siada/session/session_models.py +43 -0
- siada/support/__init__.py +0 -0
- siada/support/completer.py +204 -0
- siada/support/editor.py +146 -0
- siada/support/envprocessor.py +54 -0
- siada/support/repo.py +51 -0
- siada/support/slash_commands.py +410 -0
- siada/support/spinner.py +320 -0
- siada/tools/__init__.py +0 -0
- siada/tools/ast/README.md +345 -0
- siada/tools/ast/ast_tool.py +341 -0
- siada/tools/ast/models.py +11 -0
- siada/tools/coder/__init__.py +0 -0
- siada/tools/coder/ask_followup_question.py +70 -0
- siada/tools/coder/cmd_runner.py +132 -0
- siada/tools/coder/file_operator.py +284 -0
- siada/tools/coder/file_search/README.md +223 -0
- siada/tools/coder/file_search/__init__.py +10 -0
- siada/tools/coder/file_search/bin/rg-linux-arm64 +0 -0
- siada/tools/coder/file_search/bin/rg-linux-x64 +0 -0
- siada/tools/coder/file_search/bin/rg-macos-arm64 +0 -0
- siada/tools/coder/file_search/bin/rg-macos-x64 +0 -0
- siada/tools/coder/file_search/bin/rg.exe +0 -0
- siada/tools/coder/file_search/search.py +437 -0
- siada/tools/coder/file_search/setup_package.py +150 -0
- siada/tools/coder/file_search/test_search.py +142 -0
- siada/tools/coder/files.py +147 -0
- siada/tools/coder/fix_attempt_completion.py +58 -0
- siada/tools/coder/issue_review_completion.py +29 -0
- siada/tools/coder/observation/__init__.py +0 -0
- siada/tools/coder/observation/error.py +22 -0
- siada/tools/coder/observation/file_observation.py +216 -0
- siada/tools/coder/observation/observation.py +71 -0
- siada/tools/coder/repo_map/__init__.py +157 -0
- siada/tools/coder/repo_map/dump.py +29 -0
- siada/tools/coder/repo_map/io.py +226 -0
- siada/tools/coder/repo_map/repo_map.py +881 -0
- siada/tools/coder/repo_map/special.py +203 -0
- siada/tools/coder/repo_map/token_counter.py +154 -0
- siada/tools/coder/repo_map/waiting.py +221 -0
- siada/tools/coder/reproduce_completion.py +39 -0
- siada/tools/coder/run_cmd.py +60 -0
- siada/tools/coder/test_completion.py +25 -0
- siada/tools/coder/tool_docs.py +44 -0
- siada/tools/compression_tool.py +200 -0
- siada/tools/read_many_files/README.md +220 -0
- siada/tools/read_many_files/__init__.py +25 -0
- siada/tools/read_many_files/file_processor.py +486 -0
- siada/tools/read_many_files/filters.py +326 -0
- siada/tools/read_many_files/formatters.py +204 -0
- siada/tools/read_many_files/models.py +189 -0
- siada/tools/read_many_files_tool.py +233 -0
- siada/tools/tool_call_format/__init__.py +55 -0
- siada/tools/tool_call_format/file_to_language.py +231 -0
- siada/tools/tool_call_format/formatter_factory.py +80 -0
- siada/tools/tool_call_format/formatters.py +282 -0
- siada/tools/tool_call_format/tool_call_formatter.py +54 -0
- siada/tools/web/__init__.py +0 -0
- siada/tools/web/web_crawl.py +208 -0
- siada/utils.py +194 -0
- siada_cli-0.0.1.dist-info/LICENSE +201 -0
- siada_cli-0.0.1.dist-info/METADATA +457 -0
- siada_cli-0.0.1.dist-info/RECORD +215 -0
- siada_cli-0.0.1.dist-info/WHEEL +4 -0
- siada_cli-0.0.1.dist-info/entry_points.txt +3 -0
siada/support/spinner.py
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Thread-based, killable spinner utility with beautiful animations and colors.
|
|
5
|
+
|
|
6
|
+
Features:
|
|
7
|
+
- Unicode braille patterns for smooth animation
|
|
8
|
+
- Color support with automatic terminal detection
|
|
9
|
+
- Graceful fallback to ASCII for limited terminals
|
|
10
|
+
- Thread-safe operation
|
|
11
|
+
|
|
12
|
+
Use it like:
|
|
13
|
+
|
|
14
|
+
from siada.support.spinner import WaitingSpinner
|
|
15
|
+
|
|
16
|
+
spinner = WaitingSpinner("Waiting for LLM")
|
|
17
|
+
spinner.start()
|
|
18
|
+
... # long task
|
|
19
|
+
spinner.stop()
|
|
20
|
+
|
|
21
|
+
Or as a context manager:
|
|
22
|
+
|
|
23
|
+
with WaitingSpinner("Processing...") as spinner:
|
|
24
|
+
# long task
|
|
25
|
+
pass
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import os
|
|
29
|
+
import sys
|
|
30
|
+
import threading
|
|
31
|
+
import time
|
|
32
|
+
|
|
33
|
+
from rich.console import Console
|
|
34
|
+
from rich.text import Text
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Spinner:
|
|
38
|
+
"""
|
|
39
|
+
Modern, colorful spinner with smooth unicode animations.
|
|
40
|
+
|
|
41
|
+
Features:
|
|
42
|
+
- Uses unicode braille patterns for smooth, professional animation
|
|
43
|
+
- Automatic color detection and beautiful color cycling
|
|
44
|
+
- Graceful fallback to ASCII for limited terminals
|
|
45
|
+
- Optimized rendering with Rich console integration
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
last_frame_idx = 0 # Class variable to store the last frame index
|
|
49
|
+
|
|
50
|
+
def __init__(self, text: str, text_color: str = "bright_white"):
|
|
51
|
+
self.text = text
|
|
52
|
+
self.text_color = text_color
|
|
53
|
+
self.start_time = time.time()
|
|
54
|
+
self.last_update = 0.0
|
|
55
|
+
self.visible = False
|
|
56
|
+
self.is_tty = sys.stdout.isatty()
|
|
57
|
+
self.console = Console()
|
|
58
|
+
self.use_colors = self._supports_colors()
|
|
59
|
+
|
|
60
|
+
# Modern spinner patterns with better visual appeal
|
|
61
|
+
if self._supports_unicode() and self.use_colors:
|
|
62
|
+
# Beautiful unicode spinner with colors
|
|
63
|
+
self.frames = [
|
|
64
|
+
"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"
|
|
65
|
+
]
|
|
66
|
+
self.colors = ["cyan", "bright_cyan", "blue", "bright_blue", "magenta", "bright_magenta"]
|
|
67
|
+
self.scan_char = "⠋"
|
|
68
|
+
elif self._supports_unicode():
|
|
69
|
+
# Unicode without colors
|
|
70
|
+
self.frames = [
|
|
71
|
+
"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"
|
|
72
|
+
]
|
|
73
|
+
self.colors = None
|
|
74
|
+
self.scan_char = "⠋"
|
|
75
|
+
else:
|
|
76
|
+
# Fallback to ASCII with improved pattern
|
|
77
|
+
self.frames = [
|
|
78
|
+
"|", "/", "-", "\\", "|", "/", "-", "\\"
|
|
79
|
+
]
|
|
80
|
+
self.colors = None
|
|
81
|
+
self.scan_char = "|"
|
|
82
|
+
|
|
83
|
+
self.frame_idx = Spinner.last_frame_idx # Initialize from class variable
|
|
84
|
+
self.animation_len = 1 # Single character spinner
|
|
85
|
+
self.last_display_len = 0 # Length of the last spinner line (frame + text)
|
|
86
|
+
|
|
87
|
+
def _supports_unicode(self) -> bool:
|
|
88
|
+
"""Check if terminal supports unicode without side effects."""
|
|
89
|
+
if not self.is_tty:
|
|
90
|
+
return False
|
|
91
|
+
try:
|
|
92
|
+
# Test encoding capability without actually writing to stdout
|
|
93
|
+
test_char = "⠋"
|
|
94
|
+
test_char.encode(sys.stdout.encoding or 'utf-8')
|
|
95
|
+
return True
|
|
96
|
+
except (UnicodeEncodeError, AttributeError, LookupError):
|
|
97
|
+
return False
|
|
98
|
+
except Exception:
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
def _supports_colors(self) -> bool:
|
|
102
|
+
"""Check if terminal supports colors"""
|
|
103
|
+
if not self.is_tty:
|
|
104
|
+
return False
|
|
105
|
+
# Check environment variables that indicate color support
|
|
106
|
+
term = os.environ.get('TERM', '').lower()
|
|
107
|
+
colorterm = os.environ.get('COLORTERM', '').lower()
|
|
108
|
+
return (
|
|
109
|
+
'color' in term or
|
|
110
|
+
term in ('xterm', 'xterm-256color', 'screen', 'tmux') or
|
|
111
|
+
colorterm in ('truecolor', '24bit') or
|
|
112
|
+
hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
def _next_frame(self) -> str:
|
|
116
|
+
"""Get the next frame without applying colors (colors applied in step method)."""
|
|
117
|
+
frame = self.frames[self.frame_idx]
|
|
118
|
+
self.frame_idx = (self.frame_idx + 1) % len(self.frames)
|
|
119
|
+
Spinner.last_frame_idx = self.frame_idx # Update class variable
|
|
120
|
+
return frame
|
|
121
|
+
|
|
122
|
+
def _get_current_color(self) -> str:
|
|
123
|
+
"""Get the current color for the spinner frame."""
|
|
124
|
+
if self.use_colors and self.colors:
|
|
125
|
+
return self.colors[self.frame_idx % len(self.colors)]
|
|
126
|
+
return "white"
|
|
127
|
+
|
|
128
|
+
def set_text_color(self, color: str):
|
|
129
|
+
"""Set the color for the spinner text."""
|
|
130
|
+
self.text_color = color
|
|
131
|
+
|
|
132
|
+
def set_text(self, text: str, color: str = None):
|
|
133
|
+
"""Set the spinner text and optionally change its color."""
|
|
134
|
+
self.text = text
|
|
135
|
+
if color is not None:
|
|
136
|
+
self.text_color = color
|
|
137
|
+
|
|
138
|
+
def step(self, text: str = None) -> None:
|
|
139
|
+
"""Update the spinner animation and optionally change the text."""
|
|
140
|
+
if text is not None:
|
|
141
|
+
self.text = text
|
|
142
|
+
|
|
143
|
+
if not self.is_tty:
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
now = time.time()
|
|
148
|
+
if not self.visible and now - self.start_time >= 0.5:
|
|
149
|
+
self.visible = True
|
|
150
|
+
self.last_update = 0.0
|
|
151
|
+
if self.is_tty:
|
|
152
|
+
self.console.show_cursor(False)
|
|
153
|
+
|
|
154
|
+
if not self.visible or now - self.last_update < 0.1:
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
self.last_update = now
|
|
158
|
+
frame_str = self._next_frame()
|
|
159
|
+
except Exception:
|
|
160
|
+
# If anything goes wrong, just skip this frame
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
# Create the spinner line with consistent formatting
|
|
164
|
+
if self.use_colors and self.colors:
|
|
165
|
+
# For colored output, use rich text formatting
|
|
166
|
+
current_color = self._get_current_color()
|
|
167
|
+
text_content = Text()
|
|
168
|
+
text_content.append(frame_str, style=f"bold {current_color}")
|
|
169
|
+
text_content.append(f" {self.text}", style=self.text_color)
|
|
170
|
+
|
|
171
|
+
# Clear the line and print with rich
|
|
172
|
+
sys.stdout.write("\r")
|
|
173
|
+
with self.console.capture() as capture:
|
|
174
|
+
self.console.print(text_content, end="")
|
|
175
|
+
output = capture.get()
|
|
176
|
+
sys.stdout.write(output)
|
|
177
|
+
self.last_display_len = len(frame_str) + 1 + len(self.text) # Approximate length
|
|
178
|
+
else:
|
|
179
|
+
# Simple output for terminals without color support
|
|
180
|
+
line_to_display = f"{frame_str} {self.text}"
|
|
181
|
+
|
|
182
|
+
# Determine the maximum width for the spinner line
|
|
183
|
+
max_spinner_width = self.console.width - 2 if self.console.width > 2 else 0
|
|
184
|
+
if max_spinner_width > 0 and len(line_to_display) > max_spinner_width:
|
|
185
|
+
line_to_display = line_to_display[:max_spinner_width]
|
|
186
|
+
|
|
187
|
+
# Calculate padding to clear any remnants from a longer previous line
|
|
188
|
+
padding_to_clear = " " * max(0, self.last_display_len - len(line_to_display))
|
|
189
|
+
|
|
190
|
+
# Write the spinner frame and text
|
|
191
|
+
sys.stdout.write(f"\r{line_to_display}{padding_to_clear}")
|
|
192
|
+
self.last_display_len = len(line_to_display)
|
|
193
|
+
|
|
194
|
+
sys.stdout.flush()
|
|
195
|
+
|
|
196
|
+
def end(self) -> None:
|
|
197
|
+
"""Stop the spinner and clean up the display."""
|
|
198
|
+
try:
|
|
199
|
+
if self.visible and self.is_tty:
|
|
200
|
+
# Clear the current line more reliably
|
|
201
|
+
sys.stdout.write("\r\033[K") # \033[K clears from cursor to end of line
|
|
202
|
+
sys.stdout.flush()
|
|
203
|
+
self.console.show_cursor(True)
|
|
204
|
+
except Exception:
|
|
205
|
+
# If cleanup fails, just continue - better than crashing
|
|
206
|
+
pass
|
|
207
|
+
finally:
|
|
208
|
+
self.visible = False
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class WaitingSpinner:
|
|
212
|
+
"""Background spinner that can be started/stopped safely and restarted."""
|
|
213
|
+
|
|
214
|
+
def __init__(self, text: str = "Waiting for LLM", delay: float = 0.15, text_color: str = "bright_cyan"):
|
|
215
|
+
self.text = text
|
|
216
|
+
self.text_color = text_color
|
|
217
|
+
self.delay = delay
|
|
218
|
+
self.spinner = None
|
|
219
|
+
self._stop_event = None
|
|
220
|
+
self._thread = None
|
|
221
|
+
|
|
222
|
+
def _spin(self):
|
|
223
|
+
"""Internal spinning loop."""
|
|
224
|
+
if self.spinner is None:
|
|
225
|
+
return
|
|
226
|
+
while not self._stop_event.is_set():
|
|
227
|
+
self.spinner.step()
|
|
228
|
+
time.sleep(self.delay)
|
|
229
|
+
self.spinner.end()
|
|
230
|
+
|
|
231
|
+
def start(self):
|
|
232
|
+
"""Start the spinner in a background thread."""
|
|
233
|
+
if self._thread is not None and self._thread.is_alive():
|
|
234
|
+
return # Already running
|
|
235
|
+
|
|
236
|
+
# Create fresh instances for restart capability
|
|
237
|
+
self.spinner = Spinner(self.text, self.text_color)
|
|
238
|
+
self._stop_event = threading.Event()
|
|
239
|
+
self._thread = threading.Thread(target=self._spin, daemon=True)
|
|
240
|
+
self._thread.start()
|
|
241
|
+
|
|
242
|
+
def stop(self):
|
|
243
|
+
"""Request the spinner to stop and wait briefly for the thread to exit."""
|
|
244
|
+
if self._stop_event is not None:
|
|
245
|
+
self._stop_event.set()
|
|
246
|
+
if self._thread is not None and self._thread.is_alive():
|
|
247
|
+
self._thread.join(timeout=self.delay * 2) # Give more time for cleanup
|
|
248
|
+
if self.spinner is not None:
|
|
249
|
+
self.spinner.end()
|
|
250
|
+
|
|
251
|
+
def update_text(self, text: str, color: str = None):
|
|
252
|
+
"""Update the spinner text dynamically and optionally change its color."""
|
|
253
|
+
self.text = text
|
|
254
|
+
if color is not None:
|
|
255
|
+
self.text_color = color
|
|
256
|
+
if self.spinner is not None:
|
|
257
|
+
if color is not None:
|
|
258
|
+
self.spinner.set_text(text, color)
|
|
259
|
+
else:
|
|
260
|
+
self.spinner.text = text
|
|
261
|
+
|
|
262
|
+
def set_text_color(self, color: str):
|
|
263
|
+
"""Set the color for the spinner text."""
|
|
264
|
+
self.text_color = color
|
|
265
|
+
if self.spinner is not None:
|
|
266
|
+
self.spinner.set_text_color(color)
|
|
267
|
+
|
|
268
|
+
# Allow use as a context-manager
|
|
269
|
+
def __enter__(self):
|
|
270
|
+
self.start()
|
|
271
|
+
return self
|
|
272
|
+
|
|
273
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
274
|
+
self.stop()
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def main():
|
|
278
|
+
"""Demo the improved spinner with colors and unicode."""
|
|
279
|
+
print("🎨 Testing the colorful spinner...")
|
|
280
|
+
print("Press Ctrl+C to stop")
|
|
281
|
+
|
|
282
|
+
# Test the basic Spinner with different text colors
|
|
283
|
+
print("\n🔸 Testing basic Spinner with color changes...")
|
|
284
|
+
spinner = Spinner("Starting with white text...", "bright_white")
|
|
285
|
+
try:
|
|
286
|
+
for i in range(100):
|
|
287
|
+
time.sleep(0.1)
|
|
288
|
+
if i == 20:
|
|
289
|
+
spinner.set_text("Now in green! 🟢", "bright_green")
|
|
290
|
+
elif i == 40:
|
|
291
|
+
spinner.set_text("Switching to red! 🔴", "bright_red")
|
|
292
|
+
elif i == 60:
|
|
293
|
+
spinner.set_text("Beautiful blue! 🔵", "bright_blue")
|
|
294
|
+
elif i == 80:
|
|
295
|
+
spinner.set_text("Magnificent magenta! 🟣", "bright_magenta")
|
|
296
|
+
else:
|
|
297
|
+
spinner.step()
|
|
298
|
+
spinner.end()
|
|
299
|
+
print("✅ Color-changing spinner test completed!")
|
|
300
|
+
|
|
301
|
+
# Test the WaitingSpinner with color changes
|
|
302
|
+
print("\n🔸 Testing WaitingSpinner with dynamic colors...")
|
|
303
|
+
with WaitingSpinner("Starting in cyan...", text_color="bright_cyan") as waiting_spinner:
|
|
304
|
+
time.sleep(1)
|
|
305
|
+
waiting_spinner.update_text("Now in yellow! ⚡", "bright_yellow")
|
|
306
|
+
time.sleep(1)
|
|
307
|
+
waiting_spinner.set_text_color("bright_red")
|
|
308
|
+
waiting_spinner.update_text("Finishing in red! 🚀")
|
|
309
|
+
time.sleep(1)
|
|
310
|
+
|
|
311
|
+
print("✅ All colorful tests completed successfully! 🌈")
|
|
312
|
+
|
|
313
|
+
except KeyboardInterrupt:
|
|
314
|
+
print("\n❌ Interrupted by user.")
|
|
315
|
+
finally:
|
|
316
|
+
spinner.end()
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
if __name__ == "__main__":
|
|
320
|
+
main()
|
siada/tools/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# AST Code Definition Extraction Tool
|
|
2
|
+
|
|
3
|
+
This module provides code definition extraction functionality based on tree-sitter and pygments, primarily used for analyzing source code files and extracting function, class, method, and other definition information.
|
|
4
|
+
|
|
5
|
+
## Main Features
|
|
6
|
+
|
|
7
|
+
### `_list_code_definition_names(fname, rel_fname=None)`
|
|
8
|
+
|
|
9
|
+
Parses a source code file and returns a summary of its code definitions.
|
|
10
|
+
|
|
11
|
+
#### Parameters
|
|
12
|
+
- `fname` (str): Absolute path to the source file
|
|
13
|
+
- `rel_fname` (str, optional): Relative path to the source file, defaults to filename
|
|
14
|
+
|
|
15
|
+
#### Returns
|
|
16
|
+
- `str`: Formatted string containing file structure and code definitions
|
|
17
|
+
|
|
18
|
+
## Parsing Principles
|
|
19
|
+
|
|
20
|
+
### 1. Dual Parsing Strategy
|
|
21
|
+
- **Primary Parser**: Uses tree-sitter for syntax tree parsing, providing precise code structure analysis
|
|
22
|
+
- **Fallback Parser**: Uses pygments for lexical analysis as a backup for reference extraction
|
|
23
|
+
|
|
24
|
+
### 2. Parsing Workflow
|
|
25
|
+
1. **Language Detection**: Identifies programming language based on file extension
|
|
26
|
+
2. **Syntax Parsing**: Uses tree-sitter to build Abstract Syntax Tree (AST)
|
|
27
|
+
3. **Query Execution**: Runs predefined query patterns to extract definitions and references
|
|
28
|
+
4. **Fallback Processing**: Uses pygments to supplement reference information if only definitions are found
|
|
29
|
+
5. **Format Output**: Uses TreeContext to generate code tree structure with context
|
|
30
|
+
|
|
31
|
+
### 3. Core Components
|
|
32
|
+
- **Tag Model**: Data structure representing code identifiers
|
|
33
|
+
- **TreeContext**: Provides code context and formatting functionality
|
|
34
|
+
- **Query Files**: .scm files stored in `siada/queries/` directory, defining parsing rules for each language
|
|
35
|
+
|
|
36
|
+
## Supported Programming Languages
|
|
37
|
+
|
|
38
|
+
Based on tree-sitter language support, including but not limited to:
|
|
39
|
+
|
|
40
|
+
### Fully Supported Languages
|
|
41
|
+
- **Python** (.py) - functions, classes, methods, decorators, async functions, etc.
|
|
42
|
+
- **JavaScript** (.js) - functions, classes, arrow functions, object methods, etc.
|
|
43
|
+
- **TypeScript** (.ts) - type definitions, interfaces, classes, functions, etc.
|
|
44
|
+
- **Java** (.java) - classes, methods, interfaces, enums, etc.
|
|
45
|
+
- **C/C++** (.c, .cpp, .h, .hpp) - functions, structs, classes, etc.
|
|
46
|
+
- **Go** (.go) - functions, structs, methods, interfaces, etc.
|
|
47
|
+
- **Rust** (.rs) - functions, structs, traits, impl, etc.
|
|
48
|
+
- **Ruby** (.rb) - classes, methods, modules, etc.
|
|
49
|
+
|
|
50
|
+
### Partially Supported Languages
|
|
51
|
+
- **C#** (.cs) - classes, methods, properties, etc.
|
|
52
|
+
- **PHP** (.php) - classes, functions, methods, etc.
|
|
53
|
+
- **Scala** (.scala) - classes, objects, traits, methods, etc.
|
|
54
|
+
- **Kotlin** (.kt) - classes, functions, objects, etc.
|
|
55
|
+
|
|
56
|
+
### Query File Locations
|
|
57
|
+
- `siada/queries/tree-sitter-language-pack/` - Primary query files
|
|
58
|
+
- `siada/queries/tree-sitter-languages/` - Fallback query files
|
|
59
|
+
|
|
60
|
+
## Output Formats
|
|
61
|
+
|
|
62
|
+
### 1. Standard Format (when definitions exist)
|
|
63
|
+
```
|
|
64
|
+
File: <filename>
|
|
65
|
+
Definitions: <definition_count>, References: <reference_count>
|
|
66
|
+
|
|
67
|
+
<code_tree_structure>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2. Simplified Format (fallback mode)
|
|
71
|
+
```
|
|
72
|
+
File: <filename>
|
|
73
|
+
Definitions: <definition_count>, References: <reference_count>
|
|
74
|
+
|
|
75
|
+
Definitions found:
|
|
76
|
+
- <definition_name> (line <line_number>)
|
|
77
|
+
- <definition_name> (line <line_number>)
|
|
78
|
+
...
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 3. Error Format (no definitions or errors)
|
|
82
|
+
```
|
|
83
|
+
No code definitions found in <filename>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Output Examples
|
|
87
|
+
|
|
88
|
+
### Python File Example
|
|
89
|
+
|
|
90
|
+
**Input File** (`calculator.py`):
|
|
91
|
+
```python
|
|
92
|
+
"""A simple calculator module with multiple classes and functions."""
|
|
93
|
+
|
|
94
|
+
class Calculator:
|
|
95
|
+
"""Basic calculator with arithmetic operations."""
|
|
96
|
+
|
|
97
|
+
def __init__(self):
|
|
98
|
+
self.history = []
|
|
99
|
+
|
|
100
|
+
def add(self, a, b):
|
|
101
|
+
result = a + b
|
|
102
|
+
self.history.append(f"{a} + {b} = {result}")
|
|
103
|
+
return result
|
|
104
|
+
|
|
105
|
+
def multiply(self, a, b):
|
|
106
|
+
result = a * b
|
|
107
|
+
self.history.append(f"{a} * {b} = {result}")
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
class ScientificCalculator(Calculator):
|
|
111
|
+
"""Extended calculator with scientific functions."""
|
|
112
|
+
|
|
113
|
+
def power(self, base, exponent):
|
|
114
|
+
result = base ** exponent
|
|
115
|
+
self.history.append(f"{base} ^ {exponent} = {result}")
|
|
116
|
+
return result
|
|
117
|
+
|
|
118
|
+
def create_calculator(scientific=False):
|
|
119
|
+
"""Factory function to create calculator instances."""
|
|
120
|
+
if scientific:
|
|
121
|
+
return ScientificCalculator()
|
|
122
|
+
return Calculator()
|
|
123
|
+
|
|
124
|
+
# Global configuration
|
|
125
|
+
DEFAULT_PRECISION = 2
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Parsed Output**:
|
|
129
|
+
```
|
|
130
|
+
File: calculator.py
|
|
131
|
+
Definitions: 6, References: 15
|
|
132
|
+
|
|
133
|
+
⋮
|
|
134
|
+
│class Calculator:
|
|
135
|
+
│ """Basic calculator with arithmetic operations."""
|
|
136
|
+
│
|
|
137
|
+
│ def __init__(self):
|
|
138
|
+
⋮
|
|
139
|
+
│ def add(self, a, b):
|
|
140
|
+
⋮
|
|
141
|
+
│ def multiply(self, a, b):
|
|
142
|
+
⋮
|
|
143
|
+
│class ScientificCalculator(Calculator):
|
|
144
|
+
│ """Extended calculator with scientific functions."""
|
|
145
|
+
│
|
|
146
|
+
│ def power(self, base, exponent):
|
|
147
|
+
⋮
|
|
148
|
+
│def create_calculator(scientific=False):
|
|
149
|
+
⋮
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Key Differences from Source**:
|
|
153
|
+
- Shows only definition signatures, omitting implementation details
|
|
154
|
+
- Displays statistical summary (6 definitions, 15 references)
|
|
155
|
+
- Uses `⋮` to indicate omitted code sections
|
|
156
|
+
- Focuses on structural overview rather than complete code
|
|
157
|
+
|
|
158
|
+
### JavaScript File Example
|
|
159
|
+
|
|
160
|
+
**Input File** (`user-manager.js`):
|
|
161
|
+
```javascript
|
|
162
|
+
/**
|
|
163
|
+
* User management system with authentication
|
|
164
|
+
*/
|
|
165
|
+
|
|
166
|
+
class User {
|
|
167
|
+
constructor(username, email) {
|
|
168
|
+
this.username = username;
|
|
169
|
+
this.email = email;
|
|
170
|
+
this.isActive = true;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
activate() {
|
|
174
|
+
this.isActive = true;
|
|
175
|
+
console.log(`User ${this.username} activated`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
deactivate() {
|
|
179
|
+
this.isActive = false;
|
|
180
|
+
console.log(`User ${this.username} deactivated`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
class UserManager {
|
|
185
|
+
constructor() {
|
|
186
|
+
this.users = new Map();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
addUser(username, email) {
|
|
190
|
+
const user = new User(username, email);
|
|
191
|
+
this.users.set(username, user);
|
|
192
|
+
return user;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
static getInstance() {
|
|
196
|
+
if (!UserManager.instance) {
|
|
197
|
+
UserManager.instance = new UserManager();
|
|
198
|
+
}
|
|
199
|
+
return UserManager.instance;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const authService = {
|
|
204
|
+
login: function(username, password) {
|
|
205
|
+
// Authentication logic here
|
|
206
|
+
return true;
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
logout: (username) => {
|
|
210
|
+
// Logout logic here
|
|
211
|
+
console.log(`${username} logged out`);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
async function initializeSystem() {
|
|
216
|
+
const manager = UserManager.getInstance();
|
|
217
|
+
return manager;
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Parsed Output**:
|
|
222
|
+
```
|
|
223
|
+
File: user-manager.js
|
|
224
|
+
Definitions: 9, References: 8
|
|
225
|
+
|
|
226
|
+
⋮
|
|
227
|
+
│class User {
|
|
228
|
+
│ constructor(username, email) {
|
|
229
|
+
│ this.username = username;
|
|
230
|
+
│ this.email = email;
|
|
231
|
+
│ this.isActive = true;
|
|
232
|
+
│ }
|
|
233
|
+
│
|
|
234
|
+
│ activate() {
|
|
235
|
+
⋮
|
|
236
|
+
│ deactivate() {
|
|
237
|
+
⋮
|
|
238
|
+
│class UserManager {
|
|
239
|
+
│ constructor() {
|
|
240
|
+
│ this.users = new Map();
|
|
241
|
+
│ }
|
|
242
|
+
│
|
|
243
|
+
│ addUser(username, email) {
|
|
244
|
+
⋮
|
|
245
|
+
│ static getInstance() {
|
|
246
|
+
⋮
|
|
247
|
+
│const authService = {
|
|
248
|
+
│ login: function(username, password) {
|
|
249
|
+
⋮
|
|
250
|
+
│ logout: (username) => {
|
|
251
|
+
⋮
|
|
252
|
+
│async function initializeSystem() {
|
|
253
|
+
⋮
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Key Differences from Source**:
|
|
257
|
+
- Extracts 9 definitions (classes, methods, functions, object properties)
|
|
258
|
+
- Counts 8 references to other identifiers
|
|
259
|
+
- Omits implementation details and comments
|
|
260
|
+
- Shows structural hierarchy and method signatures
|
|
261
|
+
|
|
262
|
+
### Empty or Unsupported File Example
|
|
263
|
+
|
|
264
|
+
**Output**:
|
|
265
|
+
```
|
|
266
|
+
No code definitions found in empty_file.py
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Usage Examples
|
|
270
|
+
|
|
271
|
+
### Basic Usage
|
|
272
|
+
```python
|
|
273
|
+
from siada.tools.ast.ast_tool import _list_code_definition_names
|
|
274
|
+
|
|
275
|
+
# Analyze Python file
|
|
276
|
+
result = _list_code_definition_names("/path/to/file.py")
|
|
277
|
+
print(result)
|
|
278
|
+
|
|
279
|
+
# Specify relative path
|
|
280
|
+
result = _list_code_definition_names("/absolute/path/to/file.py", "src/file.py")
|
|
281
|
+
print(result)
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Batch Processing
|
|
285
|
+
```python
|
|
286
|
+
import os
|
|
287
|
+
from pathlib import Path
|
|
288
|
+
|
|
289
|
+
def analyze_project(project_dir):
|
|
290
|
+
"""Analyze all Python files in a project"""
|
|
291
|
+
for py_file in Path(project_dir).rglob("*.py"):
|
|
292
|
+
rel_path = py_file.relative_to(project_dir)
|
|
293
|
+
result = _list_code_definition_names(str(py_file), str(rel_path))
|
|
294
|
+
print(f"\n{'='*50}")
|
|
295
|
+
print(result)
|
|
296
|
+
|
|
297
|
+
# Usage example
|
|
298
|
+
analyze_project("/path/to/your/project")
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Error Handling
|
|
302
|
+
```python
|
|
303
|
+
def safe_analyze(file_path):
|
|
304
|
+
"""Safely analyze file with error handling"""
|
|
305
|
+
try:
|
|
306
|
+
result = _list_code_definition_names(file_path)
|
|
307
|
+
if "No code definitions found" in result:
|
|
308
|
+
print(f"No definitions found in {file_path}")
|
|
309
|
+
else:
|
|
310
|
+
print(result)
|
|
311
|
+
except Exception as e:
|
|
312
|
+
print(f"Error analyzing {file_path}: {e}")
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Technical Details
|
|
316
|
+
|
|
317
|
+
### Dependencies
|
|
318
|
+
- `grep-ast`: Provides TreeContext and language identification functionality
|
|
319
|
+
- `pygments`: Provides lexical analysis fallback functionality
|
|
320
|
+
- `tree-sitter`: Underlying syntax parsing engine
|
|
321
|
+
|
|
322
|
+
### Performance Characteristics
|
|
323
|
+
- **Memory Efficiency**: Uses generator pattern for processing large files
|
|
324
|
+
- **Error Tolerance**: Syntax errors don't completely prevent parsing
|
|
325
|
+
- **Cache Optimization**: TreeContext provides context caching
|
|
326
|
+
- **Line Length Limiting**: Automatically truncates overly long lines to prevent excessive output
|
|
327
|
+
|
|
328
|
+
### Limitations and Considerations
|
|
329
|
+
1. **File Encoding**: Only supports UTF-8 encoded files
|
|
330
|
+
2. **File Size**: May have performance impact on very large files
|
|
331
|
+
3. **Language Support**: Depends on availability of tree-sitter query files
|
|
332
|
+
4. **Accuracy**: Complex macro definitions or dynamic code may not be accurately identified
|
|
333
|
+
|
|
334
|
+
## Related Modules
|
|
335
|
+
|
|
336
|
+
- `models.py`: Defines Tag data model
|
|
337
|
+
- `../../../queries/`: Stores tree-sitter query files for various languages
|
|
338
|
+
- `../../../tests/tools/ast/`: Complete test suite
|
|
339
|
+
|
|
340
|
+
## Changelog
|
|
341
|
+
|
|
342
|
+
- Support for multiple programming languages code definition extraction
|
|
343
|
+
- Implementation of dual parsing strategy for improved accuracy
|
|
344
|
+
- Added error handling and fallback mechanisms
|
|
345
|
+
- Optimized output format and performance
|