raggiecode 0.2.1__tar.gz → 0.2.2__tar.gz
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.
- {raggiecode-0.2.1 → raggiecode-0.2.2}/PKG-INFO +11 -9
- {raggiecode-0.2.1 → raggiecode-0.2.2}/README.md +10 -8
- {raggiecode-0.2.1 → raggiecode-0.2.2}/pyproject.toml +1 -1
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Agent/agent.py +56 -108
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Agent/chat_history_db.py +84 -9
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/RAG/find.py +6 -1
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/todo_list.py +1 -3
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/code_index_sdk.py +23 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/interactive.py +49 -3
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/raggie.py +27 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/raggiecode.egg-info/PKG-INFO +11 -9
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/raggiecode.egg-info/SOURCES.txt +1 -0
- raggiecode-0.2.2/tests/test_dangling_tool_resume.py +315 -0
- raggiecode-0.2.2/tests/test_file_references.py +58 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_resumability_bugs.py +28 -0
- raggiecode-0.2.1/tests/test_dangling_tool_resume.py +0 -98
- {raggiecode-0.2.1 → raggiecode-0.2.2}/setup.cfg +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Agent/__init__.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Agent/command.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Agent/config.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Agent/effort_levels.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Agent/git_manager.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Agent/tools.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/__init__.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/effort.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/global_todo.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/help.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/reasoning.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/redo.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/reindex.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/shell.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/stream.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/undo.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/unlimited_effort.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Commands/window_size.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/RAG/__init__.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/RAG/document.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/RAG/graph.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/GetFileCodeStructure.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/GetSymbolSourceCode.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/__init__.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/ask_user.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/dispatch_subagent.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/document.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/edit_symbol.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/fuzzy_search.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/list_dir.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/read.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/read_image.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/remove.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/replace.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/search.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/shell.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/shell_kill.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/temp_background_service.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/utils.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/view_changes.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/walk_call_tree.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/web_fetch.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/web_search.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/Tools/write.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/cli.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/config/__init__.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/config/coder_system_prompt.md +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/config/roles.json +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/config/tools.json +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/__init__.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/cli.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/code_indexer.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/db_schema.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/export_to_json.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/extractors.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/file_utils.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/__init__.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/css_extractor.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/css_parser.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/css_selector_utils.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/edit_safety.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/graph.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/html_extractor.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/html_parser.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/jsx_extractor.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/location_lookup.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/resolver.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/runtime_resolver.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/semantic_output.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend/source_location.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend_config.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/frontend_models.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/language_config.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/models.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/node_utils.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/parse_worker.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/queries.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/indexing/sdk_examples.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/raggiecode.egg-info/dependency_links.txt +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/raggiecode.egg-info/entry_points.txt +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/raggiecode.egg-info/requires.txt +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/raggiecode.egg-info/top_level.txt +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/skills/__init__.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/skills/manager.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/src/skills/tool.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_commands.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_context_budget.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_dependency_tracking.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_edit_symbol.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_effort_levels.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_event_logging.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_file_edit_application.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_fuzzy_search.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_get_file_code_structure.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_get_skill.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_global_todo.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_handover.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_handover_storage.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_integration_real_db.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_interactive_effort.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_list_dir.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_rejection_feedback.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_session_files.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_session_lifecycle.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_skills_import_export.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_sqlite_persistence.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_subagent_dispatch.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_subagent_session.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_subagent_tools.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_todo_list.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_tool_call_lifecycle.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_undo_redo.py +0 -0
- {raggiecode-0.2.1 → raggiecode-0.2.2}/tests/test_vc_rollback.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: raggiecode
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: AI Coding Agent
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -28,7 +28,7 @@ Requires-Dist: ripgrep_rs
|
|
|
28
28
|
|
|
29
29
|
# Raggie Code
|
|
30
30
|
|
|
31
|
-
> *Raggie Code v0.2.
|
|
31
|
+
> *Raggie Code v0.2.2*
|
|
32
32
|
|
|
33
33
|
<p style="padding:30px 50px;">
|
|
34
34
|
<img src="Raggie.png" alt="Raggie" width="312">
|
|
@@ -134,7 +134,7 @@ Every tool call is displayed in real time with its arguments. Debug mode (`--deb
|
|
|
134
134
|
### From pip (GitHub)
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
|
-
pip install
|
|
137
|
+
pip install raggiecode
|
|
138
138
|
```
|
|
139
139
|
|
|
140
140
|
### From source
|
|
@@ -208,12 +208,13 @@ After the agent makes changes, type `/undo` as your next prompt to undo the last
|
|
|
208
208
|
$ raggie code .
|
|
209
209
|
|
|
210
210
|
Raggie Agent (code) v1.0.0 - Interactive Mode
|
|
211
|
-
Press Esc followed by Enter to send message, or type 'exit' to quit
|
|
212
211
|
--------------------------------------------------
|
|
213
212
|
Indexing codebase...
|
|
214
213
|
|
|
215
214
|
Effort: Zen - to change it use /effort
|
|
216
215
|
|
|
216
|
+
Press Esc followed by Enter to send message, or type 'exit' to quit
|
|
217
|
+
|
|
217
218
|
You:
|
|
218
219
|
> Add input validation to the login endpoint and update all callers
|
|
219
220
|
|
|
@@ -448,7 +449,7 @@ raggie/
|
|
|
448
449
|
|
|
449
450
|
### How the Agent Works
|
|
450
451
|
|
|
451
|
-
1. **Startup**: The agent loads its role config, connects to the LLM API,
|
|
452
|
+
1. **Startup**: The agent loads its role config, connects to the LLM API, indexes your codebase (tree-sitter, multiprocessing), and initialises its local git repo. Before the agent is created, the CLI entry point checks for a `.raggie` folder in the working directory and prompts the user if none is found.
|
|
452
453
|
2. **Prompt loop**: User sends a message → agent calls the LLM with full chat history + tool definitions → LLM responds with text and/or tool calls.
|
|
453
454
|
3. **Tool execution**: Each tool call is dispatched to a registered handler. Results are fed back to the LLM as tool responses.
|
|
454
455
|
4. **Re-indexing**: After each tool call, the code index is updated so the agent always has fresh context.
|
|
@@ -806,11 +807,12 @@ Languages are gracefully skipped if their tree-sitter grammar is not installed.
|
|
|
806
807
|
|
|
807
808
|
### Project directory detection
|
|
808
809
|
|
|
809
|
-
Before indexing, Raggie checks whether
|
|
810
|
+
Before indexing, Raggie checks whether a `.raggie` folder exists in the current directory. This folder is created by Raggie on first run, so its presence means the user has already confirmed this is a project directory.
|
|
810
811
|
|
|
811
|
-
-
|
|
812
|
-
- **
|
|
813
|
-
- **
|
|
812
|
+
- **`.raggie` exists**: indexing proceeds automatically as normal.
|
|
813
|
+
- **No `.raggie` folder, but directory has no subdirectories**: indexing proceeds without prompting — a flat directory is small enough to be safe.
|
|
814
|
+
- **No `.raggie` folder, directory has subdirectories**: Raggie warns that the directory doesn't look like a project and asks whether to index anyway. This prevents accidentally scanning unrelated files (e.g. if you run `raggie code .` in your home directory). If you decline, Raggie exits and suggests you `cd` into your project directory or start a new one with `raggie code <project-name>`.
|
|
815
|
+
- **Subagent sessions**: subagents inherit the parent's `.raggie` folder (same working directory), so they always index normally.
|
|
814
816
|
|
|
815
817
|
You can manually trigger re-indexing at any time with the `/reindex` command.
|
|
816
818
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Raggie Code
|
|
2
2
|
|
|
3
|
-
> *Raggie Code v0.2.
|
|
3
|
+
> *Raggie Code v0.2.2*
|
|
4
4
|
|
|
5
5
|
<p style="padding:30px 50px;">
|
|
6
6
|
<img src="Raggie.png" alt="Raggie" width="312">
|
|
@@ -106,7 +106,7 @@ Every tool call is displayed in real time with its arguments. Debug mode (`--deb
|
|
|
106
106
|
### From pip (GitHub)
|
|
107
107
|
|
|
108
108
|
```bash
|
|
109
|
-
pip install
|
|
109
|
+
pip install raggiecode
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
### From source
|
|
@@ -180,12 +180,13 @@ After the agent makes changes, type `/undo` as your next prompt to undo the last
|
|
|
180
180
|
$ raggie code .
|
|
181
181
|
|
|
182
182
|
Raggie Agent (code) v1.0.0 - Interactive Mode
|
|
183
|
-
Press Esc followed by Enter to send message, or type 'exit' to quit
|
|
184
183
|
--------------------------------------------------
|
|
185
184
|
Indexing codebase...
|
|
186
185
|
|
|
187
186
|
Effort: Zen - to change it use /effort
|
|
188
187
|
|
|
188
|
+
Press Esc followed by Enter to send message, or type 'exit' to quit
|
|
189
|
+
|
|
189
190
|
You:
|
|
190
191
|
> Add input validation to the login endpoint and update all callers
|
|
191
192
|
|
|
@@ -420,7 +421,7 @@ raggie/
|
|
|
420
421
|
|
|
421
422
|
### How the Agent Works
|
|
422
423
|
|
|
423
|
-
1. **Startup**: The agent loads its role config, connects to the LLM API,
|
|
424
|
+
1. **Startup**: The agent loads its role config, connects to the LLM API, indexes your codebase (tree-sitter, multiprocessing), and initialises its local git repo. Before the agent is created, the CLI entry point checks for a `.raggie` folder in the working directory and prompts the user if none is found.
|
|
424
425
|
2. **Prompt loop**: User sends a message → agent calls the LLM with full chat history + tool definitions → LLM responds with text and/or tool calls.
|
|
425
426
|
3. **Tool execution**: Each tool call is dispatched to a registered handler. Results are fed back to the LLM as tool responses.
|
|
426
427
|
4. **Re-indexing**: After each tool call, the code index is updated so the agent always has fresh context.
|
|
@@ -778,11 +779,12 @@ Languages are gracefully skipped if their tree-sitter grammar is not installed.
|
|
|
778
779
|
|
|
779
780
|
### Project directory detection
|
|
780
781
|
|
|
781
|
-
Before indexing, Raggie checks whether
|
|
782
|
+
Before indexing, Raggie checks whether a `.raggie` folder exists in the current directory. This folder is created by Raggie on first run, so its presence means the user has already confirmed this is a project directory.
|
|
782
783
|
|
|
783
|
-
-
|
|
784
|
-
- **
|
|
785
|
-
- **
|
|
784
|
+
- **`.raggie` exists**: indexing proceeds automatically as normal.
|
|
785
|
+
- **No `.raggie` folder, but directory has no subdirectories**: indexing proceeds without prompting — a flat directory is small enough to be safe.
|
|
786
|
+
- **No `.raggie` folder, directory has subdirectories**: Raggie warns that the directory doesn't look like a project and asks whether to index anyway. This prevents accidentally scanning unrelated files (e.g. if you run `raggie code .` in your home directory). If you decline, Raggie exits and suggests you `cd` into your project directory or start a new one with `raggie code <project-name>`.
|
|
787
|
+
- **Subagent sessions**: subagents inherit the parent's `.raggie` folder (same working directory), so they always index normally.
|
|
786
788
|
|
|
787
789
|
You can manually trigger re-indexing at any time with the `/reindex` command.
|
|
788
790
|
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import glob
|
|
3
|
-
import sys
|
|
4
2
|
import json
|
|
5
3
|
import shutil
|
|
6
4
|
import platform
|
|
@@ -13,7 +11,7 @@ from rich.markdown import Markdown
|
|
|
13
11
|
from .config import load_roles, load_tools, load_keys
|
|
14
12
|
from .tools import ToolRegistry
|
|
15
13
|
from .command import CommandRegistry
|
|
16
|
-
from .chat_history_db import init_db, get_or_create_session, load_messages, save_message, update_chat_title, generate_title, get_active_todo_list, get_todo_tasks, create_session, set_redirect_session_id, save_handover, get_old_session_ids, is_subagent_session, get_session_effort, get_session_info, resolve_session_id, migrate_todo_lists
|
|
14
|
+
from .chat_history_db import init_db, get_or_create_session, load_messages, save_message, update_chat_title, generate_title, get_active_todo_list, get_todo_tasks, create_session, set_redirect_session_id, save_handover, get_old_session_ids, is_subagent_session, get_session_effort, get_session_info, resolve_session_id, migrate_todo_lists, repair_message_sequence
|
|
17
15
|
from .git_manager import GitManager
|
|
18
16
|
from skills import SkillManager
|
|
19
17
|
from indexing.code_index_sdk import CodeIndexSDK
|
|
@@ -99,84 +97,13 @@ class Agent:
|
|
|
99
97
|
# Initialize git manager for commit/undo/redo operations
|
|
100
98
|
self.git_manager = GitManager(root_dir=os.getcwd())
|
|
101
99
|
|
|
102
|
-
#
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"Pipfile", "poetry.lock", "uv.lock", "tox.ini", "MANIFEST.in",
|
|
110
|
-
# JavaScript / TypeScript
|
|
111
|
-
"package.json", "tsconfig.json", "yarn.lock", "pnpm-lock.yaml",
|
|
112
|
-
"package-lock.json", "bower.json", ".npmrc", "deno.json",
|
|
113
|
-
# Go
|
|
114
|
-
"go.mod", "go.sum", "go.work",
|
|
115
|
-
# Rust
|
|
116
|
-
"Cargo.toml",
|
|
117
|
-
# C / C++
|
|
118
|
-
"CMakeLists.txt", "Makefile", "Makefile.am", "configure.ac",
|
|
119
|
-
"meson.build", "BUCK", "BUILD", "BUILD.bazel", "WORKSPACE",
|
|
120
|
-
# C# / .NET
|
|
121
|
-
"Directory.Build.props", "global.json",
|
|
122
|
-
# Java / Kotlin
|
|
123
|
-
"pom.xml", "build.gradle", "build.gradle.kts",
|
|
124
|
-
"settings.gradle", "settings.gradle.kts", "gradle.properties",
|
|
125
|
-
# PHP
|
|
126
|
-
"composer.json", "artisan",
|
|
127
|
-
# Ruby
|
|
128
|
-
"Gemfile", "Rakefile", ".rspec",
|
|
129
|
-
# Elixir
|
|
130
|
-
"mix.exs",
|
|
131
|
-
# Zig
|
|
132
|
-
"build.zig",
|
|
133
|
-
# Dart / Flutter
|
|
134
|
-
"pubspec.yaml",
|
|
135
|
-
# Lua
|
|
136
|
-
"rockspec",
|
|
137
|
-
# Generic / editor
|
|
138
|
-
".raggie", ".vscode", ".idea", ".editorconfig",
|
|
139
|
-
]
|
|
140
|
-
is_project = any(os.path.exists(os.path.join(cwd, m)) for m in project_markers)
|
|
141
|
-
if not is_project:
|
|
142
|
-
is_project = bool(glob.glob(os.path.join(cwd, "*.csproj")) or glob.glob(os.path.join(cwd, "*.sln")))
|
|
143
|
-
|
|
144
|
-
if is_project:
|
|
145
|
-
try:
|
|
146
|
-
with self.console.status("[bold green]Indexing codebase...", spinner="dots"):
|
|
147
|
-
self.code_indexer.index_directory()
|
|
148
|
-
except (KeyboardInterrupt, EOFError):
|
|
149
|
-
self.console.print("\n[yellow]Indexing interrupted. Using existing index.[/yellow]")
|
|
150
|
-
self.code_indexer._connect()
|
|
151
|
-
elif self.is_subagent:
|
|
152
|
-
self.console.print(
|
|
153
|
-
f"[yellow]Warning:[/yellow] '{cwd}' doesn't look like a project directory. "
|
|
154
|
-
f"Skipping indexing."
|
|
155
|
-
)
|
|
156
|
-
else:
|
|
157
|
-
self.console.print(
|
|
158
|
-
f"[yellow]Warning:[/yellow] '{cwd}' doesn't look like a project directory "
|
|
159
|
-
f"(no .git, pyproject.toml, package.json, go.mod, Cargo.toml, etc. found).\n"
|
|
160
|
-
f"Indexing here may scan unrelated files and take a long time."
|
|
161
|
-
)
|
|
162
|
-
try:
|
|
163
|
-
response = input("\nIndex anyway? (y/N): ").strip().lower()
|
|
164
|
-
except (EOFError, KeyboardInterrupt):
|
|
165
|
-
response = "n"
|
|
166
|
-
|
|
167
|
-
if response in ("y", "yes"):
|
|
168
|
-
try:
|
|
169
|
-
with self.console.status("[bold green]Indexing codebase...", spinner="dots"):
|
|
170
|
-
self.code_indexer.index_directory()
|
|
171
|
-
except (KeyboardInterrupt, EOFError):
|
|
172
|
-
self.console.print("\n[yellow]Indexing interrupted. Using existing index.[/yellow]")
|
|
173
|
-
self.code_indexer._connect()
|
|
174
|
-
else:
|
|
175
|
-
self.console.print(
|
|
176
|
-
"Please cd into your project directory and try again.\n"
|
|
177
|
-
"To start a new project: raggie code <project-name>"
|
|
178
|
-
)
|
|
179
|
-
sys.exit(0)
|
|
100
|
+
# Index the codebase at the start of each conversation
|
|
101
|
+
try:
|
|
102
|
+
with self.console.status("[bold green]Indexing codebase...", spinner="dots"):
|
|
103
|
+
self.code_indexer.index_directory()
|
|
104
|
+
except (KeyboardInterrupt, EOFError):
|
|
105
|
+
self.console.print("\n[yellow]Indexing interrupted. Using existing index.[/yellow]")
|
|
106
|
+
self.code_indexer._connect()
|
|
180
107
|
|
|
181
108
|
# Display previous chat history if it exists (skip for subagents — they
|
|
182
109
|
# share the chat_id but don't need the parent's conversation printed)
|
|
@@ -378,12 +305,38 @@ class Agent:
|
|
|
378
305
|
"content": message,
|
|
379
306
|
}
|
|
380
307
|
|
|
308
|
+
@staticmethod
|
|
309
|
+
def _normalize_message_content(msg):
|
|
310
|
+
"""Ensure message content is a string or content-block list, as the API requires."""
|
|
311
|
+
content = msg.get("content")
|
|
312
|
+
if content is not None and not isinstance(content, (str, list)):
|
|
313
|
+
msg["content"] = json.dumps(content)
|
|
314
|
+
|
|
315
|
+
def _create_completion(self, **kwargs):
|
|
316
|
+
"""Single choke point for chat.completions.create.
|
|
317
|
+
|
|
318
|
+
Repairs the tool_call/tool-response sequence and normalizes content on
|
|
319
|
+
the in-memory history right before sending, so the API never receives
|
|
320
|
+
an invalid conversation regardless of how the history was produced.
|
|
321
|
+
"""
|
|
322
|
+
self.chat_history = repair_message_sequence(self.chat_history)
|
|
323
|
+
for msg in self.chat_history:
|
|
324
|
+
self._normalize_message_content(msg)
|
|
325
|
+
kwargs["messages"] = self.chat_history
|
|
326
|
+
return self.client.chat.completions.create(**kwargs)
|
|
327
|
+
|
|
381
328
|
|
|
382
329
|
|
|
383
330
|
def _has_dangling_tool_work(self):
|
|
384
331
|
if not self.chat_history:
|
|
385
332
|
return False
|
|
386
333
|
|
|
334
|
+
# An assistant tool_calls message with missing responses anywhere in the
|
|
335
|
+
# history (not just at the tail — a crash can be followed by stored user
|
|
336
|
+
# messages) makes the sequence invalid for the API.
|
|
337
|
+
if self._get_pending_toolcalls():
|
|
338
|
+
return True
|
|
339
|
+
|
|
387
340
|
last_msg = self.chat_history[-1]
|
|
388
341
|
if last_msg.get("role") == "tool":
|
|
389
342
|
return True
|
|
@@ -392,7 +345,7 @@ class Agent:
|
|
|
392
345
|
if last_msg.get("role") == "user" and last_msg.get("tool_call_id"):
|
|
393
346
|
return True
|
|
394
347
|
|
|
395
|
-
return
|
|
348
|
+
return False
|
|
396
349
|
|
|
397
350
|
def _get_pending_toolcalls(self):
|
|
398
351
|
"""Find tool calls from the last assistant message that don't have tool responses yet.
|
|
@@ -438,6 +391,9 @@ class Agent:
|
|
|
438
391
|
tool_output = self.tool_registry.call(
|
|
439
392
|
tool_name, args_dict, toolcall_id, self.session_id
|
|
440
393
|
)
|
|
394
|
+
# A tool returning non-string content (e.g. a dict) would make the
|
|
395
|
+
# next API call fail; normalize before it enters the history.
|
|
396
|
+
self._normalize_message_content(tool_output)
|
|
441
397
|
|
|
442
398
|
if self.debug:
|
|
443
399
|
print(f"\n[DEBUG] Tool output for {tool_name}:")
|
|
@@ -479,24 +435,20 @@ class Agent:
|
|
|
479
435
|
if not self._has_dangling_tool_work():
|
|
480
436
|
return
|
|
481
437
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
toolcall.get("id"),
|
|
497
|
-
function.get("name"),
|
|
498
|
-
function.get("arguments", "{}"),
|
|
499
|
-
)
|
|
438
|
+
# Re-execute every tool call that never got a response, wherever the
|
|
439
|
+
# dangling assistant message sits in the history.
|
|
440
|
+
for toolcall in self._get_pending_toolcalls():
|
|
441
|
+
function = toolcall.get("function", {})
|
|
442
|
+
yield from self._execute_tool_call(
|
|
443
|
+
toolcall.get("id"),
|
|
444
|
+
function.get("name"),
|
|
445
|
+
function.get("arguments", "{}"),
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Executed tool responses are appended to the end of the history; pull
|
|
449
|
+
# them up next to their assistant message so the sequence sent to the
|
|
450
|
+
# API stays valid.
|
|
451
|
+
self.chat_history = repair_message_sequence(self.chat_history)
|
|
500
452
|
|
|
501
453
|
yield from self.start()
|
|
502
454
|
|
|
@@ -530,9 +482,8 @@ class Agent:
|
|
|
530
482
|
tool_calls_accum = []
|
|
531
483
|
|
|
532
484
|
try:
|
|
533
|
-
stream = self.
|
|
485
|
+
stream = self._create_completion(
|
|
534
486
|
model=model,
|
|
535
|
-
messages=self.chat_history,
|
|
536
487
|
tools=self._get_tools(self.agent_role),
|
|
537
488
|
stream=True,
|
|
538
489
|
stream_options={"include_usage": True},
|
|
@@ -595,9 +546,8 @@ class Agent:
|
|
|
595
546
|
|
|
596
547
|
try:
|
|
597
548
|
with self.console.status("[bold green]Thinking...", spinner="dots"):
|
|
598
|
-
chat = self.
|
|
549
|
+
chat = self._create_completion(
|
|
599
550
|
model=model,
|
|
600
|
-
messages=self.chat_history,
|
|
601
551
|
tools=self._get_tools(self.agent_role),
|
|
602
552
|
)
|
|
603
553
|
except Exception as err:
|
|
@@ -652,9 +602,8 @@ class Agent:
|
|
|
652
602
|
self._handover_text = None
|
|
653
603
|
handover_text = ""
|
|
654
604
|
try:
|
|
655
|
-
stream = self.
|
|
605
|
+
stream = self._create_completion(
|
|
656
606
|
model=model,
|
|
657
|
-
messages=self.chat_history,
|
|
658
607
|
stream=True,
|
|
659
608
|
)
|
|
660
609
|
for chunk in stream:
|
|
@@ -687,9 +636,8 @@ class Agent:
|
|
|
687
636
|
self._handover_text = None
|
|
688
637
|
try:
|
|
689
638
|
with self.console.status("[bold green]Generating handover instructions...", spinner="dots"):
|
|
690
|
-
chat = self.
|
|
639
|
+
chat = self._create_completion(
|
|
691
640
|
model=model,
|
|
692
|
-
messages=self.chat_history,
|
|
693
641
|
)
|
|
694
642
|
except Exception as err:
|
|
695
643
|
self.chat_history.pop()
|
|
@@ -493,29 +493,104 @@ def save_message(session_id: int, message: Dict):
|
|
|
493
493
|
conn.close()
|
|
494
494
|
|
|
495
495
|
|
|
496
|
+
def repair_message_sequence(messages: List[Dict]) -> List[Dict]:
|
|
497
|
+
"""Move each tool response to directly follow the assistant message that requested it.
|
|
498
|
+
|
|
499
|
+
A crash or interrupt can leave a session where an assistant message with
|
|
500
|
+
tool_calls is followed by user/other messages, with the tool responses
|
|
501
|
+
appended later. The chat API requires every tool response to directly
|
|
502
|
+
follow its assistant tool_calls message, so pull each one forward.
|
|
503
|
+
"""
|
|
504
|
+
result = []
|
|
505
|
+
owner = {} # tool_call_id -> position of owning assistant message in result
|
|
506
|
+
placed = {} # assistant position -> number of its tool responses already placed
|
|
507
|
+
for msg in messages:
|
|
508
|
+
tool_call_id = msg.get("tool_call_id")
|
|
509
|
+
if tool_call_id is not None and tool_call_id in owner:
|
|
510
|
+
asst_pos = owner.pop(tool_call_id)
|
|
511
|
+
insert_at = asst_pos + 1 + placed.get(asst_pos, 0)
|
|
512
|
+
result.insert(insert_at, msg)
|
|
513
|
+
placed = {k + 1 if k >= insert_at else k: v for k, v in placed.items()}
|
|
514
|
+
placed[asst_pos] = placed.get(asst_pos, 0) + 1
|
|
515
|
+
owner = {k: v + 1 if v >= insert_at else v for k, v in owner.items()}
|
|
516
|
+
else:
|
|
517
|
+
if msg.get("role") == "assistant" and msg.get("tool_calls"):
|
|
518
|
+
asst_pos = len(result)
|
|
519
|
+
for tc in msg["tool_calls"]:
|
|
520
|
+
tcid = tc.get("id")
|
|
521
|
+
if tcid:
|
|
522
|
+
owner[tcid] = asst_pos
|
|
523
|
+
placed[asst_pos] = 0
|
|
524
|
+
result.append(msg)
|
|
525
|
+
return result
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def validate_message_sequence(messages: List[Dict]) -> List[str]:
|
|
529
|
+
"""Return a list of API-contract violations in the message sequence (empty if valid).
|
|
530
|
+
|
|
531
|
+
Checks the two invariants the chat API enforces:
|
|
532
|
+
- content must be a string or a content-block list
|
|
533
|
+
- every assistant tool_calls entry must be answered by a tool response
|
|
534
|
+
(role "tool", or role "user" with tool_call_id for vision outputs) before
|
|
535
|
+
any other message follows
|
|
536
|
+
"""
|
|
537
|
+
errors = []
|
|
538
|
+
pending: List[str] = []
|
|
539
|
+
for i, msg in enumerate(messages):
|
|
540
|
+
content = msg.get("content")
|
|
541
|
+
if content is not None and not isinstance(content, (str, list)):
|
|
542
|
+
errors.append(f"messages[{i}]: content is {type(content).__name__}, expected str or list")
|
|
543
|
+
|
|
544
|
+
tool_call_id = msg.get("tool_call_id")
|
|
545
|
+
if tool_call_id and msg.get("role") in ("tool", "user"):
|
|
546
|
+
if tool_call_id in pending:
|
|
547
|
+
pending.remove(tool_call_id)
|
|
548
|
+
else:
|
|
549
|
+
errors.append(f"messages[{i}]: tool response without matching tool_call")
|
|
550
|
+
continue
|
|
551
|
+
|
|
552
|
+
if pending:
|
|
553
|
+
errors.append(f"messages[{i}]: {msg.get('role')} message arrived before tool responses to {pending}")
|
|
554
|
+
pending = []
|
|
555
|
+
|
|
556
|
+
if msg.get("role") == "assistant" and msg.get("tool_calls"):
|
|
557
|
+
pending = [tc.get("id") for tc in msg["tool_calls"]]
|
|
558
|
+
|
|
559
|
+
if pending:
|
|
560
|
+
errors.append(f"unanswered tool_calls at end of history: {pending}")
|
|
561
|
+
return errors
|
|
562
|
+
|
|
563
|
+
|
|
496
564
|
def load_messages(session_id: int) -> List[Dict]:
|
|
497
|
-
"""Load all messages for a given session
|
|
565
|
+
"""Load all messages for a given session in insertion order."""
|
|
498
566
|
conn = _get_conn()
|
|
499
567
|
cursor = conn.cursor()
|
|
500
|
-
|
|
568
|
+
|
|
569
|
+
# Order by id, not timestamp: timestamps can tie (same-second inserts),
|
|
570
|
+
# which makes the ordering of tool_calls vs tool responses nondeterministic.
|
|
501
571
|
cursor.execute("""
|
|
502
572
|
SELECT role, content, tool_calls, tool_call_id
|
|
503
573
|
FROM messages
|
|
504
574
|
WHERE session_id = ?
|
|
505
|
-
ORDER BY
|
|
575
|
+
ORDER BY id ASC
|
|
506
576
|
""", (session_id,))
|
|
507
577
|
|
|
508
578
|
messages = []
|
|
509
579
|
for row in cursor.fetchall():
|
|
510
580
|
content_str = str(row[1]) if row[1] is not None else ""
|
|
581
|
+
content = content_str
|
|
511
582
|
try:
|
|
512
583
|
parsed = json.loads(content_str)
|
|
513
|
-
|
|
584
|
+
# Only restore structured content for vision-style content-block
|
|
585
|
+
# lists (e.g. [{"type": "text", ...}, {"type": "image_url", ...}]).
|
|
586
|
+
# Any other JSON value (a tool result that happens to be valid JSON,
|
|
587
|
+
# like a package.json dump) must stay a plain string, or the API
|
|
588
|
+
# rejects it with "content should be a string or a list".
|
|
589
|
+
if (isinstance(parsed, list) and parsed
|
|
590
|
+
and all(isinstance(item, dict) and "type" in item for item in parsed)):
|
|
514
591
|
content = parsed
|
|
515
|
-
else:
|
|
516
|
-
content = content_str
|
|
517
592
|
except (json.JSONDecodeError, TypeError):
|
|
518
|
-
|
|
593
|
+
pass
|
|
519
594
|
|
|
520
595
|
message = {
|
|
521
596
|
"role": str(row[0]) if row[0] is not None else "user",
|
|
@@ -538,9 +613,9 @@ def load_messages(session_id: int) -> List[Dict]:
|
|
|
538
613
|
continue
|
|
539
614
|
|
|
540
615
|
messages.append(message)
|
|
541
|
-
|
|
616
|
+
|
|
542
617
|
conn.close()
|
|
543
|
-
return messages
|
|
618
|
+
return repair_message_sequence(messages)
|
|
544
619
|
|
|
545
620
|
|
|
546
621
|
def delete_chat(chat_id: int):
|
|
@@ -392,7 +392,12 @@ def find_symbol_implementation(symbol_name: str, file_path: str = None) -> str:
|
|
|
392
392
|
class_body = sdk.get_class_body(symbol_name, file_path)
|
|
393
393
|
if class_body:
|
|
394
394
|
return class_body
|
|
395
|
-
|
|
395
|
+
|
|
396
|
+
# Try to find as variable
|
|
397
|
+
variable_body = sdk.get_variable_body(symbol_name, file_path)
|
|
398
|
+
if variable_body:
|
|
399
|
+
return variable_body
|
|
400
|
+
|
|
396
401
|
# Search for similar symbols by name and description
|
|
397
402
|
matches = sdk.search_symbols(symbol_name, limit=10)
|
|
398
403
|
|
|
@@ -116,9 +116,7 @@ def handle_get_todo_list(arguments, toolcall_id, parent_session_id=None):
|
|
|
116
116
|
if task.get('cancel_reason'):
|
|
117
117
|
output += f"{YELLOW} Cancel reason: {task['cancel_reason']}{RESET}\n"
|
|
118
118
|
output += "\n"
|
|
119
|
-
|
|
120
|
-
print(output)
|
|
121
|
-
|
|
119
|
+
|
|
122
120
|
return {
|
|
123
121
|
"role": "tool",
|
|
124
122
|
"tool_call_id": toolcall_id,
|
|
@@ -804,6 +804,29 @@ class CodeIndexSDK(QueryMixin, DescriptionMixin):
|
|
|
804
804
|
|
|
805
805
|
return ""
|
|
806
806
|
|
|
807
|
+
def get_variable_body(self, variable_name: str, file_path: Optional[str] = None) -> Optional[str]:
|
|
808
|
+
"""Get the declaration source lines of a variable by name.
|
|
809
|
+
|
|
810
|
+
Args:
|
|
811
|
+
variable_name: Name of the variable.
|
|
812
|
+
file_path: Optional relative file path to disambiguate same-name variables.
|
|
813
|
+
|
|
814
|
+
Returns:
|
|
815
|
+
Source code string of the declaration, or None if not found / file unresolvable.
|
|
816
|
+
"""
|
|
817
|
+
file_id = None
|
|
818
|
+
if file_path:
|
|
819
|
+
f = self.get_file_by_path(file_path)
|
|
820
|
+
if f:
|
|
821
|
+
file_id = f.id
|
|
822
|
+
|
|
823
|
+
matches = self.get_variable_by_name(variable_name, file_id)
|
|
824
|
+
if not matches:
|
|
825
|
+
return None
|
|
826
|
+
|
|
827
|
+
var = matches[0]
|
|
828
|
+
return self._read_source_lines(var.file_id, var.location.start_line, var.location.end_line)
|
|
829
|
+
|
|
807
830
|
def get_class_body(self, class_name: str, file_path: Optional[str] = None) -> Optional[str]:
|
|
808
831
|
"""Get the source body of a class by name.
|
|
809
832
|
|
|
@@ -3,8 +3,10 @@ Agent run loops for Raggie (interactive and non-interactive modes).
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
+
import re
|
|
6
7
|
import sys
|
|
7
8
|
from importlib.metadata import version
|
|
9
|
+
from pathlib import Path
|
|
8
10
|
from prompt_toolkit import prompt
|
|
9
11
|
from rich.console import Console
|
|
10
12
|
from rich.markdown import Markdown
|
|
@@ -18,6 +20,50 @@ GREEN = "\033[32m"
|
|
|
18
20
|
DIM = "\033[2m"
|
|
19
21
|
RESET = "\033[0m"
|
|
20
22
|
|
|
23
|
+
# Matches @path/to/file.x:23 and @path/to/file.x:23-43. The lookbehind rejects
|
|
24
|
+
# @-mentions embedded in emails/handles, and the mandatory :N suffix ensures
|
|
25
|
+
# only explicit line references are expanded.
|
|
26
|
+
_FILE_REF_RE = re.compile(r'(?<![\w.@-])@([\w./\\-]+\.[A-Za-z0-9]+):(\d+)(?:-(\d+))?')
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def expand_file_references(text):
|
|
30
|
+
"""Append referenced code snippets for @path/file.x:N[-M] mentions in user input.
|
|
31
|
+
|
|
32
|
+
References pointing to non-existent files are left untouched so emails,
|
|
33
|
+
handles, and plain text pass through unchanged.
|
|
34
|
+
"""
|
|
35
|
+
snippets = []
|
|
36
|
+
seen = set()
|
|
37
|
+
for m in _FILE_REF_RE.finditer(text):
|
|
38
|
+
ref = m.group(0)
|
|
39
|
+
if ref in seen:
|
|
40
|
+
continue
|
|
41
|
+
seen.add(ref)
|
|
42
|
+
|
|
43
|
+
path_str, start, end = m.group(1), int(m.group(2)), m.group(3)
|
|
44
|
+
end = int(end) if end else start
|
|
45
|
+
if end < start:
|
|
46
|
+
start, end = end, start
|
|
47
|
+
|
|
48
|
+
path = Path(path_str)
|
|
49
|
+
if not path.is_file():
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
lines = path.read_text(errors="replace").splitlines()
|
|
54
|
+
except OSError:
|
|
55
|
+
continue
|
|
56
|
+
if start > len(lines):
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
end = min(end, len(lines))
|
|
60
|
+
chunk = "\n".join(f"{i}: {lines[i - 1]}" for i in range(start, end + 1))
|
|
61
|
+
snippets.append(f"--- {path_str}:{start}-{end} ---\n{chunk}")
|
|
62
|
+
|
|
63
|
+
if not snippets:
|
|
64
|
+
return text
|
|
65
|
+
return text + "\n\n<referenced_code>\n" + "\n\n".join(snippets) + "\n</referenced_code>"
|
|
66
|
+
|
|
21
67
|
|
|
22
68
|
def _prompt_effort(session_id, value=None):
|
|
23
69
|
"""Ask the user to pick an effort level and store it on the session."""
|
|
@@ -167,7 +213,6 @@ def run_interactive(agent, role):
|
|
|
167
213
|
"""
|
|
168
214
|
model_name = agent.roles[role].get("model", "unknown")
|
|
169
215
|
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
216
|
print("-" * 50)
|
|
172
217
|
print("\nuse /help to see all available commands")
|
|
173
218
|
|
|
@@ -190,6 +235,7 @@ def run_interactive(agent, role):
|
|
|
190
235
|
set_session_effort(agent.session_id, DEFAULT_EFFORT)
|
|
191
236
|
current = DEFAULT_EFFORT
|
|
192
237
|
print(f"\n{DIM}Effort: {effort_name(current)} - to change it use /effort{RESET}")
|
|
238
|
+
print(f"{DIM}Press Esc followed by Enter to send message, or type 'exit' to quit{RESET}")
|
|
193
239
|
print(f"{GREEN}\n\nYou:{RESET}")
|
|
194
240
|
user_input = prompt("> ", multiline=True)
|
|
195
241
|
except (EOFError, KeyboardInterrupt):
|
|
@@ -206,7 +252,7 @@ def run_interactive(agent, role):
|
|
|
206
252
|
state = StreamState()
|
|
207
253
|
depth = get_session_depth(agent.session_id)
|
|
208
254
|
model_name = agent.roles[role].get("model", "unknown")
|
|
209
|
-
for event in agent.start(user_input):
|
|
255
|
+
for event in agent.start(expand_file_references(user_input)):
|
|
210
256
|
state = _print_event(event, state, depth, model_name)
|
|
211
257
|
except KeyboardInterrupt:
|
|
212
258
|
print("\n[interrupted]")
|
|
@@ -233,7 +279,7 @@ def run_non_interactive(agent, prompt_text, effort=None):
|
|
|
233
279
|
|
|
234
280
|
try:
|
|
235
281
|
state = StreamState()
|
|
236
|
-
for event in agent.start(prompt_text):
|
|
282
|
+
for event in agent.start(expand_file_references(prompt_text)):
|
|
237
283
|
if event[0] == "error":
|
|
238
284
|
state._stop_live()
|
|
239
285
|
if state.reasoning_started:
|