ara-cli 0.1.10.5__py3-none-any.whl → 0.1.11.0__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.
Potentially problematic release.
This version of ara-cli might be problematic. Click here for more details.
- ara_cli/__init__.py +0 -1
- ara_cli/__main__.py +23 -49
- ara_cli/chat.py +142 -18
- ara_cli/chat_agent/__init__.py +0 -0
- ara_cli/chat_agent/agent_communicator.py +62 -0
- ara_cli/chat_agent/agent_process_manager.py +211 -0
- ara_cli/chat_agent/agent_status_manager.py +73 -0
- ara_cli/chat_agent/agent_workspace_manager.py +76 -0
- ara_cli/directory_navigator.py +37 -4
- ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
- 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/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/version.py +1 -1
- {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.11.0.dist-info}/METADATA +31 -1
- {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.11.0.dist-info}/RECORD +27 -28
- 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.11.0.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.11.0.dist-info}/entry_points.txt +0 -0
- {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.11.0.dist-info}/top_level.txt +0 -0
ara_cli/__init__.py
CHANGED
ara_cli/__main__.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import typer
|
|
2
|
-
import sys
|
|
3
2
|
import os
|
|
4
3
|
from typing import Optional
|
|
5
4
|
from os import getenv
|
|
6
|
-
from ara_cli.error_handler import AraError
|
|
7
5
|
from ara_cli.version import __version__
|
|
8
6
|
from ara_cli import error_handler
|
|
9
7
|
from ara_cli.ara_subcommands.create import register as register_create_cli
|
|
@@ -27,6 +25,8 @@ from ara_cli.ara_subcommands.autofix import register as register_autofix_cli
|
|
|
27
25
|
from ara_cli.ara_subcommands.extract import register as register_extract_cli
|
|
28
26
|
from ara_cli.ara_subcommands.load import register as register_load_cli
|
|
29
27
|
|
|
28
|
+
from ara_cli.directory_navigator import DirectoryNavigator
|
|
29
|
+
|
|
30
30
|
|
|
31
31
|
def version_callback(value: bool):
|
|
32
32
|
if value:
|
|
@@ -45,58 +45,32 @@ def configure_debug_mode(debug: bool, env_debug_mode: bool):
|
|
|
45
45
|
error_handler.debug_mode = True
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def find_ara_directory_root():
|
|
49
|
-
"""Find the root ara directory by traversing up the directory tree."""
|
|
50
|
-
current_dir = os.getcwd()
|
|
51
|
-
|
|
52
|
-
# Check if we're already inside an ara directory structure
|
|
53
|
-
path_parts = current_dir.split(os.sep)
|
|
54
|
-
|
|
55
|
-
# Look for 'ara' in the path parts
|
|
56
|
-
if 'ara' in path_parts:
|
|
57
|
-
ara_index = path_parts.index('ara')
|
|
58
|
-
# Reconstruct path up to and including 'ara'
|
|
59
|
-
ara_root_parts = path_parts[:ara_index + 1]
|
|
60
|
-
potential_ara_root = os.sep.join(ara_root_parts)
|
|
61
|
-
if os.path.exists(potential_ara_root) and os.path.isdir(potential_ara_root):
|
|
62
|
-
return potential_ara_root
|
|
63
|
-
|
|
64
|
-
# If not inside ara directory, check current directory and parents
|
|
65
|
-
check_dir = current_dir
|
|
66
|
-
while check_dir != os.path.dirname(check_dir): # Stop at filesystem root
|
|
67
|
-
ara_path = os.path.join(check_dir, 'ara')
|
|
68
|
-
if os.path.exists(ara_path) and os.path.isdir(ara_path):
|
|
69
|
-
return ara_path
|
|
70
|
-
check_dir = os.path.dirname(check_dir)
|
|
71
|
-
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
|
-
|
|
75
48
|
def check_ara_directory_exists():
|
|
76
49
|
"""Check if ara directory exists or if we're inside ara directory tree."""
|
|
77
|
-
return find_ara_directory_root() is not None
|
|
50
|
+
return DirectoryNavigator.find_ara_directory_root() is not None
|
|
78
51
|
|
|
79
52
|
|
|
80
53
|
def prompt_create_ara_directory():
|
|
81
54
|
"""Prompt user to create ara directory and create it if confirmed."""
|
|
82
55
|
# Print the prompt message
|
|
83
|
-
print("No 'ara' directory found. Create one in the current directory? (Y/n)",
|
|
84
|
-
|
|
56
|
+
print("No 'ara' directory found. Create one in the current directory? (Y/n)",
|
|
57
|
+
end=" ", flush=True)
|
|
58
|
+
|
|
85
59
|
# Read user input
|
|
86
60
|
try:
|
|
87
61
|
response = input().strip()
|
|
88
62
|
except (EOFError, KeyboardInterrupt):
|
|
89
63
|
typer.echo("\nOperation cancelled.")
|
|
90
64
|
raise typer.Exit(1)
|
|
91
|
-
|
|
65
|
+
|
|
92
66
|
if response.lower() in ('y', 'yes', ''):
|
|
93
67
|
current_dir = os.getcwd()
|
|
94
68
|
ara_path = os.path.join(current_dir, 'ara')
|
|
95
|
-
|
|
69
|
+
|
|
96
70
|
# Create ara directory structure
|
|
97
71
|
subdirectories = [
|
|
98
72
|
'businessgoals',
|
|
99
|
-
'capabilities',
|
|
73
|
+
'capabilities',
|
|
100
74
|
'epics',
|
|
101
75
|
'examples',
|
|
102
76
|
'features',
|
|
@@ -105,35 +79,35 @@ def prompt_create_ara_directory():
|
|
|
105
79
|
'userstories',
|
|
106
80
|
'vision'
|
|
107
81
|
]
|
|
108
|
-
|
|
82
|
+
|
|
109
83
|
try:
|
|
110
84
|
# Create main ara directory
|
|
111
85
|
os.makedirs(ara_path, exist_ok=True)
|
|
112
|
-
|
|
86
|
+
|
|
113
87
|
# Create subdirectories for artefact types
|
|
114
88
|
for subdir in subdirectories:
|
|
115
89
|
os.makedirs(os.path.join(ara_path, subdir), exist_ok=True)
|
|
116
|
-
|
|
90
|
+
|
|
117
91
|
# Create .araconfig directory
|
|
118
92
|
araconfig_path = os.path.join(ara_path, '.araconfig')
|
|
119
93
|
os.makedirs(araconfig_path, exist_ok=True)
|
|
120
|
-
|
|
94
|
+
|
|
121
95
|
# Create default ara_config.json using ConfigManager
|
|
122
96
|
from ara_cli.ara_config import ConfigManager, ARAconfig
|
|
123
97
|
config_file_path = os.path.join(araconfig_path, 'ara_config.json')
|
|
124
|
-
|
|
98
|
+
|
|
125
99
|
# Reset ConfigManager to ensure clean state
|
|
126
100
|
ConfigManager.reset()
|
|
127
|
-
|
|
101
|
+
|
|
128
102
|
# Create default config and save it
|
|
129
103
|
default_config = ARAconfig()
|
|
130
104
|
from ara_cli.ara_config import save_data
|
|
131
105
|
save_data(config_file_path, default_config)
|
|
132
|
-
|
|
106
|
+
|
|
133
107
|
typer.echo(f"Created ara directory structure at {ara_path}")
|
|
134
108
|
typer.echo(f"Created default configuration at {config_file_path}")
|
|
135
109
|
return True
|
|
136
|
-
|
|
110
|
+
|
|
137
111
|
except OSError as e:
|
|
138
112
|
typer.echo(f"Error creating ara directory: {e}", err=True)
|
|
139
113
|
raise typer.Exit(1)
|
|
@@ -232,15 +206,15 @@ ara chat examples:
|
|
|
232
206
|
if ctx.invoked_subcommand is None:
|
|
233
207
|
ctx.get_help()
|
|
234
208
|
ctx.exit()
|
|
235
|
-
|
|
209
|
+
|
|
236
210
|
# Check for ara directory before executing any command
|
|
237
211
|
# Skip check for commands that don't require ara directory
|
|
238
212
|
commands_requiring_ara = {
|
|
239
|
-
'create', 'delete', 'rename', 'list', 'list-tags', 'prompt',
|
|
240
|
-
'read', 'reconnect', 'read-status', 'read-user', 'set-status',
|
|
241
|
-
'set-user', '
|
|
213
|
+
'create', 'delete', 'rename', 'list', 'list-tags', 'prompt',
|
|
214
|
+
'read', 'reconnect', 'read-status', 'read-user', 'set-status',
|
|
215
|
+
'set-user', 'scan', 'autofix'
|
|
242
216
|
}
|
|
243
|
-
|
|
217
|
+
|
|
244
218
|
if ctx.invoked_subcommand in commands_requiring_ara:
|
|
245
219
|
requires_ara_directory()
|
|
246
220
|
|
|
@@ -281,4 +255,4 @@ def cli():
|
|
|
281
255
|
|
|
282
256
|
|
|
283
257
|
if __name__ == "__main__":
|
|
284
|
-
cli()
|
|
258
|
+
cli()
|
ara_cli/chat.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import errno
|
|
3
2
|
import argparse
|
|
4
3
|
import cmd2
|
|
5
4
|
|
|
@@ -11,6 +10,7 @@ from ara_cli.error_handler import AraError, AraConfigurationError
|
|
|
11
10
|
from ara_cli.file_loaders.document_file_loader import DocumentFileLoader
|
|
12
11
|
from ara_cli.file_loaders.binary_file_loader import BinaryFileLoader
|
|
13
12
|
from ara_cli.file_loaders.text_file_loader import TextFileLoader
|
|
13
|
+
from ara_cli.chat_agent.agent_process_manager import AgentProcessManager
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
extract_parser = argparse.ArgumentParser()
|
|
@@ -25,7 +25,8 @@ extract_parser.add_argument(
|
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
load_parser = argparse.ArgumentParser()
|
|
28
|
-
load_parser.add_argument("file_name", nargs="?",
|
|
28
|
+
load_parser.add_argument("file_name", nargs="?",
|
|
29
|
+
default="", help="File to load")
|
|
29
30
|
load_parser.add_argument(
|
|
30
31
|
"--load-images",
|
|
31
32
|
action="store_true",
|
|
@@ -36,6 +37,7 @@ load_parser.add_argument(
|
|
|
36
37
|
class Chat(cmd2.Cmd):
|
|
37
38
|
CATEGORY_CHAT_CONTROL = "Chat control commands"
|
|
38
39
|
CATEGORY_LLM_CONTROL = "Language model controls"
|
|
40
|
+
CATEGORY_AGENT_CONTROL = "Agent control commands"
|
|
39
41
|
|
|
40
42
|
INTRO = """/***************************************/
|
|
41
43
|
araarar
|
|
@@ -59,6 +61,14 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
59
61
|
ROLE_PROMPT = "ara prompt"
|
|
60
62
|
ROLE_RESPONSE = "ara response"
|
|
61
63
|
|
|
64
|
+
# Available agents
|
|
65
|
+
AVAILABLE_AGENTS = [
|
|
66
|
+
"interview_agent",
|
|
67
|
+
"autocoder_v2_agent",
|
|
68
|
+
"question_and_answer_agent",
|
|
69
|
+
"feature_creation_guide_agent",
|
|
70
|
+
]
|
|
71
|
+
|
|
62
72
|
BINARY_TYPE_MAPPING = {
|
|
63
73
|
".png": "image/png",
|
|
64
74
|
".jpg": "image/jpeg",
|
|
@@ -74,6 +84,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
74
84
|
enable_commands: list[str] | None = None,
|
|
75
85
|
):
|
|
76
86
|
from ara_cli.template_loader import TemplateLoader
|
|
87
|
+
|
|
77
88
|
shortcuts = dict(cmd2.DEFAULT_SHORTCUTS)
|
|
78
89
|
if enable_commands:
|
|
79
90
|
enable_commands.append("quit") # always allow quitting
|
|
@@ -107,6 +118,9 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
107
118
|
self.config = self._retrieve_ara_config()
|
|
108
119
|
self.template_loader = TemplateLoader(chat_instance=self)
|
|
109
120
|
|
|
121
|
+
# Initialize agent process manager
|
|
122
|
+
self.agent_manager = AgentProcessManager(self)
|
|
123
|
+
|
|
110
124
|
def disable_commands(self, commands: list[str]):
|
|
111
125
|
for command in commands:
|
|
112
126
|
setattr(self, f"do_{command}", self.default)
|
|
@@ -134,6 +148,11 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
134
148
|
self.aliases["lg"] = "LOAD_GIVENS"
|
|
135
149
|
self.aliases["lb"] = "LOAD_BLUEPRINT"
|
|
136
150
|
self.aliases["lt"] = "LOAD_TEMPLATE"
|
|
151
|
+
# Agent control aliases
|
|
152
|
+
self.aliases["a"] = "AGENT_RUN"
|
|
153
|
+
self.aliases["as"] = "AGENT_STOP"
|
|
154
|
+
self.aliases["ac"] = "AGENT_CONTINUE"
|
|
155
|
+
self.aliases["astat"] = "AGENT_STATUS"
|
|
137
156
|
|
|
138
157
|
def setup_chat(self, chat_name, reset: bool = None):
|
|
139
158
|
if os.path.exists(chat_name):
|
|
@@ -178,6 +197,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
178
197
|
print(f"File {file_name} not found.")
|
|
179
198
|
return False
|
|
180
199
|
return method(self, file_path, *args, **kwargs)
|
|
200
|
+
|
|
181
201
|
return wrapper
|
|
182
202
|
|
|
183
203
|
@staticmethod
|
|
@@ -232,7 +252,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
232
252
|
for line in message.splitlines():
|
|
233
253
|
match = image_pattern.search(line)
|
|
234
254
|
if match:
|
|
235
|
-
image_data = {"type": "image_url",
|
|
255
|
+
image_data = {"type": "image_url",
|
|
256
|
+
"image_url": {"url": match.group(1)}}
|
|
236
257
|
image_data_list.append(image_data)
|
|
237
258
|
else:
|
|
238
259
|
text_content.append(line)
|
|
@@ -359,6 +380,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
359
380
|
def add_prompt_tag_if_needed(self, chat_file: str):
|
|
360
381
|
with open(chat_file, "r", encoding="utf-8") as file:
|
|
361
382
|
lines = file.readlines()
|
|
383
|
+
|
|
362
384
|
prompt_tag = f"# {Chat.ROLE_PROMPT}:"
|
|
363
385
|
if Chat.get_last_role_marker(lines) == prompt_tag:
|
|
364
386
|
return
|
|
@@ -369,7 +391,6 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
369
391
|
with open(chat_file, "a", encoding="utf-8") as file:
|
|
370
392
|
file.write(append)
|
|
371
393
|
|
|
372
|
-
# @file_exists_check
|
|
373
394
|
def load_text_file(
|
|
374
395
|
self,
|
|
375
396
|
file_path,
|
|
@@ -387,7 +408,6 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
387
408
|
extract_images=extract_images,
|
|
388
409
|
)
|
|
389
410
|
|
|
390
|
-
# @file_exists_check
|
|
391
411
|
def load_binary_file(
|
|
392
412
|
self, file_path, mime_type: str, prefix: str = "", suffix: str = ""
|
|
393
413
|
):
|
|
@@ -401,7 +421,6 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
401
421
|
reader = MarkdownReader(file_path)
|
|
402
422
|
return reader.read(extract_images=extract_images)
|
|
403
423
|
|
|
404
|
-
# @file_exists_check
|
|
405
424
|
def load_document_file(
|
|
406
425
|
self,
|
|
407
426
|
file_path: str,
|
|
@@ -477,7 +496,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
477
496
|
return None
|
|
478
497
|
file_path = files[choice_index]
|
|
479
498
|
except ValueError as e:
|
|
480
|
-
error_handler.report_error(
|
|
499
|
+
error_handler.report_error(
|
|
500
|
+
ValueError("Invalid input. Aborting load."))
|
|
481
501
|
return None
|
|
482
502
|
else:
|
|
483
503
|
file_path = files[0]
|
|
@@ -493,6 +513,7 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
493
513
|
|
|
494
514
|
def do_quit(self, _):
|
|
495
515
|
"""Exit ara-cli"""
|
|
516
|
+
self.agent_manager.cleanup_agent_process()
|
|
496
517
|
print("Chat ended")
|
|
497
518
|
self.last_result = True
|
|
498
519
|
return True
|
|
@@ -512,7 +533,16 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
512
533
|
)
|
|
513
534
|
|
|
514
535
|
def default(self, line):
|
|
515
|
-
self.
|
|
536
|
+
if self.agent_manager.agent_mode:
|
|
537
|
+
if self.full_input.lower() in ["quit", "exit"]:
|
|
538
|
+
self.agent_manager.cleanup_agent_process()
|
|
539
|
+
print("Agent stopped.")
|
|
540
|
+
else:
|
|
541
|
+
# In agent mode, send input directly to agent
|
|
542
|
+
self.agent_manager.send_to_agent(self.full_input)
|
|
543
|
+
else:
|
|
544
|
+
# In normal chat mode, buffer the message
|
|
545
|
+
self.message_buffer.append(self.full_input)
|
|
516
546
|
|
|
517
547
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
518
548
|
@cmd2.with_argparser(load_parser)
|
|
@@ -587,7 +617,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
587
617
|
file_path=file_name, mime_type=file_type, prefix=prefix, suffix=suffix
|
|
588
618
|
)
|
|
589
619
|
error_handler.report_error(
|
|
590
|
-
AraError(
|
|
620
|
+
AraError(
|
|
621
|
+
f"File {file_name} not recognized as image, could not load")
|
|
591
622
|
)
|
|
592
623
|
|
|
593
624
|
def _verify_llm_choice(self, model_name):
|
|
@@ -750,22 +781,29 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
750
781
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
751
782
|
def do_LOAD_RULES(self, rules_name):
|
|
752
783
|
"""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(
|
|
784
|
+
self.template_loader.load_template(
|
|
785
|
+
rules_name, "rules", self.chat_name, "*.rules.md"
|
|
786
|
+
)
|
|
754
787
|
|
|
755
788
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
756
789
|
def do_LOAD_INTENTION(self, intention_name):
|
|
757
790
|
"""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(
|
|
791
|
+
self.template_loader.load_template(
|
|
792
|
+
intention_name, "intention", self.chat_name, "*.intention.md"
|
|
793
|
+
)
|
|
759
794
|
|
|
760
795
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
761
796
|
def do_LOAD_COMMANDS(self, commands_name):
|
|
762
797
|
"""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(
|
|
798
|
+
self.template_loader.load_template(
|
|
799
|
+
commands_name, "commands", self.chat_name, "*.commands.md"
|
|
800
|
+
)
|
|
764
801
|
|
|
765
802
|
@cmd2.with_category(CATEGORY_CHAT_CONTROL)
|
|
766
803
|
def do_LOAD_BLUEPRINT(self, blueprint_name):
|
|
767
804
|
"""Load specified blueprint. Specify global/<blueprint_name> to access globally defined blueprints"""
|
|
768
|
-
self.template_loader.load_template(
|
|
805
|
+
self.template_loader.load_template(
|
|
806
|
+
blueprint_name, "blueprint", self.chat_name)
|
|
769
807
|
|
|
770
808
|
def _load_helper(
|
|
771
809
|
self,
|
|
@@ -776,7 +814,8 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
776
814
|
):
|
|
777
815
|
import glob
|
|
778
816
|
|
|
779
|
-
directory_path = os.path.join(
|
|
817
|
+
directory_path = os.path.join(
|
|
818
|
+
os.path.dirname(self.chat_name), directory)
|
|
780
819
|
file_pattern = os.path.join(directory_path, pattern)
|
|
781
820
|
|
|
782
821
|
exclude_files = []
|
|
@@ -887,12 +926,14 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
887
926
|
|
|
888
927
|
# If no file_name, check for defaults
|
|
889
928
|
default_files_to_check = [
|
|
890
|
-
os.path.join(base_directory, "prompt.data",
|
|
929
|
+
os.path.join(base_directory, "prompt.data",
|
|
930
|
+
"config.prompt_givens.md"),
|
|
891
931
|
os.path.join(
|
|
892
932
|
base_directory, "prompt.data", "config.prompt_global_givens.md"
|
|
893
933
|
),
|
|
894
934
|
]
|
|
895
|
-
existing_defaults = [
|
|
935
|
+
existing_defaults = [
|
|
936
|
+
f for f in default_files_to_check if os.path.exists(f)]
|
|
896
937
|
if existing_defaults:
|
|
897
938
|
return existing_defaults
|
|
898
939
|
|
|
@@ -1053,7 +1094,9 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
1053
1094
|
|
|
1054
1095
|
def _template_completer(self, text: str, template_type: str) -> list[str]:
|
|
1055
1096
|
"""Generic completer for different template types."""
|
|
1056
|
-
available_templates = self.template_loader.get_available_templates(
|
|
1097
|
+
available_templates = self.template_loader.get_available_templates(
|
|
1098
|
+
template_type, os.path.dirname(self.chat_name)
|
|
1099
|
+
)
|
|
1057
1100
|
if not text:
|
|
1058
1101
|
return available_templates
|
|
1059
1102
|
return [t for t in available_templates if t.startswith(text)]
|
|
@@ -1072,4 +1115,85 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
|
|
|
1072
1115
|
|
|
1073
1116
|
def complete_LOAD_BLUEPRINT(self, text, line, begidx, endidx):
|
|
1074
1117
|
"""Completer for the LOAD_BLUEPRINT command."""
|
|
1075
|
-
return self._template_completer(text, "blueprint")
|
|
1118
|
+
return self._template_completer(text, "blueprint")
|
|
1119
|
+
|
|
1120
|
+
# ===== AGENT CONTROL COMMANDS =====
|
|
1121
|
+
|
|
1122
|
+
@cmd2.with_category(CATEGORY_AGENT_CONTROL)
|
|
1123
|
+
def do_AGENT_RUN(self, args):
|
|
1124
|
+
"""Run an agent. Usage: AGENT_RUN <agent_name> [artefact_classifier artefact_name]
|
|
1125
|
+
|
|
1126
|
+
Examples:
|
|
1127
|
+
AGENT_RUN interview_agent
|
|
1128
|
+
AGENT_RUN interview_agent feature my_feature
|
|
1129
|
+
"""
|
|
1130
|
+
parts = args.split()
|
|
1131
|
+
if not parts:
|
|
1132
|
+
print(f"Available agents: {', '.join(self.AVAILABLE_AGENTS)}")
|
|
1133
|
+
print(
|
|
1134
|
+
"Usage: AGENT_RUN <agent_name> [artefact_classifier artefact_name]")
|
|
1135
|
+
return
|
|
1136
|
+
|
|
1137
|
+
agent_name = parts[0]
|
|
1138
|
+
if agent_name not in self.AVAILABLE_AGENTS:
|
|
1139
|
+
print(f"Unknown agent: {agent_name}")
|
|
1140
|
+
print(f"Available agents: {', '.join(self.AVAILABLE_AGENTS)}")
|
|
1141
|
+
return
|
|
1142
|
+
|
|
1143
|
+
# Get initial prompt
|
|
1144
|
+
initial_prompt = input(
|
|
1145
|
+
"Enter initial prompt (or press Enter for default): "
|
|
1146
|
+
).strip()
|
|
1147
|
+
if not initial_prompt:
|
|
1148
|
+
initial_prompt = "Let's begin the interview."
|
|
1149
|
+
|
|
1150
|
+
# Extract artefact info if provided
|
|
1151
|
+
artefact_classifier = None
|
|
1152
|
+
artefact_name = None
|
|
1153
|
+
if len(parts) >= 3:
|
|
1154
|
+
artefact_classifier = parts[1]
|
|
1155
|
+
artefact_name = parts[2]
|
|
1156
|
+
|
|
1157
|
+
try:
|
|
1158
|
+
self.agent_manager.start_agent(
|
|
1159
|
+
agent_name, initial_prompt, artefact_classifier, artefact_name
|
|
1160
|
+
)
|
|
1161
|
+
except AraError as e:
|
|
1162
|
+
print(f"Error: {e}")
|
|
1163
|
+
|
|
1164
|
+
@cmd2.with_category(CATEGORY_AGENT_CONTROL)
|
|
1165
|
+
def do_AGENT_STOP(self, _):
|
|
1166
|
+
"""Stop the currently running agent gracefully (sends termination signal)."""
|
|
1167
|
+
if not self.agent_manager.agent_process:
|
|
1168
|
+
print("No agent is currently running.")
|
|
1169
|
+
return
|
|
1170
|
+
|
|
1171
|
+
print(f"Stopping agent {self.agent_manager.agent_name}...")
|
|
1172
|
+
self.agent_manager.cleanup_agent_process()
|
|
1173
|
+
print("Agent stopped.")
|
|
1174
|
+
|
|
1175
|
+
@cmd2.with_category(CATEGORY_AGENT_CONTROL)
|
|
1176
|
+
def do_AGENT_CONTINUE(self, _):
|
|
1177
|
+
"""Continue with the agent without providing new input (sends empty line)."""
|
|
1178
|
+
self.agent_manager.continue_agent()
|
|
1179
|
+
|
|
1180
|
+
@cmd2.with_category(CATEGORY_AGENT_CONTROL)
|
|
1181
|
+
def do_AGENT_STATUS(self, _):
|
|
1182
|
+
"""Show status of the current agent."""
|
|
1183
|
+
print(self.agent_manager.get_agent_status())
|
|
1184
|
+
|
|
1185
|
+
def complete_AGENT_RUN(self, text, line, begidx, endidx):
|
|
1186
|
+
"""Completer for AGENT_RUN command."""
|
|
1187
|
+
parts = line.split()
|
|
1188
|
+
|
|
1189
|
+
# Complete agent name
|
|
1190
|
+
if len(parts) <= 2:
|
|
1191
|
+
if not text:
|
|
1192
|
+
return self.AVAILABLE_AGENTS
|
|
1193
|
+
return [a for a in self.AVAILABLE_AGENTS if a.startswith(text)]
|
|
1194
|
+
|
|
1195
|
+
# Complete classifier
|
|
1196
|
+
if len(parts) == 3:
|
|
1197
|
+
return self._complete_classifiers(text, line, begidx, endidx)
|
|
1198
|
+
|
|
1199
|
+
return []
|
|
File without changes
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import queue
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AgentCommunicator:
|
|
6
|
+
def __init__(self, agent_process_manager):
|
|
7
|
+
self.agent_process_manager = agent_process_manager
|
|
8
|
+
self.agent_process = agent_process_manager.agent_process
|
|
9
|
+
self.agent_output_queue = agent_process_manager.agent_output_queue
|
|
10
|
+
self.chat_instance = agent_process_manager.chat_instance
|
|
11
|
+
self.status_manager = agent_process_manager.status_manager
|
|
12
|
+
|
|
13
|
+
def read_agent_output(self):
|
|
14
|
+
try:
|
|
15
|
+
for line in iter(self.agent_process.stdout.readline, ""):
|
|
16
|
+
if not line:
|
|
17
|
+
break
|
|
18
|
+
self.agent_output_queue.put(line)
|
|
19
|
+
except Exception as e:
|
|
20
|
+
self.agent_output_queue.put(f"Error reading agent output: {e}\n")
|
|
21
|
+
|
|
22
|
+
def _handle_agent_answer(self, lines, answer_index):
|
|
23
|
+
for j in range(answer_index + 1, len(lines) - 1):
|
|
24
|
+
print(lines[j], flush=True)
|
|
25
|
+
print(flush=True)
|
|
26
|
+
|
|
27
|
+
self.agent_process_manager.agent_mode = True
|
|
28
|
+
self.chat_instance.prompt = "ara-agent> "
|
|
29
|
+
self.status_manager.update_status_file(mode="waiting for input")
|
|
30
|
+
|
|
31
|
+
def _process_lines(self, lines):
|
|
32
|
+
for i, line_text in enumerate(lines[:-1]):
|
|
33
|
+
print(line_text, flush=True)
|
|
34
|
+
if re.match(r'^\s*Answer:', line_text):
|
|
35
|
+
self._handle_agent_answer(lines, i)
|
|
36
|
+
return True, lines[-1]
|
|
37
|
+
return False, lines[-1]
|
|
38
|
+
|
|
39
|
+
def process_agent_output(self):
|
|
40
|
+
buffer = ""
|
|
41
|
+
while True:
|
|
42
|
+
try:
|
|
43
|
+
line = self.agent_output_queue.get(timeout=0.1)
|
|
44
|
+
buffer += line
|
|
45
|
+
|
|
46
|
+
if "\n" in buffer:
|
|
47
|
+
lines = buffer.split("\n")
|
|
48
|
+
found_answer, buffer = self._process_lines(lines)
|
|
49
|
+
if found_answer:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
except queue.Empty:
|
|
53
|
+
if self.agent_process and self.agent_process.poll() is not None:
|
|
54
|
+
if buffer:
|
|
55
|
+
print(buffer, flush=True)
|
|
56
|
+
self.agent_process_manager.cleanup_agent_process()
|
|
57
|
+
return
|
|
58
|
+
continue
|
|
59
|
+
except Exception as e:
|
|
60
|
+
print(f"Error processing agent output: {e}", flush=True)
|
|
61
|
+
self.agent_process_manager.cleanup_agent_process()
|
|
62
|
+
return
|