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.
- ara_cli/__init__.py +18 -2
- ara_cli/__main__.py +248 -62
- ara_cli/ara_command_action.py +155 -86
- ara_cli/ara_config.py +226 -80
- ara_cli/ara_subcommands/__init__.py +0 -0
- ara_cli/ara_subcommands/autofix.py +26 -0
- ara_cli/ara_subcommands/chat.py +27 -0
- ara_cli/ara_subcommands/classifier_directory.py +16 -0
- ara_cli/ara_subcommands/common.py +100 -0
- ara_cli/ara_subcommands/create.py +75 -0
- ara_cli/ara_subcommands/delete.py +22 -0
- ara_cli/ara_subcommands/extract.py +22 -0
- ara_cli/ara_subcommands/fetch_templates.py +14 -0
- ara_cli/ara_subcommands/list.py +65 -0
- ara_cli/ara_subcommands/list_tags.py +25 -0
- ara_cli/ara_subcommands/load.py +48 -0
- ara_cli/ara_subcommands/prompt.py +136 -0
- ara_cli/ara_subcommands/read.py +47 -0
- ara_cli/ara_subcommands/read_status.py +20 -0
- ara_cli/ara_subcommands/read_user.py +20 -0
- ara_cli/ara_subcommands/reconnect.py +27 -0
- ara_cli/ara_subcommands/rename.py +22 -0
- ara_cli/ara_subcommands/scan.py +14 -0
- ara_cli/ara_subcommands/set_status.py +22 -0
- ara_cli/ara_subcommands/set_user.py +22 -0
- ara_cli/ara_subcommands/template.py +16 -0
- ara_cli/artefact_autofix.py +649 -68
- ara_cli/artefact_creator.py +8 -11
- ara_cli/artefact_deleter.py +2 -4
- ara_cli/artefact_fuzzy_search.py +22 -10
- ara_cli/artefact_link_updater.py +4 -4
- ara_cli/artefact_lister.py +29 -55
- ara_cli/artefact_models/artefact_data_retrieval.py +23 -0
- ara_cli/artefact_models/artefact_load.py +11 -3
- ara_cli/artefact_models/artefact_model.py +146 -39
- ara_cli/artefact_models/artefact_templates.py +70 -44
- ara_cli/artefact_models/businessgoal_artefact_model.py +23 -25
- ara_cli/artefact_models/epic_artefact_model.py +34 -26
- ara_cli/artefact_models/feature_artefact_model.py +203 -64
- ara_cli/artefact_models/keyfeature_artefact_model.py +21 -24
- ara_cli/artefact_models/serialize_helper.py +1 -1
- ara_cli/artefact_models/task_artefact_model.py +83 -15
- ara_cli/artefact_models/userstory_artefact_model.py +37 -27
- ara_cli/artefact_models/vision_artefact_model.py +23 -42
- ara_cli/artefact_reader.py +92 -91
- ara_cli/artefact_renamer.py +8 -4
- ara_cli/artefact_scan.py +66 -3
- ara_cli/chat.py +622 -162
- ara_cli/chat_agent/__init__.py +0 -0
- ara_cli/chat_agent/agent_communicator.py +62 -0
- ara_cli/chat_agent/agent_process_manager.py +211 -0
- ara_cli/chat_agent/agent_status_manager.py +73 -0
- ara_cli/chat_agent/agent_workspace_manager.py +76 -0
- ara_cli/commands/__init__.py +0 -0
- ara_cli/commands/command.py +7 -0
- ara_cli/commands/extract_command.py +15 -0
- ara_cli/commands/load_command.py +65 -0
- ara_cli/commands/load_image_command.py +34 -0
- ara_cli/commands/read_command.py +117 -0
- ara_cli/completers.py +144 -0
- ara_cli/directory_navigator.py +37 -4
- ara_cli/error_handler.py +134 -0
- ara_cli/file_classifier.py +6 -5
- ara_cli/file_lister.py +1 -1
- ara_cli/file_loaders/__init__.py +0 -0
- ara_cli/file_loaders/binary_file_loader.py +33 -0
- ara_cli/file_loaders/document_file_loader.py +34 -0
- ara_cli/file_loaders/document_reader.py +245 -0
- ara_cli/file_loaders/document_readers.py +233 -0
- ara_cli/file_loaders/file_loader.py +50 -0
- ara_cli/file_loaders/file_loaders.py +123 -0
- ara_cli/file_loaders/image_processor.py +89 -0
- ara_cli/file_loaders/markdown_reader.py +75 -0
- ara_cli/file_loaders/text_file_loader.py +187 -0
- ara_cli/global_file_lister.py +51 -0
- ara_cli/list_filter.py +1 -1
- ara_cli/output_suppressor.py +1 -1
- ara_cli/prompt_extractor.py +215 -88
- ara_cli/prompt_handler.py +521 -134
- ara_cli/prompt_rag.py +2 -2
- ara_cli/tag_extractor.py +83 -38
- ara_cli/template_loader.py +245 -0
- ara_cli/template_manager.py +18 -13
- ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
- ara_cli/templates/prompt-modules/commands/extract_general.commands.md +12 -0
- ara_cli/templates/prompt-modules/commands/extract_markdown.commands.md +11 -0
- ara_cli/templates/prompt-modules/commands/extract_python.commands.md +13 -0
- ara_cli/templates/prompt-modules/commands/feature_add_or_modifiy_specified_behavior.commands.md +36 -0
- ara_cli/templates/prompt-modules/commands/feature_generate_initial_specified_bevahior.commands.md +53 -0
- ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
- ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
- ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
- ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
- ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
- ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
- ara_cli/update_config_prompt.py +9 -3
- ara_cli/version.py +1 -1
- ara_cli-0.1.10.8.dist-info/METADATA +241 -0
- ara_cli-0.1.10.8.dist-info/RECORD +193 -0
- tests/test_ara_command_action.py +73 -59
- tests/test_ara_config.py +341 -36
- tests/test_artefact_autofix.py +1060 -0
- tests/test_artefact_link_updater.py +3 -3
- tests/test_artefact_lister.py +52 -132
- tests/test_artefact_renamer.py +2 -2
- tests/test_artefact_scan.py +327 -33
- tests/test_chat.py +2063 -498
- tests/test_file_classifier.py +24 -1
- tests/test_file_creator.py +3 -5
- tests/test_file_lister.py +1 -1
- tests/test_global_file_lister.py +131 -0
- tests/test_list_filter.py +2 -2
- tests/test_prompt_handler.py +746 -0
- tests/test_tag_extractor.py +19 -13
- tests/test_template_loader.py +192 -0
- tests/test_template_manager.py +5 -4
- tests/test_update_config_prompt.py +2 -2
- ara_cli/ara_command_parser.py +0 -327
- ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
- ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
- ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
- ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
- ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
- ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
- ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
- ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
- ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
- ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
- ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
- ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
- ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
- ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
- ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
- ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
- ara_cli/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.69.dist-info/METADATA +0 -16
- ara_cli-0.1.9.69.dist-info/RECORD +0 -158
- tests/test_ara_autofix.py +0 -219
- {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/entry_points.txt +0 -0
- {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/top_level.txt +0 -0
tests/test_file_classifier.py
CHANGED
|
@@ -11,6 +11,12 @@ def mock_file_system():
|
|
|
11
11
|
return MagicMock()
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def mock_error_handler():
|
|
16
|
+
with patch('ara_cli.file_classifier.error_handler') as mock_handler:
|
|
17
|
+
yield mock_handler
|
|
18
|
+
|
|
19
|
+
|
|
14
20
|
@pytest.fixture
|
|
15
21
|
def mock_classifier():
|
|
16
22
|
with patch.object(Classifier, 'ordered_classifiers', return_value=['py', 'txt', 'bin']):
|
|
@@ -64,6 +70,23 @@ def test_is_binary_file(mock_file_system):
|
|
|
64
70
|
assert result is False
|
|
65
71
|
|
|
66
72
|
|
|
73
|
+
def test_is_binary_file_handles_error(mock_file_system, mock_error_handler):
|
|
74
|
+
classifier = FileClassifier(mock_file_system)
|
|
75
|
+
test_binary_file_path = "test_binary_file.bin"
|
|
76
|
+
|
|
77
|
+
# Simulate an exception being raised when attempting to open the file
|
|
78
|
+
with patch("builtins.open", side_effect=Exception("Unexpected error")):
|
|
79
|
+
result = classifier.is_binary_file(test_binary_file_path)
|
|
80
|
+
assert result is False
|
|
81
|
+
|
|
82
|
+
# Check that the error handler's report_error method was called
|
|
83
|
+
mock_error_handler.report_error.assert_called_once()
|
|
84
|
+
# You can also verify the specific arguments if needed
|
|
85
|
+
args, kwargs = mock_error_handler.report_error.call_args
|
|
86
|
+
assert "Unexpected error" in str(args[0])
|
|
87
|
+
assert "checking if file is binary" in args[1]
|
|
88
|
+
|
|
89
|
+
|
|
67
90
|
def test_read_file_with_fallback(mock_file_system):
|
|
68
91
|
classifier = FileClassifier(mock_file_system)
|
|
69
92
|
test_file_path = "test_file.txt"
|
|
@@ -241,7 +264,7 @@ def test_find_closest_artefact_name_match(mock_file_system):
|
|
|
241
264
|
'file1', 'py') == 'file1'
|
|
242
265
|
|
|
243
266
|
# Fuzzy match
|
|
244
|
-
with patch('ara_cli.file_classifier.
|
|
267
|
+
with patch('ara_cli.file_classifier.find_closest_name_matches', return_value='file2') as mock_fuzzy:
|
|
245
268
|
assert classifier.find_closest_artefact_name_match(
|
|
246
269
|
'file3', 'py') == 'file2'
|
|
247
270
|
mock_fuzzy.assert_called_once_with('file3', ['file1', 'file2'])
|
tests/test_file_creator.py
CHANGED
|
@@ -15,12 +15,10 @@ def test_template_exists_with_valid_path():
|
|
|
15
15
|
assert result
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def
|
|
18
|
+
def test_run_with_invalid_classifier_raises_error(capfd):
|
|
19
19
|
fc = ArtefactCreator()
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
captured = capfd.readouterr()
|
|
23
|
-
assert "Invalid classifier provided. Please provide a valid classifier." in captured.out
|
|
20
|
+
with pytest.raises(ValueError):
|
|
21
|
+
fc.run("filename", "invalid_classifier")
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
@patch("ara_cli.artefact_creator.input", return_value="n")
|
tests/test_file_lister.py
CHANGED
|
@@ -55,7 +55,7 @@ def test_generate_markdown_listing_multiple_directories(setup_test_environment):
|
|
|
55
55
|
|
|
56
56
|
generate_markdown_listing([temp_dir, another_temp_dir], ['*.py'], output_file_path)
|
|
57
57
|
|
|
58
|
-
with open(output_file_path, 'r') as f:
|
|
58
|
+
with open(output_file_path, 'r', encoding='utf-8') as f:
|
|
59
59
|
output_content = f.read().splitlines()
|
|
60
60
|
|
|
61
61
|
assert output_content == expected_content
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# tests/test_global_file_lister.py
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import pytest
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
from io import StringIO
|
|
7
|
+
from ara_cli.global_file_lister import (
|
|
8
|
+
_build_tree,
|
|
9
|
+
_write_tree_to_markdown,
|
|
10
|
+
generate_global_markdown_listing,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def temp_dir_structure(tmp_path):
|
|
15
|
+
"""Create a temporary directory structure for comprehensive testing."""
|
|
16
|
+
root = tmp_path / "global_root"
|
|
17
|
+
root.mkdir()
|
|
18
|
+
(root / "src").mkdir()
|
|
19
|
+
(root / "src" / "main.py").touch()
|
|
20
|
+
(root / "src" / "utils.py").touch()
|
|
21
|
+
(root / "docs").mkdir()
|
|
22
|
+
(root / "docs" / "guide.md").touch()
|
|
23
|
+
(root / "docs" / "images").mkdir() # Empty dir, should be ignored
|
|
24
|
+
(root / "tests").mkdir()
|
|
25
|
+
(root / "tests" / "test_main.py").touch()
|
|
26
|
+
(root / "config.txt").touch() # Should be ignored by patterns
|
|
27
|
+
(root / "empty_dir").mkdir() # Should be ignored
|
|
28
|
+
return str(root)
|
|
29
|
+
|
|
30
|
+
class TestBuildTree:
|
|
31
|
+
"""Tests for the _build_tree function."""
|
|
32
|
+
def test_build_tree_with_matching_files(self, temp_dir_structure):
|
|
33
|
+
"""Tests building a tree, correctly filtering by patterns and excluding empty dirs."""
|
|
34
|
+
patterns = ["*.py", "*.md"]
|
|
35
|
+
tree = _build_tree(temp_dir_structure, patterns)
|
|
36
|
+
|
|
37
|
+
assert sorted(tree["dirs"]["src"]["files"]) == ["main.py", "utils.py"]
|
|
38
|
+
assert tree["dirs"]["docs"]["files"] == ["guide.md"]
|
|
39
|
+
assert tree["dirs"]["tests"]["files"] == ["test_main.py"]
|
|
40
|
+
assert "images" not in tree["dirs"]["docs"]["dirs"] # Empty dir excluded
|
|
41
|
+
assert "empty_dir" not in tree["dirs"] # Empty dir excluded
|
|
42
|
+
assert not tree["files"] # No files in root
|
|
43
|
+
|
|
44
|
+
def test_build_tree_no_matching_files(self, temp_dir_structure):
|
|
45
|
+
"""Tests building a tree where no files match the given patterns."""
|
|
46
|
+
patterns = ["*.json", "*.yml"]
|
|
47
|
+
tree = _build_tree(temp_dir_structure, patterns)
|
|
48
|
+
assert not tree["files"] and not tree["dirs"]
|
|
49
|
+
|
|
50
|
+
def test_build_tree_on_empty_directory(self, tmp_path):
|
|
51
|
+
"""Tests that an empty directory results in an empty tree."""
|
|
52
|
+
empty_dir = tmp_path / "empty"
|
|
53
|
+
empty_dir.mkdir()
|
|
54
|
+
tree = _build_tree(str(empty_dir), ["*.*"])
|
|
55
|
+
assert not tree["files"] and not tree["dirs"]
|
|
56
|
+
|
|
57
|
+
@patch('os.listdir')
|
|
58
|
+
@patch('sys.stdout', new_callable=StringIO)
|
|
59
|
+
def test_build_tree_handles_os_error(self, mock_stdout, mock_listdir):
|
|
60
|
+
"""Tests that an OSError during directory listing is caught and handled."""
|
|
61
|
+
mock_listdir.side_effect = OSError("Permission denied")
|
|
62
|
+
tree = _build_tree("/unreadable_dir", ["*.*"])
|
|
63
|
+
assert "Warning: Could not access path /unreadable_dir" in mock_stdout.getvalue()
|
|
64
|
+
assert not tree["files"] and not tree["dirs"]
|
|
65
|
+
|
|
66
|
+
class TestWriteTreeToMarkdown:
|
|
67
|
+
"""Tests for the _write_tree_to_markdown function."""
|
|
68
|
+
def test_write_tree_to_markdown_output_format(self):
|
|
69
|
+
"""Verifies that the markdown output is correctly formatted with proper indentation."""
|
|
70
|
+
tree = {
|
|
71
|
+
'files': ['root_file.md'],
|
|
72
|
+
'dirs': {
|
|
73
|
+
'dir1': {'files': ['a.py', 'b.py'], 'dirs': {}},
|
|
74
|
+
'dir2': {'files': [], 'dirs': {'subdir': {'files': ['c.md'], 'dirs': {}}}}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
md_file = StringIO()
|
|
79
|
+
_write_tree_to_markdown(md_file, tree, level=1)
|
|
80
|
+
output = md_file.getvalue()
|
|
81
|
+
|
|
82
|
+
# FIX: Added 4 spaces before "### subdir\n" to match the actual output.
|
|
83
|
+
expected = (
|
|
84
|
+
" - [] root_file.md\n"
|
|
85
|
+
"## dir1\n"
|
|
86
|
+
" - [] a.py\n"
|
|
87
|
+
" - [] b.py\n"
|
|
88
|
+
"## dir2\n"
|
|
89
|
+
" ### subdir\n"
|
|
90
|
+
" - [] c.md\n"
|
|
91
|
+
)
|
|
92
|
+
assert output == expected
|
|
93
|
+
|
|
94
|
+
class TestGenerateGlobalMarkdownListing:
|
|
95
|
+
"""Tests for the main generate_global_markdown_listing function."""
|
|
96
|
+
def test_generate_listing_with_valid_dirs(self, temp_dir_structure, tmp_path):
|
|
97
|
+
"""Tests the end-to-end generation of a markdown file from a valid directory."""
|
|
98
|
+
output_file = tmp_path / "output.md"
|
|
99
|
+
patterns = ["*.py"]
|
|
100
|
+
|
|
101
|
+
generate_global_markdown_listing([temp_dir_structure], patterns, str(output_file))
|
|
102
|
+
|
|
103
|
+
content = output_file.read_text()
|
|
104
|
+
abs_dir = os.path.abspath(temp_dir_structure)
|
|
105
|
+
|
|
106
|
+
assert f"# {abs_dir}\n" in content
|
|
107
|
+
assert "## src\n" in content
|
|
108
|
+
assert " - [] main.py\n" in content
|
|
109
|
+
assert "## tests\n" in content
|
|
110
|
+
assert "guide.md" not in content # Does not match pattern
|
|
111
|
+
|
|
112
|
+
def test_generate_listing_with_nonexistent_dir(self, tmp_path):
|
|
113
|
+
"""Tests that a warning is written to the file for a non-existent directory."""
|
|
114
|
+
output_file = tmp_path / "output.md"
|
|
115
|
+
non_existent_dir = "/path/to/nothing"
|
|
116
|
+
|
|
117
|
+
generate_global_markdown_listing([non_existent_dir], ["*.*"], str(output_file))
|
|
118
|
+
|
|
119
|
+
content = output_file.read_text()
|
|
120
|
+
abs_path = os.path.abspath(non_existent_dir)
|
|
121
|
+
|
|
122
|
+
assert f"# {non_existent_dir}\n" in content
|
|
123
|
+
assert f" - !! Warning: Global directory not found: {abs_path}" in content
|
|
124
|
+
|
|
125
|
+
def test_generate_listing_with_no_matching_files(self, temp_dir_structure, tmp_path):
|
|
126
|
+
"""Tests that the output file is empty if no files match the patterns."""
|
|
127
|
+
output_file = tmp_path / "output.md"
|
|
128
|
+
|
|
129
|
+
generate_global_markdown_listing([temp_dir_structure], ["*.nonexistent"], str(output_file))
|
|
130
|
+
|
|
131
|
+
assert output_file.read_text() == ""
|
tests/test_list_filter.py
CHANGED
|
@@ -65,7 +65,7 @@ def test_default_content_retrieval():
|
|
|
65
65
|
with patch("builtins.open", mock_open(read_data=mock_data)) as mocked_file:
|
|
66
66
|
content = ListFilterMonad.default_content_retrieval("dummy_path")
|
|
67
67
|
assert content == mock_data
|
|
68
|
-
mocked_file.assert_called_once_with("dummy_path", 'r')
|
|
68
|
+
mocked_file.assert_called_once_with("dummy_path", 'r', encoding='utf-8')
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
def test_default_tag_retrieval():
|
|
@@ -77,7 +77,7 @@ def test_default_content_retrieval_exception():
|
|
|
77
77
|
with patch("builtins.open", side_effect=Exception("Mocked exception")) as mocked_file:
|
|
78
78
|
content = ListFilterMonad.default_content_retrieval("dummy_path")
|
|
79
79
|
assert content == "" # Expect empty string on exception
|
|
80
|
-
mocked_file.assert_called_once_with("dummy_path", 'r')
|
|
80
|
+
mocked_file.assert_called_once_with("dummy_path", 'r', encoding='utf-8')
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
@pytest.mark.parametrize("include, exclude, expected", [
|