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
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import logging
|
|
3
|
+
from ara_cli.prompt_handler import LLMSingleton
|
|
4
|
+
from langfuse.api.resources.commons.errors import Error as LangfuseError, NotFoundError
|
|
5
|
+
from ara_cli.classifier import Classifier
|
|
6
|
+
from ara_cli.artefact_reader import ArtefactReader
|
|
7
|
+
from ara_cli.artefact_creator import ArtefactCreator
|
|
8
|
+
from ara_cli.error_handler import AraError
|
|
9
|
+
from ara_cli.directory_navigator import DirectoryNavigator
|
|
10
|
+
from ara_cli.artefact_deleter import ArtefactDeleter
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AraArtefactConverter:
|
|
14
|
+
def __init__(self, file_system=None):
|
|
15
|
+
self.file_system = file_system or os
|
|
16
|
+
self.reader = ArtefactReader()
|
|
17
|
+
self.creator = ArtefactCreator(self.file_system)
|
|
18
|
+
|
|
19
|
+
def convert(
|
|
20
|
+
self,
|
|
21
|
+
old_classifier: str,
|
|
22
|
+
artefact_name: str,
|
|
23
|
+
new_classifier: str,
|
|
24
|
+
merge: bool = False,
|
|
25
|
+
override: bool = False,
|
|
26
|
+
):
|
|
27
|
+
try:
|
|
28
|
+
self._validate_classifiers(old_classifier, new_classifier)
|
|
29
|
+
|
|
30
|
+
content, artefact_info = self.reader.read_artefact_data(
|
|
31
|
+
artefact_name, old_classifier
|
|
32
|
+
)
|
|
33
|
+
if not content or not artefact_info:
|
|
34
|
+
raise AraError(
|
|
35
|
+
f"Artefact '{artefact_name}' of type '{old_classifier}' not found"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
target_content_existing = self._resolve_target_content(
|
|
39
|
+
artefact_name, new_classifier, merge, override
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
target_class = self._get_target_class(new_classifier)
|
|
43
|
+
|
|
44
|
+
prompt = self._get_prompt(
|
|
45
|
+
old_classifier=old_classifier,
|
|
46
|
+
new_classifier=new_classifier,
|
|
47
|
+
artefact_name=artefact_name,
|
|
48
|
+
content=content,
|
|
49
|
+
target_content_existing=target_content_existing,
|
|
50
|
+
merge=merge,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
print(
|
|
54
|
+
f"{'Merging' if merge and target_content_existing else 'Converting'} '{artefact_name}' from {old_classifier} to {new_classifier}..."
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
converted_artefact = self._run_conversion_agent(prompt, target_class)
|
|
58
|
+
artefact_content = converted_artefact.serialize()
|
|
59
|
+
|
|
60
|
+
self._write_artefact(
|
|
61
|
+
new_classifier,
|
|
62
|
+
artefact_name,
|
|
63
|
+
artefact_content,
|
|
64
|
+
merge=merge,
|
|
65
|
+
override=override,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if old_classifier != new_classifier:
|
|
69
|
+
self._move_data_folder_content(
|
|
70
|
+
old_classifier, new_classifier, artefact_name
|
|
71
|
+
)
|
|
72
|
+
deleter = ArtefactDeleter(self.file_system)
|
|
73
|
+
deleter.delete(artefact_name, old_classifier, force=True)
|
|
74
|
+
|
|
75
|
+
except ValueError as e:
|
|
76
|
+
raise e
|
|
77
|
+
except AraError as e:
|
|
78
|
+
raise e
|
|
79
|
+
except Exception as e:
|
|
80
|
+
raise e
|
|
81
|
+
|
|
82
|
+
def _validate_classifiers(self, old_classifier: str, new_classifier: str):
|
|
83
|
+
if not Classifier.is_valid_classifier(old_classifier):
|
|
84
|
+
raise ValueError(f"Invalid classifier: {old_classifier}")
|
|
85
|
+
if not Classifier.is_valid_classifier(new_classifier):
|
|
86
|
+
raise ValueError(f"Invalid classifier: {new_classifier}")
|
|
87
|
+
|
|
88
|
+
def _move_data_folder_content(self, old_classifier, new_classifier, artefact_name):
|
|
89
|
+
import shutil
|
|
90
|
+
|
|
91
|
+
navigator = DirectoryNavigator()
|
|
92
|
+
navigator.navigate_to_target()
|
|
93
|
+
|
|
94
|
+
sub_directory_old = Classifier.get_sub_directory(old_classifier)
|
|
95
|
+
dir_path_old = self.file_system.path.join(
|
|
96
|
+
sub_directory_old, f"{artefact_name}.data"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
sub_directory_new = Classifier.get_sub_directory(new_classifier)
|
|
100
|
+
dir_path_new = self.file_system.path.join(
|
|
101
|
+
sub_directory_new, f"{artefact_name}.data"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if self.file_system.path.exists(dir_path_old):
|
|
105
|
+
backup_folder_name = f"{artefact_name}.data.old"
|
|
106
|
+
destination_path = self.file_system.path.join(
|
|
107
|
+
dir_path_new, backup_folder_name
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if not self.file_system.path.exists(dir_path_new):
|
|
111
|
+
os.makedirs(dir_path_new, exist_ok=True)
|
|
112
|
+
|
|
113
|
+
if self.file_system.path.exists(destination_path):
|
|
114
|
+
shutil.rmtree(destination_path)
|
|
115
|
+
|
|
116
|
+
try:
|
|
117
|
+
shutil.move(dir_path_old, destination_path)
|
|
118
|
+
print(f"Moved old data to {destination_path}")
|
|
119
|
+
except Exception as e:
|
|
120
|
+
print(f"Error moving data directory: {e}")
|
|
121
|
+
|
|
122
|
+
def _resolve_target_content(
|
|
123
|
+
self, artefact_name: str, new_classifier: str, merge: bool, override: bool
|
|
124
|
+
):
|
|
125
|
+
target_content_existing = None
|
|
126
|
+
if not merge and not override:
|
|
127
|
+
_, new_artefact_info = self.reader.read_artefact_data(
|
|
128
|
+
artefact_name, new_classifier
|
|
129
|
+
)
|
|
130
|
+
if new_artefact_info:
|
|
131
|
+
raise ValueError(
|
|
132
|
+
f"Found already exiting {new_classifier} {artefact_name}. Rerun the command with --override or --merge."
|
|
133
|
+
)
|
|
134
|
+
elif merge:
|
|
135
|
+
target_content_existing, _ = self.reader.read_artefact_data(
|
|
136
|
+
artefact_name, new_classifier
|
|
137
|
+
)
|
|
138
|
+
return target_content_existing
|
|
139
|
+
|
|
140
|
+
def _get_target_class(self, new_classifier: str):
|
|
141
|
+
from ara_cli.artefact_models.artefact_mapping import artefact_type_mapping
|
|
142
|
+
from ara_cli.artefact_models.artefact_model import ArtefactType
|
|
143
|
+
|
|
144
|
+
target_type = ArtefactType(new_classifier)
|
|
145
|
+
target_class = artefact_type_mapping.get(target_type)
|
|
146
|
+
|
|
147
|
+
if not target_class:
|
|
148
|
+
raise AraError(f"No artefact class found for classifier: {new_classifier}")
|
|
149
|
+
return target_class
|
|
150
|
+
|
|
151
|
+
def _get_prompt(
|
|
152
|
+
self,
|
|
153
|
+
old_classifier,
|
|
154
|
+
new_classifier,
|
|
155
|
+
artefact_name,
|
|
156
|
+
content,
|
|
157
|
+
target_content_existing,
|
|
158
|
+
merge,
|
|
159
|
+
):
|
|
160
|
+
try:
|
|
161
|
+
langfuse = LLMSingleton.get_instance().langfuse
|
|
162
|
+
if langfuse is None:
|
|
163
|
+
# This mimics the behavior if authentication failed or env vars missing in Singleton
|
|
164
|
+
raise Exception("Langfuse not initialized in Singleton")
|
|
165
|
+
|
|
166
|
+
if merge and target_content_existing:
|
|
167
|
+
prompt_template = langfuse.get_prompt("ara-cli/artefact-convert/merge")
|
|
168
|
+
return prompt_template.compile(
|
|
169
|
+
old_classifier=old_classifier,
|
|
170
|
+
new_classifier=new_classifier,
|
|
171
|
+
artefact_name=artefact_name,
|
|
172
|
+
content=content,
|
|
173
|
+
target_content_existing=target_content_existing,
|
|
174
|
+
)
|
|
175
|
+
else:
|
|
176
|
+
prompt_template = langfuse.get_prompt(
|
|
177
|
+
"ara-cli/artefact-convert/default"
|
|
178
|
+
)
|
|
179
|
+
return prompt_template.compile(
|
|
180
|
+
old_classifier=old_classifier,
|
|
181
|
+
new_classifier=new_classifier,
|
|
182
|
+
artefact_name=artefact_name,
|
|
183
|
+
content=content,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
except (LangfuseError, NotFoundError, Exception) as e:
|
|
187
|
+
logging.info(f"Could not fetch Langfuse prompt: {e}. Using fallback.")
|
|
188
|
+
# Fallback prompts
|
|
189
|
+
formatting_instructions = (
|
|
190
|
+
"### Data Extraction Rules:\n"
|
|
191
|
+
"- **Users**: Extract usernames ONLY. Do NOT add '@' or 'user_' prefixes. "
|
|
192
|
+
"The system adds these automatically. (e.g., return 'hans', NOT '@user_hans').\n"
|
|
193
|
+
"- **Author**: Extract the author as 'creator_<name>'. Do NOT add the '@' symbol. "
|
|
194
|
+
"(e.g., return 'creator_unknown', NOT '@creator_unknown').\n"
|
|
195
|
+
f"- **Artefact Name**: Use strictly '{artefact_name}'.\n"
|
|
196
|
+
"- **Content**: Adapt the content to the target schema fields."
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
if merge and target_content_existing:
|
|
200
|
+
return (
|
|
201
|
+
f"Merge the following {old_classifier} artefact into the existing {new_classifier} artefact. "
|
|
202
|
+
"Combine the information from both, prioritizing the structure of the target artefact schema. "
|
|
203
|
+
"Ensure no critical information is lost. "
|
|
204
|
+
"\n\n"
|
|
205
|
+
f"{formatting_instructions}"
|
|
206
|
+
"\n\n"
|
|
207
|
+
f"Source Artefact ({old_classifier}):\n```\n{content}\n```"
|
|
208
|
+
f"\n\nTarget Artefact ({new_classifier}):\n```\n{target_content_existing}\n```"
|
|
209
|
+
)
|
|
210
|
+
else:
|
|
211
|
+
return (
|
|
212
|
+
f"Convert the following {old_classifier} artefact to a {new_classifier} artefact. "
|
|
213
|
+
"Preserve the core meaning, business value, and description. "
|
|
214
|
+
"Map the content to the fields required by the target schema. "
|
|
215
|
+
"\n\n"
|
|
216
|
+
f"{formatting_instructions}"
|
|
217
|
+
"\n\n"
|
|
218
|
+
f"Source Artefact Content:\n```\n{content}\n```"
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
def _run_conversion_agent(self, prompt, target_class):
|
|
222
|
+
from ara_cli.llm_utils import create_pydantic_ai_agent
|
|
223
|
+
|
|
224
|
+
agent = create_pydantic_ai_agent(output_type=target_class, instrument=True)
|
|
225
|
+
try:
|
|
226
|
+
result = agent.run_sync(prompt)
|
|
227
|
+
return result.output
|
|
228
|
+
except Exception as e:
|
|
229
|
+
raise AraError(f"LLM conversion failed: {e}")
|
|
230
|
+
|
|
231
|
+
def _write_artefact(
|
|
232
|
+
self, new_classifier, artefact_name, artefact_content, merge, override
|
|
233
|
+
):
|
|
234
|
+
from shutil import rmtree
|
|
235
|
+
|
|
236
|
+
navigator = DirectoryNavigator()
|
|
237
|
+
navigator.navigate_to_target()
|
|
238
|
+
|
|
239
|
+
sub_directory = Classifier.get_sub_directory(new_classifier)
|
|
240
|
+
file_path = self.file_system.path.join(
|
|
241
|
+
sub_directory, f"{artefact_name}.{new_classifier}"
|
|
242
|
+
)
|
|
243
|
+
dir_path = self.file_system.path.join(sub_directory, f"{artefact_name}.data")
|
|
244
|
+
|
|
245
|
+
if self.file_system.path.exists(file_path) and not (override or merge):
|
|
246
|
+
raise ValueError(f"Target file {file_path} already exists.")
|
|
247
|
+
|
|
248
|
+
if not merge:
|
|
249
|
+
rmtree(dir_path, ignore_errors=True)
|
|
250
|
+
|
|
251
|
+
os.makedirs(dir_path, exist_ok=True)
|
|
252
|
+
|
|
253
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
254
|
+
f.write(artefact_content)
|
|
255
|
+
|
|
256
|
+
print(f"Conversion successful. Created: {file_path}")
|