ara-cli 0.1.10.5__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 +87 -75
- ara_cli/ara_command_action.py +95 -57
- ara_cli/ara_config.py +187 -128
- ara_cli/ara_subcommands/common.py +2 -2
- ara_cli/ara_subcommands/config.py +221 -0
- ara_cli/ara_subcommands/convert.py +43 -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 +15 -10
- ara_cli/ara_subcommands/list.py +97 -23
- ara_cli/artefact_autofix.py +115 -62
- ara_cli/artefact_converter.py +256 -0
- ara_cli/chat.py +283 -62
- 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/completers.py +71 -35
- ara_cli/constants.py +2 -0
- ara_cli/directory_navigator.py +37 -4
- ara_cli/llm_utils.py +58 -0
- ara_cli/prompt_chat.py +20 -4
- ara_cli/prompt_extractor.py +47 -32
- ara_cli/template_loader.py +2 -1
- 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.5.dist-info → ara_cli-0.1.13.3.dist-info}/METADATA +33 -1
- {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.13.3.dist-info}/RECORD +89 -43
- 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_chat.py +162 -126
- tests/test_chat_givens_images.py +603 -0
- tests/test_chat_script_runner.py +454 -0
- tests/test_llm_utils.py +164 -0
- tests/test_prompt_chat.py +343 -0
- tests/test_prompt_extractor.py +683 -0
- tests/test_web_search.py +467 -0
- ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
- ara_cli/templates/prompt-modules/blueprints/pytest_unittest_prompt.blueprint.md +0 -32
- 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.5.dist-info → ara_cli-0.1.13.3.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.13.3.dist-info}/entry_points.txt +0 -0
- {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.13.3.dist-info}/top_level.txt +0 -0
ara_cli/chat.py
CHANGED
|
@@ -1,16 +1,35 @@
|
|
|
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
|
|
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,
|
|
32
|
+
)
|
|
14
33
|
|
|
15
34
|
|
|
16
35
|
extract_parser = argparse.ArgumentParser()
|
|
@@ -34,39 +53,6 @@ load_parser.add_argument(
|
|
|
34
53
|
|
|
35
54
|
|
|
36
55
|
class Chat(cmd2.Cmd):
|
|
37
|
-
CATEGORY_CHAT_CONTROL = "Chat control commands"
|
|
38
|
-
CATEGORY_LLM_CONTROL = "Language model controls"
|
|
39
|
-
|
|
40
|
-
INTRO = """/***************************************/
|
|
41
|
-
araarar
|
|
42
|
-
aa ara
|
|
43
|
-
aa aa aara
|
|
44
|
-
a araarar
|
|
45
|
-
a ar ar
|
|
46
|
-
aa ara
|
|
47
|
-
a a
|
|
48
|
-
a aa
|
|
49
|
-
a a
|
|
50
|
-
ar aa aa
|
|
51
|
-
(c) ara chat by talsen team
|
|
52
|
-
aa aa
|
|
53
|
-
aa a
|
|
54
|
-
a aa
|
|
55
|
-
aa
|
|
56
|
-
/***************************************/
|
|
57
|
-
Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat mode):"""
|
|
58
|
-
|
|
59
|
-
ROLE_PROMPT = "ara prompt"
|
|
60
|
-
ROLE_RESPONSE = "ara response"
|
|
61
|
-
|
|
62
|
-
BINARY_TYPE_MAPPING = {
|
|
63
|
-
".png": "image/png",
|
|
64
|
-
".jpg": "image/jpeg",
|
|
65
|
-
".jpeg": "image/jpeg",
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
DOCUMENT_TYPE_EXTENSIONS = [".docx", ".doc", ".odt", ".pdf"]
|
|
69
|
-
|
|
70
56
|
def __init__(
|
|
71
57
|
self,
|
|
72
58
|
chat_name: str,
|
|
@@ -74,6 +60,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
74
60
|
enable_commands: list[str] | None = None,
|
|
75
61
|
):
|
|
76
62
|
from ara_cli.template_loader import TemplateLoader
|
|
63
|
+
|
|
77
64
|
shortcuts = dict(cmd2.DEFAULT_SHORTCUTS)
|
|
78
65
|
if enable_commands:
|
|
79
66
|
enable_commands.append("quit") # always allow quitting
|
|
@@ -97,15 +84,21 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
97
84
|
self.disable_commands(commands_to_disable)
|
|
98
85
|
|
|
99
86
|
self.prompt = "ara> "
|
|
100
|
-
self.intro =
|
|
87
|
+
self.intro = INTRO
|
|
101
88
|
|
|
102
|
-
self.default_chat_content = f"# {
|
|
89
|
+
self.default_chat_content = f"# {ROLE_PROMPT}:\n"
|
|
103
90
|
self.chat_name = self.setup_chat(chat_name, reset)
|
|
104
91
|
self.chat_name = os.path.abspath(self.chat_name)
|
|
105
92
|
self.chat_history = []
|
|
106
93
|
self.message_buffer = []
|
|
107
94
|
self.config = self._retrieve_ara_config()
|
|
108
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)
|
|
109
102
|
|
|
110
103
|
def disable_commands(self, commands: list[str]):
|
|
111
104
|
for command in commands:
|
|
@@ -127,6 +120,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
127
120
|
self.aliases["h"] = "help"
|
|
128
121
|
self.aliases["n"] = "NEW"
|
|
129
122
|
self.aliases["e"] = "EXTRACT"
|
|
123
|
+
self.aliases["SEARCH"] = "search"
|
|
130
124
|
self.aliases["l"] = "LOAD"
|
|
131
125
|
self.aliases["lr"] = "LOAD_RULES"
|
|
132
126
|
self.aliases["li"] = "LOAD_INTENTION"
|
|
@@ -134,6 +128,11 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
134
128
|
self.aliases["lg"] = "LOAD_GIVENS"
|
|
135
129
|
self.aliases["lb"] = "LOAD_BLUEPRINT"
|
|
136
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"
|
|
137
136
|
|
|
138
137
|
def setup_chat(self, chat_name, reset: bool = None):
|
|
139
138
|
if os.path.exists(chat_name):
|
|
@@ -148,9 +147,12 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
148
147
|
chat_file_short = os.path.split(chat_file)[-1]
|
|
149
148
|
|
|
150
149
|
if reset is None:
|
|
151
|
-
|
|
152
|
-
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,
|
|
153
154
|
)
|
|
155
|
+
user_input = sys.stdin.readline().strip()
|
|
154
156
|
if user_input.lower() == "y":
|
|
155
157
|
self.create_empty_chat_file(chat_file)
|
|
156
158
|
if reset:
|
|
@@ -178,13 +180,14 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
178
180
|
print(f"File {file_name} not found.")
|
|
179
181
|
return False
|
|
180
182
|
return method(self, file_path, *args, **kwargs)
|
|
183
|
+
|
|
181
184
|
return wrapper
|
|
182
185
|
|
|
183
186
|
@staticmethod
|
|
184
187
|
def get_last_role_marker(lines):
|
|
185
188
|
if not lines:
|
|
186
189
|
return
|
|
187
|
-
role_markers = [f"# {
|
|
190
|
+
role_markers = [f"# {ROLE_PROMPT}:", f"# {ROLE_RESPONSE}"]
|
|
188
191
|
for line in reversed(lines):
|
|
189
192
|
stripped_line = line.strip()
|
|
190
193
|
if stripped_line.startswith(tuple(role_markers)):
|
|
@@ -246,8 +249,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
246
249
|
import re
|
|
247
250
|
from ara_cli.prompt_handler import prepend_system_prompt
|
|
248
251
|
|
|
249
|
-
prompt_marker = f"# {
|
|
250
|
-
response_marker = f"# {
|
|
252
|
+
prompt_marker = f"# {ROLE_PROMPT}:"
|
|
253
|
+
response_marker = f"# {ROLE_RESPONSE}:"
|
|
251
254
|
|
|
252
255
|
split_pattern = re.compile(f"({prompt_marker}|{response_marker})")
|
|
253
256
|
|
|
@@ -283,7 +286,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
283
286
|
def send_message(self):
|
|
284
287
|
self.chat_history = self.load_chat_history(self.chat_name)
|
|
285
288
|
prompt_to_send = self.assemble_prompt()
|
|
286
|
-
role_marker = f"# {
|
|
289
|
+
role_marker = f"# {ROLE_RESPONSE}:"
|
|
287
290
|
|
|
288
291
|
with open(self.chat_name, "a+", encoding="utf-8") as file:
|
|
289
292
|
last_line = self.get_last_line(file)
|
|
@@ -332,9 +335,9 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
332
335
|
def find_last_reply_index(self, lines: list[str]):
|
|
333
336
|
index_to_remove = None
|
|
334
337
|
for i, line in enumerate(reversed(lines)):
|
|
335
|
-
if line.strip().startswith(f"# {
|
|
338
|
+
if line.strip().startswith(f"# {ROLE_PROMPT}"):
|
|
336
339
|
break
|
|
337
|
-
if line.strip().startswith(f"# {
|
|
340
|
+
if line.strip().startswith(f"# {ROLE_RESPONSE}"):
|
|
338
341
|
index_to_remove = len(lines) - i - 1
|
|
339
342
|
break
|
|
340
343
|
return index_to_remove
|
|
@@ -359,7 +362,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
359
362
|
def add_prompt_tag_if_needed(self, chat_file: str):
|
|
360
363
|
with open(chat_file, "r", encoding="utf-8") as file:
|
|
361
364
|
lines = file.readlines()
|
|
362
|
-
|
|
365
|
+
|
|
366
|
+
prompt_tag = f"# {ROLE_PROMPT}:"
|
|
363
367
|
if Chat.get_last_role_marker(lines) == prompt_tag:
|
|
364
368
|
return
|
|
365
369
|
append = prompt_tag
|
|
@@ -369,7 +373,6 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
369
373
|
with open(chat_file, "a", encoding="utf-8") as file:
|
|
370
374
|
file.write(append)
|
|
371
375
|
|
|
372
|
-
# @file_exists_check
|
|
373
376
|
def load_text_file(
|
|
374
377
|
self,
|
|
375
378
|
file_path,
|
|
@@ -387,7 +390,6 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
387
390
|
extract_images=extract_images,
|
|
388
391
|
)
|
|
389
392
|
|
|
390
|
-
# @file_exists_check
|
|
391
393
|
def load_binary_file(
|
|
392
394
|
self, file_path, mime_type: str, prefix: str = "", suffix: str = ""
|
|
393
395
|
):
|
|
@@ -401,7 +403,6 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
401
403
|
reader = MarkdownReader(file_path)
|
|
402
404
|
return reader.read(extract_images=extract_images)
|
|
403
405
|
|
|
404
|
-
# @file_exists_check
|
|
405
406
|
def load_document_file(
|
|
406
407
|
self,
|
|
407
408
|
file_path: str,
|
|
@@ -427,8 +428,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
427
428
|
block_delimiter: str = "",
|
|
428
429
|
extract_images: bool = False,
|
|
429
430
|
):
|
|
430
|
-
binary_type_mapping =
|
|
431
|
-
document_type_extensions =
|
|
431
|
+
binary_type_mapping = BINARY_TYPE_MAPPING
|
|
432
|
+
document_type_extensions = DOCUMENT_TYPE_EXTENSIONS
|
|
432
433
|
|
|
433
434
|
file_type = None
|
|
434
435
|
file_name_lower = file_name.lower()
|
|
@@ -467,7 +468,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
467
468
|
files.sort()
|
|
468
469
|
for i, file in enumerate(files):
|
|
469
470
|
print(f"{i + 1}: {os.path.basename(file)}")
|
|
470
|
-
|
|
471
|
+
print("Please choose a file to load (enter number): ", end="", flush=True)
|
|
472
|
+
choice = sys.stdin.readline().strip()
|
|
471
473
|
try:
|
|
472
474
|
choice_index = int(choice) - 1
|
|
473
475
|
if choice_index < 0 or choice_index >= len(files):
|
|
@@ -493,6 +495,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
493
495
|
|
|
494
496
|
def do_quit(self, _):
|
|
495
497
|
"""Exit ara-cli"""
|
|
498
|
+
self.agent_manager.cleanup_agent_process()
|
|
496
499
|
print("Chat ended")
|
|
497
500
|
self.last_result = True
|
|
498
501
|
return True
|
|
@@ -562,7 +565,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
562
565
|
import glob
|
|
563
566
|
|
|
564
567
|
if file_name == "":
|
|
565
|
-
|
|
568
|
+
print("What file do you want to load? ", end="", flush=True)
|
|
569
|
+
file_name = sys.stdin.readline().strip()
|
|
566
570
|
file_pattern = os.path.join(os.path.dirname(self.chat_name), file_name)
|
|
567
571
|
matching_files = glob.glob(file_pattern)
|
|
568
572
|
if not matching_files:
|
|
@@ -573,7 +577,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
573
577
|
return matching_files
|
|
574
578
|
|
|
575
579
|
def load_image(self, file_name: str, prefix: str = "", suffix: str = ""):
|
|
576
|
-
binary_type_mapping =
|
|
580
|
+
binary_type_mapping = BINARY_TYPE_MAPPING
|
|
577
581
|
|
|
578
582
|
file_type = None
|
|
579
583
|
file_name_lower = file_name.lower()
|
|
@@ -602,6 +606,62 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
602
606
|
return False
|
|
603
607
|
return True
|
|
604
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
|
+
|
|
605
665
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
606
666
|
def do_LOAD_IMAGE(self, file_name):
|
|
607
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"""
|
|
@@ -619,7 +679,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
619
679
|
# Determine mime type
|
|
620
680
|
file_type = None
|
|
621
681
|
file_path_lower = file_path.lower()
|
|
622
|
-
for extension, mime_type in
|
|
682
|
+
for extension, mime_type in BINARY_TYPE_MAPPING.items():
|
|
623
683
|
if file_path_lower.endswith(extension):
|
|
624
684
|
file_type = mime_type
|
|
625
685
|
break
|
|
@@ -726,7 +786,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
726
786
|
def do_NEW(self, chat_name):
|
|
727
787
|
"""Create a new chat. Optionally provide a chat name in-line: NEW new_chat"""
|
|
728
788
|
if chat_name == "":
|
|
729
|
-
|
|
789
|
+
print("What should be the new chat name? ", end="", flush=True)
|
|
790
|
+
chat_name = sys.stdin.readline().strip()
|
|
730
791
|
current_directory = os.path.dirname(self.chat_name)
|
|
731
792
|
chat_file_path = os.path.join(current_directory, chat_name)
|
|
732
793
|
self.__init__(chat_file_path)
|
|
@@ -739,7 +800,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
739
800
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
740
801
|
def do_CLEAR(self, _):
|
|
741
802
|
"""Clear the chat and the file containing it"""
|
|
742
|
-
|
|
803
|
+
print("Are you sure you want to clear the chat? (y/N): ", end="", flush=True)
|
|
804
|
+
user_input = sys.stdin.readline().strip()
|
|
743
805
|
if user_input.lower() != "y":
|
|
744
806
|
return
|
|
745
807
|
self.create_empty_chat_file(self.chat_name)
|
|
@@ -750,17 +812,23 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
750
812
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
751
813
|
def do_LOAD_RULES(self, rules_name):
|
|
752
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"""
|
|
753
|
-
self.template_loader.load_template(
|
|
815
|
+
self.template_loader.load_template(
|
|
816
|
+
rules_name, "rules", self.chat_name, "*.rules.md"
|
|
817
|
+
)
|
|
754
818
|
|
|
755
819
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
756
820
|
def do_LOAD_INTENTION(self, intention_name):
|
|
757
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"""
|
|
758
|
-
self.template_loader.load_template(
|
|
822
|
+
self.template_loader.load_template(
|
|
823
|
+
intention_name, "intention", self.chat_name, "*.intention.md"
|
|
824
|
+
)
|
|
759
825
|
|
|
760
826
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
761
827
|
def do_LOAD_COMMANDS(self, commands_name):
|
|
762
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"""
|
|
763
|
-
self.template_loader.load_template(
|
|
829
|
+
self.template_loader.load_template(
|
|
830
|
+
commands_name, "commands", self.chat_name, "*.commands.md"
|
|
831
|
+
)
|
|
764
832
|
|
|
765
833
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
766
834
|
def do_LOAD_BLUEPRINT(self, blueprint_name):
|
|
@@ -934,7 +1002,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
934
1002
|
def do_SEND(self, _):
|
|
935
1003
|
"""Send prompt to the LLM"""
|
|
936
1004
|
message = "\n".join(self.message_buffer)
|
|
937
|
-
self.save_message(
|
|
1005
|
+
self.save_message(ROLE_PROMPT, message)
|
|
938
1006
|
self.send_message()
|
|
939
1007
|
|
|
940
1008
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
@@ -1053,7 +1121,9 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
1053
1121
|
|
|
1054
1122
|
def _template_completer(self, text: str, template_type: str) -> list[str]:
|
|
1055
1123
|
"""Generic completer for different template types."""
|
|
1056
|
-
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
|
+
)
|
|
1057
1127
|
if not text:
|
|
1058
1128
|
return available_templates
|
|
1059
1129
|
return [t for t in available_templates if t.startswith(text)]
|
|
@@ -1072,4 +1142,155 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
1072
1142
|
|
|
1073
1143
|
def complete_LOAD_BLUEPRINT(self, text, line, begidx, endidx):
|
|
1074
1144
|
"""Completer for the LOAD_BLUEPRINT command."""
|
|
1075
|
-
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
|