ara-cli 0.1.9.95__py3-none-any.whl → 0.1.10.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.
@@ -0,0 +1,245 @@
1
+
2
+ import os
3
+ import glob
4
+ from ara_cli.template_manager import TemplatePathManager
5
+ from ara_cli.ara_config import ConfigManager
6
+ from ara_cli.directory_navigator import DirectoryNavigator
7
+
8
+
9
+ class TemplateLoader:
10
+ """Handles template loading logic shared between CLI and chat commands"""
11
+
12
+ def __init__(self, chat_instance=None):
13
+ self.chat_instance = chat_instance
14
+
15
+ def load_template(self, template_name: str, template_type: str, chat_file_path: str, default_pattern: str | None = None) -> bool:
16
+ if not template_name:
17
+ if default_pattern:
18
+ return self.load_template_from_prompt_data(template_type, default_pattern, chat_file_path)
19
+ else:
20
+ print(f"A template name is required for template type '{template_type}'.")
21
+ return False
22
+ return self.load_template_from_global_or_local(template_name, template_type, chat_file_path)
23
+
24
+ def get_plural_template_type(self, template_type: str) -> str:
25
+ """Determines the plural form of a template type."""
26
+ plurals = {"commands": "commands", "rules": "rules"}
27
+ return plurals.get(template_type, f"{template_type}s")
28
+
29
+ def load_template_from_global_or_local(self, template_name: str, template_type: str, chat_file_path: str) -> bool:
30
+ """Load template from global or local directories"""
31
+ plural = self.get_plural_template_type(template_type)
32
+
33
+ if template_name.startswith("global/"):
34
+ return self._load_global_template(template_name, template_type, plural, chat_file_path)
35
+ else:
36
+ return self._load_local_template(template_name, template_type, plural, chat_file_path)
37
+
38
+ def _choose_file_for_cli(self, files: list[str], pattern: str) -> str | None:
39
+ """CLI-compatible file selection method"""
40
+ if len(files) <= 1:
41
+ return files[0] if files else None
42
+
43
+ if pattern in ["*", "global/*"] or "*" in pattern:
44
+ files.sort()
45
+ print("Multiple files found:")
46
+ for i, file in enumerate(files):
47
+ print(f"{i + 1}: {os.path.basename(file)}")
48
+
49
+ try:
50
+ choice = input("Please choose a file to load (enter number): ")
51
+ choice_index = int(choice) - 1
52
+ if 0 <= choice_index < len(files):
53
+ return files[choice_index]
54
+ else:
55
+ print("Invalid choice. Aborting load.")
56
+ return None
57
+ except (ValueError, KeyboardInterrupt):
58
+ print("Invalid input. Aborting load.")
59
+ return None
60
+ else:
61
+ return files[0]
62
+
63
+ def _load_global_template(self, template_name: str, template_type: str, plural: str, chat_file_path: str) -> bool:
64
+ """Load template from global directory"""
65
+ directory = f"{TemplatePathManager.get_template_base_path()}/prompt-modules/{plural}/"
66
+ template_file = template_name.removeprefix("global/")
67
+ file_pattern = os.path.join(directory, template_file)
68
+ matching_files = glob.glob(file_pattern)
69
+
70
+ if not matching_files:
71
+ print(f"No {template_type} template '{template_file}' found in global templates.")
72
+ return False
73
+
74
+ # Choose file based on context
75
+ if self.chat_instance:
76
+ file_path = self.chat_instance.choose_file_to_load(matching_files, template_file)
77
+ else:
78
+ file_path = self._choose_file_for_cli(matching_files, template_file)
79
+
80
+ if file_path is None:
81
+ return False
82
+
83
+ return self._load_file_to_chat(file_path, template_type, chat_file_path)
84
+
85
+ def _load_local_template(self, template_name: str, template_type: str, plural: str, chat_file_path: str) -> bool:
86
+ """Load template from local custom directory"""
87
+ ara_config = ConfigManager.get_config()
88
+ navigator = DirectoryNavigator()
89
+
90
+ original_directory = os.getcwd()
91
+ navigator.navigate_to_target()
92
+ local_templates_path = ara_config.local_prompt_templates_dir
93
+ os.chdir("..")
94
+ local_templates_path = os.path.join(os.getcwd(), local_templates_path)
95
+ os.chdir(original_directory)
96
+
97
+ custom_prompt_templates_subdir = ara_config.custom_prompt_templates_subdir
98
+ template_directory = f"{local_templates_path}/{custom_prompt_templates_subdir}/{plural}"
99
+ file_pattern = os.path.join(template_directory, template_name)
100
+ matching_files = glob.glob(file_pattern)
101
+
102
+ if not matching_files:
103
+ print(f"No {template_type} template '{template_name}' found in local templates.")
104
+ return False
105
+
106
+ # Choose file based on context
107
+ if self.chat_instance:
108
+ file_path = self.chat_instance.choose_file_to_load(matching_files, template_name)
109
+ else:
110
+ file_path = self._choose_file_for_cli(matching_files, template_name)
111
+
112
+ if file_path is None:
113
+ return False
114
+
115
+ return self._load_file_to_chat(file_path, template_type, chat_file_path)
116
+
117
+ def load_template_from_prompt_data(self, template_type: str, default_pattern: str, chat_file_path: str) -> bool:
118
+ """Load template from prompt.data directory with selection"""
119
+ directory_path = os.path.join(os.path.dirname(chat_file_path), "prompt.data")
120
+ file_pattern = os.path.join(directory_path, default_pattern)
121
+ matching_files = glob.glob(file_pattern)
122
+
123
+ if not matching_files:
124
+ print(f"No {template_type} file found in prompt.data directory.")
125
+ return False
126
+
127
+ # Choose file based on context
128
+ if self.chat_instance:
129
+ file_path = self.chat_instance.choose_file_to_load(matching_files, default_pattern)
130
+ else:
131
+ file_path = self._choose_file_for_cli(matching_files, "*")
132
+
133
+ if file_path is None:
134
+ return False
135
+
136
+ return self._load_file_to_chat(file_path, template_type, chat_file_path)
137
+
138
+ def _load_file_to_chat(self, file_path: str, template_type: str, chat_file_path: str) -> bool:
139
+ """Load a file into the chat file"""
140
+ if self.chat_instance:
141
+ # Use chat instance methods
142
+ self.chat_instance.add_prompt_tag_if_needed(chat_file_path)
143
+ if self.chat_instance.load_file(file_path):
144
+ print(f"Loaded {template_type} from {os.path.basename(file_path)} into {os.path.basename(chat_file_path)}")
145
+ return True
146
+ else:
147
+ # Direct file loading for CLI usage
148
+ try:
149
+ with open(file_path, 'r', encoding='utf-8') as template_file:
150
+ template_content = template_file.read()
151
+
152
+ # Add prompt tag if needed
153
+ self._add_prompt_tag_if_needed(chat_file_path)
154
+
155
+ # Append template content with newlines for separation
156
+ with open(chat_file_path, 'a', encoding='utf-8') as chat_file:
157
+ chat_file.write(f"\n{template_content}\n")
158
+
159
+ print(f"Loaded {template_type} from {os.path.basename(file_path)} into {os.path.basename(chat_file_path)}")
160
+ return True
161
+ except Exception as e:
162
+ print(f"Error loading {template_type} from {file_path}: {e}")
163
+ return False
164
+
165
+ return False
166
+
167
+ def _add_prompt_tag_if_needed(self, chat_file_path: str):
168
+ """Add prompt tag if needed for CLI usage"""
169
+ from ara_cli.chat import Chat
170
+
171
+ with open(chat_file_path, 'r', encoding='utf-8') as file:
172
+ lines = file.readlines()
173
+
174
+ prompt_tag = f"# {Chat.ROLE_PROMPT}:"
175
+ if Chat.get_last_role_marker(lines) == prompt_tag:
176
+ return
177
+
178
+ append = prompt_tag
179
+ if lines:
180
+ last_line = lines[-1].strip()
181
+ if last_line != "" and last_line != '\n':
182
+ append = f"\n{append}"
183
+
184
+ with open(chat_file_path, 'a', encoding='utf-8') as file:
185
+ file.write(append)
186
+
187
+ def _find_project_root(self, start_path: str) -> str | None:
188
+ """
189
+ Finds the project root by searching for an 'ara' directory,
190
+ starting from the given path and moving upwards.
191
+ """
192
+ current_dir = start_path
193
+ while True:
194
+ if os.path.isdir(os.path.join(current_dir, 'ara')):
195
+ return current_dir
196
+ parent_dir = os.path.dirname(current_dir)
197
+ if parent_dir == current_dir: # Reached the filesystem root
198
+ return None
199
+ current_dir = parent_dir
200
+
201
+ def _gather_templates_from_path(self, search_path: str, templates_set: set, prefix: str = ""):
202
+ """
203
+ Scans a given path for items and adds them to the provided set,
204
+ optionally prepending a prefix.
205
+ """
206
+ if not os.path.isdir(search_path):
207
+ return
208
+ for path in glob.glob(os.path.join(search_path, '*')):
209
+ templates_set.add(f"{prefix}{os.path.basename(path)}")
210
+
211
+ def get_available_templates(self, template_type: str, context_path: str) -> list[str]:
212
+ """
213
+ Scans for available global and project-local custom templates.
214
+ This method safely searches for template files without changing the
215
+ current directory, making it safe for use in autocompleters.
216
+ Args:
217
+ template_type: The type of template to search for (e.g., 'rules').
218
+ context_path: The directory path to start the search for project root from.
219
+ Returns:
220
+ A sorted list of unique template names. Global templates are
221
+ prefixed with 'global/'.
222
+ """
223
+ plural_type = self.get_plural_template_type(template_type)
224
+ templates = set()
225
+
226
+ # 1. Find Global Templates
227
+ try:
228
+ global_base_path = TemplatePathManager.get_template_base_path()
229
+ global_template_dir = os.path.join(global_base_path, "prompt-modules", plural_type)
230
+ self._gather_templates_from_path(global_template_dir, templates, prefix="global/")
231
+ except Exception:
232
+ pass # Silently ignore if global templates are not found
233
+
234
+ # 2. Find Local Custom Templates
235
+ try:
236
+ project_root = self._find_project_root(context_path)
237
+ if project_root:
238
+ config = ConfigManager.get_config()
239
+ local_templates_base = os.path.join(project_root, config.local_prompt_templates_dir)
240
+ custom_dir = os.path.join(local_templates_base, config.custom_prompt_templates_subdir, plural_type)
241
+ self._gather_templates_from_path(custom_dir, templates)
242
+ except Exception:
243
+ pass # Silently ignore if local templates cannot be resolved
244
+
245
+ return sorted(list(templates))
ara_cli/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # version.py
2
- __version__ = "0.1.9.95" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
2
+ __version__ = "0.1.10.0" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
@@ -1,8 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ara_cli
3
- Version: 0.1.9.95
3
+ Version: 0.1.10.0
4
4
  Summary: Powerful, open source command-line tool for managing, structuring and automating software development artifacts in line with Business-Driven Development (BDD) and AI-assisted processes
5
5
  Description-Content-Type: text/markdown
6
+ Requires-Dist: langfuse
6
7
  Requires-Dist: litellm
7
8
  Requires-Dist: llama-index
8
9
  Requires-Dist: llama-index-llms-openai
@@ -1,49 +1,51 @@
1
- ara_cli/__init__.py,sha256=CzGZUcf4WQP051xeNziNKQj8oNr-n_lZCW5_cR_WNVw,455
2
- ara_cli/__main__.py,sha256=J5DCDLRZ6UcpYwM1-NkjaLo4PTetcSj2dB4HrrftkUw,2064
3
- ara_cli/ara_command_action.py,sha256=uyMN05ZYffWqN9nwL53MmQ_yHpuxHVqZ_scAMEoD1jw,21516
4
- ara_cli/ara_command_parser.py,sha256=A1lMc9Gc0EMJt-380PTcv3aKoxbXGfx5gGax-sZqV3I,21020
5
- ara_cli/ara_config.py,sha256=VJeage_v-446OtSXIfpazUbetpH7kGNv8Un1lKYx5ZE,9321
6
- ara_cli/artefact_autofix.py,sha256=w6erUYrpPiwtHToiZEfjkDgDDPPSA9zaNc7w2NqrZ2M,20730
7
- ara_cli/artefact_creator.py,sha256=wchIq1w636ui_kRCfNWPffqiIiXqSb49pgTpQj3KzA0,6132
8
- ara_cli/artefact_deleter.py,sha256=Co4wwCH3yW8H9NrOq7_2p5571EeHr0TsfE-H8KqoOfY,1900
9
- ara_cli/artefact_fuzzy_search.py,sha256=iBlDqjZf-_D3VUjFf7ZwkiQbpQDcwRndIU7aG_sRTgE,2668
1
+ ara_cli/__init__.py,sha256=DuzXKimZ6JtUEnp48qCQcnojDflBtYjT6Na_twO5EzM,505
2
+ ara_cli/__main__.py,sha256=sumQaIYg6wJdcT-nQjJpK9DGaBUTszjPrgZLPN2bUVs,3520
3
+ ara_cli/ara_command_action.py,sha256=VJSReKfgUQk62DeMcY4EN228FvgF1m8NiHf-ckrigdA,24450
4
+ ara_cli/ara_command_parser.py,sha256=4_LXxj9w7eAY8d_s8pMlKmxtMr9cX9y2pD5azNeJjsg,22288
5
+ ara_cli/ara_config.py,sha256=vZsY2zYJdlSExRE84L5LqRH3DjveeuMSmG5fC8HDIVc,9794
6
+ ara_cli/artefact_autofix.py,sha256=9j_bh0HGnN6HVT9OGKVp85VgDklpx3XpSc9MxBCldU4,25050
7
+ ara_cli/artefact_creator.py,sha256=fRrDaGZvOqJqDb_DLXqMTed2XfIvQMIHjLgOuHOi3Qg,5973
8
+ ara_cli/artefact_deleter.py,sha256=T1vS2s3k_BW86Sd8FExx8nC3BIL05xE9KZLkeZsZrKM,1891
9
+ ara_cli/artefact_fuzzy_search.py,sha256=XMzbMBOJ2YrgFi566jYNB3XeRAmJh7-zqV2QJYbhtlc,3006
10
10
  ara_cli/artefact_link_updater.py,sha256=nKdxTpDKqWTOAMD8viKmUaklSFGWzJZ8S8E8xW_ADuM,3775
11
11
  ara_cli/artefact_lister.py,sha256=M-ggazAgZ-OLeW9NB48r_sd6zPx0p4hEpeS63qHwI1A,4176
12
- ara_cli/artefact_reader.py,sha256=Pho0_Eqm7kD9CNbVMhKb6mkNM0I3iJiCJXbXmVp1DJU,7827
12
+ ara_cli/artefact_reader.py,sha256=-6E1VhIlh2oJE1Rn8ARcHRc_E9N4uk8cEViKMoywm6E,7753
13
13
  ara_cli/artefact_renamer.py,sha256=8S4QWD19_FGKsKlWojnu_RUOxx0u9rmLugydM4s4VDc,4219
14
14
  ara_cli/artefact_scan.py,sha256=msPCm-vPWOAZ_e_z5GylXxq1MtNlmJ4zvKrsdOFCWF4,4813
15
- ara_cli/chat.py,sha256=i2v-Ctem66sgO-HUV3kvMbk4wfotsc2oNet_i1-wfAI,39901
15
+ ara_cli/chat.py,sha256=Vp1HzSaaUsfOldOjxJc0ff68lQJWiIet0LJ22jjMYUs,40776
16
16
  ara_cli/classifier.py,sha256=zWskj7rBYdqYBGjksBm46iTgVU5IIf2PZsJr4qeiwVU,1878
17
17
  ara_cli/codefusionretriever.py,sha256=fCHgXdIBRzkVAnapX-KI2NQ44XbrrF4tEQmn5J6clUI,1980
18
18
  ara_cli/codehierachieretriever.py,sha256=Xd3EgEWWhkSf1TmTWtf8X5_YvyE_4B66nRrqarwSiTU,1182
19
19
  ara_cli/commandline_completer.py,sha256=b00Dqb5n7SecpxYIDLxAfYhp8X6e3c8a5qYz6ko0i3E,1192
20
20
  ara_cli/directory_navigator.py,sha256=6QbSAjJrJ5a6Lutol9J4HFgVDMiAQ672ny9TATrh04U,3318
21
- ara_cli/file_classifier.py,sha256=A7wilPtIFm81iMgvqD0PjkOVL_QMUc9TB2w2Z9UcPcM,4001
21
+ ara_cli/error_handler.py,sha256=nNaJSq82f3xiz_QFRKPg5kX_-oI-UoFdRJ2OTj1AR18,4019
22
+ ara_cli/file_classifier.py,sha256=nUcNrhflUydCyCRbXHjEEXYwwwfUm65lYnNEvc86fpM,4026
22
23
  ara_cli/file_lister.py,sha256=0C-j8IzajXo5qlvnuy5WFfe43ALwJ-0JFh2K6Xx2ccw,2332
23
24
  ara_cli/filename_validator.py,sha256=Aw9PL8d5-Ymhp3EY6lDrUBk3cudaNqo1Uw5RzPpI1jA,118
24
25
  ara_cli/global_file_lister.py,sha256=IIrtFoN5KYyJ3jVPanXZJ4UbYZfSdONRwxkZzvmq6-k,2806
25
26
  ara_cli/list_filter.py,sha256=qKGwwQsrWe7L5FbdxEbBYD1bbbi8c-RMypjXqXvLbgs,5291
26
27
  ara_cli/output_suppressor.py,sha256=nwiHaQLwabOjMoJOeUESBnZszGMxrQZfJ3N2OvahX7Y,389
27
28
  ara_cli/prompt_chat.py,sha256=kd_OINDQFit6jN04bb7mzgY259JBbRaTaNp9F-webkc,1346
28
- ara_cli/prompt_extractor.py,sha256=-_17aVYXYH6kPX5FOSb9T8lbEkKPXE6nlHWq1pvO_Og,8423
29
- ara_cli/prompt_handler.py,sha256=8a9fcMwE_C6ntbw7UeroNJeU5LxrxEppiUtvYNUTB2U,23292
29
+ ara_cli/prompt_extractor.py,sha256=WloRgfcEdIVq37BpdWAd2X3EMu0bcNN_Wuws1T2YiUg,8418
30
+ ara_cli/prompt_handler.py,sha256=N0zH5k9T6udKAbMolxEAaAwiFo03h5aF-IY3BmMLEos,27924
30
31
  ara_cli/prompt_rag.py,sha256=ydlhe4CUqz0jdzlY7jBbpKaf_5fjMrAZKnriKea3ZAg,7485
31
32
  ara_cli/run_file_lister.py,sha256=XbrrDTJXp1LFGx9Lv91SNsEHZPP-PyEMBF_P4btjbDA,2360
32
33
  ara_cli/tag_extractor.py,sha256=k2yRl7dAMZ4YTARzUke4wgY0oEIOmWkOHGet7nXB6uw,3317
34
+ ara_cli/template_loader.py,sha256=uEpYOchT5d-OO5r-W0-h605Xilvuv56i1VKSy4_9NaE,10734
33
35
  ara_cli/template_manager.py,sha256=l2c785YHB7m0e2TjE0CX-nwXrS4v3EiT9qrS5KuatAc,7105
34
36
  ara_cli/update_config_prompt.py,sha256=moqj2Kha7S7fEGzTReU0v2y8UjXC8QfnoiieOQr35C4,5157
35
- ara_cli/version.py,sha256=rYDZfGIzWScBRjMjgeqPAsdSGThurJ7PgpedF2Ekr2U,146
37
+ ara_cli/version.py,sha256=AvikXK1EI29z3MY864MZ0oxZJZ4-tSdFYdogqQEkDac,146
36
38
  ara_cli/artefact_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
39
  ara_cli/artefact_models/artefact_data_retrieval.py,sha256=CooXOJBYWSyiViN2xkC8baS8OUaslry3YGVVUeDxRAU,527
38
40
  ara_cli/artefact_models/artefact_load.py,sha256=IXzWxP-Q_j_oDGMno0m-OuXCQ7Vd5c_NctshGr4ROBw,621
39
41
  ara_cli/artefact_models/artefact_mapping.py,sha256=8aD0spBjkJ8toMAmFawc6UTUxB6-tEEViZXv2I-r88Q,1874
40
42
  ara_cli/artefact_models/artefact_model.py,sha256=qSbcrmFWAYgBqcNl9QARI1_uLQJm-TPVgP5q2AEFnjE,15983
41
- ara_cli/artefact_models/artefact_templates.py,sha256=8HNM-TsNvKgTpruOBs751yRDXJypTiJhc1tkWCiYG7s,9830
43
+ ara_cli/artefact_models/artefact_templates.py,sha256=CFa_vIA-cnbZEHuACd24vNJB_7LueQi-8x7ga_AyLKI,9830
42
44
  ara_cli/artefact_models/businessgoal_artefact_model.py,sha256=GYT5S2xEnQHwv-k-lEeX5NMSqA-UEfV3PhNjgPDUJpw,4698
43
45
  ara_cli/artefact_models/capability_artefact_model.py,sha256=SZqHx4O2mj4urn77Stnj4_Jxtlq3-LgBBU9SMkByppI,3079
44
46
  ara_cli/artefact_models/epic_artefact_model.py,sha256=h9pC00ZxCL-t_NMjwTCeOnIJZPa9hhB-R05wr110LXs,5619
45
47
  ara_cli/artefact_models/example_artefact_model.py,sha256=UXrKbaPotg1jwcrVSdCeo-XH4tTD_-U1e3giaBn5_xg,1384
46
- ara_cli/artefact_models/feature_artefact_model.py,sha256=FrR7_xydOmMySAz0QpWgrNFF6UcbFsDj7Ji1R2dc82g,19179
48
+ ara_cli/artefact_models/feature_artefact_model.py,sha256=iUCrdgH_VhRCBVyCQARpl9cWzavC310lKZHWLofsq9s,20256
47
49
  ara_cli/artefact_models/issue_artefact_model.py,sha256=v6CpKnkqiUh6Wch2kkEmyyW49c8ysdy1qz8l1Ft9uJA,2552
48
50
  ara_cli/artefact_models/keyfeature_artefact_model.py,sha256=J9oXLsCAo22AW31D5Z104y02ss0S0O4tPCcd09zYCD0,4066
49
51
  ara_cli/artefact_models/serialize_helper.py,sha256=Wks30wy-UrwJURetydKykLgJkdGRgXFHkDT24vHe5tU,595
@@ -52,7 +54,7 @@ ara_cli/artefact_models/userstory_artefact_model.py,sha256=2awH31ROtm7j4T44Bv4cy
52
54
  ara_cli/artefact_models/vision_artefact_model.py,sha256=frjaUJj-mmIlVHEhzAQztCGs-CtvNu_odSborgztfzo,5251
53
55
  ara_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
56
  ara_cli/commands/command.py,sha256=Y_2dNeuxRjbyI3ScXNv55lptSe8Hs_ya78L0nPYNZHA,154
55
- ara_cli/commands/extract_command.py,sha256=qpi2_ac3DyxS7FiOz4GsTtRR4xtpegckUmfXzDOwymM,858
57
+ ara_cli/commands/extract_command.py,sha256=CzUOwDembG587PYbxg5rge4XSfdsuTyOPUvkobkXCIs,573
56
58
  ara_cli/commands/load_command.py,sha256=H3CfeHIL-criDU5oi4BONTSpyzJ4m8DzJ0ZCIiAZFeI,2204
57
59
  ara_cli/commands/load_image_command.py,sha256=g9-PXAYdqx5Ed1PdVo-FIb4CyJGEpRFbgQf9Dxg6DmM,886
58
60
  ara_cli/commands/read_command.py,sha256=bo1BvRWuNKdFqBNN1EWORNrX_yuFAOyBruDUolHq1Vc,3791
@@ -140,29 +142,30 @@ ara_cli/templates/specification_breakdown_files/template.step.md,sha256=nzDRl9Xo
140
142
  ara_cli/templates/specification_breakdown_files/template.technology.exploration.md,sha256=zQyiJcmbUfXdte-5uZwZUpT6ey0zwfZ00P4VwI97jQk,2274
141
143
  ara_cli/templates/specification_breakdown_files/template.technology.md,sha256=bySiksz-8xtq0Nnj4svqe2MgUftWrVkbK9AcrDUE3KY,952
142
144
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
143
- tests/test_ara_command_action.py,sha256=JTLqXM9BSMlU33OQgrk_sZnoowFJZKZAx8q-st-wa34,25821
145
+ tests/test_ara_command_action.py,sha256=gLXFAW6K0qCkNKvWvmbshfpJqczvdgBoNGT4rugF7JI,27181
144
146
  tests/test_ara_config.py,sha256=RbVhS0SS1lr_SVopEMT1Fake5a-4rWN8MprgJtgI-FA,15883
145
- tests/test_artefact_autofix.py,sha256=pApZ-N0dW8Ujt-cNLbgvd4bhiIIK8oXb-saLf6QlA-8,25022
147
+ tests/test_artefact_autofix.py,sha256=vcN11CY6XJq97N0php4OW-rPbEMEh_nYFISrg-pN1mE,38332
146
148
  tests/test_artefact_fuzzy_search.py,sha256=5Sh3_l9QK8-WHn6JpGPU1b6h4QEnl2JoMq1Tdp2cj1U,1261
147
149
  tests/test_artefact_link_updater.py,sha256=biqbEp2jCOz8giv72hu2P2hDfeJfJ9OrVGdAv5d9cK4,2191
148
150
  tests/test_artefact_lister.py,sha256=35R13UU-YsX1HOsEN8M2-vIiCUA9RSBm6SwestDaFhE,20388
149
151
  tests/test_artefact_reader.py,sha256=660K-d8ed-j8hulsUB_7baPD2-hhbg9TffUR5yVc4Uo,927
150
152
  tests/test_artefact_renamer.py,sha256=lSnKCCfoFGgKhTdDZrEaeBq1xJAak1QoqH5aSeOe9Ro,3494
151
153
  tests/test_artefact_scan.py,sha256=uNWgrt7ieZ4ogKACsPqzAsh59JF2BhTKSag31hpVrTQ,16887
152
- tests/test_chat.py,sha256=sf4mXmOjXZeaYPNSYXSyfz0b5pZA6Mq7_R3gWjQaJw4,56152
154
+ tests/test_chat.py,sha256=D2HRRTdnvcDvB9TWz4O91ZONbLJL6w4v8TRTDwjtzzI,86104
153
155
  tests/test_classifier.py,sha256=grYGPksydNdPsaEBQxYHZTuTdcJWz7VQtikCKA6BNaQ,1920
154
156
  tests/test_directory_navigator.py,sha256=7G0MVrBbtBvbrFUpL0zb_9EkEWi1dulWuHsrQxMJxDY,140
155
- tests/test_file_classifier.py,sha256=kLWPiePu3F5mkVuI_lK_2QlLh2kXD_Mt2K8KZZ1fAnA,10940
156
- tests/test_file_creator.py,sha256=D3G7MbgE0m8JmZihxnTryxLco6iZdbV--2CGc0L20FM,2109
157
+ tests/test_file_classifier.py,sha256=4O1C_iDpGGm35b7aI-HIJd5kkWxFUOrI2n4lEpiDNTM,11855
158
+ tests/test_file_creator.py,sha256=tgBCq6KPv-qMSDhj9AZvQIJABiAqgpFRnEg1fqbVrTI,2013
157
159
  tests/test_file_lister.py,sha256=Q9HwhKKx540EPzTmfzOCnvtAgON0aMmpJE2eOe1J3EA,4324
158
160
  tests/test_global_file_lister.py,sha256=ycvf2YL8q5QSEMwcnQfUdoWnQQ8xTSyEtccAeXwl6QU,5487
159
161
  tests/test_list_filter.py,sha256=fJA3d_SdaOAUkE7jn68MOVS0THXGghy1fye_64Zvo1U,7964
160
- tests/test_prompt_handler.py,sha256=kW8FU09ho4I5qC-f4G9r4ZgI-NlqdOkTmAazG7FaTrw,32299
162
+ tests/test_prompt_handler.py,sha256=9s1zavcW81uz8wOBM_2X2KqdLNoc3E9bt0Oqt2-Sgmk,33926
161
163
  tests/test_tag_extractor.py,sha256=nSiAYlTKZ7TLAOtcJpwK5zTWHhFYU0tI5xKnivLc1dU,2712
164
+ tests/test_template_loader.py,sha256=R7s8HJZbKqja-1TRBMBkVKPTgajofUjjRKUJq7a3_Oc,7427
162
165
  tests/test_template_manager.py,sha256=qliEeYgAEakn8JIqIHa8u0Ht6DY4L3T6DcHBXkjzR4I,4167
163
166
  tests/test_update_config_prompt.py,sha256=xsqj1WTn4BsG5Q2t-sNPfu7EoMURFcS-hfb5VSXUnJc,6765
164
- ara_cli-0.1.9.95.dist-info/METADATA,sha256=ZVJWS_s9Xmdeftrsy6337QTfO_BVESYERmL5r_2tT3s,6789
165
- ara_cli-0.1.9.95.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
166
- ara_cli-0.1.9.95.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
167
- ara_cli-0.1.9.95.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
168
- ara_cli-0.1.9.95.dist-info/RECORD,,
167
+ ara_cli-0.1.10.0.dist-info/METADATA,sha256=9DcNT8mkoI9D6aDt0gRORWFl_MagH7yTw_ISo4uc_Tw,6813
168
+ ara_cli-0.1.10.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
169
+ ara_cli-0.1.10.0.dist-info/entry_points.txt,sha256=v4h7MzysTgSIDYfEo3oj4Kz_8lzsRa3hq-KJHEcLVX8,45
170
+ ara_cli-0.1.10.0.dist-info/top_level.txt,sha256=WM4cLHT5DYUaWzLtRj-gu3yVNFpGQ6lLRI3FMmC-38I,14
171
+ ara_cli-0.1.10.0.dist-info/RECORD,,
@@ -1,5 +1,6 @@
1
1
  import pytest
2
2
  from unittest.mock import patch, MagicMock, call, mock_open
3
+ from ara_cli.error_handler import AraValidationError
3
4
  from ara_cli.ara_command_action import (
4
5
  check_validity,
5
6
  create_action,
@@ -16,6 +17,12 @@ from ara_cli.ara_command_action import (
16
17
  )
17
18
 
18
19
 
20
+ @pytest.fixture
21
+ def mock_handle_errors():
22
+ with patch("ara_cli.ara_command_action.handle_errors", lambda context: (lambda f: f)):
23
+ yield
24
+
25
+
19
26
  @pytest.fixture
20
27
  def mock_dependencies():
21
28
  with patch(
@@ -28,8 +35,10 @@ def mock_dependencies():
28
35
  "ara_cli.template_manager.SpecificationBreakdownAspects"
29
36
  ) as MockSpecificationBreakdownAspects, patch(
30
37
  "ara_cli.artefact_fuzzy_search.find_closest_rule"
31
- ) as mock_find_closest_rule:
32
- yield MockArtefactCreator, MockClassifier, mock_is_valid_filename, MockSpecificationBreakdownAspects, mock_find_closest_rule
38
+ ) as mock_find_closest_rule, patch(
39
+ "ara_cli.artefact_reader.ArtefactReader"
40
+ ) as MockArtefactReader:
41
+ yield MockArtefactCreator, MockClassifier, mock_is_valid_filename, MockSpecificationBreakdownAspects, mock_find_closest_rule, MockArtefactReader
33
42
 
34
43
 
35
44
  @pytest.fixture
@@ -102,65 +111,70 @@ def mock_suggest_close_name_matches():
102
111
  yield mock_suggest_close_name_matches
103
112
 
104
113
 
105
- @pytest.mark.parametrize(
106
- "condition, error_message",
107
- [(True, "This should not be printed"),
108
- (False, "This is a test error message")],
109
- )
110
- def test_check_validity(condition, error_message):
111
- with patch("sys.exit") as mock_exit, patch("builtins.print") as mock_print:
112
- if condition:
113
- check_validity(condition, error_message)
114
- mock_exit.assert_not_called()
115
- mock_print.assert_not_called()
116
- else:
117
- check_validity(condition, error_message)
118
- mock_exit.assert_called_once_with(1)
119
- mock_print.assert_called_once_with(error_message)
114
+ def test_check_validity_with_true_condition():
115
+ """Test that check_validity does nothing when condition is True."""
116
+ # This should not raise any exception
117
+ check_validity(True, "This should not be printed")
120
118
 
121
119
 
122
- @pytest.mark.parametrize(
123
- "classifier_valid, filename_valid",
124
- [(True, True), (False, True), (True, False), (False, False)],
125
- )
126
- def test_create_action_validity_checks(
127
- mock_dependencies, classifier_valid, filename_valid
128
- ):
129
- (
130
- MockArtefactCreator,
131
- MockClassifier,
132
- mock_is_valid_filename,
133
- MockSpecificationBreakdownAspects,
134
- mock_find_closest_rule,
135
- ) = mock_dependencies
120
+ def test_check_validity_with_false_condition():
121
+ """Test that check_validity raises AraValidationError when condition is False."""
122
+ error_message = "This is a test error message"
123
+
124
+ with pytest.raises(AraValidationError, match=error_message):
125
+ check_validity(False, error_message)
126
+
127
+
128
+ def test_check_validity_error_message_content():
129
+ """Test that the raised exception contains the correct error message."""
130
+ error_message = "Custom validation error"
131
+
132
+ with pytest.raises(AraValidationError) as exc_info:
133
+ check_validity(False, error_message)
134
+
135
+ assert str(exc_info.value) == error_message
136
+
137
+
138
+ def setup_create_args_and_mocks(classifier_valid, filename_valid, mock_dependencies):
139
+ (MockArtefactCreator, MockClassifier, mock_is_valid_filename, _, _, MockArtefactReader) = mock_dependencies
136
140
  MockClassifier.is_valid_classifier.return_value = classifier_valid
137
141
  mock_is_valid_filename.return_value = filename_valid
138
- mock_find_closest_rule.return_value = None
142
+ MockArtefactReader.read_artefact.return_value = (None, None)
139
143
 
140
144
  args = MagicMock()
141
145
  args.classifier = "test_classifier"
142
146
  args.parameter = "test_parameter"
147
+ return args
143
148
 
149
+ # Test for valid classifier and filename
150
+ def test_create_action_with_valid_params(mock_handle_errors, mock_dependencies):
151
+ args = setup_create_args_and_mocks(True, True, mock_dependencies)
144
152
  with patch("ara_cli.ara_command_action.check_validity") as mock_check_validity:
145
- if classifier_valid and filename_valid:
146
- create_action(args)
147
- mock_check_validity.assert_any_call(
148
- True, "Invalid classifier provided. Please provide a valid classifier."
149
- )
150
- mock_check_validity.assert_any_call(
151
- True, "Invalid filename provided. Please provide a valid filename."
152
- )
153
- else:
154
- create_action(args)
155
- if not classifier_valid:
156
- mock_check_validity.assert_any_call(
157
- False,
158
- "Invalid classifier provided. Please provide a valid classifier.",
159
- )
160
- if not filename_valid:
161
- mock_check_validity.assert_any_call(
162
- False, "Invalid filename provided. Please provide a valid filename."
163
- )
153
+ create_action(args)
154
+ mock_check_validity.assert_any_call(True, "Invalid classifier provided. Please provide a valid classifier.")
155
+ mock_check_validity.assert_any_call(True, "Invalid filename provided. Please provide a valid filename.")
156
+
157
+ # Test for invalid classifier
158
+ def test_create_action_with_invalid_classifier(mock_handle_errors, mock_dependencies):
159
+ args = setup_create_args_and_mocks(False, True, mock_dependencies)
160
+ with patch("ara_cli.ara_command_action.check_validity") as mock_check_validity:
161
+ create_action(args)
162
+ mock_check_validity.assert_any_call(False, "Invalid classifier provided. Please provide a valid classifier.")
163
+
164
+ # Test for invalid filename
165
+ def test_create_action_with_invalid_filename(mock_handle_errors, mock_dependencies):
166
+ args = setup_create_args_and_mocks(True, False, mock_dependencies)
167
+ with patch("ara_cli.ara_command_action.check_validity") as mock_check_validity:
168
+ create_action(args)
169
+ mock_check_validity.assert_any_call(False, "Invalid filename provided. Please provide a valid filename.")
170
+
171
+ # Test for both invalid classifier and filename
172
+ def test_create_action_with_invalid_classifier_and_filename(mock_handle_errors, mock_dependencies):
173
+ args = setup_create_args_and_mocks(False, False, mock_dependencies)
174
+ with patch("ara_cli.ara_command_action.check_validity") as mock_check_validity:
175
+ create_action(args)
176
+ mock_check_validity.assert_any_call(False, "Invalid classifier provided. Please provide a valid classifier.")
177
+ mock_check_validity.assert_any_call(False, "Invalid filename provided. Please provide a valid filename.")
164
178
 
165
179
 
166
180
  @pytest.mark.parametrize(
@@ -406,7 +420,7 @@ def test_read_status_action(classifier, artefact_name, artefact_exists, status,
406
420
  # Verify behavior
407
421
  if not artefact_exists:
408
422
  # Should suggest close matches when artefact not found
409
- mock_suggest_close_name_matches.assert_called_once_with(artefact_name, all_artefact_names)
423
+ mock_suggest_close_name_matches.assert_called_once_with(artefact_name, all_artefact_names, report_as_error=True)
410
424
  mock_open.assert_not_called()
411
425
  else:
412
426
  # Should open the file and read content