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
@@ -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.find_closest_name_match', return_value='file2') as mock_fuzzy:
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'])
@@ -15,12 +15,10 @@ def test_template_exists_with_valid_path():
15
15
  assert result
16
16
 
17
17
 
18
- def test_run_with_invalid_classifier_prints_error_message(capfd):
18
+ def test_run_with_invalid_classifier_raises_error(capfd):
19
19
  fc = ArtefactCreator()
20
- fc.run("filename", "invalid_classifier")
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", [