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.
- ara_cli/ara_command_action.py +15 -15
- ara_cli/ara_command_parser.py +2 -1
- ara_cli/ara_config.py +181 -73
- ara_cli/artefact_autofix.py +130 -68
- ara_cli/artefact_creator.py +1 -1
- ara_cli/artefact_models/artefact_model.py +26 -7
- ara_cli/artefact_models/artefact_templates.py +47 -31
- ara_cli/artefact_models/businessgoal_artefact_model.py +23 -25
- ara_cli/artefact_models/epic_artefact_model.py +23 -24
- ara_cli/artefact_models/feature_artefact_model.py +76 -46
- ara_cli/artefact_models/keyfeature_artefact_model.py +21 -24
- ara_cli/artefact_models/task_artefact_model.py +73 -13
- ara_cli/artefact_models/userstory_artefact_model.py +22 -24
- ara_cli/artefact_models/vision_artefact_model.py +23 -42
- ara_cli/artefact_scan.py +55 -17
- ara_cli/chat.py +23 -5
- ara_cli/prompt_handler.py +4 -4
- ara_cli/tag_extractor.py +43 -28
- ara_cli/template_manager.py +3 -8
- ara_cli/version.py +1 -1
- {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/METADATA +1 -1
- {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/RECORD +29 -39
- tests/test_ara_config.py +420 -36
- tests/test_artefact_autofix.py +289 -25
- tests/test_artefact_scan.py +296 -35
- tests/test_chat.py +35 -15
- ara_cli/templates/template.businessgoal +0 -10
- ara_cli/templates/template.capability +0 -10
- ara_cli/templates/template.epic +0 -15
- ara_cli/templates/template.example +0 -6
- ara_cli/templates/template.feature +0 -26
- ara_cli/templates/template.issue +0 -14
- ara_cli/templates/template.keyfeature +0 -15
- ara_cli/templates/template.task +0 -6
- ara_cli/templates/template.userstory +0 -17
- ara_cli/templates/template.vision +0 -14
- {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.9.73.dist-info → ara_cli-0.1.9.75.dist-info}/entry_points.txt +0 -0
- {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,
|
|
37
|
-
if not
|
|
36
|
+
def deserialize(cls, text: str) -> Optional['ActionItem']:
|
|
37
|
+
if not text:
|
|
38
38
|
return None
|
|
39
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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 = [
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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 (
|
|
61
|
-
|
|
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(
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
index
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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"] + [
|
|
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
|
-
|
|
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
|
-
|
|
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
|
ara_cli/template_manager.py
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
2
|
+
__version__ = "0.1.9.75" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
|