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.

Files changed (44) hide show
  1. ara_cli/__init__.py +0 -1
  2. ara_cli/__main__.py +23 -49
  3. ara_cli/chat.py +142 -18
  4. ara_cli/chat_agent/__init__.py +0 -0
  5. ara_cli/chat_agent/agent_communicator.py +62 -0
  6. ara_cli/chat_agent/agent_process_manager.py +211 -0
  7. ara_cli/chat_agent/agent_status_manager.py +73 -0
  8. ara_cli/chat_agent/agent_workspace_manager.py +76 -0
  9. ara_cli/directory_navigator.py +37 -4
  10. ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
  11. ara_cli/templates/prompt-modules/commands/extract_general.commands.md +12 -0
  12. ara_cli/templates/prompt-modules/commands/extract_markdown.commands.md +11 -0
  13. ara_cli/templates/prompt-modules/commands/extract_python.commands.md +13 -0
  14. ara_cli/templates/prompt-modules/commands/feature_add_or_modifiy_specified_behavior.commands.md +36 -0
  15. ara_cli/templates/prompt-modules/commands/feature_generate_initial_specified_bevahior.commands.md +53 -0
  16. ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
  17. ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
  18. ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
  19. ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
  20. ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
  21. ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
  22. ara_cli/version.py +1 -1
  23. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.11.0.dist-info}/METADATA +31 -1
  24. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.11.0.dist-info}/RECORD +27 -28
  25. ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
  26. ara_cli/templates/prompt-modules/blueprints/pytest_unittest_prompt.blueprint.md +0 -32
  27. ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
  28. ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
  29. ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
  30. ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
  31. ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
  32. ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
  33. ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
  34. ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
  35. ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
  36. ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
  37. ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
  38. ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
  39. ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
  40. ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
  41. ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
  42. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.11.0.dist-info}/WHEEL +0 -0
  43. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.11.0.dist-info}/entry_points.txt +0 -0
  44. {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
@@ -4,7 +4,6 @@ from .error_handler import ErrorHandler
4
4
 
5
5
  whitelisted_commands = ["RERUN", "SEND", "EXTRACT", "LOAD_IMAGE", "CHOOSE_MODEL", "CHOOSE_EXTRACTION_MODEL", "CURRENT_MODEL", "CURRENT_EXTRACTION_MODEL", "LIST_MODELS"]
6
6
 
7
-
8
7
  error_handler = ErrorHandler()
9
8
 
10
9
 
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)", end=" ", flush=True)
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', 'classifier-directory', 'scan', 'autofix'
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="?", default="", help="File to load")
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", "image_url": {"url": match.group(1)}}
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(ValueError("Invalid input. Aborting load."))
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.message_buffer.append(self.full_input)
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(f"File {file_name} not recognized as image, could not load")
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(rules_name, "rules", self.chat_name, "*.rules.md")
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(intention_name, "intention", self.chat_name, "*.intention.md")
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(commands_name, "commands", self.chat_name, "*.commands.md")
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(blueprint_name, "blueprint", self.chat_name)
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(os.path.dirname(self.chat_name), directory)
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", "config.prompt_givens.md"),
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 = [f for f in default_files_to_check if os.path.exists(f)]
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(template_type, os.path.dirname(self.chat_name))
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