ara-cli 0.1.9.73__py3-none-any.whl → 0.1.9.75__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 (39) hide show
  1. ara_cli/ara_command_action.py +15 -15
  2. ara_cli/ara_command_parser.py +2 -1
  3. ara_cli/ara_config.py +181 -73
  4. ara_cli/artefact_autofix.py +130 -68
  5. ara_cli/artefact_creator.py +1 -1
  6. ara_cli/artefact_models/artefact_model.py +26 -7
  7. ara_cli/artefact_models/artefact_templates.py +47 -31
  8. ara_cli/artefact_models/businessgoal_artefact_model.py +23 -25
  9. ara_cli/artefact_models/epic_artefact_model.py +23 -24
  10. ara_cli/artefact_models/feature_artefact_model.py +76 -46
  11. ara_cli/artefact_models/keyfeature_artefact_model.py +21 -24
  12. ara_cli/artefact_models/task_artefact_model.py +73 -13
  13. ara_cli/artefact_models/userstory_artefact_model.py +22 -24
  14. ara_cli/artefact_models/vision_artefact_model.py +23 -42
  15. ara_cli/artefact_scan.py +55 -17
  16. ara_cli/chat.py +23 -5
  17. ara_cli/prompt_handler.py +4 -4
  18. ara_cli/tag_extractor.py +43 -28
  19. ara_cli/template_manager.py +3 -8
  20. ara_cli/version.py +1 -1
  21. {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/METADATA +1 -1
  22. {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/RECORD +29 -39
  23. tests/test_ara_config.py +420 -36
  24. tests/test_artefact_autofix.py +289 -25
  25. tests/test_artefact_scan.py +296 -35
  26. tests/test_chat.py +35 -15
  27. ara_cli/templates/template.businessgoal +0 -10
  28. ara_cli/templates/template.capability +0 -10
  29. ara_cli/templates/template.epic +0 -15
  30. ara_cli/templates/template.example +0 -6
  31. ara_cli/templates/template.feature +0 -26
  32. ara_cli/templates/template.issue +0 -14
  33. ara_cli/templates/template.keyfeature +0 -15
  34. ara_cli/templates/template.task +0 -6
  35. ara_cli/templates/template.userstory +0 -17
  36. ara_cli/templates/template.vision +0 -14
  37. {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/WHEEL +0 -0
  38. {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/entry_points.txt +0 -0
  39. {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/top_level.txt +0 -0
@@ -33,17 +33,41 @@ class ActionItem(BaseModel):
33
33
  return v
34
34
 
35
35
  @classmethod
36
- def deserialize(cls, line: str) -> Optional['ActionItem']:
37
- if not line:
36
+ def deserialize(cls, text: str) -> Optional['ActionItem']:
37
+ if not text:
38
38
  return None
39
- match = re.match(r'\[@(to-do|in-progress|done)\]\s+(.*)', line.strip())
39
+
40
+ lines = text.strip().split('\n')
41
+ first_line = lines[0]
42
+
43
+ match = re.match(r'\[@(.*?)\]\s+(.*)', first_line)
40
44
  if not match:
41
45
  return None
42
- status, text = match.groups()
43
- return cls(status=status, text=text)
46
+
47
+ status, first_line_text = match.groups()
48
+
49
+ # Validate the status before creating the ActionItem
50
+ if status not in ["to-do", "in-progress", "done"]:
51
+ raise ValueError(f"invalid status '{status}' in action item. Allowed values are 'to-do', 'in-progress', 'done'")
52
+
53
+ # If there are multiple lines, join them
54
+ if len(lines) > 1:
55
+ all_text = '\n'.join([first_line_text] + lines[1:])
56
+ else:
57
+ all_text = first_line_text
58
+
59
+ return cls(status=status, text=all_text)
44
60
 
45
61
  def serialize(self) -> str:
46
- return f"[@{self.status}] {self.text}"
62
+ lines = self.text.split('\n')
63
+ # First line includes the status marker
64
+ first_line = f"[@{self.status}] {lines[0]}"
65
+ if len(lines) == 1:
66
+ return first_line
67
+
68
+ # Additional lines follow without status marker
69
+ result_lines = [first_line] + lines[1:]
70
+ return '\n'.join(result_lines)
47
71
 
48
72
 
49
73
  class TaskArtefact(Artefact):
@@ -54,13 +78,47 @@ class TaskArtefact(Artefact):
54
78
  def _deserialize_action_items(cls, text) -> Tuple[List[ActionItem], List[str]]:
55
79
  lines = [line.strip() for line in text.strip().splitlines() if line.strip()]
56
80
 
57
- remaining_lines = []
58
81
  action_items = []
59
- for line in lines:
82
+ remaining_lines = []
83
+ i = 0
84
+
85
+ contribution_marker = cls._contribution_starts_with()
86
+ description_marker = cls._description_starts_with()
87
+
88
+ while i < len(lines):
89
+ line = lines[i]
90
+
60
91
  if line.startswith('[@'):
61
- action_items.append(ActionItem.deserialize(line))
62
- continue
63
- remaining_lines.append(line)
92
+ # Collect all lines for this action item
93
+ action_item_lines = [line]
94
+ j = i + 1
95
+ # Collect lines until we hit another action item or a known section
96
+ while j < len(lines):
97
+ next_line = lines[j]
98
+ # Check if next line is a new action item or a known section
99
+ if (next_line.startswith('[@') or
100
+ next_line.startswith(description_marker) or
101
+ next_line.startswith(contribution_marker)):
102
+ break
103
+ action_item_lines.append(next_line)
104
+ j += 1
105
+
106
+ # Join all lines and pass as a single string to deserialize
107
+ action_item_text = '\n'.join(action_item_lines)
108
+ try:
109
+ action_item = ActionItem.deserialize(action_item_text)
110
+ if action_item:
111
+ action_items.append(action_item)
112
+ except ValueError as e:
113
+ # Re-raise with more context about where the error occurred
114
+ raise ValueError(f"Error parsing action item: {e}")
115
+
116
+ # Move index to the next unprocessed line
117
+ i = j
118
+ else:
119
+ remaining_lines.append(line)
120
+ i += 1
121
+
64
122
  return action_items, remaining_lines
65
123
 
66
124
  @classmethod
@@ -82,7 +140,9 @@ class TaskArtefact(Artefact):
82
140
  return ArtefactType.task
83
141
 
84
142
  def _serialize_action_items(self) -> str:
85
- action_item_lines = [action_item.serialize() for action_item in self.action_items]
143
+ action_item_lines = []
144
+ for action_item in self.action_items:
145
+ action_item_lines.append(action_item.serialize())
86
146
  return "\n".join(action_item_lines)
87
147
 
88
148
  def serialize(self) -> str:
@@ -106,4 +166,4 @@ class TaskArtefact(Artefact):
106
166
  lines.append(description)
107
167
  lines.append("")
108
168
 
109
- return "\n".join(lines)
169
+ return "\n".join(lines)
@@ -47,39 +47,37 @@ class UserstoryIntent(Intent):
47
47
 
48
48
  @classmethod
49
49
  def deserialize_from_lines(cls, lines: List[str], start_index: int = 0) -> 'UserstoryIntent':
50
- in_order_to = None
51
- as_a = None
52
- i_want = None
53
-
54
- in_order_to_prefix = "In order to "
55
- as_a_prefix = "As a "
56
- as_a_prefix_alt = "As an "
57
- i_want_prefix = "I want "
50
+ prefixes = [
51
+ ("In order to ", "in_order_to"),
52
+ ("As a ", "as_a"),
53
+ ("As an ", "as_a"),
54
+ ("I want ", "i_want"),
55
+ ]
56
+ found = {"in_order_to": None, "as_a": None, "i_want": None}
57
+
58
+ def match_and_store(line):
59
+ for prefix, field in prefixes:
60
+ if line.startswith(prefix) and found[field] is None:
61
+ found[field] = line[len(prefix):].strip()
62
+ return True
63
+ return False
58
64
 
59
65
  index = start_index
60
- while index < len(lines) and (not in_order_to or not as_a or not i_want):
61
- line = lines[index].strip()
62
- if line.startswith(in_order_to_prefix) and not in_order_to:
63
- in_order_to = line[len(in_order_to_prefix):].strip()
64
- elif line.startswith(as_a_prefix) and not as_a:
65
- as_a = line[len(as_a_prefix):].strip()
66
- elif line.startswith(as_a_prefix_alt) and not as_a:
67
- as_a = line[len(as_a_prefix_alt):].strip()
68
- elif line.startswith(i_want_prefix) and not i_want:
69
- i_want = line[len(i_want_prefix):].strip()
66
+ while index < len(lines) and any(v is None for v in found.values()):
67
+ match_and_store(lines[index].strip())
70
68
  index += 1
71
69
 
72
- if not in_order_to:
70
+ if not found["in_order_to"]:
73
71
  raise ValueError("Could not find 'In order to' line")
74
- if not as_a:
72
+ if not found["as_a"]:
75
73
  raise ValueError("Could not find 'As a' line")
76
- if not i_want:
74
+ if not found["i_want"]:
77
75
  raise ValueError("Could not find 'I want' line")
78
76
 
79
77
  return cls(
80
- in_order_to=in_order_to,
81
- as_a=as_a,
82
- i_want=i_want
78
+ in_order_to=found["in_order_to"],
79
+ as_a=found["as_a"],
80
+ i_want=found["i_want"]
83
81
  )
84
82
 
85
83
 
@@ -76,54 +76,35 @@ class VisionIntent(Intent):
76
76
 
77
77
  @classmethod
78
78
  def deserialize_from_lines(cls, lines: List[str], start_index: int = 0) -> 'VisionIntent':
79
+ prefixes = [
80
+ ("For ", "for_"),
81
+ ("Who ", "who"),
82
+ ("The ", "the"),
83
+ ("That ", "that"),
84
+ ("Unlike ", "unlike"),
85
+ ("Our product ", "our_product"),
86
+ ]
87
+ found = {field: "" for _, field in prefixes}
88
+
89
+ # Find the first "For " line, if it exists
79
90
  intent_start_index = start_index
80
-
81
- for_ = ""
82
- who = ""
83
- the = ""
84
- that = ""
85
- unlike = ""
86
- our_product = ""
87
-
88
- for_prefix = "For "
89
- who_prefix = "Who "
90
- the_prefix = "The "
91
- that_prefix = "That "
92
- unlike_prefix = "Unlike "
93
- our_product_prefix = "Our product "
94
-
95
91
  for i in range(start_index, len(lines)):
96
- if lines[i].startswith(for_prefix):
92
+ if lines[i].startswith("For "):
97
93
  intent_start_index = i
98
94
  break
99
95
 
96
+ def match_and_store(line):
97
+ for prefix, field in prefixes:
98
+ if line.startswith(prefix) and not found[field]:
99
+ found[field] = line[len(prefix):].strip()
100
+ return
101
+
100
102
  index = intent_start_index
101
- if index < len(lines) and lines[index].startswith(for_prefix):
102
- for_ = lines[index][len(for_prefix):]
103
- index = index + 1
104
- if index < len(lines) and lines[index].startswith(who_prefix):
105
- who = lines[index][len(who_prefix):]
106
- index = index + 1
107
- if index < len(lines) and lines[index].startswith(the_prefix):
108
- the = lines[index][len(the_prefix):]
109
- index = index + 1
110
- if index < len(lines) and lines[index].startswith(that_prefix):
111
- that = lines[index][len(that_prefix):]
112
- index = index + 1
113
- if index < len(lines) and lines[index].startswith(unlike_prefix):
114
- unlike = lines[index][len(unlike_prefix):]
115
- index = index + 1
116
- if index < len(lines) and lines[index].startswith(our_product_prefix):
117
- our_product = lines[index][len(our_product_prefix):]
118
-
119
- return cls(
120
- for_=for_,
121
- who=who,
122
- the=the,
123
- that=that,
124
- unlike=unlike,
125
- our_product=our_product,
126
- )
103
+ while index < len(lines) and any(not v for v in found.values()):
104
+ match_and_store(lines[index])
105
+ index += 1
106
+
107
+ return cls(**found)
127
108
 
128
109
 
129
110
  class VisionArtefact(Artefact):
ara_cli/artefact_scan.py CHANGED
@@ -2,9 +2,57 @@ from textwrap import indent
2
2
  import os
3
3
 
4
4
 
5
+ def is_contribution_valid(contribution, classified_artefact_info) -> bool:
6
+ from ara_cli.artefact_fuzzy_search import extract_artefact_names_of_classifier
7
+ if not contribution or not contribution.artefact_name or not contribution.classifier:
8
+ return True
9
+
10
+ all_artefact_names = extract_artefact_names_of_classifier(
11
+ classified_files=classified_artefact_info,
12
+ classifier=contribution.classifier
13
+ )
14
+ if contribution.artefact_name not in all_artefact_names:
15
+ return False
16
+ return True
17
+
18
+
19
+ def is_rule_valid(contribution, classified_artefact_info) -> bool:
20
+ from ara_cli.artefact_reader import ArtefactReader
21
+
22
+ if not contribution or not contribution.artefact_name or not contribution.classifier:
23
+ return True
24
+ rule = contribution.rule
25
+ if not rule:
26
+ return True
27
+ parent = ArtefactReader.read_artefact(contribution.artefact_name, contribution.classifier)
28
+ if not parent or not parent.rules:
29
+ return True
30
+ rules = parent.rules
31
+ if rule not in rules:
32
+ return False
33
+ return True
34
+
35
+
36
+ def check_contribution(contribution, classified_artefact_info, file_path) -> tuple[bool, str]:
37
+ if not contribution:
38
+ return True, None
39
+
40
+ if not is_contribution_valid(contribution, classified_artefact_info):
41
+ reason = (f"Invalid Contribution Reference: The contribution references "
42
+ f"'{contribution.classifier}' artefact '{contribution.artefact_name}' "
43
+ f"which does not exist.")
44
+ return False, reason
45
+
46
+ if not is_rule_valid(contribution, classified_artefact_info):
47
+ reason = (f"Rule Mismatch: The contribution references "
48
+ f"rule '{contribution.rule}' which the parent "
49
+ f"{contribution.classifier} '{contribution.artefact_name}' does not have.")
50
+ return False, reason
51
+ return True, None
52
+
53
+
5
54
  def check_file(file_path, artefact_class, classified_artefact_info=None):
6
55
  from pydantic import ValidationError
7
- from ara_cli.artefact_fuzzy_search import extract_artefact_names_of_classifier
8
56
  from ara_cli.file_classifier import FileClassifier
9
57
 
10
58
  try:
@@ -23,27 +71,17 @@ def check_file(file_path, artefact_class, classified_artefact_info=None):
23
71
  base_name = os.path.basename(file_path)
24
72
  file_name_without_ext, _ = os.path.splitext(base_name)
25
73
 
74
+ # Check title and file name matching
26
75
  if artefact_instance.title != file_name_without_ext:
27
76
  reason = (f"Filename-Title Mismatch: The file name '{file_name_without_ext}' "
28
77
  f"does not match the artefact title '{artefact_instance.title}'.")
29
78
  return False, reason
30
-
31
- # Check contribution reference validity
79
+
32
80
  contribution = artefact_instance.contribution
33
- if contribution and contribution.artefact_name and contribution.classifier:
34
-
35
- # Find all artefact names of the referenced classifier
36
- all_artefact_names = extract_artefact_names_of_classifier(
37
- classified_files=classified_artefact_info,
38
- classifier=contribution.classifier
39
- )
40
-
41
- # Check if the referenced artefact exists
42
- if contribution.artefact_name not in all_artefact_names:
43
- reason = (f"Invalid Contribution Reference: The contribution references "
44
- f"'{contribution.classifier}' artefact '{contribution.artefact_name}' "
45
- f"which does not exist.")
46
- return False, reason
81
+
82
+ contribution_valid, reason = check_contribution(contribution, classified_artefact_info, file_path)
83
+ if not contribution_valid:
84
+ return False, reason
47
85
 
48
86
  return True, None
49
87
  except (ValidationError, ValueError, AssertionError) as e:
ara_cli/chat.py CHANGED
@@ -731,9 +731,27 @@ Start chatting (type 'HELP'/'h' for available commands, 'QUIT'/'q' to exit chat
731
731
  @cmd2.with_category(CATEGORY_CHAT_CONTROL)
732
732
  def do_LOAD_TEMPLATE(self, template_name):
733
733
  """Load artefact template"""
734
- directory = os.path.join(os.path.dirname(__file__), 'templates')
735
- pattern = f"template.{template_name}"
736
- file_type = "template"
737
- exclude_pattern = os.path.join(directory, "template.*.prompt_log.md")
734
+ from ara_cli.artefact_models.artefact_templates import template_artefact_of_type
738
735
 
739
- self._load_helper(directory, pattern, file_type, exclude_pattern)
736
+ artefact = template_artefact_of_type(''.join(template_name))
737
+ if not artefact:
738
+ return
739
+ write_content = artefact.serialize()
740
+ self.add_prompt_tag_if_needed(self.chat_name)
741
+ with open(self.chat_name, 'a', encoding='utf-8') as chat_file:
742
+ chat_file.write(write_content)
743
+ print(f"Loaded {template_name} artefact template")
744
+
745
+ def complete_LOAD_TEMPLATE(self, text, line, begidx, endidx):
746
+ return self._complete_classifiers(self, text, line, begidx, endidx)
747
+
748
+ def _complete_classifiers(self, text, line, begidx, endidx):
749
+ from ara_cli.classifier import Classifier
750
+
751
+ classifiers = Classifier.ordered_classifiers()
752
+ if not text:
753
+ completions = classifiers
754
+ else:
755
+ completions = [classifier for classifier in classifiers if classifier.startswith(text)]
756
+
757
+ return completions
ara_cli/prompt_handler.py CHANGED
@@ -239,7 +239,7 @@ def move_and_copy_files(source_path, prompt_data_path, prompt_archive_path):
239
239
  file_name = os.path.basename(source_path)
240
240
 
241
241
  # Check the name ending and extension of source path
242
- endings = [".commands.md", ".rules.md", ".intention.md"]
242
+ endings = [".blueprint.md", ".commands.md", ".rules.md", ".intention.md"]
243
243
  if any(file_name.endswith(ext) for ext in endings):
244
244
  for ext in endings:
245
245
  if file_name.endswith(ext):
@@ -394,7 +394,7 @@ def create_and_send_custom_prompt(classifier, parameter):
394
394
  prompt_data_path = f"ara/{sub_directory}/{parameter}.data/prompt.data"
395
395
  prompt_file_path_markdown = join(prompt_data_path, f"{classifier}.prompt.md")
396
396
 
397
- extensions = [".rules.md", ".prompt_givens.md", ".intention.md", ".commands.md"]
397
+ extensions = [".blueprint.md", ".rules.md", ".prompt_givens.md", ".intention.md", ".commands.md"]
398
398
  combined_content_markdown, image_data_list = collect_file_content_by_extension(prompt_data_path, extensions)
399
399
 
400
400
  with open(prompt_file_path_markdown, 'w', encoding='utf-8') as file:
@@ -430,7 +430,7 @@ def generate_config_prompt_template_file(prompt_data_path, config_prompt_templat
430
430
  config = ConfigManager.get_config()
431
431
  global_prompt_template_path = TemplatePathManager.get_template_base_path()
432
432
  dir_list = ["ara/.araconfig/custom-prompt-modules"] + [f"{os.path.join(global_prompt_template_path,'prompt-modules')}"]
433
- file_list = ['*.rules.md','*.intention.md', '*.commands.md']
433
+ file_list = ['*.blueprint.md','*.rules.md','*.intention.md', '*.commands.md']
434
434
 
435
435
  print(f"used {dir_list} for prompt templates file listing")
436
436
  generate_markdown_listing(dir_list, file_list, config_prompt_templates_path)
@@ -439,7 +439,7 @@ def generate_config_prompt_template_file(prompt_data_path, config_prompt_templat
439
439
  def generate_config_prompt_givens_file(prompt_data_path, config_prompt_givens_name, artefact_to_mark=None):
440
440
  config_prompt_givens_path = os.path.join(prompt_data_path, config_prompt_givens_name)
441
441
  config = ConfigManager.get_config()
442
- dir_list = ["ara"] + [item for ext in config.ext_code_dirs for key, item in ext.items()] + [config.doc_dir] + [config.glossary_dir]
442
+ dir_list = ["ara"] + [ext.source_dir for ext in config.ext_code_dirs] + [config.doc_dir] + [config.glossary_dir]
443
443
 
444
444
  print(f"used {dir_list} for prompt givens file listing")
445
445
  generate_markdown_listing(dir_list, config.ara_prompt_given_list_includes, config_prompt_givens_path)
ara_cli/tag_extractor.py CHANGED
@@ -7,6 +7,47 @@ class TagExtractor:
7
7
  def __init__(self, file_system=None):
8
8
  self.file_system = file_system or os
9
9
 
10
+ def filter_column(self, tags_set, filtered_artefacts):
11
+ status_tags = {"to-do", "in-progress", "review", "done", "closed"}
12
+
13
+ artefacts_to_process = self._get_artefacts_without_status_tags(filtered_artefacts, status_tags)
14
+ self._add_non_status_tags_to_set(tags_set, artefacts_to_process, status_tags)
15
+
16
+ def _get_artefacts_without_status_tags(self, filtered_artefacts, status_tags):
17
+ artefacts_to_process = []
18
+ for artefact_list in filtered_artefacts.values():
19
+ for artefact in artefact_list:
20
+ tag_set = self._get_tag_set(artefact)
21
+ if not tag_set & status_tags:
22
+ artefacts_to_process.append(artefact)
23
+ return artefacts_to_process
24
+
25
+ def _get_tag_set(self, artefact):
26
+ tags = artefact.tags + [artefact.status] if artefact.status else artefact.tags
27
+ return set(tag for tag in tags if tag is not None)
28
+
29
+ def _add_non_status_tags_to_set(self, tags_set, artefacts, status_tags):
30
+ for artefact in artefacts:
31
+ tags = [tag for tag in (artefact.tags + [artefact.status]) if tag is not None]
32
+ for tag in tags:
33
+ if self._is_skipped_tag(tag, status_tags):
34
+ continue
35
+ tags_set.add(tag)
36
+
37
+ def _is_skipped_tag(self, tag, status_tags):
38
+ return (
39
+ tag in status_tags
40
+ or tag.startswith("priority_")
41
+ or tag.startswith("user_")
42
+ )
43
+
44
+ def add_to_tags_set(self, tags_set, filtered_artefacts):
45
+ for artefact_list in filtered_artefacts.values():
46
+ for artefact in artefact_list:
47
+ user_tags = [f"user_{tag}" for tag in artefact.users]
48
+ tags = [tag for tag in (artefact.tags + [artefact.status] + user_tags) if tag is not None]
49
+ tags_set.update(tags)
50
+
10
51
  def extract_tags(
11
52
  self,
12
53
  navigate_to_target=False,
@@ -33,35 +74,9 @@ class TagExtractor:
33
74
  unique_tags = set()
34
75
 
35
76
  if filtered_extra_column:
36
- status_tags = {"to-do", "in-progress", "review", "done", "closed"}
37
- artefacts_to_process = []
38
-
39
- for artefact_list in filtered_artefacts.values():
40
- for artefact in artefact_list:
41
- tags = artefact.tags + \
42
- [artefact.status] if artefact.status else artefact.tags
43
- tag_set = set(tag for tag in tags if tag is not None)
44
- if not tag_set & status_tags:
45
- artefacts_to_process.append(artefact)
46
-
47
- for artefact in artefacts_to_process:
48
- tags = [tag for tag in (
49
- artefact.tags + [artefact.status]) if tag is not None]
50
- for tag in tags:
51
- if (
52
- tag in status_tags
53
- or tag.startswith("priority_")
54
- or tag.startswith("user_")
55
- ):
56
- continue
57
- unique_tags.add(tag)
58
-
77
+ self.filter_column(unique_tags, filtered_artefacts)
59
78
  else:
60
- for artefact_list in filtered_artefacts.values():
61
- for artefact in artefact_list:
62
- user_tags = [f"user_{tag}" for tag in artefact.users]
63
- tags = [tag for tag in (artefact.tags + [artefact.status] + user_tags) if tag is not None]
64
- unique_tags.update(tags)
79
+ self.add_to_tags_set(unique_tags, filtered_artefacts)
65
80
 
66
81
  sorted_tags = sorted(unique_tags)
67
82
  return sorted_tags
@@ -4,6 +4,7 @@ from pathlib import Path
4
4
  from shutil import copy
5
5
  from ara_cli.classifier import Classifier
6
6
  from ara_cli.directory_navigator import DirectoryNavigator
7
+ from ara_cli.artefact_models.artefact_templates import template_artefact_of_type
7
8
 
8
9
 
9
10
  class TemplatePathManager:
@@ -30,14 +31,8 @@ class TemplatePathManager:
30
31
  ]
31
32
 
32
33
  def get_template_content(self, classifier):
33
- base_path = self.get_template_base_path()
34
-
35
- template_path = (base_path / f"template.{classifier}")
36
-
37
- with template_path.open('r', encoding='utf-8') as file:
38
- content = file.read()
39
-
40
- return content
34
+ artefact = template_artefact_of_type(classifier)
35
+ return artefact.serialize()
41
36
 
42
37
 
43
38
  class ArtefactFileManager:
ara_cli/version.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # version.py
2
- __version__ = "0.1.9.73" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
2
+ __version__ = "0.1.9.75" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ara_cli
3
- Version: 0.1.9.73
3
+ Version: 0.1.9.75
4
4
  Requires-Dist: litellm
5
5
  Requires-Dist: llama-index
6
6
  Requires-Dist: llama-index-llms-openai