ara-cli 0.1.10.5__py3-none-any.whl → 0.1.14.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.
Files changed (151) hide show
  1. ara_cli/__init__.py +51 -6
  2. ara_cli/__main__.py +87 -75
  3. ara_cli/ara_command_action.py +189 -101
  4. ara_cli/ara_config.py +187 -128
  5. ara_cli/ara_subcommands/common.py +2 -2
  6. ara_cli/ara_subcommands/config.py +221 -0
  7. ara_cli/ara_subcommands/convert.py +107 -0
  8. ara_cli/ara_subcommands/fetch.py +41 -0
  9. ara_cli/ara_subcommands/fetch_agents.py +22 -0
  10. ara_cli/ara_subcommands/fetch_scripts.py +19 -0
  11. ara_cli/ara_subcommands/fetch_templates.py +15 -10
  12. ara_cli/ara_subcommands/list.py +97 -23
  13. ara_cli/ara_subcommands/prompt.py +266 -106
  14. ara_cli/artefact_autofix.py +117 -64
  15. ara_cli/artefact_converter.py +355 -0
  16. ara_cli/artefact_creator.py +41 -17
  17. ara_cli/artefact_lister.py +3 -3
  18. ara_cli/artefact_models/artefact_model.py +1 -1
  19. ara_cli/artefact_models/artefact_templates.py +0 -9
  20. ara_cli/artefact_models/feature_artefact_model.py +8 -8
  21. ara_cli/artefact_reader.py +62 -43
  22. ara_cli/artefact_scan.py +39 -17
  23. ara_cli/chat.py +300 -71
  24. ara_cli/chat_agent/__init__.py +0 -0
  25. ara_cli/chat_agent/agent_process_manager.py +155 -0
  26. ara_cli/chat_script_runner/__init__.py +0 -0
  27. ara_cli/chat_script_runner/script_completer.py +23 -0
  28. ara_cli/chat_script_runner/script_finder.py +41 -0
  29. ara_cli/chat_script_runner/script_lister.py +36 -0
  30. ara_cli/chat_script_runner/script_runner.py +36 -0
  31. ara_cli/chat_web_search/__init__.py +0 -0
  32. ara_cli/chat_web_search/web_search.py +263 -0
  33. ara_cli/children_contribution_updater.py +737 -0
  34. ara_cli/classifier.py +34 -0
  35. ara_cli/commands/agent_run_command.py +98 -0
  36. ara_cli/commands/fetch_agents_command.py +106 -0
  37. ara_cli/commands/fetch_scripts_command.py +43 -0
  38. ara_cli/commands/fetch_templates_command.py +39 -0
  39. ara_cli/commands/fetch_templates_commands.py +39 -0
  40. ara_cli/commands/list_agents_command.py +39 -0
  41. ara_cli/commands/load_command.py +4 -3
  42. ara_cli/commands/load_image_command.py +1 -1
  43. ara_cli/commands/read_command.py +23 -27
  44. ara_cli/completers.py +95 -35
  45. ara_cli/constants.py +2 -0
  46. ara_cli/directory_navigator.py +37 -4
  47. ara_cli/error_handler.py +26 -11
  48. ara_cli/file_loaders/document_reader.py +0 -178
  49. ara_cli/file_loaders/factories/__init__.py +0 -0
  50. ara_cli/file_loaders/factories/document_reader_factory.py +32 -0
  51. ara_cli/file_loaders/factories/file_loader_factory.py +27 -0
  52. ara_cli/file_loaders/file_loader.py +1 -30
  53. ara_cli/file_loaders/loaders/__init__.py +0 -0
  54. ara_cli/file_loaders/{document_file_loader.py → loaders/document_file_loader.py} +1 -1
  55. ara_cli/file_loaders/loaders/text_file_loader.py +47 -0
  56. ara_cli/file_loaders/readers/__init__.py +0 -0
  57. ara_cli/file_loaders/readers/docx_reader.py +49 -0
  58. ara_cli/file_loaders/readers/excel_reader.py +27 -0
  59. ara_cli/file_loaders/{markdown_reader.py → readers/markdown_reader.py} +1 -1
  60. ara_cli/file_loaders/readers/odt_reader.py +59 -0
  61. ara_cli/file_loaders/readers/pdf_reader.py +54 -0
  62. ara_cli/file_loaders/readers/pptx_reader.py +104 -0
  63. ara_cli/file_loaders/tools/__init__.py +0 -0
  64. ara_cli/llm_utils.py +58 -0
  65. ara_cli/output_suppressor.py +53 -0
  66. ara_cli/prompt_chat.py +20 -4
  67. ara_cli/prompt_extractor.py +47 -32
  68. ara_cli/prompt_handler.py +123 -17
  69. ara_cli/tag_extractor.py +8 -7
  70. ara_cli/template_loader.py +2 -1
  71. ara_cli/template_manager.py +52 -21
  72. ara_cli/templates/global-scripts/hello_global.py +1 -0
  73. ara_cli/templates/prompt-modules/commands/add_scenarios_for_new_behaviour.feature_creation_agent.commands.md +1 -0
  74. ara_cli/templates/prompt-modules/commands/align_feature_with_implementation_changes.interview_agent.commands.md +1 -0
  75. ara_cli/templates/prompt-modules/commands/analyze_codebase_and_plan_tasks.interview_agent.commands.md +1 -0
  76. ara_cli/templates/prompt-modules/commands/choose_best_parent_artefact.interview_agent.commands.md +1 -0
  77. ara_cli/templates/prompt-modules/commands/create_tasks_from_artefact_content.interview_agent.commands.md +1 -0
  78. ara_cli/templates/prompt-modules/commands/create_tests_for_uncovered_modules.test_generation_agent.commands.md +1 -0
  79. ara_cli/templates/prompt-modules/commands/derive_features_from_video_description.feature_creation_agent.commands.md +1 -0
  80. ara_cli/templates/prompt-modules/commands/describe_agent_capabilities.agent.commands.md +1 -0
  81. ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
  82. ara_cli/templates/prompt-modules/commands/execute_scoped_todos_in_task.interview_agent.commands.md +1 -0
  83. ara_cli/templates/prompt-modules/commands/explain_single_file_purpose.interview_agent.commands.md +1 -0
  84. ara_cli/templates/prompt-modules/commands/extract_file_information_bullets.interview_agent.commands.md +1 -0
  85. ara_cli/templates/prompt-modules/commands/extract_general.commands.md +12 -0
  86. ara_cli/templates/prompt-modules/commands/extract_markdown.commands.md +11 -0
  87. ara_cli/templates/prompt-modules/commands/extract_python.commands.md +13 -0
  88. ara_cli/templates/prompt-modules/commands/feature_add_or_modifiy_specified_behavior.commands.md +36 -0
  89. ara_cli/templates/prompt-modules/commands/feature_generate_initial_specified_bevahior.commands.md +53 -0
  90. ara_cli/templates/prompt-modules/commands/fix_failing_behave_step_definitions.interview_agent.commands.md +1 -0
  91. ara_cli/templates/prompt-modules/commands/fix_failing_pytest_tests.interview_agent.commands.md +1 -0
  92. ara_cli/templates/prompt-modules/commands/general_instruction_policy.commands.md +47 -0
  93. ara_cli/templates/prompt-modules/commands/generate_and_fix_pytest_tests.test_generation_agent.commands.md +1 -0
  94. ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
  95. ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
  96. ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
  97. ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
  98. ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
  99. ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
  100. ara_cli/templates/prompt-modules/commands/suggest_next_story_child_tasks.interview_agent.commands.md +1 -0
  101. ara_cli/templates/prompt-modules/commands/summarize_or_transcribe_media.interview_agent.commands.md +1 -0
  102. ara_cli/templates/prompt-modules/commands/update_feature_to_match_implementation.feature_creation_agent.commands.md +1 -0
  103. ara_cli/templates/prompt-modules/commands/update_user_story_with_requirements.interview_agent.commands.md +1 -0
  104. ara_cli/version.py +1 -1
  105. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.14.0.dist-info}/METADATA +49 -11
  106. ara_cli-0.1.14.0.dist-info/RECORD +253 -0
  107. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.14.0.dist-info}/WHEEL +1 -1
  108. tests/test_ara_command_action.py +31 -19
  109. tests/test_ara_config.py +177 -90
  110. tests/test_artefact_autofix.py +170 -97
  111. tests/test_artefact_autofix_integration.py +495 -0
  112. tests/test_artefact_converter.py +312 -0
  113. tests/test_artefact_extraction.py +564 -0
  114. tests/test_artefact_lister.py +11 -8
  115. tests/test_chat.py +166 -130
  116. tests/test_chat_givens_images.py +603 -0
  117. tests/test_chat_script_runner.py +454 -0
  118. tests/test_children_contribution_updater.py +98 -0
  119. tests/test_document_loader_office.py +267 -0
  120. tests/test_llm_utils.py +164 -0
  121. tests/test_prompt_chat.py +343 -0
  122. tests/test_prompt_extractor.py +683 -0
  123. tests/test_prompt_handler.py +416 -214
  124. tests/test_setup_default_chat_prompt_mode.py +198 -0
  125. tests/test_tag_extractor.py +95 -49
  126. tests/test_web_search.py +467 -0
  127. ara_cli/file_loaders/document_readers.py +0 -233
  128. ara_cli/file_loaders/file_loaders.py +0 -123
  129. ara_cli/file_loaders/text_file_loader.py +0 -187
  130. ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
  131. ara_cli/templates/prompt-modules/blueprints/pytest_unittest_prompt.blueprint.md +0 -32
  132. ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
  133. ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
  134. ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
  135. ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
  136. ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
  137. ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
  138. ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
  139. ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
  140. ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
  141. ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
  142. ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
  143. ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
  144. ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
  145. ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
  146. ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
  147. ara_cli-0.1.10.5.dist-info/RECORD +0 -194
  148. /ara_cli/file_loaders/{binary_file_loader.py → loaders/binary_file_loader.py} +0 -0
  149. /ara_cli/file_loaders/{image_processor.py → tools/image_processor.py} +0 -0
  150. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.14.0.dist-info}/entry_points.txt +0 -0
  151. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.14.0.dist-info}/top_level.txt +0 -0
ara_cli/classifier.py CHANGED
@@ -72,3 +72,37 @@ class Classifier:
72
72
  @lru_cache(maxsize=None)
73
73
  def artefact_titles():
74
74
  return Classifier.artefact_title.values()
75
+
76
+ # Leaf-node classifiers that cannot have children
77
+ _leaf_classifiers = {"task", "issue"}
78
+
79
+ # Contribution hierarchy: child -> valid parent classifiers
80
+ contribution_hierarchy = {
81
+ "businessgoal": ["vision"],
82
+ "capability": ["vision", "businessgoal"],
83
+ "keyfeature": ["vision", "businessgoal", "capability"],
84
+ "epic": ["vision", "businessgoal", "capability", "keyfeature"],
85
+ "userstory": ["vision", "businessgoal", "capability", "keyfeature", "epic"],
86
+ "example": ["vision", "businessgoal", "capability", "keyfeature", "epic", "userstory"],
87
+ "feature": ["vision", "businessgoal", "capability", "keyfeature", "epic", "userstory"],
88
+ "task": ["vision", "businessgoal", "capability", "keyfeature", "epic", "userstory", "feature"],
89
+ "issue": ["vision", "businessgoal", "capability", "keyfeature", "epic", "userstory", "feature"],
90
+ }
91
+
92
+ @staticmethod
93
+ @lru_cache(maxsize=None)
94
+ def can_have_children(classifier: str) -> bool:
95
+ """
96
+ Check if a classifier can have children artefacts.
97
+ Task and Issue are leaf-node classifiers and cannot have children.
98
+ """
99
+ return classifier not in Classifier._leaf_classifiers
100
+
101
+ @staticmethod
102
+ @lru_cache(maxsize=None)
103
+ def get_valid_parent_classifiers(child_classifier: str) -> list:
104
+ """
105
+ Get list of valid parent classifiers for a given child classifier.
106
+ Returns empty list if classifier has no valid parents (e.g., vision).
107
+ """
108
+ return Classifier.contribution_hierarchy.get(child_classifier, [])
@@ -0,0 +1,98 @@
1
+ import argparse
2
+ from ara_cli.commands.command import Command
3
+ from ara_cli.error_handler import AraError
4
+
5
+
6
+ class AgentRunCommand(Command):
7
+ def __init__(self, chat_instance, args):
8
+ self.chat_instance = chat_instance
9
+ self.args = args
10
+ self.parser = self._create_parser()
11
+
12
+ def _create_parser(self):
13
+ """Creates and configures the argument parser."""
14
+ parser = argparse.ArgumentParser(
15
+ prog="AGENT_RUN",
16
+ description="Run a binary agent.",
17
+ add_help=False, # We handle help manually
18
+ )
19
+ parser.add_argument("agent_name", nargs="?",
20
+ help="The name of the agent to run.")
21
+ parser.add_argument("-b", "--base-dir", dest="base_dir",
22
+ help="Specify the base directory for the agent.")
23
+ parser.add_argument("-r", "--requirements", dest="requirements",
24
+ action="append", help="Specify a requirements file or directory.")
25
+ parser.add_argument("-h", "--help", action="store_true",
26
+ help="Show this help message.")
27
+ return parser
28
+
29
+ def _handle_help(self, arg_list):
30
+ """Handles the --help flag for the agent command."""
31
+ agent_name = next(
32
+ (arg for arg in arg_list if not arg.startswith('-')), None)
33
+
34
+ if agent_name:
35
+ try:
36
+ self.chat_instance.agent_manager.run_agent(
37
+ agent_name, ["--help"])
38
+ except SystemExit:
39
+ pass # Common with argparse's --help
40
+ except Exception:
41
+ print(f"Displaying built-in help for {agent_name}:\n")
42
+ self.parser.print_help()
43
+ else:
44
+ self.parser.print_help()
45
+ return True
46
+
47
+ def _prepare_agent_args(self, parsed_args, unknown_args):
48
+ """Prepares the arguments to be passed to the agent."""
49
+ agent_args = list(unknown_args)
50
+
51
+ if parsed_args.base_dir:
52
+ print(f"Using base directory: {parsed_args.base_dir}")
53
+ agent_args.extend(["--base-dir", parsed_args.base_dir])
54
+
55
+ if parsed_args.requirements:
56
+ print(f"Requirements Paths ({len(parsed_args.requirements)}):")
57
+ for req_path in parsed_args.requirements:
58
+ print(f" - {req_path}")
59
+ agent_args.extend(["--requirements", req_path])
60
+
61
+ # elif self.chat_instance.source_artefact_path:
62
+ # print(
63
+ # f"INFO: Automatically passing source artefact to agent: {self.chat_instance.source_artefact_path}")
64
+ # agent_args.insert(0, self.chat_instance.source_artefact_path)
65
+ # agent_args.insert(0, "-r")
66
+
67
+ return agent_args
68
+
69
+ def _run_agent(self, parsed_args, unknown_args):
70
+ """Runs the agent with the prepared arguments."""
71
+ if not parsed_args.agent_name:
72
+ raise AraError("Usage: AGENT_RUN <agent_name> [args...]")
73
+
74
+ agent_name = parsed_args.agent_name
75
+ agent_args = self._prepare_agent_args(parsed_args, unknown_args)
76
+
77
+ self.chat_instance.agent_manager.run_agent(agent_name, agent_args)
78
+
79
+ def execute(self):
80
+ """
81
+ Parses arguments and runs a binary agent, handling help requests and errors.
82
+ """
83
+ try:
84
+ arg_list = self.args.split()
85
+ if "-h" in arg_list or "--help" in arg_list:
86
+ self._handle_help(arg_list)
87
+ return
88
+
89
+ parsed_args, unknown_args = self.parser.parse_known_args(arg_list)
90
+ self._run_agent(parsed_args, unknown_args)
91
+
92
+ except SystemExit:
93
+ # Argparse may exit, which is fine.
94
+ pass
95
+ except AraError as e:
96
+ raise e
97
+ except Exception as e:
98
+ print(f"An unexpected error occurred: {e}")
@@ -0,0 +1,106 @@
1
+ import os
2
+
3
+ from ara_cli.commands.command import Command
4
+ from ara_cli.directory_navigator import DirectoryNavigator
5
+
6
+
7
+ class FetchAgentsCommand(Command):
8
+ """Command to fetch binary agents from a remote URL.
9
+
10
+ This command downloads a binary agent from a hardcoded URL and
11
+ saves it to the project's ara/.araconfig/agents/ directory.
12
+ """
13
+
14
+ AGENT_URL = "https://s3-public.talsen.team/so-agents/feature-creation"
15
+
16
+ def __init__(self, output=None):
17
+ """Initialize the FetchAgentsCommand.
18
+
19
+ Parameters
20
+ ----------
21
+ output : callable, optional
22
+ Output function for displaying messages. Defaults to print.
23
+ """
24
+ self.output = output or print
25
+
26
+ def execute(self):
27
+ """Execute the fetch-agents command.
28
+
29
+ Downloads a binary agent from a remote URL and saves it to the
30
+ project's .araconfig/agents directory.
31
+ """
32
+ navigator = DirectoryNavigator()
33
+ original_directory = os.getcwd()
34
+
35
+ import requests
36
+ from rich.progress import (
37
+ BarColumn,
38
+ DownloadColumn,
39
+ Progress,
40
+ TextColumn,
41
+ TimeRemainingColumn,
42
+ TransferSpeedColumn,
43
+ )
44
+
45
+ try:
46
+ # Navigate to ara directory
47
+ navigator.navigate_to_target()
48
+
49
+ dest_dir = self._get_project_agents_dir()
50
+ os.makedirs(dest_dir, exist_ok=True)
51
+
52
+ agent_name = self.AGENT_URL.split("/")[-1]
53
+ dest_path = os.path.join(dest_dir, agent_name)
54
+
55
+ self.output(f"Downloading agent from {self.AGENT_URL}...")
56
+
57
+ response = requests.get(self.AGENT_URL, stream=True)
58
+ response.raise_for_status()
59
+
60
+ total_size = int(response.headers.get("content-length", 0))
61
+ block_size = 1024
62
+ progress = Progress(
63
+ TextColumn("[bold blue]{task.description}", justify="right"),
64
+ BarColumn(bar_width=None),
65
+ "[progress.percentage]{task.percentage:>3.1f}%",
66
+ "•",
67
+ DownloadColumn(),
68
+ "•",
69
+ TransferSpeedColumn(),
70
+ "•",
71
+ TimeRemainingColumn(),
72
+ )
73
+
74
+ with progress:
75
+ task_id = progress.add_task(
76
+ f"Downloading {agent_name}", total=total_size
77
+ )
78
+ with open(dest_path, "wb") as f:
79
+ for data in response.iter_content(block_size):
80
+ progress.update(task_id, advance=len(data))
81
+ f.write(data)
82
+
83
+ if total_size != 0 and os.path.getsize(dest_path) != total_size:
84
+ raise Exception("ERROR, something went wrong during download")
85
+
86
+ # Make the binary executable
87
+ os.chmod(dest_path, 0o755)
88
+
89
+ self.output(f"Downloaded {agent_name} to ara/.araconfig/agents/")
90
+ self.output("Binary agents fetched successfully to ara/.araconfig/agents/")
91
+
92
+ except requests.exceptions.RequestException as e:
93
+ self.output(f"Error downloading agent: {e}")
94
+ finally:
95
+ # Return to original directory
96
+ os.chdir(original_directory)
97
+
98
+ def _get_project_agents_dir(self):
99
+ """Get the path to the project agents directory.
100
+
101
+ Returns
102
+ -------
103
+ str
104
+ Path to ara/.araconfig/agents directory.
105
+ """
106
+ return os.path.join(".araconfig", "agents")
@@ -0,0 +1,43 @@
1
+ import os
2
+ import shutil
3
+ from ara_cli.commands.command import Command
4
+ from ara_cli.ara_config import ConfigManager
5
+ from ara_cli.directory_navigator import DirectoryNavigator
6
+
7
+ class FetchScriptsCommand(Command):
8
+ def __init__(self, output=None):
9
+ self.output = output or print
10
+ self.config = ConfigManager.get_config()
11
+
12
+ def execute(self):
13
+ navigator = DirectoryNavigator()
14
+ original_directory = os.getcwd()
15
+ navigator.navigate_to_target()
16
+ os.chdir('..')
17
+
18
+ global_scripts_dir = self._get_global_scripts_dir()
19
+ global_scripts_config_dir = self._get_global_scripts_config_dir()
20
+
21
+ if not os.path.exists(global_scripts_dir):
22
+ self.output("Global scripts directory not found.")
23
+ os.chdir(original_directory)
24
+ return
25
+
26
+ if not os.path.exists(global_scripts_config_dir):
27
+ os.makedirs(global_scripts_config_dir)
28
+
29
+ for item in os.listdir(global_scripts_dir):
30
+ source = os.path.join(global_scripts_dir, item)
31
+ destination = os.path.join(global_scripts_config_dir, item)
32
+ if os.path.isfile(source):
33
+ shutil.copy2(source, destination)
34
+ self.output(f"Copied {item} to global scripts directory.")
35
+
36
+ os.chdir(original_directory)
37
+
38
+ def _get_global_scripts_dir(self):
39
+ base_path = os.path.dirname(os.path.dirname(__file__))
40
+ return os.path.join(base_path, "templates", "global-scripts")
41
+
42
+ def _get_global_scripts_config_dir(self):
43
+ return os.path.join(self.config.local_prompt_templates_dir, "global-scripts")
@@ -0,0 +1,39 @@
1
+ from os.path import join
2
+ import os
3
+ import shutil
4
+ from ara_cli.commands.command import Command
5
+ from ara_cli.ara_config import ConfigManager
6
+ from ara_cli.template_manager import TemplatePathManager
7
+
8
+
9
+ class FetchTemplatesCommand(Command):
10
+ def __init__(self, output=None):
11
+ self.output = output or print
12
+
13
+ def execute(self):
14
+ config = ConfigManager().get_config()
15
+ prompt_templates_dir = config.local_prompt_templates_dir
16
+ template_base_path = TemplatePathManager.get_template_base_path()
17
+ global_prompt_templates_path = join(
18
+ template_base_path, "prompt-modules")
19
+
20
+ subdirs = ["commands", "rules", "intentions", "blueprints"]
21
+
22
+ os.makedirs(join(prompt_templates_dir,
23
+ "global-prompt-modules"), exist_ok=True)
24
+ for subdir in subdirs:
25
+ target_dir = join(prompt_templates_dir,
26
+ "global-prompt-modules", subdir)
27
+ source_dir = join(global_prompt_templates_path, subdir)
28
+ os.makedirs(target_dir, exist_ok=True)
29
+ for item in os.listdir(source_dir):
30
+ source = join(source_dir, item)
31
+ target = join(target_dir, item)
32
+ shutil.copy2(source, target)
33
+
34
+ custom_prompt_templates_subdir = config.custom_prompt_templates_subdir
35
+ local_prompt_modules_dir = join(
36
+ prompt_templates_dir, custom_prompt_templates_subdir)
37
+ os.makedirs(local_prompt_modules_dir, exist_ok=True)
38
+ for subdir in subdirs:
39
+ os.makedirs(join(local_prompt_modules_dir, subdir), exist_ok=True)
@@ -0,0 +1,39 @@
1
+ from os.path import join
2
+ import os
3
+ import shutil
4
+ from ara_cli.commands.command import Command
5
+ from ara_cli.ara_config import ConfigManager
6
+ from ara_cli.template_manager import TemplatePathManager
7
+
8
+
9
+ class FetchTemplatesCommand(Command):
10
+ def __init__(self, output=None):
11
+ self.output = output or print
12
+
13
+ def execute(self):
14
+ config = ConfigManager().get_config()
15
+ prompt_templates_dir = config.local_prompt_templates_dir
16
+ template_base_path = TemplatePathManager.get_template_base_path()
17
+ global_prompt_templates_path = join(
18
+ template_base_path, "prompt-modules")
19
+
20
+ subdirs = ["commands", "rules", "intentions", "blueprints"]
21
+
22
+ os.makedirs(join(prompt_templates_dir,
23
+ "global-prompt-modules"), exist_ok=True)
24
+ for subdir in subdirs:
25
+ target_dir = join(prompt_templates_dir,
26
+ "global-prompt-modules", subdir)
27
+ source_dir = join(global_prompt_templates_path, subdir)
28
+ os.makedirs(target_dir, exist_ok=True)
29
+ for item in os.listdir(source_dir):
30
+ source = join(source_dir, item)
31
+ target = join(target_dir, item)
32
+ shutil.copy2(source, target)
33
+
34
+ custom_prompt_templates_subdir = config.custom_prompt_templates_subdir
35
+ local_prompt_modules_dir = join(
36
+ prompt_templates_dir, custom_prompt_templates_subdir)
37
+ os.makedirs(local_prompt_modules_dir, exist_ok=True)
38
+ for subdir in subdirs:
39
+ os.makedirs(join(local_prompt_modules_dir, subdir), exist_ok=True)
@@ -0,0 +1,39 @@
1
+ import os
2
+ from ara_cli.commands.command import Command
3
+
4
+
5
+ def list_available_binary_agents(chat_instance):
6
+ """Helper to list executable files in the agents directory."""
7
+ try:
8
+ base_dir = chat_instance._find_project_root()
9
+ if not base_dir:
10
+ return [] # Can't find project root
11
+
12
+ agents_dir = os.path.join(base_dir, "ara", ".araconfig", "agents")
13
+ if not os.path.isdir(agents_dir):
14
+ return []
15
+
16
+ available_agents = []
17
+ for f in os.listdir(agents_dir):
18
+ path = os.path.join(agents_dir, f)
19
+ if os.path.isfile(path) and os.access(path, os.X_OK):
20
+ available_agents.append(f)
21
+ return available_agents
22
+ except Exception:
23
+ return [] # Fail silently
24
+
25
+
26
+ class ListAgentsCommand(Command):
27
+ def __init__(self, chat_instance):
28
+ self.chat_instance = chat_instance
29
+
30
+ def execute(self):
31
+ """Lists all available executable binary agents."""
32
+ print("Searching for available agents in 'ara/.araconfig/agents/'...")
33
+ available_agents = list_available_binary_agents(self.chat_instance)
34
+ if available_agents:
35
+ print("\nAvailable binary agents:")
36
+ for agent in available_agents:
37
+ print(f" - {agent}")
38
+ else:
39
+ print("No executable binary agents found.")
@@ -1,6 +1,7 @@
1
1
  from ara_cli.commands.command import Command
2
- from ara_cli.file_loaders.file_loader import FileLoaderFactory
3
- from ara_cli.file_loaders.binary_file_loader import BinaryFileLoader
2
+ from ara_cli.file_loaders.factories.file_loader_factory import FileLoaderFactory
3
+ from ara_cli.file_loaders.loaders.binary_file_loader import BinaryFileLoader
4
+ from ara_cli import BINARY_TYPE_MAPPING
4
5
 
5
6
 
6
7
  class LoadCommand(Command):
@@ -29,7 +30,7 @@ class LoadCommand(Command):
29
30
  # Determine mime type for binary files
30
31
  file_name_lower = self.file_path.lower()
31
32
  mime_type = None
32
- for extension, mt in FileLoaderFactory.BINARY_TYPE_MAPPING.items():
33
+ for extension, mt in BINARY_TYPE_MAPPING.items():
33
34
  if file_name_lower.endswith(extension):
34
35
  mime_type = mt
35
36
  break
@@ -1,5 +1,5 @@
1
1
  from ara_cli.commands.command import Command
2
- from ara_cli.file_loaders.binary_file_loader import BinaryFileLoader
2
+ from ara_cli.file_loaders.loaders.binary_file_loader import BinaryFileLoader
3
3
 
4
4
 
5
5
  class LoadImageCommand(Command):
@@ -3,9 +3,9 @@ from ara_cli.artefact_reader import ArtefactReader
3
3
  from ara_cli.file_classifier import FileClassifier
4
4
  from ara_cli.list_filter import ListFilter, filter_list
5
5
  from ara_cli.artefact_models.artefact_data_retrieval import (
6
- artefact_content_retrieval,
7
- artefact_path_retrieval,
8
- artefact_tags_retrieval
6
+ artefact_content_retrieval,
7
+ artefact_path_retrieval,
8
+ artefact_tags_retrieval,
9
9
  )
10
10
  from ara_cli.artefact_fuzzy_search import suggest_close_name_matches
11
11
  import os
@@ -18,7 +18,7 @@ class ReadCommand(Command):
18
18
  artefact_name: str,
19
19
  read_mode: str = "default",
20
20
  list_filter: ListFilter = None,
21
- output=None
21
+ output=None,
22
22
  ):
23
23
  self.classifier = classifier
24
24
  self.artefact_name = artefact_name
@@ -29,7 +29,8 @@ class ReadCommand(Command):
29
29
  def execute(self) -> bool:
30
30
  """Execute the read command and return success status."""
31
31
  file_classifier = FileClassifier(os)
32
- classified_artefacts = ArtefactReader.read_artefacts()
32
+ reader = ArtefactReader()
33
+ classified_artefacts = reader.read_artefacts()
33
34
 
34
35
  if not self.classifier or not self.artefact_name:
35
36
  self._filter_and_print(classified_artefacts, file_classifier)
@@ -39,15 +40,12 @@ class ReadCommand(Command):
39
40
  all_artefact_names = [a.title for a in artefacts]
40
41
 
41
42
  if self.artefact_name not in all_artefact_names:
42
- suggest_close_name_matches(
43
- self.artefact_name,
44
- all_artefact_names
45
- )
43
+ suggest_close_name_matches(self.artefact_name, all_artefact_names)
46
44
  return False
47
45
 
48
- target_artefact = next(filter(
49
- lambda x: x.title == self.artefact_name, artefacts
50
- ))
46
+ target_artefact = next(
47
+ filter(lambda x: x.title == self.artefact_name, artefacts)
48
+ )
51
49
 
52
50
  artefacts_by_classifier = {self.classifier: []}
53
51
 
@@ -55,16 +53,14 @@ class ReadCommand(Command):
55
53
  match self.read_mode:
56
54
  case "branch":
57
55
  self._handle_branch_mode(
58
- classified_artefacts, artefacts_by_classifier
56
+ classified_artefacts, artefacts_by_classifier, reader
59
57
  )
60
58
  case "children":
61
59
  artefacts_by_classifier = self._handle_children_mode(
62
- classified_artefacts
60
+ classified_artefacts, reader
63
61
  )
64
62
  case _:
65
- self._handle_default_mode(
66
- target_artefact, artefacts_by_classifier
67
- )
63
+ self._handle_default_mode(target_artefact, artefacts_by_classifier)
68
64
 
69
65
  # Apply filtering and print results
70
66
  self._filter_and_print(artefacts_by_classifier, file_classifier)
@@ -78,21 +74,23 @@ class ReadCommand(Command):
78
74
  self.output(f"Error reading artefact: {e}")
79
75
  return False
80
76
 
81
- def _handle_branch_mode(self, classified_artefacts, artefacts_by_classifier):
77
+ def _handle_branch_mode(
78
+ self, classified_artefacts, artefacts_by_classifier, reader
79
+ ):
82
80
  """Handle branch read mode."""
83
- ArtefactReader.step_through_value_chain(
81
+ reader.step_through_value_chain(
84
82
  artefact_name=self.artefact_name,
85
83
  classifier=self.classifier,
86
84
  artefacts_by_classifier=artefacts_by_classifier,
87
- classified_artefacts=classified_artefacts
85
+ classified_artefacts=classified_artefacts,
88
86
  )
89
87
 
90
- def _handle_children_mode(self, classified_artefacts):
88
+ def _handle_children_mode(self, classified_artefacts, reader):
91
89
  """Handle children read mode."""
92
- return ArtefactReader.find_children(
90
+ return reader.find_children(
93
91
  artefact_name=self.artefact_name,
94
92
  classifier=self.classifier,
95
- classified_artefacts=classified_artefacts
93
+ classified_artefacts=classified_artefacts,
96
94
  )
97
95
 
98
96
  def _handle_default_mode(self, target_artefact, artefacts_by_classifier):
@@ -106,12 +104,10 @@ class ReadCommand(Command):
106
104
  list_filter=self.list_filter,
107
105
  content_retrieval_strategy=artefact_content_retrieval,
108
106
  file_path_retrieval=artefact_path_retrieval,
109
- tag_retrieval=artefact_tags_retrieval
107
+ tag_retrieval=artefact_tags_retrieval,
110
108
  )
111
109
 
112
110
  def _filter_and_print(self, artefacts_by_classifier, file_classifier):
113
111
  """Apply list filtering and print results"""
114
112
  filtered_artefacts = self._apply_filtering(artefacts_by_classifier)
115
- file_classifier.print_classified_files(
116
- filtered_artefacts, print_content=True
117
- )
113
+ file_classifier.print_classified_files(filtered_artefacts, print_content=True)