ara-cli 0.1.9.69__py3-none-any.whl → 0.1.10.8__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 (150) hide show
  1. ara_cli/__init__.py +18 -2
  2. ara_cli/__main__.py +248 -62
  3. ara_cli/ara_command_action.py +155 -86
  4. ara_cli/ara_config.py +226 -80
  5. ara_cli/ara_subcommands/__init__.py +0 -0
  6. ara_cli/ara_subcommands/autofix.py +26 -0
  7. ara_cli/ara_subcommands/chat.py +27 -0
  8. ara_cli/ara_subcommands/classifier_directory.py +16 -0
  9. ara_cli/ara_subcommands/common.py +100 -0
  10. ara_cli/ara_subcommands/create.py +75 -0
  11. ara_cli/ara_subcommands/delete.py +22 -0
  12. ara_cli/ara_subcommands/extract.py +22 -0
  13. ara_cli/ara_subcommands/fetch_templates.py +14 -0
  14. ara_cli/ara_subcommands/list.py +65 -0
  15. ara_cli/ara_subcommands/list_tags.py +25 -0
  16. ara_cli/ara_subcommands/load.py +48 -0
  17. ara_cli/ara_subcommands/prompt.py +136 -0
  18. ara_cli/ara_subcommands/read.py +47 -0
  19. ara_cli/ara_subcommands/read_status.py +20 -0
  20. ara_cli/ara_subcommands/read_user.py +20 -0
  21. ara_cli/ara_subcommands/reconnect.py +27 -0
  22. ara_cli/ara_subcommands/rename.py +22 -0
  23. ara_cli/ara_subcommands/scan.py +14 -0
  24. ara_cli/ara_subcommands/set_status.py +22 -0
  25. ara_cli/ara_subcommands/set_user.py +22 -0
  26. ara_cli/ara_subcommands/template.py +16 -0
  27. ara_cli/artefact_autofix.py +649 -68
  28. ara_cli/artefact_creator.py +8 -11
  29. ara_cli/artefact_deleter.py +2 -4
  30. ara_cli/artefact_fuzzy_search.py +22 -10
  31. ara_cli/artefact_link_updater.py +4 -4
  32. ara_cli/artefact_lister.py +29 -55
  33. ara_cli/artefact_models/artefact_data_retrieval.py +23 -0
  34. ara_cli/artefact_models/artefact_load.py +11 -3
  35. ara_cli/artefact_models/artefact_model.py +146 -39
  36. ara_cli/artefact_models/artefact_templates.py +70 -44
  37. ara_cli/artefact_models/businessgoal_artefact_model.py +23 -25
  38. ara_cli/artefact_models/epic_artefact_model.py +34 -26
  39. ara_cli/artefact_models/feature_artefact_model.py +203 -64
  40. ara_cli/artefact_models/keyfeature_artefact_model.py +21 -24
  41. ara_cli/artefact_models/serialize_helper.py +1 -1
  42. ara_cli/artefact_models/task_artefact_model.py +83 -15
  43. ara_cli/artefact_models/userstory_artefact_model.py +37 -27
  44. ara_cli/artefact_models/vision_artefact_model.py +23 -42
  45. ara_cli/artefact_reader.py +92 -91
  46. ara_cli/artefact_renamer.py +8 -4
  47. ara_cli/artefact_scan.py +66 -3
  48. ara_cli/chat.py +622 -162
  49. ara_cli/chat_agent/__init__.py +0 -0
  50. ara_cli/chat_agent/agent_communicator.py +62 -0
  51. ara_cli/chat_agent/agent_process_manager.py +211 -0
  52. ara_cli/chat_agent/agent_status_manager.py +73 -0
  53. ara_cli/chat_agent/agent_workspace_manager.py +76 -0
  54. ara_cli/commands/__init__.py +0 -0
  55. ara_cli/commands/command.py +7 -0
  56. ara_cli/commands/extract_command.py +15 -0
  57. ara_cli/commands/load_command.py +65 -0
  58. ara_cli/commands/load_image_command.py +34 -0
  59. ara_cli/commands/read_command.py +117 -0
  60. ara_cli/completers.py +144 -0
  61. ara_cli/directory_navigator.py +37 -4
  62. ara_cli/error_handler.py +134 -0
  63. ara_cli/file_classifier.py +6 -5
  64. ara_cli/file_lister.py +1 -1
  65. ara_cli/file_loaders/__init__.py +0 -0
  66. ara_cli/file_loaders/binary_file_loader.py +33 -0
  67. ara_cli/file_loaders/document_file_loader.py +34 -0
  68. ara_cli/file_loaders/document_reader.py +245 -0
  69. ara_cli/file_loaders/document_readers.py +233 -0
  70. ara_cli/file_loaders/file_loader.py +50 -0
  71. ara_cli/file_loaders/file_loaders.py +123 -0
  72. ara_cli/file_loaders/image_processor.py +89 -0
  73. ara_cli/file_loaders/markdown_reader.py +75 -0
  74. ara_cli/file_loaders/text_file_loader.py +187 -0
  75. ara_cli/global_file_lister.py +51 -0
  76. ara_cli/list_filter.py +1 -1
  77. ara_cli/output_suppressor.py +1 -1
  78. ara_cli/prompt_extractor.py +215 -88
  79. ara_cli/prompt_handler.py +521 -134
  80. ara_cli/prompt_rag.py +2 -2
  81. ara_cli/tag_extractor.py +83 -38
  82. ara_cli/template_loader.py +245 -0
  83. ara_cli/template_manager.py +18 -13
  84. ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
  85. ara_cli/templates/prompt-modules/commands/extract_general.commands.md +12 -0
  86. ara_cli/templates/prompt-modules/commands/extract_markdown.commands.md +11 -0
  87. ara_cli/templates/prompt-modules/commands/extract_python.commands.md +13 -0
  88. ara_cli/templates/prompt-modules/commands/feature_add_or_modifiy_specified_behavior.commands.md +36 -0
  89. ara_cli/templates/prompt-modules/commands/feature_generate_initial_specified_bevahior.commands.md +53 -0
  90. ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
  91. ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
  92. ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
  93. ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
  94. ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
  95. ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
  96. ara_cli/update_config_prompt.py +9 -3
  97. ara_cli/version.py +1 -1
  98. ara_cli-0.1.10.8.dist-info/METADATA +241 -0
  99. ara_cli-0.1.10.8.dist-info/RECORD +193 -0
  100. tests/test_ara_command_action.py +73 -59
  101. tests/test_ara_config.py +341 -36
  102. tests/test_artefact_autofix.py +1060 -0
  103. tests/test_artefact_link_updater.py +3 -3
  104. tests/test_artefact_lister.py +52 -132
  105. tests/test_artefact_renamer.py +2 -2
  106. tests/test_artefact_scan.py +327 -33
  107. tests/test_chat.py +2063 -498
  108. tests/test_file_classifier.py +24 -1
  109. tests/test_file_creator.py +3 -5
  110. tests/test_file_lister.py +1 -1
  111. tests/test_global_file_lister.py +131 -0
  112. tests/test_list_filter.py +2 -2
  113. tests/test_prompt_handler.py +746 -0
  114. tests/test_tag_extractor.py +19 -13
  115. tests/test_template_loader.py +192 -0
  116. tests/test_template_manager.py +5 -4
  117. tests/test_update_config_prompt.py +2 -2
  118. ara_cli/ara_command_parser.py +0 -327
  119. ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
  120. ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
  121. ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
  122. ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
  123. ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
  124. ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
  125. ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
  126. ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
  127. ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
  128. ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
  129. ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
  130. ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
  131. ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
  132. ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
  133. ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
  134. ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
  135. ara_cli/templates/template.businessgoal +0 -10
  136. ara_cli/templates/template.capability +0 -10
  137. ara_cli/templates/template.epic +0 -15
  138. ara_cli/templates/template.example +0 -6
  139. ara_cli/templates/template.feature +0 -26
  140. ara_cli/templates/template.issue +0 -14
  141. ara_cli/templates/template.keyfeature +0 -15
  142. ara_cli/templates/template.task +0 -6
  143. ara_cli/templates/template.userstory +0 -17
  144. ara_cli/templates/template.vision +0 -14
  145. ara_cli-0.1.9.69.dist-info/METADATA +0 -16
  146. ara_cli-0.1.9.69.dist-info/RECORD +0 -158
  147. tests/test_ara_autofix.py +0 -219
  148. {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/WHEEL +0 -0
  149. {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/entry_points.txt +0 -0
  150. {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/top_level.txt +0 -0
@@ -8,10 +8,11 @@ from ara_cli.list_filter import ListFilter
8
8
  def artefact():
9
9
  """Fixture to create a mock artefact object."""
10
10
  class Artefact:
11
- def __init__(self, tags, status, users, path="dummy.md", content=""):
11
+ def __init__(self, tags, status, users, author="creator_unknown", path="dummy.md", content=""):
12
12
  self.tags = tags
13
13
  self.status = status
14
14
  self.users = users
15
+ self.author = author
15
16
  self.path = path
16
17
  self.content = content
17
18
  return Artefact
@@ -21,33 +22,33 @@ def artefact():
21
22
  (
22
23
  False, False, None,
23
24
  {'artefacts': [
24
- (['tag1', 'tag2'], 'in-progress', ['user1']),
25
- (['tag3'], 'done', ['user2'])
25
+ (['tag1', 'tag2'], 'in-progress', ['user1'], "creator_unknown"),
26
+ (['tag3'], 'done', ['user2'], "creator_unknown")
26
27
  ]},
27
- ['done', 'in-progress', 'tag1', 'tag2', 'tag3', 'user_user1', 'user_user2']
28
+ ['creator_unknown', 'done', 'in-progress', 'tag1', 'tag2', 'tag3', 'user_user1', 'user_user2']
28
29
  ),
29
30
  (
30
31
  False, True, None,
31
32
  {'artefacts': [
32
- (['project_a', 'priority_high'], None, ['user1']),
33
- (['feature_x'], 'done', ['user2'])
33
+ (['project_a', 'priority_high'], None, ['user1'], "creator_unknown"),
34
+ (['feature_x'], 'done', ['user2'], "creator_unknown")
34
35
  ]},
35
36
  ['project_a']
36
37
  ),
37
38
  (
38
- False, False, ListFilter(include_tags=['@kritik']),
39
+ False, False, ListFilter(include_tags=['kritik']),
39
40
  {'artefacts': [
40
- (['release', 'kritik'], 'review', ['dev1']),
41
- (['bugfix'], 'to-do', ['dev2'])
41
+ (['release', 'kritik'], 'review', ['dev1'], "creator_unknown"),
42
+ (['bugfix'], 'to-do', ['dev2'], "creator_unknown")
42
43
  ]},
43
- ['kritik', 'release', 'review', 'user_dev1']
44
+ ['creator_unknown', 'kritik', 'release', 'review', 'user_dev1']
44
45
  ),
45
46
  (
46
47
  True, False, None,
47
48
  {'artefacts': [
48
- (['tag3'], 'status2', ['user3'])
49
+ (['tag3'], 'status2', ['user3'], "creator_unknown")
49
50
  ]},
50
- ['status2', 'tag3', 'user_user3']
51
+ ['creator_unknown', 'status2', 'tag3', 'user_user3']
51
52
  ),
52
53
  (
53
54
  False, False, None,
@@ -80,4 +81,9 @@ def test_extract_tags(mock_directory_navigator, mock_artefact_reader, artefact,
80
81
 
81
82
  mock_artefact_reader.read_artefacts.assert_called_once()
82
83
 
83
- assert sorted(result) == sorted(expected_tags)
84
+ # Convert dictionary result to flat list for comparison
85
+ actual_tags = []
86
+ for group in result.values():
87
+ actual_tags.extend(group)
88
+
89
+ assert sorted(actual_tags) == sorted(expected_tags)
@@ -0,0 +1,192 @@
1
+ import os
2
+ import pytest
3
+ from unittest.mock import MagicMock, patch, mock_open
4
+ from ara_cli.template_loader import TemplateLoader
5
+
6
+
7
+ @pytest.fixture
8
+ def mock_chat_instance():
9
+ """Fixture for a mocked chat instance."""
10
+ mock = MagicMock()
11
+ mock.choose_file_to_load.return_value = "chosen_file.md"
12
+ mock.load_file.return_value = True
13
+ return mock
14
+
15
+
16
+ @pytest.fixture
17
+ def template_loader_cli():
18
+ """Fixture for a TemplateLoader in CLI mode."""
19
+ return TemplateLoader()
20
+
21
+
22
+ @pytest.fixture
23
+ def template_loader_chat(mock_chat_instance):
24
+ """Fixture for a TemplateLoader in chat mode."""
25
+ return TemplateLoader(chat_instance=mock_chat_instance)
26
+
27
+
28
+ def test_init(mock_chat_instance):
29
+ """Test the constructor."""
30
+ loader_cli = TemplateLoader()
31
+ assert loader_cli.chat_instance is None
32
+
33
+ loader_chat = TemplateLoader(chat_instance=mock_chat_instance)
34
+ assert loader_chat.chat_instance == mock_chat_instance
35
+
36
+
37
+ @pytest.mark.parametrize("template_name, default_pattern, expected_method_to_call", [
38
+ ("", "*.rules.md", "load_template_from_prompt_data"),
39
+ ("my_rule", "*.rules.md", "load_template_from_global_or_local"),
40
+ ])
41
+ def test_load_template_routing(template_loader_cli, template_name, default_pattern, expected_method_to_call):
42
+ """Test that load_template calls the correct downstream method based on inputs."""
43
+ with patch.object(TemplateLoader, 'load_template_from_prompt_data') as mock_from_prompt, \
44
+ patch.object(TemplateLoader, 'load_template_from_global_or_local') as mock_from_global_local:
45
+
46
+ template_loader_cli.load_template(
47
+ template_name, "rules", "chat.md", default_pattern)
48
+
49
+ if expected_method_to_call == "load_template_from_prompt_data":
50
+ mock_from_prompt.assert_called_once()
51
+ mock_from_global_local.assert_not_called()
52
+ else:
53
+ mock_from_prompt.assert_not_called()
54
+ mock_from_global_local.assert_called_once()
55
+
56
+
57
+ def test_load_template_no_name_no_pattern(template_loader_cli, capsys):
58
+ """Test load_template fails gracefully when no name or pattern is given."""
59
+ result = template_loader_cli.load_template("", "blueprint", "chat.md", None)
60
+ assert result is False
61
+ captured = capsys.readouterr()
62
+ assert "A template name is required for template type 'blueprint'" in captured.out
63
+
64
+
65
+ @pytest.mark.parametrize("template_type, expected_plural", [
66
+ ("rules", "rules"),
67
+ ("commands", "commands"),
68
+ ("intention", "intentions"),
69
+ ("blueprint", "blueprints"),
70
+ ("custom", "customs"),
71
+ ])
72
+ def test_get_plural_template_type(template_loader_cli, template_type, expected_plural):
73
+ """Test the pluralization of template types."""
74
+ assert template_loader_cli.get_plural_template_type(
75
+ template_type) == expected_plural
76
+
77
+
78
+ @pytest.mark.parametrize("template_name, expected_method_to_call", [
79
+ ("global/my_rule", "_load_global_template"),
80
+ ("my_rule", "_load_local_template"),
81
+ ])
82
+ def test_load_template_from_global_or_local_routing(template_loader_cli, template_name, expected_method_to_call):
83
+ """Test routing between global and local template loading."""
84
+ with patch.object(TemplateLoader, '_load_global_template') as mock_global, \
85
+ patch.object(TemplateLoader, '_load_local_template') as mock_local:
86
+
87
+ template_loader_cli.load_template_from_global_or_local(
88
+ template_name, "rules", "chat.md")
89
+
90
+ if expected_method_to_call == "_load_global_template":
91
+ mock_global.assert_called_once()
92
+ mock_local.assert_not_called()
93
+ else:
94
+ mock_global.assert_not_called()
95
+ mock_local.assert_called_once()
96
+
97
+
98
+ @pytest.mark.parametrize("files, pattern, user_input, expected_return", [
99
+ (["one.md"], "*.md", "", "one.md"),
100
+ ([], "*.md", "", None),
101
+ (["a.md", "b.md"], "*", "1", "a.md"),
102
+ (["a.md", "b.md"], "*", "2", "b.md"),
103
+ (["a.md", "b.md"], "*", "3", None),
104
+ (["a.md", "b.md"], "*", "invalid", None),
105
+ ])
106
+ def test_choose_file_for_cli(template_loader_cli, files, pattern, user_input, expected_return):
107
+ """Test the interactive file selection for the CLI."""
108
+ with patch('builtins.input', return_value=user_input):
109
+ result = template_loader_cli._choose_file_for_cli(files, pattern)
110
+ assert result == expected_return
111
+
112
+
113
+ def test_load_file_to_chat_cli_context(tmp_path):
114
+ """Test writing template content to a chat file in a CLI context."""
115
+ chat_file = tmp_path / "chat.md"
116
+ chat_file.write_text("# ara prompt:\n")
117
+ template_file = tmp_path / "template.md"
118
+ template_content = "This is the template content."
119
+ template_file.write_text(template_content)
120
+
121
+ loader = TemplateLoader()
122
+ result = loader._load_file_to_chat(
123
+ str(template_file), "rules", str(chat_file))
124
+
125
+ assert result is True
126
+ final_content = chat_file.read_text()
127
+ expected_content = f"# ara prompt:\n\n{template_content}\n"
128
+ assert final_content == expected_content
129
+
130
+
131
+ def test_load_file_to_chat_chat_context(template_loader_chat, mock_chat_instance):
132
+ """Test delegating file loading to the chat instance."""
133
+ result = template_loader_chat._load_file_to_chat(
134
+ "file.md", "rules", "chat.md")
135
+
136
+ assert result is True
137
+ mock_chat_instance.add_prompt_tag_if_needed.assert_called_once_with(
138
+ "chat.md")
139
+ mock_chat_instance.load_file.assert_called_once_with("file.md")
140
+
141
+
142
+ def test_find_project_root(tmp_path):
143
+ """Test finding the project root directory."""
144
+ project_root = tmp_path / "project"
145
+ ara_dir = project_root / "ara"
146
+ nested_dir = project_root / "src" / "component"
147
+ ara_dir.mkdir(parents=True)
148
+ nested_dir.mkdir(parents=True)
149
+
150
+ no_ara_dir = tmp_path / "other"
151
+ no_ara_dir.mkdir()
152
+
153
+ loader = TemplateLoader()
154
+
155
+ # Test finding the root from a nested directory
156
+ assert loader._find_project_root(str(nested_dir)) == str(project_root)
157
+ # Test finding the root from the root itself
158
+ assert loader._find_project_root(str(project_root)) == str(project_root)
159
+ # Test not finding the root
160
+ assert loader._find_project_root(str(no_ara_dir)) is None
161
+
162
+
163
+ @patch('ara_cli.template_loader.TemplatePathManager')
164
+ @patch('ara_cli.template_loader.ConfigManager')
165
+ def test_get_available_templates(MockConfigManager, MockTemplatePathManager, tmp_path):
166
+ """Test the discovery of global and local templates."""
167
+ # Setup mock paths and config
168
+ project_root = tmp_path / "project"
169
+ ara_dir = project_root / "ara"
170
+ araconfig_dir = project_root / ".araconfig"
171
+ custom_modules_dir = araconfig_dir / "custom-prompt-modules" / "rules"
172
+ global_modules_dir = tmp_path / "global_templates" / "prompt-modules" / "rules"
173
+
174
+ for d in [ara_dir, custom_modules_dir, global_modules_dir]:
175
+ d.mkdir(parents=True)
176
+
177
+ (custom_modules_dir / "local_rule.md").touch()
178
+ (global_modules_dir / "global_rule.md").touch()
179
+
180
+ mock_config = MagicMock()
181
+ mock_config.local_prompt_templates_dir = ".araconfig"
182
+ mock_config.custom_prompt_templates_subdir = "custom-prompt-modules"
183
+ MockConfigManager.get_config.return_value = mock_config
184
+ MockTemplatePathManager.get_template_base_path.return_value = str(
185
+ tmp_path / "global_templates")
186
+
187
+ loader = TemplateLoader()
188
+ templates = loader.get_available_templates(
189
+ "rules", context_path=str(project_root))
190
+
191
+ assert sorted(templates) == sorted(
192
+ ["global/global_rule.md", "local_rule.md"])
@@ -9,10 +9,11 @@ import os
9
9
 
10
10
  @pytest.fixture(autouse=True)
11
11
  def navigate_to_ara_directory():
12
- navigator = DirectoryNavigator("ara")
13
- original_directory = navigator.navigate_to_target()
14
- yield
15
- os.chdir(original_directory)
12
+ with patch('builtins.input', return_value='y'):
13
+ navigator = DirectoryNavigator("ara")
14
+ original_directory = navigator.navigate_to_target()
15
+ yield
16
+ os.chdir(original_directory)
16
17
 
17
18
 
18
19
  @pytest.mark.parametrize(
@@ -34,13 +34,13 @@ mock_content_updated = """
34
34
  def test_read_file():
35
35
  with patch('builtins.open', mock_open(read_data="data")) as mocked_file:
36
36
  result = read_file('dummy_path')
37
- mocked_file.assert_called_once_with('dummy_path', 'r')
37
+ mocked_file.assert_called_once_with('dummy_path', 'r', encoding='utf-8')
38
38
  assert result == "data"
39
39
 
40
40
  def test_write_file():
41
41
  with patch('builtins.open', mock_open()) as mocked_file:
42
42
  write_file('dummy_path', 'data')
43
- mocked_file.assert_called_once_with('dummy_path', 'w')
43
+ mocked_file.assert_called_once_with('dummy_path', 'w', encoding='utf-8')
44
44
  mocked_file().write.assert_called_once_with('data')
45
45
 
46
46
  def test_find_checked_items():
@@ -1,327 +0,0 @@
1
- import argparse
2
- from ara_cli.classifier import Classifier
3
- from ara_cli.commandline_completer import ArtefactCompleter, ParentNameCompleter, StatusCompleter
4
- from ara_cli.template_manager import SpecificationBreakdownAspects
5
-
6
-
7
- classifiers = Classifier.ordered_classifiers()
8
- aspects = SpecificationBreakdownAspects.VALID_ASPECTS
9
-
10
-
11
- def create_parser(subparsers):
12
- create_parser = subparsers.add_parser("create", help="Create a classified artefact with data directory")
13
- create_parser.add_argument("classifier", choices=classifiers, help="Classifier that also serves as file extension for the artefact file to be created. Valid Classifiers are: businessgoal, capability, keyfeature, feature, epic, userstory, example, task")
14
- create_parser.add_argument("parameter", help="Artefact name that serves as filename").completer = ArtefactCompleter()
15
-
16
- option_parser = create_parser.add_subparsers(dest="option")
17
-
18
- contribution_parser = option_parser.add_parser("contributes-to")
19
- contribution_parser.add_argument("parent_classifier", choices=classifiers, help="Classifier of the parent")
20
- contribution_parser.add_argument("parent_name", help="Name of a parent artefact").completer = ParentNameCompleter()
21
- contribution_parser.add_argument("-r", "--rule", dest="rule", action="store")
22
-
23
- aspect_parser = option_parser.add_parser("aspect")
24
- aspect_parser.add_argument("aspect", choices=aspects, help="Adds additional specification breakdown aspects in data directory.")
25
-
26
-
27
- def delete_parser(subparsers):
28
- delete_parser = subparsers.add_parser("delete", help="Delete an artefact file including its data directory")
29
- delete_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact to be deleted")
30
- delete_parser.add_argument("parameter", help="Filename of artefact").completer = ArtefactCompleter()
31
- delete_parser.add_argument("-f", "--force", dest="force", action="store_true", help="ignore nonexistent files and arguments, never prompt")
32
-
33
-
34
- def rename_parser(subparsers):
35
- rename_parser = subparsers.add_parser("rename", help="Rename a classified artefact and its data directory")
36
- rename_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact")
37
- rename_parser.add_argument("parameter", help="Filename of artefact").completer = ArtefactCompleter()
38
- rename_parser.add_argument("aspect", help="New artefact name and new data directory name")
39
-
40
-
41
- def add_filter_flags(parser):
42
- parser.add_argument(
43
- "-I",
44
- "--include-content",
45
- nargs="+",
46
- default=None,
47
- help="filter for files which include given content"
48
- )
49
- parser.add_argument(
50
- "-E",
51
- "--exclude-content",
52
- nargs="+",
53
- default=None,
54
- help="filter for files which do not include given content"
55
- )
56
-
57
- parser.add_argument(
58
- "--include-tags",
59
- nargs="+",
60
- default=None,
61
- help="filter for files which include given tags"
62
- )
63
- parser.add_argument(
64
- "--exclude-tags",
65
- nargs="+",
66
- default=None,
67
- help="filter for files which do not include given tags"
68
- )
69
-
70
- extension_group = parser.add_mutually_exclusive_group()
71
- extension_group.add_argument(
72
- "-i",
73
- "--include-extension",
74
- "--include-classifier",
75
- dest="include_extension",
76
- nargs='+',
77
- help='list of extensions to include in listing'
78
- )
79
- extension_group.add_argument(
80
- "-e",
81
- "--exclude-extension",
82
- "--exclude-classifier",
83
- dest="exclude_extension",
84
- nargs='+',
85
- help='list of extensions to exclude from listing'
86
- )
87
-
88
-
89
- def list_parser(subparsers):
90
- list_parser = subparsers.add_parser("list", help="List files with optional tags")
91
- list_parser.add_argument("tags", nargs="*", help="Tags for listing files")
92
-
93
- add_filter_flags(list_parser)
94
-
95
- argument_group = list_parser.add_mutually_exclusive_group()
96
- argument_group.add_argument(
97
- "-b", "--branch",
98
- dest="branch_args",
99
- nargs=2,
100
- metavar=("classifier", "artefact_name"),
101
- help="List artefacts in the parent chain of the artefact with specified classifier and artefact_name"
102
- )
103
- argument_group.add_argument(
104
- "-c", "--children",
105
- dest="children_args",
106
- nargs=2,
107
- metavar=("classifier", "artefact_name"),
108
- help="List child artefacts of the artefact with specified classifier and artefact_name"
109
- )
110
- argument_group.add_argument(
111
- "-d", "--data",
112
- dest="data_args",
113
- nargs=2,
114
- metavar=("classifier", "artefact_name"),
115
- help="List file in the data directory of the artefact with specified classifier and artefact_name"
116
- )
117
-
118
- list_parser.set_defaults(
119
- branch_args=(None, None),
120
- children_args=(None, None),
121
- data_args=(None, None)
122
- )
123
-
124
-
125
- def list_tags_parser(subparsers):
126
- tags_parser = subparsers.add_parser("list-tags", help="Show tags")
127
- tags_parser.add_argument("--json", "-j", help="Output tags as JSON", action=argparse.BooleanOptionalAction)
128
- tags_parser.add_argument("--include-classifier", choices=classifiers, help="Show tags for an artefact type")
129
- tags_parser.add_argument("--exclude_classifier", choices=classifiers, help="Show tags for an artefact type")
130
- tags_parser.add_argument("--filtered-extra-column", action="store_true", help="Filter tags for extra column")
131
-
132
-
133
- def add_chat_arguments(chat_parser):
134
- chat_parser.add_argument("chat_name", help="Optional name for a specific chat. Pass the .md file to continue an existing chat", nargs='?', default=None)
135
-
136
- chat_parser.add_argument("-r", "--reset", dest="reset", action=argparse.BooleanOptionalAction, help="Reset the chat file if it exists")
137
- chat_parser.set_defaults(reset=None)
138
-
139
- chat_parser.add_argument("--out", dest="output_mode", action="store_true", help="Output the contents of the chat file instead of entering interactive chat mode")
140
-
141
- chat_parser.add_argument("--append", nargs='*', default=None, help="Append strings to the chat file")
142
-
143
- chat_parser.add_argument("--restricted", dest="restricted", action=argparse.BooleanOptionalAction, help="Start with a limited set of commands")
144
-
145
-
146
- def prompt_parser(subparsers):
147
- prompt_parser = subparsers.add_parser("prompt", help="Base command for prompt interaction mode")
148
-
149
- steps = ['init', 'load', 'send', 'load-and-send', 'extract', 'update', 'chat', 'init-rag']
150
- steps_parser = prompt_parser.add_subparsers(dest='steps')
151
- for step in steps:
152
- step_parser = steps_parser.add_parser(step)
153
- step_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact")
154
- step_parser.add_argument("parameter", help="Name of artefact data directory for prompt creation and interaction").completer = ArtefactCompleter()
155
- if step == 'chat':
156
- add_chat_arguments(step_parser)
157
-
158
-
159
- def chat_parser(subparsers):
160
- chat_parser = subparsers.add_parser("chat", help="Command line chatbot. Chat control with SEND/s | RERUN/r | QUIT/q")
161
- add_chat_arguments(chat_parser)
162
-
163
-
164
- def template_parser(subparsers):
165
- template_parser = subparsers.add_parser("template", help="Outputs a classified ara template in the terminal")
166
- template_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact type")
167
-
168
-
169
- def fetch_templates_parser(subparsers):
170
- subparsers.add_parser("fetch-templates", help="Fetches templates and stores them in .araconfig")
171
-
172
-
173
- def read_parser(subparsers):
174
- read_parser = subparsers.add_parser("read", help="Reads contents of artefacts")
175
- read_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact type")
176
- read_parser.add_argument("parameter", help="Filename of artefact").completer = ArtefactCompleter()
177
-
178
- branch_group = read_parser.add_mutually_exclusive_group()
179
- branch_group.add_argument("-b", "--branch", dest="read_mode", action="store_const", const="branch",
180
- help="Output the contents of artefacts in the parent chain")
181
- branch_group.add_argument("-c", "--children", dest="read_mode", action="store_const", const="children",
182
- help="Output the contents of child artefacts")
183
-
184
- read_parser.set_defaults(read_mode=None)
185
-
186
-
187
- def reconnect_parser(subparsers):
188
- reconnect_parser = subparsers.add_parser("reconnect", help="Connect an artefact to a parent artefact")
189
- reconnect_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact type")
190
- reconnect_parser.add_argument("parameter", help="Filename of artefact").completer = ArtefactCompleter()
191
- reconnect_parser.add_argument("parent_classifier", choices=classifiers, help="Classifier of the parent artefact type")
192
- reconnect_parser.add_argument("parent_name", help="Filename of parent artefact").completer = ParentNameCompleter()
193
- reconnect_parser.add_argument("-r", "--rule", dest="rule", action="store")
194
-
195
-
196
- def read_status_parser(subparsers):
197
- read_status_parser = subparsers.add_parser("read-status", help="Read status of an artefact by checking its tags")
198
- read_status_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact type")
199
- read_status_parser.add_argument("parameter", help="Filename of artefact").completer = ArtefactCompleter()
200
-
201
-
202
- def read_user_parser(subparsers):
203
- read_user_parser = subparsers.add_parser("read-user", help="Read user of an artefact by checking its tags")
204
- read_user_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact type")
205
- read_user_parser.add_argument("parameter", help="Filename of artefact").completer = ArtefactCompleter()
206
-
207
-
208
- def set_status_parser(subparsers):
209
- set_status_parser = subparsers.add_parser("set-status", help="Set the status of a task")
210
- set_status_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact type, typically 'task'")
211
- set_status_parser.add_argument("parameter", help="Name of the task artefact").completer = ArtefactCompleter()
212
- set_status_parser.add_argument("new_status", help="New status to set for the task").completer = StatusCompleter()
213
-
214
-
215
- def set_user_parser(subparsers):
216
- set_user_parser = subparsers.add_parser("set-user", help="Set the user of a task")
217
- set_user_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact type, typically 'task'")
218
- set_user_parser.add_argument("parameter", help="Name of the task artefact").completer = ArtefactCompleter()
219
- set_user_parser.add_argument("new_user", help="New user to assign to the task")
220
-
221
-
222
- def classifier_directory_parser(subparsers):
223
- classifier_directory_parser = subparsers.add_parser("classifier-directory", help="Print the ara subdirectory for an artefact classifier")
224
- classifier_directory_parser.add_argument("classifier", choices=classifiers, help="Classifier of the artefact type")
225
-
226
-
227
- def scan_parser(subparsers):
228
- subparsers.add_parser("scan", help="Scan ARA tree for incompatible artefacts.")
229
-
230
-
231
- def autofix_parser(subparsers):
232
- autofix_parser = subparsers.add_parser("autofix", help="Fix ARA tree with llm models for scanned artefacts with ara scan command.")
233
- determinism_group = autofix_parser.add_mutually_exclusive_group()
234
- determinism_group.add_argument("--deterministic", "-d", action="store_true", help="Run only deterministic fixes e.g Title-FileName Mismatch fix")
235
- determinism_group.add_argument("--non-deterministic", "-nd", action="store_true", help="Run only non-deterministic fixes")
236
-
237
-
238
- class CustomHelpFormatter(argparse.HelpFormatter):
239
- def format_help(self):
240
- from sys import argv
241
- # Get the default help message
242
- help_message = super().format_help()
243
-
244
- # Check if '-h' or '--help' is in the arguments
245
- if '-h' in argv or '--help' in argv:
246
- # Add custom examples or additional information
247
- examples = (
248
- "\nValid classified artefacts are: businessgoal, vision, capability, keyfeature, feature, epic, userstory, example, feature, task.\n"
249
- "The default ara directory structure of classified artefact of the ara cli tool is:\n"
250
- ".\n"
251
- "└── ara\n"
252
- " ├── businessgoals\n"
253
- " ├── capabilities\n"
254
- " ├── epics\n"
255
- " ├── examples\n"
256
- " ├── features\n"
257
- " ├── keyfeatures\n"
258
- " ├── tasks\n"
259
- " ├── userstories\n"
260
- " └── vision\n"
261
- "\n"
262
- "\nara artefact handling examples:\n"
263
- " > create a new artefact for e.g. a feature: ara create feature {feature_name}\n"
264
- " > create a new artefact for e.g. a feature that contributes to an userstory: ara create feature {feature_name} contributes-to userstory {story_name}\n"
265
- " > read an artefact and return the content as terminal output, for eg. of a task: ara read task {task_name}\n"
266
- " > read an artefact and its full chain of contributions to its parents and return\n"
267
- " the content as terminal output, for eg. of a task: ara read task {task_name} --branch\n"
268
- " > delete an artefact for e.g. feature: ara delete feature {feature_name}\n"
269
- " > rename artefact and artefact data directory for e.g. a feature: ara rename feature {initial_feature_name} {new_feature_name}\n"
270
- " > create additional templates for a specific aspect (valid aspects are: customer,\n"
271
- " persona, concept, technology) related to an existing artefact like a feature: ara create feature {feature_name} aspect {aspect_name}\n"
272
- " > list artefact data with .md file extension ara list --data {classifier} {artefact_name} --include-extension .md\n"
273
- " > list artefact data with .md and .json file extensions ara list --data {classifier} {artefact_name} --include-extension .md .json\n"
274
- " > list everything but userstories ara list --exclude-extension .userstory\n"
275
- " > list all existing features: ara list --include-extension .feature\n"
276
- " > list all child artefacts contributing value to a parent artefact: ara list --include-content \"Contributes to {name_of_parent_artefact} {ara classifier_of_parent_artefact}\"\n"
277
- " > list tasks which contain 'example content' ara list --include-extension .task --include-content \"example content\"\n"
278
- " > list children artefacts of a userstory ara list --children userstory {name_of_userstory}\n"
279
- " > list parent artefacts of a userstory ara list --branch userstory {name_of_userstory}\n"
280
- " > list parent businessgoal artefact of a userstory ara list --branch userstory {name_of_userstory} --include-extension .businessgoal\n"
281
- " > print any artefact template for e.g. a feature file template in the terminal: ara template feature\n"
282
- "\n"
283
- " \nara prompt templates examples:\n"
284
- " > get and copy all prompt templates (blueprints, rules, intentions, commands\n"
285
- " in the ara/.araconfig/global-prompt-modules directory: ara fetch-templates\n"
286
- "\n"
287
- " \nara chat examples:\n"
288
- " > chat with ara and save the default chat.md file in the working directory: ara chat\n"
289
- " > chat with ara and save the default task_chat.md file in the task.data directory: ara prompt chat task {task_name}\n"
290
- "\n"
291
- " > initialize a macro prompt for a task: ara prompt init task {task_name}\n"
292
- " > load selected templates in config_prompt_templates.md for the task {task_name}: ara prompt load task {task_name}\n"
293
- " > create and send configured prompt of the task {task_name} to the configured LLM: ara prompt send task {task_name}\n"
294
- " > extract the selected LLM response in task.exploration.md and save to disk: ara prompt extract task {task_name}\n"
295
-
296
- )
297
- # Combine the default help message with the examples
298
- return help_message + examples
299
-
300
- return help_message
301
-
302
-
303
- def action_parser():
304
- # Use the CustomHelpFormatter here
305
- parser = argparse.ArgumentParser(description="The ara cli terminal tool is a management tool for classified ara artefacts.", formatter_class=CustomHelpFormatter)
306
-
307
- subparsers = parser.add_subparsers(dest="action", help="Action to perform")
308
- create_parser(subparsers)
309
- delete_parser(subparsers)
310
- rename_parser(subparsers)
311
- list_parser(subparsers)
312
- list_tags_parser(subparsers)
313
- prompt_parser(subparsers)
314
- chat_parser(subparsers)
315
- template_parser(subparsers)
316
- fetch_templates_parser(subparsers)
317
- read_parser(subparsers)
318
- reconnect_parser(subparsers)
319
- read_status_parser(subparsers)
320
- read_user_parser(subparsers)
321
- set_status_parser(subparsers)
322
- set_user_parser(subparsers)
323
- classifier_directory_parser(subparsers)
324
- scan_parser(subparsers)
325
- autofix_parser(subparsers)
326
-
327
- return parser
@@ -1,27 +0,0 @@
1
- # Usage:
2
- # necessary input and adaption:
3
- # replace text snippets in <> with specific context
4
- # ...
5
- # expected output:
6
- # ...
7
- Do not use usage information as prompt instructions
8
-
9
-
10
- Given source code
11
- ```python
12
- <source code for context, skip irrelevant for current task>
13
- ```
14
-
15
- Given existing unit tests
16
- ```python
17
- <existing unit tests>
18
- ```
19
-
20
- Given pytest is available
21
-
22
- Modify and/or create unit tests so this is fully covered:
23
- ```python
24
- <snippet you want to cover in the next step>
25
- ```
26
-
27
- Give me only what is relevant to testing this snippet. Use parametrization where applicable. Mock all dependencies of tested code.