ara-cli 0.1.10.0__py3-none-any.whl → 0.1.13.3__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.
- ara_cli/__init__.py +51 -6
- ara_cli/__main__.py +270 -103
- ara_cli/ara_command_action.py +106 -63
- ara_cli/ara_config.py +187 -128
- ara_cli/ara_subcommands/__init__.py +0 -0
- ara_cli/ara_subcommands/autofix.py +26 -0
- ara_cli/ara_subcommands/chat.py +27 -0
- ara_cli/ara_subcommands/classifier_directory.py +16 -0
- ara_cli/ara_subcommands/common.py +100 -0
- ara_cli/ara_subcommands/config.py +221 -0
- ara_cli/ara_subcommands/convert.py +43 -0
- ara_cli/ara_subcommands/create.py +75 -0
- ara_cli/ara_subcommands/delete.py +22 -0
- ara_cli/ara_subcommands/extract.py +22 -0
- ara_cli/ara_subcommands/fetch.py +41 -0
- ara_cli/ara_subcommands/fetch_agents.py +22 -0
- ara_cli/ara_subcommands/fetch_scripts.py +19 -0
- ara_cli/ara_subcommands/fetch_templates.py +19 -0
- ara_cli/ara_subcommands/list.py +139 -0
- ara_cli/ara_subcommands/list_tags.py +25 -0
- ara_cli/ara_subcommands/load.py +48 -0
- ara_cli/ara_subcommands/prompt.py +136 -0
- ara_cli/ara_subcommands/read.py +47 -0
- ara_cli/ara_subcommands/read_status.py +20 -0
- ara_cli/ara_subcommands/read_user.py +20 -0
- ara_cli/ara_subcommands/reconnect.py +27 -0
- ara_cli/ara_subcommands/rename.py +22 -0
- ara_cli/ara_subcommands/scan.py +14 -0
- ara_cli/ara_subcommands/set_status.py +22 -0
- ara_cli/ara_subcommands/set_user.py +22 -0
- ara_cli/ara_subcommands/template.py +16 -0
- ara_cli/artefact_autofix.py +154 -63
- ara_cli/artefact_converter.py +256 -0
- ara_cli/artefact_models/artefact_model.py +106 -25
- ara_cli/artefact_models/artefact_templates.py +20 -10
- ara_cli/artefact_models/epic_artefact_model.py +11 -2
- ara_cli/artefact_models/feature_artefact_model.py +31 -1
- ara_cli/artefact_models/userstory_artefact_model.py +15 -3
- ara_cli/artefact_scan.py +2 -2
- ara_cli/chat.py +283 -80
- ara_cli/chat_agent/__init__.py +0 -0
- ara_cli/chat_agent/agent_process_manager.py +155 -0
- ara_cli/chat_script_runner/__init__.py +0 -0
- ara_cli/chat_script_runner/script_completer.py +23 -0
- ara_cli/chat_script_runner/script_finder.py +41 -0
- ara_cli/chat_script_runner/script_lister.py +36 -0
- ara_cli/chat_script_runner/script_runner.py +36 -0
- ara_cli/chat_web_search/__init__.py +0 -0
- ara_cli/chat_web_search/web_search.py +263 -0
- ara_cli/commands/agent_run_command.py +98 -0
- ara_cli/commands/fetch_agents_command.py +106 -0
- ara_cli/commands/fetch_scripts_command.py +43 -0
- ara_cli/commands/fetch_templates_command.py +39 -0
- ara_cli/commands/fetch_templates_commands.py +39 -0
- ara_cli/commands/list_agents_command.py +39 -0
- ara_cli/commands/read_command.py +17 -4
- ara_cli/completers.py +180 -0
- ara_cli/constants.py +2 -0
- ara_cli/directory_navigator.py +37 -4
- ara_cli/file_loaders/text_file_loader.py +2 -2
- ara_cli/global_file_lister.py +5 -15
- ara_cli/llm_utils.py +58 -0
- ara_cli/prompt_chat.py +20 -4
- ara_cli/prompt_extractor.py +199 -76
- ara_cli/prompt_handler.py +160 -59
- ara_cli/tag_extractor.py +38 -18
- ara_cli/template_loader.py +3 -2
- ara_cli/template_manager.py +52 -21
- ara_cli/templates/global-scripts/hello_global.py +1 -0
- ara_cli/templates/prompt-modules/commands/add_scenarios_for_new_behaviour.feature_creation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/align_feature_with_implementation_changes.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/analyze_codebase_and_plan_tasks.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/choose_best_parent_artefact.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/create_tasks_from_artefact_content.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/create_tests_for_uncovered_modules.test_generation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/derive_features_from_video_description.feature_creation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/describe_agent_capabilities.agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
- ara_cli/templates/prompt-modules/commands/execute_scoped_todos_in_task.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/explain_single_file_purpose.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/extract_file_information_bullets.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/extract_general.commands.md +12 -0
- ara_cli/templates/prompt-modules/commands/extract_markdown.commands.md +11 -0
- ara_cli/templates/prompt-modules/commands/extract_python.commands.md +13 -0
- ara_cli/templates/prompt-modules/commands/feature_add_or_modifiy_specified_behavior.commands.md +36 -0
- ara_cli/templates/prompt-modules/commands/feature_generate_initial_specified_bevahior.commands.md +53 -0
- ara_cli/templates/prompt-modules/commands/fix_failing_behave_step_definitions.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/fix_failing_pytest_tests.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/general_instruction_policy.commands.md +47 -0
- ara_cli/templates/prompt-modules/commands/generate_and_fix_pytest_tests.test_generation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
- ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
- ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
- ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
- ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
- ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
- ara_cli/templates/prompt-modules/commands/suggest_next_story_child_tasks.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/summarize_or_transcribe_media.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/update_feature_to_match_implementation.feature_creation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/update_user_story_with_requirements.interview_agent.commands.md +1 -0
- ara_cli/version.py +1 -1
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/METADATA +34 -1
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/RECORD +123 -54
- tests/test_ara_command_action.py +31 -19
- tests/test_ara_config.py +177 -90
- tests/test_artefact_autofix.py +170 -97
- tests/test_artefact_autofix_integration.py +495 -0
- tests/test_artefact_converter.py +357 -0
- tests/test_artefact_extraction.py +564 -0
- tests/test_artefact_scan.py +1 -1
- tests/test_chat.py +162 -126
- tests/test_chat_givens_images.py +603 -0
- tests/test_chat_script_runner.py +454 -0
- tests/test_global_file_lister.py +1 -1
- tests/test_llm_utils.py +164 -0
- tests/test_prompt_chat.py +343 -0
- tests/test_prompt_extractor.py +683 -0
- tests/test_prompt_handler.py +12 -4
- tests/test_tag_extractor.py +19 -13
- tests/test_web_search.py +467 -0
- ara_cli/ara_command_parser.py +0 -605
- ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
- ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
- ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
- ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
- ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
- ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
- ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
- ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
- ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
- ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
- ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
- ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
- ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
- ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
- ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
- ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/entry_points.txt +0 -0
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/top_level.txt +0 -0
ara_cli/chat.py
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import
|
|
2
|
+
import sys
|
|
3
3
|
import argparse
|
|
4
4
|
import cmd2
|
|
5
5
|
|
|
6
6
|
from ara_cli.prompt_handler import send_prompt
|
|
7
7
|
|
|
8
|
+
from . import (
|
|
9
|
+
CATEGORY_CHAT_CONTROL,
|
|
10
|
+
CATEGORY_LLM_CONTROL,
|
|
11
|
+
CATEGORY_SCRIPT_CONTROL,
|
|
12
|
+
CATEGORY_AGENT_CONTROL,
|
|
13
|
+
)
|
|
14
|
+
from . import ROLE_PROMPT, ROLE_RESPONSE, INTRO
|
|
15
|
+
from . import BINARY_TYPE_MAPPING, DOCUMENT_TYPE_EXTENSIONS
|
|
16
|
+
|
|
8
17
|
from . import error_handler
|
|
9
18
|
from ara_cli.error_handler import AraError, AraConfigurationError
|
|
10
19
|
|
|
11
20
|
from ara_cli.file_loaders.document_file_loader import DocumentFileLoader
|
|
12
21
|
from ara_cli.file_loaders.binary_file_loader import BinaryFileLoader
|
|
13
22
|
from ara_cli.file_loaders.text_file_loader import TextFileLoader
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
action="store_true",
|
|
24
|
-
help="Overwrite existing files without using LLM for merging.",
|
|
23
|
+
from ara_cli.chat_agent.agent_process_manager import AgentProcessManager
|
|
24
|
+
|
|
25
|
+
from ara_cli.chat_script_runner.script_runner import ScriptRunner
|
|
26
|
+
from ara_cli.chat_script_runner.script_completer import ScriptCompleter
|
|
27
|
+
from ara_cli.chat_script_runner.script_lister import ScriptLister
|
|
28
|
+
from ara_cli.chat_web_search.web_search import (
|
|
29
|
+
perform_web_search_completion,
|
|
30
|
+
is_web_search_supported,
|
|
31
|
+
get_supported_models_message,
|
|
25
32
|
)
|
|
26
33
|
|
|
27
|
-
load_parser = argparse.ArgumentParser()
|
|
28
|
-
load_parser.add_argument("file_name", nargs="?", default="", help="File to load")
|
|
29
|
-
load_parser.add_argument(
|
|
30
|
-
"--load-images",
|
|
31
|
-
action="store_true",
|
|
32
|
-
help="Extract and describe images from documents",
|
|
33
|
-
)
|
|
34
34
|
|
|
35
35
|
extract_parser = argparse.ArgumentParser()
|
|
36
36
|
extract_parser.add_argument(
|
|
@@ -53,39 +53,6 @@ load_parser.add_argument(
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
class Chat(cmd2.Cmd):
|
|
56
|
-
CATEGORY_CHAT_CONTROL = "Chat control commands"
|
|
57
|
-
CATEGORY_LLM_CONTROL = "Language model controls"
|
|
58
|
-
|
|
59
|
-
INTRO = """/***************************************/
|
|
60
|
-
araarar
|
|
61
|
-
aa ara
|
|
62
|
-
aa aa aara
|
|
63
|
-
a araarar
|
|
64
|
-
a ar ar
|
|
65
|
-
aa ara
|
|
66
|
-
a a
|
|
67
|
-
a aa
|
|
68
|
-
a a
|
|
69
|
-
ar aa aa
|
|
70
|
-
(c) ara chat by talsen team
|
|
71
|
-
aa aa
|
|
72
|
-
aa a
|
|
73
|
-
a aa
|
|
74
|
-
aa
|
|
75
|
-
/***************************************/
|
|
76
|
-
Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat mode):"""
|
|
77
|
-
|
|
78
|
-
ROLE_PROMPT = "ara prompt"
|
|
79
|
-
ROLE_RESPONSE = "ara response"
|
|
80
|
-
|
|
81
|
-
BINARY_TYPE_MAPPING = {
|
|
82
|
-
".png": "image/png",
|
|
83
|
-
".jpg": "image/jpeg",
|
|
84
|
-
".jpeg": "image/jpeg",
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
DOCUMENT_TYPE_EXTENSIONS = [".docx", ".doc", ".odt", ".pdf"]
|
|
88
|
-
|
|
89
56
|
def __init__(
|
|
90
57
|
self,
|
|
91
58
|
chat_name: str,
|
|
@@ -93,6 +60,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
93
60
|
enable_commands: list[str] | None = None,
|
|
94
61
|
):
|
|
95
62
|
from ara_cli.template_loader import TemplateLoader
|
|
63
|
+
|
|
96
64
|
shortcuts = dict(cmd2.DEFAULT_SHORTCUTS)
|
|
97
65
|
if enable_commands:
|
|
98
66
|
enable_commands.append("quit") # always allow quitting
|
|
@@ -116,15 +84,21 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
116
84
|
self.disable_commands(commands_to_disable)
|
|
117
85
|
|
|
118
86
|
self.prompt = "ara> "
|
|
119
|
-
self.intro =
|
|
87
|
+
self.intro = INTRO
|
|
120
88
|
|
|
121
|
-
self.default_chat_content = f"# {
|
|
89
|
+
self.default_chat_content = f"# {ROLE_PROMPT}:\n"
|
|
122
90
|
self.chat_name = self.setup_chat(chat_name, reset)
|
|
123
91
|
self.chat_name = os.path.abspath(self.chat_name)
|
|
124
92
|
self.chat_history = []
|
|
125
93
|
self.message_buffer = []
|
|
126
94
|
self.config = self._retrieve_ara_config()
|
|
127
95
|
self.template_loader = TemplateLoader(chat_instance=self)
|
|
96
|
+
self.script_runner = ScriptRunner(chat_instance=self)
|
|
97
|
+
self.script_lister = ScriptLister()
|
|
98
|
+
self.script_completer = ScriptCompleter()
|
|
99
|
+
|
|
100
|
+
# Initialize agent process manager
|
|
101
|
+
self.agent_manager = AgentProcessManager(self)
|
|
128
102
|
|
|
129
103
|
def disable_commands(self, commands: list[str]):
|
|
130
104
|
for command in commands:
|
|
@@ -146,6 +120,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
146
120
|
self.aliases["h"] = "help"
|
|
147
121
|
self.aliases["n"] = "NEW"
|
|
148
122
|
self.aliases["e"] = "EXTRACT"
|
|
123
|
+
self.aliases["SEARCH"] = "search"
|
|
149
124
|
self.aliases["l"] = "LOAD"
|
|
150
125
|
self.aliases["lr"] = "LOAD_RULES"
|
|
151
126
|
self.aliases["li"] = "LOAD_INTENTION"
|
|
@@ -153,6 +128,11 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
153
128
|
self.aliases["lg"] = "LOAD_GIVENS"
|
|
154
129
|
self.aliases["lb"] = "LOAD_BLUEPRINT"
|
|
155
130
|
self.aliases["lt"] = "LOAD_TEMPLATE"
|
|
131
|
+
self.aliases["rpy"] = "run_pyscript"
|
|
132
|
+
self.aliases["a"] = "AGENT_RUN"
|
|
133
|
+
self.aliases["al"] = "LIST_AGENTS"
|
|
134
|
+
self.aliases["la"] = "LIST_AGENTS"
|
|
135
|
+
self.aliases["AGENT_LIST"] = "LIST_AGENTS"
|
|
156
136
|
|
|
157
137
|
def setup_chat(self, chat_name, reset: bool = None):
|
|
158
138
|
if os.path.exists(chat_name):
|
|
@@ -167,9 +147,12 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
167
147
|
chat_file_short = os.path.split(chat_file)[-1]
|
|
168
148
|
|
|
169
149
|
if reset is None:
|
|
170
|
-
|
|
171
|
-
f"{chat_file_short} already exists. Do you want to reset the chat? (y/N): "
|
|
150
|
+
print(
|
|
151
|
+
f"{chat_file_short} already exists. Do you want to reset the chat? (y/N): ",
|
|
152
|
+
end="",
|
|
153
|
+
flush=True,
|
|
172
154
|
)
|
|
155
|
+
user_input = sys.stdin.readline().strip()
|
|
173
156
|
if user_input.lower() == "y":
|
|
174
157
|
self.create_empty_chat_file(chat_file)
|
|
175
158
|
if reset:
|
|
@@ -197,13 +180,14 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
197
180
|
print(f"File {file_name} not found.")
|
|
198
181
|
return False
|
|
199
182
|
return method(self, file_path, *args, **kwargs)
|
|
183
|
+
|
|
200
184
|
return wrapper
|
|
201
185
|
|
|
202
186
|
@staticmethod
|
|
203
187
|
def get_last_role_marker(lines):
|
|
204
188
|
if not lines:
|
|
205
189
|
return
|
|
206
|
-
role_markers = [f"# {
|
|
190
|
+
role_markers = [f"# {ROLE_PROMPT}:", f"# {ROLE_RESPONSE}"]
|
|
207
191
|
for line in reversed(lines):
|
|
208
192
|
stripped_line = line.strip()
|
|
209
193
|
if stripped_line.startswith(tuple(role_markers)):
|
|
@@ -265,8 +249,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
265
249
|
import re
|
|
266
250
|
from ara_cli.prompt_handler import prepend_system_prompt
|
|
267
251
|
|
|
268
|
-
prompt_marker = f"# {
|
|
269
|
-
response_marker = f"# {
|
|
252
|
+
prompt_marker = f"# {ROLE_PROMPT}:"
|
|
253
|
+
response_marker = f"# {ROLE_RESPONSE}:"
|
|
270
254
|
|
|
271
255
|
split_pattern = re.compile(f"({prompt_marker}|{response_marker})")
|
|
272
256
|
|
|
@@ -302,7 +286,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
302
286
|
def send_message(self):
|
|
303
287
|
self.chat_history = self.load_chat_history(self.chat_name)
|
|
304
288
|
prompt_to_send = self.assemble_prompt()
|
|
305
|
-
role_marker = f"# {
|
|
289
|
+
role_marker = f"# {ROLE_RESPONSE}:"
|
|
306
290
|
|
|
307
291
|
with open(self.chat_name, "a+", encoding="utf-8") as file:
|
|
308
292
|
last_line = self.get_last_line(file)
|
|
@@ -351,9 +335,9 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
351
335
|
def find_last_reply_index(self, lines: list[str]):
|
|
352
336
|
index_to_remove = None
|
|
353
337
|
for i, line in enumerate(reversed(lines)):
|
|
354
|
-
if line.strip().startswith(f"# {
|
|
338
|
+
if line.strip().startswith(f"# {ROLE_PROMPT}"):
|
|
355
339
|
break
|
|
356
|
-
if line.strip().startswith(f"# {
|
|
340
|
+
if line.strip().startswith(f"# {ROLE_RESPONSE}"):
|
|
357
341
|
index_to_remove = len(lines) - i - 1
|
|
358
342
|
break
|
|
359
343
|
return index_to_remove
|
|
@@ -378,7 +362,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
378
362
|
def add_prompt_tag_if_needed(self, chat_file: str):
|
|
379
363
|
with open(chat_file, "r", encoding="utf-8") as file:
|
|
380
364
|
lines = file.readlines()
|
|
381
|
-
|
|
365
|
+
|
|
366
|
+
prompt_tag = f"# {ROLE_PROMPT}:"
|
|
382
367
|
if Chat.get_last_role_marker(lines) == prompt_tag:
|
|
383
368
|
return
|
|
384
369
|
append = prompt_tag
|
|
@@ -388,7 +373,6 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
388
373
|
with open(chat_file, "a", encoding="utf-8") as file:
|
|
389
374
|
file.write(append)
|
|
390
375
|
|
|
391
|
-
# @file_exists_check
|
|
392
376
|
def load_text_file(
|
|
393
377
|
self,
|
|
394
378
|
file_path,
|
|
@@ -406,7 +390,6 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
406
390
|
extract_images=extract_images,
|
|
407
391
|
)
|
|
408
392
|
|
|
409
|
-
# @file_exists_check
|
|
410
393
|
def load_binary_file(
|
|
411
394
|
self, file_path, mime_type: str, prefix: str = "", suffix: str = ""
|
|
412
395
|
):
|
|
@@ -420,7 +403,6 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
420
403
|
reader = MarkdownReader(file_path)
|
|
421
404
|
return reader.read(extract_images=extract_images)
|
|
422
405
|
|
|
423
|
-
# @file_exists_check
|
|
424
406
|
def load_document_file(
|
|
425
407
|
self,
|
|
426
408
|
file_path: str,
|
|
@@ -446,8 +428,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
446
428
|
block_delimiter: str = "",
|
|
447
429
|
extract_images: bool = False,
|
|
448
430
|
):
|
|
449
|
-
binary_type_mapping =
|
|
450
|
-
document_type_extensions =
|
|
431
|
+
binary_type_mapping = BINARY_TYPE_MAPPING
|
|
432
|
+
document_type_extensions = DOCUMENT_TYPE_EXTENSIONS
|
|
451
433
|
|
|
452
434
|
file_type = None
|
|
453
435
|
file_name_lower = file_name.lower()
|
|
@@ -486,7 +468,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
486
468
|
files.sort()
|
|
487
469
|
for i, file in enumerate(files):
|
|
488
470
|
print(f"{i + 1}: {os.path.basename(file)}")
|
|
489
|
-
|
|
471
|
+
print("Please choose a file to load (enter number): ", end="", flush=True)
|
|
472
|
+
choice = sys.stdin.readline().strip()
|
|
490
473
|
try:
|
|
491
474
|
choice_index = int(choice) - 1
|
|
492
475
|
if choice_index < 0 or choice_index >= len(files):
|
|
@@ -512,6 +495,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
512
495
|
|
|
513
496
|
def do_quit(self, _):
|
|
514
497
|
"""Exit ara-cli"""
|
|
498
|
+
self.agent_manager.cleanup_agent_process()
|
|
515
499
|
print("Chat ended")
|
|
516
500
|
self.last_result = True
|
|
517
501
|
return True
|
|
@@ -581,7 +565,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
581
565
|
import glob
|
|
582
566
|
|
|
583
567
|
if file_name == "":
|
|
584
|
-
|
|
568
|
+
print("What file do you want to load? ", end="", flush=True)
|
|
569
|
+
file_name = sys.stdin.readline().strip()
|
|
585
570
|
file_pattern = os.path.join(os.path.dirname(self.chat_name), file_name)
|
|
586
571
|
matching_files = glob.glob(file_pattern)
|
|
587
572
|
if not matching_files:
|
|
@@ -592,7 +577,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
592
577
|
return matching_files
|
|
593
578
|
|
|
594
579
|
def load_image(self, file_name: str, prefix: str = "", suffix: str = ""):
|
|
595
|
-
binary_type_mapping =
|
|
580
|
+
binary_type_mapping = BINARY_TYPE_MAPPING
|
|
596
581
|
|
|
597
582
|
file_type = None
|
|
598
583
|
file_name_lower = file_name.lower()
|
|
@@ -621,6 +606,62 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
621
606
|
return False
|
|
622
607
|
return True
|
|
623
608
|
|
|
609
|
+
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
610
|
+
def do_search(self, query: str):
|
|
611
|
+
"""Perform a web search and append the results to the chat.
|
|
612
|
+
Usage: search <query>
|
|
613
|
+
"""
|
|
614
|
+
if not query:
|
|
615
|
+
self.poutput("Please provide a search query.")
|
|
616
|
+
return
|
|
617
|
+
|
|
618
|
+
# Check if web search is supported by the current model
|
|
619
|
+
from ara_cli.prompt_handler import LLMSingleton
|
|
620
|
+
|
|
621
|
+
chat_instance = LLMSingleton.get_instance()
|
|
622
|
+
config_parameters = chat_instance.get_config_by_purpose("default")
|
|
623
|
+
default_llm = config_parameters.get("model")
|
|
624
|
+
|
|
625
|
+
is_supported, _ = is_web_search_supported(default_llm)
|
|
626
|
+
if not is_supported:
|
|
627
|
+
self.poutput(get_supported_models_message(default_llm))
|
|
628
|
+
return
|
|
629
|
+
|
|
630
|
+
self.add_prompt_tag_if_needed(self.chat_name)
|
|
631
|
+
|
|
632
|
+
role_marker = f"# Web Search Results for '{query}':"
|
|
633
|
+
|
|
634
|
+
with open(self.chat_name, "a+", encoding="utf-8") as file:
|
|
635
|
+
last_line = self.get_last_line(file)
|
|
636
|
+
|
|
637
|
+
self.poutput(role_marker)
|
|
638
|
+
|
|
639
|
+
if not last_line.startswith(role_marker):
|
|
640
|
+
if last_line:
|
|
641
|
+
file.write("\n")
|
|
642
|
+
file.write(role_marker + "\n")
|
|
643
|
+
|
|
644
|
+
try:
|
|
645
|
+
# perform_web_search_completion now returns a generator or a string
|
|
646
|
+
search_result = perform_web_search_completion(query)
|
|
647
|
+
|
|
648
|
+
if isinstance(search_result, str):
|
|
649
|
+
# If it's a string, it's an error/info message
|
|
650
|
+
self.poutput(search_result)
|
|
651
|
+
file.write(search_result + "\n")
|
|
652
|
+
else:
|
|
653
|
+
# Otherwise, it's a generator, stream the content
|
|
654
|
+
for chunk in search_result:
|
|
655
|
+
chunk_content = chunk.choices[0].delta.content
|
|
656
|
+
if not chunk_content:
|
|
657
|
+
continue
|
|
658
|
+
self.poutput(chunk_content, end="")
|
|
659
|
+
file.write(chunk_content)
|
|
660
|
+
file.flush()
|
|
661
|
+
self.poutput("")
|
|
662
|
+
except Exception as e:
|
|
663
|
+
error_handler.report_error(e)
|
|
664
|
+
|
|
624
665
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
625
666
|
def do_LOAD_IMAGE(self, file_name):
|
|
626
667
|
"""Load an image file and append it to chat file. Can be given the file name in-line. Will attempt to find the file relative to chat file first, then treat the given path as absolute"""
|
|
@@ -638,7 +679,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
638
679
|
# Determine mime type
|
|
639
680
|
file_type = None
|
|
640
681
|
file_path_lower = file_path.lower()
|
|
641
|
-
for extension, mime_type in
|
|
682
|
+
for extension, mime_type in BINARY_TYPE_MAPPING.items():
|
|
642
683
|
if file_path_lower.endswith(extension):
|
|
643
684
|
file_type = mime_type
|
|
644
685
|
break
|
|
@@ -745,7 +786,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
745
786
|
def do_NEW(self, chat_name):
|
|
746
787
|
"""Create a new chat. Optionally provide a chat name in-line: NEW new_chat"""
|
|
747
788
|
if chat_name == "":
|
|
748
|
-
|
|
789
|
+
print("What should be the new chat name? ", end="", flush=True)
|
|
790
|
+
chat_name = sys.stdin.readline().strip()
|
|
749
791
|
current_directory = os.path.dirname(self.chat_name)
|
|
750
792
|
chat_file_path = os.path.join(current_directory, chat_name)
|
|
751
793
|
self.__init__(chat_file_path)
|
|
@@ -758,27 +800,35 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
758
800
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
759
801
|
def do_CLEAR(self, _):
|
|
760
802
|
"""Clear the chat and the file containing it"""
|
|
761
|
-
|
|
803
|
+
print("Are you sure you want to clear the chat? (y/N): ", end="", flush=True)
|
|
804
|
+
user_input = sys.stdin.readline().strip()
|
|
762
805
|
if user_input.lower() != "y":
|
|
763
806
|
return
|
|
764
807
|
self.create_empty_chat_file(self.chat_name)
|
|
765
808
|
self.chat_history = self.load_chat_history(self.chat_name)
|
|
809
|
+
self.message_buffer.clear()
|
|
766
810
|
print(f"Cleared content of {self.chat_name}")
|
|
767
811
|
|
|
768
812
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
769
813
|
def do_LOAD_RULES(self, rules_name):
|
|
770
814
|
"""Load rules from ./prompt.data/*.rules.md or from a specified template directory if an argument is given. Specify global/<rules_template> to access globally defined rules templates"""
|
|
771
|
-
self.template_loader.load_template(
|
|
815
|
+
self.template_loader.load_template(
|
|
816
|
+
rules_name, "rules", self.chat_name, "*.rules.md"
|
|
817
|
+
)
|
|
772
818
|
|
|
773
819
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
774
820
|
def do_LOAD_INTENTION(self, intention_name):
|
|
775
821
|
"""Load intention from ./prompt.data/*.intention.md or from a specified template directory if an argument is given. Specify global/<intention_template> to access globally defined intention templates"""
|
|
776
|
-
self.template_loader.load_template(
|
|
822
|
+
self.template_loader.load_template(
|
|
823
|
+
intention_name, "intention", self.chat_name, "*.intention.md"
|
|
824
|
+
)
|
|
777
825
|
|
|
778
826
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
779
827
|
def do_LOAD_COMMANDS(self, commands_name):
|
|
780
828
|
"""Load commands from ./prompt.data/*.commands.md or from a specified template directory if an argument is given. Specify global/<commands_template> to access globally defined commands templates"""
|
|
781
|
-
self.template_loader.load_template(
|
|
829
|
+
self.template_loader.load_template(
|
|
830
|
+
commands_name, "commands", self.chat_name, "*.commands.md"
|
|
831
|
+
)
|
|
782
832
|
|
|
783
833
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
784
834
|
def do_LOAD_BLUEPRINT(self, blueprint_name):
|
|
@@ -952,7 +1002,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
952
1002
|
def do_SEND(self, _):
|
|
953
1003
|
"""Send prompt to the LLM"""
|
|
954
1004
|
message = "\n".join(self.message_buffer)
|
|
955
|
-
self.save_message(
|
|
1005
|
+
self.save_message(ROLE_PROMPT, message)
|
|
956
1006
|
self.send_message()
|
|
957
1007
|
|
|
958
1008
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
@@ -1071,7 +1121,9 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
1071
1121
|
|
|
1072
1122
|
def _template_completer(self, text: str, template_type: str) -> list[str]:
|
|
1073
1123
|
"""Generic completer for different template types."""
|
|
1074
|
-
available_templates = self.template_loader.get_available_templates(
|
|
1124
|
+
available_templates = self.template_loader.get_available_templates(
|
|
1125
|
+
template_type, os.path.dirname(self.chat_name)
|
|
1126
|
+
)
|
|
1075
1127
|
if not text:
|
|
1076
1128
|
return available_templates
|
|
1077
1129
|
return [t for t in available_templates if t.startswith(text)]
|
|
@@ -1090,4 +1142,155 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
1090
1142
|
|
|
1091
1143
|
def complete_LOAD_BLUEPRINT(self, text, line, begidx, endidx):
|
|
1092
1144
|
"""Completer for the LOAD_BLUEPRINT command."""
|
|
1093
|
-
return self._template_completer(text, "blueprint")
|
|
1145
|
+
return self._template_completer(text, "blueprint")
|
|
1146
|
+
|
|
1147
|
+
def _select_script_from_list(
|
|
1148
|
+
self, scripts: list[str], not_found_message: str, prompt: str
|
|
1149
|
+
) -> str | None:
|
|
1150
|
+
"""Displays a list of scripts and prompts the user to select one."""
|
|
1151
|
+
if not scripts:
|
|
1152
|
+
self.poutput(not_found_message)
|
|
1153
|
+
return None
|
|
1154
|
+
|
|
1155
|
+
# Sort the scripts alphabetically by their basename for consistent display
|
|
1156
|
+
# Create a list of (basename, full_script_name) tuples for sorting and later retrieval
|
|
1157
|
+
scripts_with_basenames = [(os.path.basename(s), s) for s in scripts]
|
|
1158
|
+
scripts_with_basenames.sort(
|
|
1159
|
+
key=lambda x: x[0].lower()
|
|
1160
|
+
) # Sort by lowercase basename
|
|
1161
|
+
|
|
1162
|
+
for i, (basename, full_script_name) in enumerate(scripts_with_basenames):
|
|
1163
|
+
self.poutput(f"{i + 1}: {basename}")
|
|
1164
|
+
|
|
1165
|
+
try:
|
|
1166
|
+
choice = input(prompt)
|
|
1167
|
+
choice_index = int(choice) - 1
|
|
1168
|
+
if 0 <= choice_index < len(scripts_with_basenames):
|
|
1169
|
+
# Return the full script name from the sorted list
|
|
1170
|
+
return scripts_with_basenames[choice_index][1]
|
|
1171
|
+
else:
|
|
1172
|
+
self.poutput("Invalid choice. Aborting.")
|
|
1173
|
+
return None
|
|
1174
|
+
except (ValueError, EOFError):
|
|
1175
|
+
self.poutput("Invalid input. Aborting.")
|
|
1176
|
+
return None
|
|
1177
|
+
|
|
1178
|
+
@cmd2.with_category(CATEGORY_SCRIPT_CONTROL)
|
|
1179
|
+
def do_run_pyscript(self, args):
|
|
1180
|
+
"""Run a python script from the chat.
|
|
1181
|
+
Usage: run_pyscript <script_name> [args...]
|
|
1182
|
+
"""
|
|
1183
|
+
script_name, script_args = self._parse_run_pyscript_args(args)
|
|
1184
|
+
|
|
1185
|
+
# If no script name provided, list available scripts grouped by type
|
|
1186
|
+
if not script_name:
|
|
1187
|
+
self._list_available_scripts()
|
|
1188
|
+
return
|
|
1189
|
+
|
|
1190
|
+
script_to_run = self._resolve_script_to_run(script_name, script_args)
|
|
1191
|
+
|
|
1192
|
+
if not script_to_run:
|
|
1193
|
+
return
|
|
1194
|
+
|
|
1195
|
+
# Pass arguments to script runner
|
|
1196
|
+
output = self.script_runner.run_script(script_to_run, script_args)
|
|
1197
|
+
if output:
|
|
1198
|
+
self.poutput(output.strip())
|
|
1199
|
+
|
|
1200
|
+
def _list_available_scripts(self):
|
|
1201
|
+
"""Lists available scripts grouped by type (global and custom)."""
|
|
1202
|
+
global_scripts = self.script_lister.get_global_scripts()
|
|
1203
|
+
custom_scripts = self.script_lister.get_custom_scripts()
|
|
1204
|
+
|
|
1205
|
+
if not global_scripts and not custom_scripts:
|
|
1206
|
+
self.poutput("No scripts found.")
|
|
1207
|
+
return
|
|
1208
|
+
|
|
1209
|
+
self.poutput("Available scripts:")
|
|
1210
|
+
self.poutput("")
|
|
1211
|
+
|
|
1212
|
+
if custom_scripts:
|
|
1213
|
+
self.poutput("Custom scripts:")
|
|
1214
|
+
for script in sorted(custom_scripts):
|
|
1215
|
+
self.poutput(f" {script}")
|
|
1216
|
+
self.poutput("")
|
|
1217
|
+
|
|
1218
|
+
if global_scripts:
|
|
1219
|
+
self.poutput("Global scripts:")
|
|
1220
|
+
for script in sorted(global_scripts):
|
|
1221
|
+
self.poutput(f" global/{script}")
|
|
1222
|
+
|
|
1223
|
+
def _parse_run_pyscript_args(self, args):
|
|
1224
|
+
"""Parses arguments for run_pyscript command."""
|
|
1225
|
+
import shlex
|
|
1226
|
+
|
|
1227
|
+
if not args:
|
|
1228
|
+
return "", []
|
|
1229
|
+
|
|
1230
|
+
# args is a cmd2.Statement (subclass of str), so we can use it directly
|
|
1231
|
+
full_args = str(args)
|
|
1232
|
+
# Use shlex to split arguments, enabling quoted args support
|
|
1233
|
+
split_args = shlex.split(full_args)
|
|
1234
|
+
if not split_args:
|
|
1235
|
+
return "", []
|
|
1236
|
+
|
|
1237
|
+
script_name = split_args[0]
|
|
1238
|
+
script_args = split_args[1:] if len(split_args) > 1 else []
|
|
1239
|
+
return script_name, script_args
|
|
1240
|
+
|
|
1241
|
+
def _resolve_script_to_run(self, script_name, script_args):
|
|
1242
|
+
"""Resolves the script name to run."""
|
|
1243
|
+
return script_name
|
|
1244
|
+
|
|
1245
|
+
def complete_run_pyscript(self, text, line, begidx, endidx):
|
|
1246
|
+
"""Completer for the run_pyscript command."""
|
|
1247
|
+
# Get all scripts: ['custom.py', 'global/global.py']
|
|
1248
|
+
available_scripts = self.script_lister.get_all_scripts()
|
|
1249
|
+
|
|
1250
|
+
# Add special commands
|
|
1251
|
+
special_commands = [
|
|
1252
|
+
# "global/"
|
|
1253
|
+
# "*", "global/*"
|
|
1254
|
+
]
|
|
1255
|
+
|
|
1256
|
+
possible_completions = sorted(list(set(available_scripts + special_commands)))
|
|
1257
|
+
|
|
1258
|
+
# Filter based on what the user has typed
|
|
1259
|
+
return [s for s in possible_completions if s.startswith(text)]
|
|
1260
|
+
|
|
1261
|
+
# ===== AGENT CONTROL COMMANDS =====
|
|
1262
|
+
|
|
1263
|
+
@cmd2.with_category(CATEGORY_AGENT_CONTROL)
|
|
1264
|
+
def do_LIST_AGENTS(self, _):
|
|
1265
|
+
"""Lists all available executable binary agents."""
|
|
1266
|
+
from ara_cli.commands.list_agents_command import ListAgentsCommand
|
|
1267
|
+
|
|
1268
|
+
command = ListAgentsCommand(chat_instance=self)
|
|
1269
|
+
command.execute()
|
|
1270
|
+
|
|
1271
|
+
@cmd2.with_category(CATEGORY_AGENT_CONTROL)
|
|
1272
|
+
def do_AGENT_RUN(self, args):
|
|
1273
|
+
"""Run a binary agent interactively from the 'ara/.araconfig/agents' directory.
|
|
1274
|
+
Usage: AGENT_RUN <agent_name> [arg1] [arg2] ...
|
|
1275
|
+
Example:
|
|
1276
|
+
AGENT_RUN feature-creation -b .
|
|
1277
|
+
"""
|
|
1278
|
+
|
|
1279
|
+
from ara_cli.commands.agent_run_command import AgentRunCommand
|
|
1280
|
+
|
|
1281
|
+
command = AgentRunCommand(self, args)
|
|
1282
|
+
command.execute()
|
|
1283
|
+
|
|
1284
|
+
def complete_AGENT_RUN(self, text, line, begidx, endidx):
|
|
1285
|
+
"""Completer for AGENT_RUN command."""
|
|
1286
|
+
from ara_cli.commands.list_agents_command import list_available_binary_agents
|
|
1287
|
+
|
|
1288
|
+
parts = line.split()
|
|
1289
|
+
# This completer runs when the user is typing the first argument (the agent name)
|
|
1290
|
+
if len(parts) < 2 or (len(parts) == 2 and not line.endswith(" ")):
|
|
1291
|
+
available_agents = list_available_binary_agents(self)
|
|
1292
|
+
if not text:
|
|
1293
|
+
return available_agents
|
|
1294
|
+
return [a for a in available_agents if a.startswith(text)]
|
|
1295
|
+
# For subsequent arguments, we can offer file/directory completion
|
|
1296
|
+
return self.path_complete(text, line, begidx, endidx)
|
|
File without changes
|