raggiecode 0.2.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.
Files changed (93) hide show
  1. Agent/__init__.py +0 -0
  2. Agent/agent.py +891 -0
  3. Agent/chat_history_db.py +1500 -0
  4. Agent/command.py +49 -0
  5. Agent/config.py +46 -0
  6. Agent/effort_levels.py +33 -0
  7. Agent/git_manager.py +727 -0
  8. Agent/tools.py +35 -0
  9. Commands/__init__.py +18 -0
  10. Commands/effort.py +42 -0
  11. Commands/global_todo.py +23 -0
  12. Commands/help.py +22 -0
  13. Commands/reasoning.py +24 -0
  14. Commands/redo.py +11 -0
  15. Commands/reindex.py +27 -0
  16. Commands/shell.py +28 -0
  17. Commands/stream.py +24 -0
  18. Commands/undo.py +13 -0
  19. Commands/unlimited_effort.py +8 -0
  20. Commands/window_size.py +29 -0
  21. RAG/__init__.py +0 -0
  22. RAG/document.py +119 -0
  23. RAG/find.py +408 -0
  24. RAG/graph.py +231 -0
  25. Tools/GetFileCodeStructure.py +43 -0
  26. Tools/GetSymbolSourceCode.py +27 -0
  27. Tools/__init__.py +39 -0
  28. Tools/ask_user.py +102 -0
  29. Tools/dispatch_subagent.py +215 -0
  30. Tools/document.py +35 -0
  31. Tools/edit_symbol.py +250 -0
  32. Tools/fuzzy_search.py +119 -0
  33. Tools/list_dir.py +51 -0
  34. Tools/read.py +49 -0
  35. Tools/read_image.py +75 -0
  36. Tools/remove.py +75 -0
  37. Tools/replace.py +305 -0
  38. Tools/search.py +41 -0
  39. Tools/shell.py +149 -0
  40. Tools/shell_kill.py +87 -0
  41. Tools/temp_background_service.py +113 -0
  42. Tools/todo_list.py +481 -0
  43. Tools/utils.py +116 -0
  44. Tools/view_changes.py +179 -0
  45. Tools/walk_call_tree.py +30 -0
  46. Tools/web_fetch.py +175 -0
  47. Tools/web_search.py +69 -0
  48. Tools/write.py +48 -0
  49. cli.py +111 -0
  50. config/__init__.py +0 -0
  51. config/coder_system_prompt.md +119 -0
  52. config/roles.json +43 -0
  53. config/tools.json +709 -0
  54. indexing/__init__.py +0 -0
  55. indexing/cli.py +128 -0
  56. indexing/code_index_sdk.py +832 -0
  57. indexing/code_indexer.py +1763 -0
  58. indexing/db_schema.py +396 -0
  59. indexing/export_to_json.py +346 -0
  60. indexing/extractors.py +189 -0
  61. indexing/file_utils.py +97 -0
  62. indexing/frontend/__init__.py +0 -0
  63. indexing/frontend/css_extractor.py +195 -0
  64. indexing/frontend/css_parser.py +387 -0
  65. indexing/frontend/css_selector_utils.py +226 -0
  66. indexing/frontend/edit_safety.py +573 -0
  67. indexing/frontend/graph.py +838 -0
  68. indexing/frontend/html_extractor.py +496 -0
  69. indexing/frontend/html_parser.py +314 -0
  70. indexing/frontend/jsx_extractor.py +1204 -0
  71. indexing/frontend/location_lookup.py +247 -0
  72. indexing/frontend/resolver.py +485 -0
  73. indexing/frontend/runtime_resolver.py +862 -0
  74. indexing/frontend/semantic_output.py +705 -0
  75. indexing/frontend/source_location.py +69 -0
  76. indexing/frontend_config.py +72 -0
  77. indexing/frontend_models.py +347 -0
  78. indexing/language_config.py +360 -0
  79. indexing/models.py +284 -0
  80. indexing/node_utils.py +1112 -0
  81. indexing/parse_worker.py +1082 -0
  82. indexing/queries.py +1542 -0
  83. indexing/sdk_examples.py +426 -0
  84. interactive.py +248 -0
  85. raggie.py +673 -0
  86. raggiecode-0.2.1.dist-info/METADATA +944 -0
  87. raggiecode-0.2.1.dist-info/RECORD +93 -0
  88. raggiecode-0.2.1.dist-info/WHEEL +5 -0
  89. raggiecode-0.2.1.dist-info/entry_points.txt +2 -0
  90. raggiecode-0.2.1.dist-info/top_level.txt +10 -0
  91. skills/__init__.py +3 -0
  92. skills/manager.py +114 -0
  93. skills/tool.py +121 -0
@@ -0,0 +1,426 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Examples demonstrating the Code Index SDK for AI Agents.
4
+ """
5
+
6
+ from indexing.code_index_sdk import CodeIndexSDK
7
+
8
+
9
+ def example_basic_queries():
10
+ """Basic query examples."""
11
+ print("=== Basic Queries ===\n")
12
+
13
+ with CodeIndexSDK("code_index.db") as sdk:
14
+ # Get all files
15
+ files = sdk.get_files()
16
+ print(f"Total files: {len(files)}")
17
+ for file in files[:5]:
18
+ print(f" - {file.path} ({file.language})")
19
+
20
+ # Get statistics
21
+ stats = sdk.get_statistics()
22
+ print(f"\nStatistics: {stats}")
23
+
24
+ # Get language breakdown
25
+ lang_stats = sdk.get_language_statistics()
26
+ print(f"Language breakdown: {lang_stats}")
27
+
28
+
29
+ def example_function_search():
30
+ """Function search examples."""
31
+ print("\n=== Function Search ===\n")
32
+
33
+ with CodeIndexSDK("code_index.db") as sdk:
34
+ # Search for functions by pattern
35
+ render_funcs = sdk.search_functions("render")
36
+ print(f"Functions matching 'render': {len(render_funcs)}")
37
+ for func in render_funcs[:3]:
38
+ print(f" - {func.name} in {func.file_path}")
39
+ print(f" Parameters: {[p['name'] for p in func.parameters]}")
40
+
41
+ # Get specific function by name
42
+ main_funcs = sdk.get_function_by_name("main")
43
+ print(f"\nFunctions named 'main': {len(main_funcs)}")
44
+ for func in main_funcs:
45
+ print(f" - {func.file_path}:{func.location.start_line}")
46
+
47
+
48
+ def example_class_exploration():
49
+ """Class exploration examples."""
50
+ print("\n=== Class Exploration ===\n")
51
+
52
+ with CodeIndexSDK("code_index.db") as sdk:
53
+ # Search for classes
54
+ classes = sdk.search_classes("Service")
55
+ print(f"Classes matching 'Service': {len(classes)}")
56
+
57
+ if classes:
58
+ cls = classes[0]
59
+ print(f"\nExploring class: {cls.name}")
60
+ print(f" File: {cls.file_path}")
61
+ print(f" Base classes: {cls.base_classes}")
62
+
63
+ # Get class methods
64
+ methods = sdk.get_class_methods(cls.id)
65
+ print(f" Methods ({len(methods)}):")
66
+ for method in methods[:5]:
67
+ print(f" - {method.name}({', '.join(p['name'] for p in method.parameters)})")
68
+
69
+ # Get class variables
70
+ variables = sdk.get_class_variables(cls.id)
71
+ print(f" Variables ({len(variables)}):")
72
+ for var in variables[:5]:
73
+ print(f" - {var.name}: {var.field_type or 'unknown'}")
74
+
75
+ # Get nested classes
76
+ nested = sdk.get_nested_classes(cls.id)
77
+ if nested:
78
+ print(f" Nested classes: {[c.name for c in nested]}")
79
+
80
+
81
+ def example_file_analysis():
82
+ """File analysis examples."""
83
+ print("\n=== File Analysis ===\n")
84
+
85
+ with CodeIndexSDK("code_index.db") as sdk:
86
+ # Get a specific file
87
+ file = sdk.get_files()[0]
88
+ print(f"Analyzing file: {file.path}")
89
+
90
+ # Get complete file summary
91
+ summary = sdk.get_file_summary(file.id)
92
+ print(f"\nFile summary:")
93
+ print(f" Functions: {len(summary['functions'])}")
94
+ print(f" Classes: {len(summary['classes'])}")
95
+ print(f" Variables: {len(summary['variables'])}")
96
+ print(f" Type aliases: {len(summary['type_aliases'])}")
97
+
98
+
99
+ def example_cross_reference():
100
+ """Cross-reference examples."""
101
+ print("\n=== Cross-Reference ===\n")
102
+
103
+ with CodeIndexSDK("code_index.db") as sdk:
104
+ # Find all usages of a pattern across different entity types
105
+ results = sdk.search_all("auth")
106
+ print(f"Results for 'auth' pattern:")
107
+ print(f" Functions: {len(results['functions'])}")
108
+ print(f" Classes: {len(results['classes'])}")
109
+ print(f" Variables: {len(results['variables'])}")
110
+ print(f" Type aliases: {len(results['type_aliases'])}")
111
+
112
+ # Show some results
113
+ if results['functions']:
114
+ print(f"\n Sample functions:")
115
+ for func in results['functions'][:3]:
116
+ print(f" - {func['name']} in {func['file_path']}")
117
+
118
+
119
+ def example_ai_agent_workflow():
120
+ """Example workflow for an AI agent."""
121
+ print("\n=== AI Agent Workflow ===\n")
122
+
123
+ with CodeIndexSDK("code_index.db") as sdk:
124
+ # Agent wants to understand the codebase structure
125
+ print("1. Understanding codebase structure...")
126
+ stats = sdk.get_statistics()
127
+ lang_stats = sdk.get_language_statistics()
128
+ print(f" Total files: {stats['total_files']}")
129
+ print(f" Languages: {list(lang_stats.keys())}")
130
+
131
+ # Agent wants to find authentication-related code
132
+ print("\n2. Finding authentication code...")
133
+ auth_funcs = sdk.search_functions("auth")
134
+ auth_classes = sdk.search_classes("Auth")
135
+ print(f" Found {len(auth_funcs)} auth-related functions")
136
+ print(f" Found {len(auth_classes)} auth-related classes")
137
+
138
+ # Agent wants to explore a specific class
139
+ if auth_classes:
140
+ print(f"\n3. Exploring {auth_classes[0].name} class...")
141
+ cls = auth_classes[0]
142
+ methods = sdk.get_class_methods(cls.id)
143
+ print(f" Class has {len(methods)} methods")
144
+
145
+ # Agent wants to understand method signatures
146
+ print(f"\n4. Method signatures:")
147
+ for method in methods[:5]:
148
+ params = ", ".join(f"{p['name']}: {p.get('type', 'any')}" for p in method.parameters)
149
+ return_type = method.return_type or "void"
150
+ print(f" {method.name}({params}) -> {return_type}")
151
+
152
+
153
+ def example_context_manager():
154
+ """Context manager usage example."""
155
+ print("\n=== Context Manager ===\n")
156
+
157
+ # Using context manager for automatic cleanup
158
+ with CodeIndexSDK("code_index.db") as sdk:
159
+ files = sdk.get_files()
160
+ print(f"Files in context: {len(files)}")
161
+
162
+ # Connection is automatically closed here
163
+ print("Connection closed automatically.")
164
+
165
+
166
+ def example_manual_connection():
167
+ """Manual connection management example."""
168
+ print("\n=== Manual Connection ===\n")
169
+
170
+ sdk = CodeIndexSDK("code_index.db")
171
+ try:
172
+ files = sdk.get_files()
173
+ print(f"Files: {len(files)}")
174
+ finally:
175
+ sdk.close()
176
+ print("Connection closed manually.")
177
+
178
+
179
+ def example_internal_external_imports():
180
+ """Example of filtering imports by internal/external dependencies."""
181
+ print("\n=== Internal vs External Imports ===\n")
182
+
183
+ with CodeIndexSDK("code_index.db") as sdk:
184
+ # Get all files
185
+ files = sdk.get_files()
186
+ if not files:
187
+ print("No files in database. Please run indexing first.")
188
+ return
189
+
190
+ # Analyze first file
191
+ file = files[0]
192
+ print(f"Analyzing file: {file.path}")
193
+
194
+ # Get all imports
195
+ all_imports = sdk.get_file_imports(file.id)
196
+ print(f"\nTotal imports: {len(all_imports)}")
197
+
198
+ # Get external imports
199
+ external_imports = sdk.get_external_imports(file.id)
200
+ print(f"External (third-party) imports: {len(external_imports)}")
201
+ for imp in external_imports[:5]:
202
+ print(f" - {imp.name}")
203
+
204
+ # Get internal imports
205
+ internal_imports = sdk.get_internal_imports(file.id)
206
+ print(f"\nInternal (project) imports: {len(internal_imports)}")
207
+ for imp in internal_imports[:5]:
208
+ print(f" - {imp.name}")
209
+
210
+ # Get all external imports across the entire codebase
211
+ print("\n--- All External Dependencies in Codebase ---")
212
+ all_external = sdk.get_external_imports()
213
+ # Group by import name to show unique external dependencies
214
+ unique_external = {}
215
+ for imp in all_external:
216
+ if imp.name not in unique_external:
217
+ unique_external[imp.name] = []
218
+ unique_external[imp.name].append(imp.file_path)
219
+
220
+ print(f"Unique external dependencies: {len(unique_external)}")
221
+ for dep_name, files in list(unique_external.items())[:10]:
222
+ print(f" - {dep_name} (used in {len(files)} file(s))")
223
+
224
+
225
+ def example_source_body_reading():
226
+ """Source body reading examples."""
227
+ print("\n=== Source Body Reading ===\n")
228
+
229
+ with CodeIndexSDK("code_index.db") as sdk:
230
+ # Read a function body by name
231
+ funcs = sdk.get_functions()
232
+ if not funcs:
233
+ print("No functions in database. Please run indexing first.")
234
+ return
235
+
236
+ func_name = funcs[0].name
237
+ print(f"Reading body of function: {func_name}")
238
+ body = sdk.get_function_body(func_name)
239
+ if body:
240
+ preview = body[:200] + "..." if len(body) > 200 else body
241
+ print(f"\n{preview}")
242
+ else:
243
+ print(" (source file not resolvable)")
244
+
245
+ # Disambiguate by file path when the same name appears in multiple files
246
+ matches = sdk.get_function_by_name(func_name)
247
+ if len(matches) > 1:
248
+ print(f"\n'{func_name}' appears in {len(matches)} files — reading from first:")
249
+ body = sdk.get_function_body(func_name, file_path=matches[0].file_path)
250
+ if body:
251
+ print(body[:200])
252
+
253
+ # Read a class body
254
+ classes = sdk.get_classes()
255
+ if classes:
256
+ cls_name = classes[0].name
257
+ print(f"\nReading body of class: {cls_name}")
258
+ body = sdk.get_class_body(cls_name)
259
+ if body:
260
+ preview = body[:200] + "..." if len(body) > 200 else body
261
+ print(f"\n{preview}")
262
+ else:
263
+ print(" (source file not resolvable)")
264
+
265
+
266
+ def example_complexity_filtering():
267
+ """Filter symbols by complexity (branches and lines of code)."""
268
+ print("\n=== Complexity Filtering ===\n")
269
+
270
+ with CodeIndexSDK("code_index.db") as sdk:
271
+ # Find functions with high branching complexity (5+ branches)
272
+ high_branch_funcs = sdk.get_functions_by_complexity(min_branches=5)
273
+ print(f"Functions with 5+ branches: {len(high_branch_funcs)}")
274
+ for func in high_branch_funcs[:5]:
275
+ lines = func.location.end_line - func.location.start_line + 1
276
+ print(f" - {func.name}: {func.branch_count} branches, {lines} LOC")
277
+
278
+ # Find long functions (30+ lines)
279
+ long_funcs = sdk.get_functions_by_complexity(min_lines=30)
280
+ print(f"\nFunctions with 30+ lines: {len(long_funcs)}")
281
+ for func in long_funcs[:5]:
282
+ lines = func.location.end_line - func.location.start_line + 1
283
+ print(f" - {func.name}: {lines} lines, {func.branch_count} branches")
284
+
285
+ # LOGICAL OR: Find functions that are EITHER high-branch OR long
286
+ # (good for finding "suspicious" code to review)
287
+ complex_funcs = sdk.get_functions_by_complexity(
288
+ min_branches=5,
289
+ min_lines=30,
290
+ match_any=True # LOGICAL OR
291
+ )
292
+ print(f"\nComplex functions (5+ branches OR 30+ lines): {len(complex_funcs)}")
293
+
294
+ # LOGICAL AND: Find functions that are BOTH high-branch AND long
295
+ # (the truly complex functions)
296
+ very_complex_funcs = sdk.get_functions_by_complexity(
297
+ min_branches=5,
298
+ min_lines=30,
299
+ match_any=False # LOGICAL AND
300
+ )
301
+ print(f"Very complex functions (5+ branches AND 30+ lines): {len(very_complex_funcs)}")
302
+
303
+ # Range filtering: medium complexity functions
304
+ medium_funcs = sdk.get_functions_by_complexity(
305
+ min_branches=2,
306
+ max_branches=5,
307
+ min_lines=10,
308
+ max_lines=30
309
+ )
310
+ print(f"\nMedium complexity functions (2-5 branches, 10-30 lines): {len(medium_funcs)}")
311
+
312
+ # Filter by file
313
+ files = sdk.get_files()
314
+ if files:
315
+ file_funcs = sdk.get_functions_by_complexity(
316
+ min_branches=3,
317
+ file_id=files[0].id
318
+ )
319
+ print(f"\nComplex functions in {files[0].path}: {len(file_funcs)}")
320
+
321
+ # Convenience method for finding complex code
322
+ complex_symbols = sdk.get_complex_symbols(min_branches=5, min_lines=30)
323
+ print(f"\nAll complex symbols: {len(complex_symbols)}")
324
+
325
+ # Filter class methods by complexity
326
+ classes = sdk.get_classes()
327
+ if classes:
328
+ complex_methods = sdk.get_methods_by_complexity(
329
+ class_id=classes[0].id,
330
+ min_branches=3,
331
+ match_any=True
332
+ )
333
+ print(f"\nComplex methods in class '{classes[0].name}': {len(complex_methods)}")
334
+
335
+
336
+ def example_description_management():
337
+ """Description management examples for AI-generated documentation."""
338
+ print("\n=== Description Management ===\n")
339
+
340
+ with CodeIndexSDK("code_index.db") as sdk:
341
+ # Get a function to work with
342
+ funcs = sdk.get_functions()
343
+ if funcs:
344
+ func = funcs[0]
345
+ print(f"Setting description for function: {func.name}")
346
+
347
+ # Set description by ID
348
+ success = sdk.set_function_description(func.id, "Handles user authentication flow")
349
+ print(f" Updated: {success}")
350
+
351
+ # Verify the description was set
352
+ updated_func = sdk.get_function_by_id(func.id)
353
+ print(f" Description: {updated_func.description}")
354
+
355
+ # Set description by name (with optional file path disambiguation)
356
+ success = sdk.set_function_description_by_name(
357
+ "main",
358
+ "Application entry point",
359
+ file_path="src/main.py" # Optional: disambiguate if multiple "main" functions exist
360
+ )
361
+ print(f"\nSet 'main' function description: {success}")
362
+
363
+ # Set description for a class
364
+ classes = sdk.get_classes()
365
+ if classes:
366
+ cls = classes[0]
367
+ print(f"\nSetting description for class: {cls.name}")
368
+ sdk.set_class_description(cls.id, "Service class for managing user accounts")
369
+
370
+ # Set by name
371
+ sdk.set_class_description_by_name("UserService", "Handles user operations")
372
+
373
+ # Set description for a variable
374
+ variables = sdk.get_variables()
375
+ if variables:
376
+ var = variables[0]
377
+ print(f"\nSetting description for variable: {var.name}")
378
+ sdk.set_variable_description(var.id, "Configuration timeout in seconds")
379
+
380
+ # Set description for a type alias
381
+ type_aliases = sdk.get_type_aliases()
382
+ if type_aliases:
383
+ alias = type_aliases[0]
384
+ print(f"\nSetting description for type alias: {alias.name}")
385
+ sdk.set_type_alias_description(alias.id, "User ID type alias for clarity")
386
+
387
+ # Set description for a struct (Go)
388
+ structs = sdk.get_structs()
389
+ if structs:
390
+ struct = structs[0]
391
+ print(f"\nSetting description for struct: {struct.name}")
392
+ sdk.set_struct_description(struct.id, "Represents a database connection pool")
393
+
394
+ # Set description for an interface (Go)
395
+ interfaces = sdk.get_interfaces()
396
+ if interfaces:
397
+ interface = interfaces[0]
398
+ print(f"\nSetting description for interface: {interface.name}")
399
+ sdk.set_interface_description(interface.id, "Defines storage backend interface")
400
+
401
+ # Generic method for any symbol type
402
+ if funcs:
403
+ print(f"\nUsing generic set_symbol_description for function {funcs[0].name}")
404
+ sdk.set_symbol_description(
405
+ symbol_type='function',
406
+ symbol_id=funcs[0].id,
407
+ description="Updated via generic method"
408
+ )
409
+
410
+
411
+ if __name__ == "__main__":
412
+ # Run all examples
413
+ example_basic_queries()
414
+ example_function_search()
415
+ example_class_exploration()
416
+ example_file_analysis()
417
+ example_cross_reference()
418
+ example_ai_agent_workflow()
419
+ example_context_manager()
420
+ example_manual_connection()
421
+ example_internal_external_imports()
422
+ example_source_body_reading()
423
+ example_complexity_filtering()
424
+ example_description_management()
425
+
426
+ print("\n=== All examples completed ===")
interactive.py ADDED
@@ -0,0 +1,248 @@
1
+ """
2
+ Agent run loops for Raggie (interactive and non-interactive modes).
3
+ """
4
+
5
+ import json
6
+ import sys
7
+ from importlib.metadata import version
8
+ from prompt_toolkit import prompt
9
+ from rich.console import Console
10
+ from rich.markdown import Markdown
11
+ from rich.live import Live
12
+
13
+ from Agent.effort_levels import EFFORT_LEVELS, DEFAULT_EFFORT, effort_name
14
+ from Agent.chat_history_db import set_session_effort, get_session_effort, get_session_depth
15
+
16
+ console = Console()
17
+ GREEN = "\033[32m"
18
+ DIM = "\033[2m"
19
+ RESET = "\033[0m"
20
+
21
+
22
+ def _prompt_effort(session_id, value=None):
23
+ """Ask the user to pick an effort level and store it on the session."""
24
+ current = get_session_effort(session_id)
25
+ default_num = current if current is not None else DEFAULT_EFFORT
26
+
27
+ for num, info in EFFORT_LEVELS.items():
28
+ marker = " (selected)" if num == default_num else ""
29
+ print(f" {num}. {info['name']}{marker}")
30
+
31
+ while True:
32
+
33
+ if value is None:
34
+ try:
35
+ print(f"\nEffort level:")
36
+ choice = prompt(f"Select effort (1-{len(EFFORT_LEVELS)}) [{default_num}]: ").strip()
37
+ except (EOFError, KeyboardInterrupt):
38
+ effort = default_num
39
+ set_session_effort(session_id, effort)
40
+ return effort
41
+ else:
42
+ choice = value
43
+
44
+ if not choice:
45
+ effort = default_num
46
+ break
47
+ try:
48
+ effort = int(choice)
49
+ except ValueError:
50
+ print("Invalid number. Try again.")
51
+ continue
52
+ if effort not in EFFORT_LEVELS:
53
+ print(f"Pick a number between 1 and {len(EFFORT_LEVELS)}.")
54
+ continue
55
+ break
56
+
57
+ set_session_effort(session_id, effort)
58
+ return effort
59
+
60
+
61
+ class StreamState:
62
+ """Holds streaming display state across event calls."""
63
+ __slots__ = ("reasoning_started", "response_started", "live", "accumulated")
64
+
65
+ def __init__(self):
66
+ self.reasoning_started = False
67
+ self.response_started = False
68
+ self.live = None
69
+ self.accumulated = ""
70
+
71
+ def _stop_live(self):
72
+ if self.live is not None:
73
+ self.live.stop()
74
+ self.live = None
75
+ self.response_started = False
76
+
77
+
78
+ def _print_event(event, state, depth=0, model=""):
79
+ """Process a single agent event and update stream state.
80
+
81
+ Returns the StreamState (mutated in place).
82
+ """
83
+ kind = event[0]
84
+
85
+ if kind == "tool_call":
86
+ state._stop_live()
87
+ if state.reasoning_started:
88
+ print(f"{RESET}")
89
+ state.reasoning_started = False
90
+ tool_name = event[1]
91
+ tool_args = event[2]
92
+ tool_name = tool_name.replace("Ugly", "")
93
+ try:
94
+ args_dict = json.loads(tool_args)
95
+ desc = ", ".join(f"{k}={str(v)[:40]}" for k, v in args_dict.items())
96
+ console.print(f"[dim][tool] {tool_name}({desc})[/dim]")
97
+ except Exception:
98
+ console.print(f"[dim][tool] {tool_name}[/dim]")
99
+
100
+ elif kind == "reasoning":
101
+ state._stop_live()
102
+ if state.reasoning_started:
103
+ print(f"{RESET}")
104
+ state.reasoning_started = False
105
+ content = event[1]
106
+ print(f"{DIM}")
107
+ console.print(Markdown(content))
108
+ print(f"{RESET}")
109
+
110
+ elif kind == "reasoning_chunk":
111
+ state._stop_live()
112
+ if not state.reasoning_started:
113
+ print(f"{DIM}", end="", flush=True)
114
+ state.reasoning_started = True
115
+ print(event[1], end="", flush=True)
116
+
117
+ elif kind == "response":
118
+ state._stop_live()
119
+ if state.reasoning_started:
120
+ print(f"{RESET}")
121
+ state.reasoning_started = False
122
+ content = event[1]
123
+ print(f"{GREEN}\n\nAgent ({model}:{depth}):{RESET}")
124
+ console.print(Markdown(content))
125
+
126
+ elif kind == "response_chunk":
127
+ if not state.response_started:
128
+ if state.reasoning_started:
129
+ print(f"{RESET}")
130
+ state.reasoning_started = False
131
+ print(f"{GREEN}\n\nAgent ({model}:{depth}):{RESET}")
132
+ state.response_started = True
133
+ state.live = Live(
134
+ Markdown(event[1]),
135
+ console=console,
136
+ refresh_per_second=12,
137
+ transient=True,
138
+ )
139
+ state.live.start()
140
+ state.accumulated = event[1]
141
+ else:
142
+ state.accumulated += event[1]
143
+ state.live.update(Markdown(state.accumulated))
144
+
145
+ elif kind == "response_end":
146
+ final_content = state.accumulated
147
+ state._stop_live()
148
+ if final_content:
149
+ console.print(Markdown(final_content))
150
+
151
+ elif kind == "error":
152
+ state._stop_live()
153
+ if state.reasoning_started:
154
+ print(f"{RESET}")
155
+ state.reasoning_started = False
156
+ print(f"Error: {event[1]}")
157
+
158
+ return state
159
+
160
+
161
+ def run_interactive(agent, role):
162
+ """Run the interactive agent loop.
163
+
164
+ Args:
165
+ agent: An Agent instance with tools and commands already set up.
166
+ role: The role name string.
167
+ """
168
+ model_name = agent.roles[role].get("model", "unknown")
169
+ print(f"{GREEN}Raggie Agent ({role}) v{version('raggiecode')} - Interactive Mode{RESET}")
170
+ print(f"{GREEN}Press Esc followed by Enter to send message, or type 'exit' to quit{RESET}")
171
+ print("-" * 50)
172
+ print("\nuse /help to see all available commands")
173
+
174
+ try:
175
+ state = StreamState()
176
+ depth = get_session_depth(agent.session_id)
177
+ model_name = agent.roles[role].get("model", "unknown")
178
+ for event in agent.resume_dangling_tool_work():
179
+ state = _print_event(event, state, depth, model_name)
180
+ except KeyboardInterrupt:
181
+ print("\n[interrupted]")
182
+ except EOFError:
183
+ print("\n\nAgent: Goodbye!")
184
+ return
185
+
186
+ while True:
187
+ try:
188
+ current = get_session_effort(agent.session_id)
189
+ if current is None:
190
+ set_session_effort(agent.session_id, DEFAULT_EFFORT)
191
+ current = DEFAULT_EFFORT
192
+ print(f"\n{DIM}Effort: {effort_name(current)} - to change it use /effort{RESET}")
193
+ print(f"{GREEN}\n\nYou:{RESET}")
194
+ user_input = prompt("> ", multiline=True)
195
+ except (EOFError, KeyboardInterrupt):
196
+ print("\n\nAgent: Goodbye!")
197
+ break
198
+
199
+ if user_input.lower().strip() in ['exit', 'quit']:
200
+ break
201
+
202
+ if not user_input.strip():
203
+ continue
204
+
205
+ try:
206
+ state = StreamState()
207
+ depth = get_session_depth(agent.session_id)
208
+ model_name = agent.roles[role].get("model", "unknown")
209
+ for event in agent.start(user_input):
210
+ state = _print_event(event, state, depth, model_name)
211
+ except KeyboardInterrupt:
212
+ print("\n[interrupted]")
213
+ continue
214
+ except EOFError:
215
+ print("\n\nAgent: Goodbye!")
216
+ break
217
+
218
+ print()
219
+
220
+
221
+ def run_non_interactive(agent, prompt_text, effort=None):
222
+ """Run the agent with a single prompt and exit.
223
+
224
+ Args:
225
+ agent: An Agent instance with tools and commands already set up.
226
+ prompt_text: The user prompt string.
227
+ effort: Optional effort level number (1-5). Defaults to session's current or DEFAULT_EFFORT.
228
+ """
229
+ if effort is not None:
230
+ set_session_effort(agent.session_id, effort)
231
+ elif get_session_effort(agent.session_id) is None:
232
+ set_session_effort(agent.session_id, DEFAULT_EFFORT)
233
+
234
+ try:
235
+ state = StreamState()
236
+ for event in agent.start(prompt_text):
237
+ if event[0] == "error":
238
+ state._stop_live()
239
+ if state.reasoning_started:
240
+ print()
241
+ state.reasoning_started = False
242
+ print(f"Error: {event[1]}", file=sys.stderr)
243
+ sys.exit(1)
244
+ state = _print_event(event, state, get_session_depth(agent.session_id), agent.roles[agent.agent_role].get("model", "unknown"))
245
+ except KeyboardInterrupt:
246
+ print("\n[interrupted]")
247
+ except EOFError:
248
+ print("\nAgent: Goodbye!")