ara-cli 0.1.10.5__py3-none-any.whl → 0.1.14.0__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.
Files changed (151) hide show
  1. ara_cli/__init__.py +51 -6
  2. ara_cli/__main__.py +87 -75
  3. ara_cli/ara_command_action.py +189 -101
  4. ara_cli/ara_config.py +187 -128
  5. ara_cli/ara_subcommands/common.py +2 -2
  6. ara_cli/ara_subcommands/config.py +221 -0
  7. ara_cli/ara_subcommands/convert.py +107 -0
  8. ara_cli/ara_subcommands/fetch.py +41 -0
  9. ara_cli/ara_subcommands/fetch_agents.py +22 -0
  10. ara_cli/ara_subcommands/fetch_scripts.py +19 -0
  11. ara_cli/ara_subcommands/fetch_templates.py +15 -10
  12. ara_cli/ara_subcommands/list.py +97 -23
  13. ara_cli/ara_subcommands/prompt.py +266 -106
  14. ara_cli/artefact_autofix.py +117 -64
  15. ara_cli/artefact_converter.py +355 -0
  16. ara_cli/artefact_creator.py +41 -17
  17. ara_cli/artefact_lister.py +3 -3
  18. ara_cli/artefact_models/artefact_model.py +1 -1
  19. ara_cli/artefact_models/artefact_templates.py +0 -9
  20. ara_cli/artefact_models/feature_artefact_model.py +8 -8
  21. ara_cli/artefact_reader.py +62 -43
  22. ara_cli/artefact_scan.py +39 -17
  23. ara_cli/chat.py +300 -71
  24. ara_cli/chat_agent/__init__.py +0 -0
  25. ara_cli/chat_agent/agent_process_manager.py +155 -0
  26. ara_cli/chat_script_runner/__init__.py +0 -0
  27. ara_cli/chat_script_runner/script_completer.py +23 -0
  28. ara_cli/chat_script_runner/script_finder.py +41 -0
  29. ara_cli/chat_script_runner/script_lister.py +36 -0
  30. ara_cli/chat_script_runner/script_runner.py +36 -0
  31. ara_cli/chat_web_search/__init__.py +0 -0
  32. ara_cli/chat_web_search/web_search.py +263 -0
  33. ara_cli/children_contribution_updater.py +737 -0
  34. ara_cli/classifier.py +34 -0
  35. ara_cli/commands/agent_run_command.py +98 -0
  36. ara_cli/commands/fetch_agents_command.py +106 -0
  37. ara_cli/commands/fetch_scripts_command.py +43 -0
  38. ara_cli/commands/fetch_templates_command.py +39 -0
  39. ara_cli/commands/fetch_templates_commands.py +39 -0
  40. ara_cli/commands/list_agents_command.py +39 -0
  41. ara_cli/commands/load_command.py +4 -3
  42. ara_cli/commands/load_image_command.py +1 -1
  43. ara_cli/commands/read_command.py +23 -27
  44. ara_cli/completers.py +95 -35
  45. ara_cli/constants.py +2 -0
  46. ara_cli/directory_navigator.py +37 -4
  47. ara_cli/error_handler.py +26 -11
  48. ara_cli/file_loaders/document_reader.py +0 -178
  49. ara_cli/file_loaders/factories/__init__.py +0 -0
  50. ara_cli/file_loaders/factories/document_reader_factory.py +32 -0
  51. ara_cli/file_loaders/factories/file_loader_factory.py +27 -0
  52. ara_cli/file_loaders/file_loader.py +1 -30
  53. ara_cli/file_loaders/loaders/__init__.py +0 -0
  54. ara_cli/file_loaders/{document_file_loader.py → loaders/document_file_loader.py} +1 -1
  55. ara_cli/file_loaders/loaders/text_file_loader.py +47 -0
  56. ara_cli/file_loaders/readers/__init__.py +0 -0
  57. ara_cli/file_loaders/readers/docx_reader.py +49 -0
  58. ara_cli/file_loaders/readers/excel_reader.py +27 -0
  59. ara_cli/file_loaders/{markdown_reader.py → readers/markdown_reader.py} +1 -1
  60. ara_cli/file_loaders/readers/odt_reader.py +59 -0
  61. ara_cli/file_loaders/readers/pdf_reader.py +54 -0
  62. ara_cli/file_loaders/readers/pptx_reader.py +104 -0
  63. ara_cli/file_loaders/tools/__init__.py +0 -0
  64. ara_cli/llm_utils.py +58 -0
  65. ara_cli/output_suppressor.py +53 -0
  66. ara_cli/prompt_chat.py +20 -4
  67. ara_cli/prompt_extractor.py +47 -32
  68. ara_cli/prompt_handler.py +123 -17
  69. ara_cli/tag_extractor.py +8 -7
  70. ara_cli/template_loader.py +2 -1
  71. ara_cli/template_manager.py +52 -21
  72. ara_cli/templates/global-scripts/hello_global.py +1 -0
  73. ara_cli/templates/prompt-modules/commands/add_scenarios_for_new_behaviour.feature_creation_agent.commands.md +1 -0
  74. ara_cli/templates/prompt-modules/commands/align_feature_with_implementation_changes.interview_agent.commands.md +1 -0
  75. ara_cli/templates/prompt-modules/commands/analyze_codebase_and_plan_tasks.interview_agent.commands.md +1 -0
  76. ara_cli/templates/prompt-modules/commands/choose_best_parent_artefact.interview_agent.commands.md +1 -0
  77. ara_cli/templates/prompt-modules/commands/create_tasks_from_artefact_content.interview_agent.commands.md +1 -0
  78. ara_cli/templates/prompt-modules/commands/create_tests_for_uncovered_modules.test_generation_agent.commands.md +1 -0
  79. ara_cli/templates/prompt-modules/commands/derive_features_from_video_description.feature_creation_agent.commands.md +1 -0
  80. ara_cli/templates/prompt-modules/commands/describe_agent_capabilities.agent.commands.md +1 -0
  81. ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
  82. ara_cli/templates/prompt-modules/commands/execute_scoped_todos_in_task.interview_agent.commands.md +1 -0
  83. ara_cli/templates/prompt-modules/commands/explain_single_file_purpose.interview_agent.commands.md +1 -0
  84. ara_cli/templates/prompt-modules/commands/extract_file_information_bullets.interview_agent.commands.md +1 -0
  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/fix_failing_behave_step_definitions.interview_agent.commands.md +1 -0
  91. ara_cli/templates/prompt-modules/commands/fix_failing_pytest_tests.interview_agent.commands.md +1 -0
  92. ara_cli/templates/prompt-modules/commands/general_instruction_policy.commands.md +47 -0
  93. ara_cli/templates/prompt-modules/commands/generate_and_fix_pytest_tests.test_generation_agent.commands.md +1 -0
  94. ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
  95. ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
  96. ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
  97. ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
  98. ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
  99. ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
  100. ara_cli/templates/prompt-modules/commands/suggest_next_story_child_tasks.interview_agent.commands.md +1 -0
  101. ara_cli/templates/prompt-modules/commands/summarize_or_transcribe_media.interview_agent.commands.md +1 -0
  102. ara_cli/templates/prompt-modules/commands/update_feature_to_match_implementation.feature_creation_agent.commands.md +1 -0
  103. ara_cli/templates/prompt-modules/commands/update_user_story_with_requirements.interview_agent.commands.md +1 -0
  104. ara_cli/version.py +1 -1
  105. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.14.0.dist-info}/METADATA +49 -11
  106. ara_cli-0.1.14.0.dist-info/RECORD +253 -0
  107. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.14.0.dist-info}/WHEEL +1 -1
  108. tests/test_ara_command_action.py +31 -19
  109. tests/test_ara_config.py +177 -90
  110. tests/test_artefact_autofix.py +170 -97
  111. tests/test_artefact_autofix_integration.py +495 -0
  112. tests/test_artefact_converter.py +312 -0
  113. tests/test_artefact_extraction.py +564 -0
  114. tests/test_artefact_lister.py +11 -8
  115. tests/test_chat.py +166 -130
  116. tests/test_chat_givens_images.py +603 -0
  117. tests/test_chat_script_runner.py +454 -0
  118. tests/test_children_contribution_updater.py +98 -0
  119. tests/test_document_loader_office.py +267 -0
  120. tests/test_llm_utils.py +164 -0
  121. tests/test_prompt_chat.py +343 -0
  122. tests/test_prompt_extractor.py +683 -0
  123. tests/test_prompt_handler.py +416 -214
  124. tests/test_setup_default_chat_prompt_mode.py +198 -0
  125. tests/test_tag_extractor.py +95 -49
  126. tests/test_web_search.py +467 -0
  127. ara_cli/file_loaders/document_readers.py +0 -233
  128. ara_cli/file_loaders/file_loaders.py +0 -123
  129. ara_cli/file_loaders/text_file_loader.py +0 -187
  130. ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
  131. ara_cli/templates/prompt-modules/blueprints/pytest_unittest_prompt.blueprint.md +0 -32
  132. ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
  133. ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
  134. ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
  135. ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
  136. ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
  137. ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
  138. ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
  139. ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
  140. ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
  141. ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
  142. ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
  143. ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
  144. ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
  145. ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
  146. ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
  147. ara_cli-0.1.10.5.dist-info/RECORD +0 -194
  148. /ara_cli/file_loaders/{binary_file_loader.py → loaders/binary_file_loader.py} +0 -0
  149. /ara_cli/file_loaders/{image_processor.py → tools/image_processor.py} +0 -0
  150. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.14.0.dist-info}/entry_points.txt +0 -0
  151. {ara_cli-0.1.10.5.dist-info → ara_cli-0.1.14.0.dist-info}/top_level.txt +0 -0
tests/test_chat.py CHANGED
@@ -4,14 +4,17 @@ import tempfile
4
4
  import base64
5
5
  import glob
6
6
  import cmd2
7
+ import sys
7
8
  import ara_cli
8
9
  from unittest.mock import patch, MagicMock, mock_open
9
10
  from types import SimpleNamespace
11
+
12
+ from io import StringIO
10
13
  from ara_cli.chat import Chat
11
14
  from ara_cli.error_handler import AraError
12
15
  from ara_cli.template_manager import TemplatePathManager
13
16
  from ara_cli.ara_config import ConfigManager
14
- from ara_cli.file_loaders.text_file_loader import TextFileLoader
17
+ from ara_cli.file_loaders.loaders.text_file_loader import TextFileLoader
15
18
 
16
19
 
17
20
  def get_default_config():
@@ -66,7 +69,7 @@ def temp_load_file():
66
69
 
67
70
 
68
71
  def test_handle_existing_chat_no_reset(temp_chat_file):
69
- with patch("builtins.input", return_value="n"):
72
+ with patch("sys.stdin.readline", return_value="n"):
70
73
  mock_config = get_default_config()
71
74
  with patch(
72
75
  "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
@@ -76,7 +79,7 @@ def test_handle_existing_chat_no_reset(temp_chat_file):
76
79
 
77
80
 
78
81
  def test_handle_existing_chat_with_reset(temp_chat_file):
79
- with patch("builtins.input", return_value="y"):
82
+ with patch("sys.stdin.readline", return_value="y"):
80
83
  mock_config = get_default_config()
81
84
  with patch(
82
85
  "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
@@ -489,7 +492,7 @@ def test_send_message(
489
492
 
490
493
  mock_chunks = [mock_chunk1, mock_chunk2]
491
494
 
492
- with patch("ara_cli.chat.send_prompt", return_value=mock_chunks), patch.object(
495
+ with patch("ara_cli.prompt_handler.send_prompt", return_value=mock_chunks), patch.object(
493
496
  chat, "get_last_line", return_value=last_line_in_file
494
497
  ), patch.object(chat, "assemble_prompt", return_value="mocked prompt"):
495
498
 
@@ -740,7 +743,7 @@ def test_load_binary_file(temp_chat_file, path_exists):
740
743
 
741
744
  # We patch open within the loader's module
742
745
  with patch(
743
- "ara_cli.file_loaders.binary_file_loader.open",
746
+ "ara_cli.file_loaders.loaders.binary_file_loader.open",
744
747
  mock_open(read_data=file_content),
745
748
  ) as mock_loader_open:
746
749
 
@@ -816,7 +819,7 @@ def test_load_document_file(
816
819
  mock_setup(mock_modules[module_to_mock])
817
820
 
818
821
  with patch(
819
- "ara_cli.file_loaders.document_file_loader.open", mock_open()
822
+ "builtins.open", mock_open()
820
823
  ) as mock_chat_open:
821
824
  # FIX: Call with a positional argument `file_name` as the decorator expects, not a keyword `file_path`.
822
825
  result = chat.load_document_file(
@@ -951,13 +954,14 @@ def test_choose_file_to_load_valid_cases(
951
954
  monkeypatch.setattr("builtins.input", mock_input)
952
955
 
953
956
  mock_config = get_default_config()
954
- with patch(
955
- "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
956
- ):
957
- with patch("builtins.open", mock_open()):
958
- chat = Chat("dummy_chat_name", reset=False)
957
+ with patch("sys.stdin.readline", return_value=f"{user_input}\n"):
958
+ with patch(
959
+ "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
960
+ ):
961
+ with patch("builtins.open", mock_open()):
962
+ chat = Chat("dummy_chat_name", reset=False)
959
963
 
960
- file_path = chat.choose_file_to_load(files, pattern)
964
+ file_path = chat.choose_file_to_load(files, pattern)
961
965
 
962
966
  captured = capsys.readouterr()
963
967
 
@@ -1000,19 +1004,23 @@ def test_choose_file_to_load_invalid_choice_index(
1000
1004
  ):
1001
1005
  """Test choose_file_to_load with invalid choice indices"""
1002
1006
 
1007
+ # Mock input for the test setup, but actual test uses sys.stdin.readline
1008
+ # Mock input for the test setup, but actual test uses sys.stdin.readline
1003
1009
  def mock_input(prompt):
1004
1010
  return user_input
1005
1011
 
1006
1012
  monkeypatch.setattr("builtins.input", mock_input)
1013
+
1014
+ # We also need to mock sys.stdin.readline because that's what's actually used in the code
1015
+ with patch("sys.stdin.readline", return_value=f"{user_input}\n"):
1016
+ mock_config = get_default_config()
1017
+ with patch(
1018
+ "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1019
+ ):
1020
+ with patch("builtins.open", mock_open()):
1021
+ chat = Chat("dummy_chat_name", reset=False)
1007
1022
 
1008
- mock_config = get_default_config()
1009
- with patch(
1010
- "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1011
- ):
1012
- with patch("builtins.open", mock_open()):
1013
- chat = Chat("dummy_chat_name", reset=False)
1014
-
1015
- file_path = chat.choose_file_to_load(files, pattern)
1023
+ file_path = chat.choose_file_to_load(files, pattern)
1016
1024
 
1017
1025
  # Verify error was reported
1018
1026
  mock_report_error.assert_called_once()
@@ -1061,16 +1069,19 @@ def test_choose_file_to_load_invalid_input_format(
1061
1069
  def mock_input(prompt):
1062
1070
  return user_input
1063
1071
 
1072
+ # Mock input for the test setup, but actual test uses sys.stdin.readline
1064
1073
  monkeypatch.setattr("builtins.input", mock_input)
1074
+
1075
+ # We also need to mock sys.stdin.readline because that's what's actually used in the code
1076
+ with patch("sys.stdin.readline", return_value=f"{user_input}\n"):
1077
+ mock_config = get_default_config()
1078
+ with patch(
1079
+ "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1080
+ ):
1081
+ with patch("builtins.open", mock_open()):
1082
+ chat = Chat("dummy_chat_name", reset=False)
1065
1083
 
1066
- mock_config = get_default_config()
1067
- with patch(
1068
- "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1069
- ):
1070
- with patch("builtins.open", mock_open()):
1071
- chat = Chat("dummy_chat_name", reset=False)
1072
-
1073
- file_path = chat.choose_file_to_load(files, pattern)
1084
+ file_path = chat.choose_file_to_load(files, pattern)
1074
1085
 
1075
1086
  # Verify error was reported
1076
1087
  mock_report_error.assert_called_once()
@@ -1091,15 +1102,17 @@ def test_choose_file_to_load_files_are_sorted(monkeypatch, capsys):
1091
1102
  return "2" # Choose the second file (beta.md after sorting)
1092
1103
 
1093
1104
  monkeypatch.setattr("builtins.input", mock_input)
1105
+
1106
+ # Mock sys.stdin.readline as used in implementation
1107
+ with patch("sys.stdin.readline", return_value="2\n"):
1108
+ mock_config = get_default_config()
1109
+ with patch(
1110
+ "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1111
+ ):
1112
+ with patch("builtins.open", mock_open()):
1113
+ chat = Chat("dummy_chat_name", reset=False)
1094
1114
 
1095
- mock_config = get_default_config()
1096
- with patch(
1097
- "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1098
- ):
1099
- with patch("builtins.open", mock_open()):
1100
- chat = Chat("dummy_chat_name", reset=False)
1101
-
1102
- file_path = chat.choose_file_to_load(files, "*.md")
1115
+ file_path = chat.choose_file_to_load(files, "*.md")
1103
1116
 
1104
1117
  captured = capsys.readouterr()
1105
1118
 
@@ -1120,15 +1133,17 @@ def test_choose_file_to_load_basename_displayed(monkeypatch, capsys):
1120
1133
  return "1"
1121
1134
 
1122
1135
  monkeypatch.setattr("builtins.input", mock_input)
1136
+
1137
+ # Mock sys.stdin.readline as used in implementation
1138
+ with patch("sys.stdin.readline", return_value="1\n"):
1139
+ mock_config = get_default_config()
1140
+ with patch(
1141
+ "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1142
+ ):
1143
+ with patch("builtins.open", mock_open()):
1144
+ chat = Chat("dummy_chat_name", reset=False)
1123
1145
 
1124
- mock_config = get_default_config()
1125
- with patch(
1126
- "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1127
- ):
1128
- with patch("builtins.open", mock_open()):
1129
- chat = Chat("dummy_chat_name", reset=False)
1130
-
1131
- file_path = chat.choose_file_to_load(files, "*.md")
1146
+ file_path = chat.choose_file_to_load(files, "*.md")
1132
1147
 
1133
1148
  captured = capsys.readouterr()
1134
1149
 
@@ -1173,15 +1188,20 @@ def test_choose_file_to_load_input_prompt_message(monkeypatch, capsys):
1173
1188
  return "1"
1174
1189
 
1175
1190
  monkeypatch.setattr("builtins.input", mock_input)
1191
+ # Use sys.stdin.readline to provide input and StringIO to capture stdout
1192
+ with patch("sys.stdin.readline", return_value="1\n"), patch(
1193
+ "sys.stdout", new_callable=StringIO
1194
+ ) as mock_stdout:
1195
+ mock_config = get_default_config()
1196
+ with patch(
1197
+ "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1198
+ ):
1199
+ with patch("builtins.open", mock_open()):
1200
+ chat = Chat("dummy_chat_name", reset=False)
1176
1201
 
1177
- mock_config = get_default_config()
1178
- with patch(
1179
- "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1180
- ):
1181
- with patch("builtins.open", mock_open()):
1182
- chat = Chat("dummy_chat_name", reset=False)
1183
-
1184
- chat.choose_file_to_load(files, "*.md")
1202
+ chat.choose_file_to_load(files, "*.md")
1203
+ output = mock_stdout.getvalue()
1204
+ assert expected_prompt in output
1185
1205
 
1186
1206
 
1187
1207
  @pytest.mark.parametrize(
@@ -1304,24 +1324,22 @@ def test_load_helper_basic_scenarios(
1304
1324
  def mock_glob(file_pattern):
1305
1325
  return existing_files
1306
1326
 
1307
- def mock_input(prompt):
1308
- return user_input
1309
-
1310
- def mock_load_file(self, file_path):
1311
- return True
1312
-
1313
1327
  monkeypatch.setattr(glob, "glob", mock_glob)
1314
- monkeypatch.setattr("builtins.input", mock_input)
1315
- monkeypatch.setattr(Chat, "load_file", mock_load_file)
1316
1328
  monkeypatch.setattr(Chat, "add_prompt_tag_if_needed", lambda self, chat_file: None)
1317
1329
 
1318
- mock_config = get_default_config()
1319
- with patch(
1320
- "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1321
- ):
1322
- chat = Chat(temp_chat_file.name, reset=False)
1330
+ with patch("sys.stdin.readline", return_value=f"{user_input}\n"):
1331
+ def mock_load_file(self, file_path):
1332
+ return True
1333
+
1334
+ monkeypatch.setattr(Chat, "load_file", mock_load_file)
1335
+
1336
+ mock_config = get_default_config()
1337
+ with patch(
1338
+ "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1339
+ ):
1340
+ chat = Chat(temp_chat_file.name, reset=False)
1323
1341
 
1324
- chat._load_helper(directory, pattern, file_type, exclude_pattern)
1342
+ chat._load_helper(directory, pattern, file_type, exclude_pattern)
1325
1343
 
1326
1344
  captured = capsys.readouterr()
1327
1345
  # Check both stdout and stderr since error messages go to stderr
@@ -1394,24 +1412,22 @@ def test_load_helper_with_exclusions(
1394
1412
  return excluded_files
1395
1413
  return existing_files
1396
1414
 
1397
- def mock_input(prompt):
1398
- return user_input
1399
-
1400
- def mock_load_file(self, file_path):
1401
- return True
1402
-
1403
1415
  monkeypatch.setattr(glob, "glob", mock_glob)
1404
- monkeypatch.setattr("builtins.input", mock_input)
1405
- monkeypatch.setattr(Chat, "load_file", mock_load_file)
1406
1416
  monkeypatch.setattr(Chat, "add_prompt_tag_if_needed", lambda self, chat_file: None)
1407
1417
 
1408
- mock_config = get_default_config()
1409
- with patch(
1410
- "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1411
- ):
1412
- chat = Chat(temp_chat_file.name, reset=False)
1418
+ with patch("sys.stdin.readline", return_value=f"{user_input}\n"):
1419
+ def mock_load_file(self, file_path):
1420
+ return True
1421
+
1422
+ monkeypatch.setattr(Chat, "load_file", mock_load_file)
1423
+
1424
+ mock_config = get_default_config()
1425
+ with patch(
1426
+ "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1427
+ ):
1428
+ chat = Chat(temp_chat_file.name, reset=False)
1413
1429
 
1414
- chat._load_helper(directory, pattern, file_type, exclude_pattern)
1430
+ chat._load_helper(directory, pattern, file_type, exclude_pattern)
1415
1431
 
1416
1432
  captured = capsys.readouterr()
1417
1433
  # Check both stdout and stderr since error messages go to stderr
@@ -1441,7 +1457,8 @@ def test_load_helper_load_file_fails(monkeypatch, capsys, temp_chat_file):
1441
1457
  ):
1442
1458
  chat = Chat(temp_chat_file.name, reset=False)
1443
1459
 
1444
- chat._load_helper("prompt.data", "*.rules.md", "rules")
1460
+ with patch("sys.stdin.readline", return_value="1\n"): # Add stdin mock for choose_file_to_load
1461
+ chat._load_helper("prompt.data", "*.rules.md", "rules")
1445
1462
 
1446
1463
  captured = capsys.readouterr()
1447
1464
  output = captured.out + captured.err
@@ -1495,8 +1512,8 @@ def test_load_helper_directory_path_construction(temp_chat_file):
1495
1512
  with patch("glob.glob", side_effect=mock_glob), patch.object(
1496
1513
  chat, "load_file", return_value=True
1497
1514
  ), patch.object(chat, "add_prompt_tag_if_needed"):
1498
-
1499
- chat._load_helper("custom_dir", "*.custom.md", "custom")
1515
+ with patch("sys.stdin.readline", return_value="1\n"): # Add stdin mock for choose_file_to_load
1516
+ chat._load_helper("custom_dir", "*.custom.md", "custom")
1500
1517
 
1501
1518
 
1502
1519
  def test_load_helper_calls_add_prompt_tag_before_load(temp_chat_file):
@@ -1519,8 +1536,8 @@ def test_load_helper_calls_add_prompt_tag_before_load(temp_chat_file):
1519
1536
  with patch("glob.glob", return_value=["rules1.md"]), patch.object(
1520
1537
  chat, "add_prompt_tag_if_needed", side_effect=mock_add_prompt_tag
1521
1538
  ), patch.object(chat, "load_file", side_effect=mock_load_file):
1522
-
1523
- chat._load_helper("prompt.data", "*.rules.md", "rules")
1539
+ with patch("sys.stdin.readline", return_value="1\n"): # Add stdin mock for choose_file_to_load
1540
+ chat._load_helper("prompt.data", "*.rules.md", "rules")
1524
1541
 
1525
1542
  # Verify correct call order
1526
1543
  assert call_order == ["add_prompt_tag", "load_file"]
@@ -1547,20 +1564,6 @@ def test_load_helper_reports_error_when_no_files_found(
1547
1564
  assert "No rules file found." in str(error_call[0])
1548
1565
 
1549
1566
 
1550
- def test_help_menu_with_aliases(temp_chat_file, capsys):
1551
- mock_config = get_default_config()
1552
- with patch(
1553
- "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1554
- ):
1555
- chat = Chat(temp_chat_file.name, reset=False)
1556
-
1557
- chat._help_menu(verbose=False)
1558
- captured = capsys.readouterr()
1559
-
1560
- assert "Aliases" in captured.out
1561
- assert "q -> quit" in captured.out
1562
- assert "h -> help" in captured.out
1563
- assert "s -> SEND" in captured.out
1564
1567
 
1565
1568
 
1566
1569
  def test_do_quit(temp_chat_file, capsys):
@@ -1658,11 +1661,7 @@ def test_do_LOAD_interactive(monkeypatch, capsys, temp_chat_file, temp_load_file
1658
1661
  def mock_glob(file_pattern):
1659
1662
  return [temp_load_file.name]
1660
1663
 
1661
- def mock_input(prompt):
1662
- return temp_load_file.name
1663
-
1664
1664
  monkeypatch.setattr(glob, "glob", mock_glob)
1665
- monkeypatch.setattr("builtins.input", mock_input)
1666
1665
  monkeypatch.setattr(Chat, "add_prompt_tag_if_needed", lambda self, chat_file: None)
1667
1666
 
1668
1667
  mock_config = get_default_config()
@@ -1670,7 +1669,8 @@ def test_do_LOAD_interactive(monkeypatch, capsys, temp_chat_file, temp_load_file
1670
1669
  "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1671
1670
  ):
1672
1671
  chat = Chat(temp_chat_file.name, reset=False)
1673
- chat.do_LOAD("")
1672
+ with patch("sys.stdin.readline", return_value=f"1\n"): # Simulate user choosing the first file
1673
+ chat.do_LOAD("")
1674
1674
 
1675
1675
  captured = capsys.readouterr()
1676
1676
  assert f"Loaded contents of file {temp_load_file.name}" in captured.out
@@ -1703,7 +1703,11 @@ def test_complete_LOAD(
1703
1703
  "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1704
1704
  ):
1705
1705
  chat = Chat(temp_chat_file.name, reset=False)
1706
- completions = chat.complete_LOAD(text, line, begidx, endidx)
1706
+ monkeypatch.setattr(Chat, "add_prompt_tag_if_needed", lambda self, chat_file: None)
1707
+
1708
+ # No user input is needed for completion, but if choose_file_to_load is called, it needs stdin
1709
+ with patch("sys.stdin.readline", return_value="1\n"):
1710
+ completions = chat.complete_LOAD(text, line, begidx, endidx)
1707
1711
 
1708
1712
  assert completions == matching_files
1709
1713
 
@@ -1832,7 +1836,7 @@ def test_load_image_binary_type_mapping_usage(mock_report_error, temp_chat_file)
1832
1836
  chat = Chat(temp_chat_file.name, reset=False)
1833
1837
 
1834
1838
  # Verify the mapping is used correctly by testing each supported extension
1835
- original_mapping = Chat.BINARY_TYPE_MAPPING.copy()
1839
+ original_mapping = ara_cli.BINARY_TYPE_MAPPING.copy()
1836
1840
 
1837
1841
  with patch.object(chat, "load_binary_file", return_value=True) as mock_load_binary:
1838
1842
  for extension, expected_mime in original_mapping.items():
@@ -1895,35 +1899,30 @@ def test_do_LOAD_IMAGE(
1895
1899
 
1896
1900
 
1897
1901
  @pytest.mark.parametrize(
1898
- "input_chat_name, expected_chat_name",
1902
+ "input_chat_name, user_input, expected_name_part",
1899
1903
  [
1900
- ("", "What should be the new chat name? "),
1901
- ("new_chat", "new_chat_chat.md"),
1902
- ("new_chat.md", "new_chat.md"),
1904
+ ("", "interactive_name\n", "interactive_name"),
1905
+ ("cli_arg_name", "", "cli_arg_name"),
1903
1906
  ],
1904
1907
  )
1905
- def test_do_new(monkeypatch, temp_chat_file, input_chat_name, expected_chat_name):
1906
- def mock_input(prompt):
1907
- return "input_chat_name"
1908
-
1909
- monkeypatch.setattr("builtins.input", mock_input)
1910
-
1908
+ def test_do_new(temp_chat_file, capsys, input_chat_name, user_input, expected_name_part):
1911
1909
  mock_config = get_default_config()
1912
1910
  with patch(
1913
1911
  "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
1914
1912
  ):
1915
1913
  chat = Chat(temp_chat_file.name, reset=False)
1916
1914
 
1917
- with patch.object(Chat, "__init__", return_value=None) as mock_init:
1915
+ with patch("sys.stdin.readline", return_value=user_input), \
1916
+ patch.object(Chat, "__init__", return_value=None) as mock_init:
1917
+
1918
1918
  chat.do_NEW(input_chat_name)
1919
- if input_chat_name == "":
1920
- mock_init.assert_called_with(
1921
- os.path.join(os.path.dirname(temp_chat_file.name), "input_chat_name")
1922
- )
1923
- else:
1924
- mock_init.assert_called_with(
1925
- os.path.join(os.path.dirname(temp_chat_file.name), input_chat_name)
1926
- )
1919
+
1920
+ expected_path = os.path.join(os.path.dirname(temp_chat_file.name), expected_name_part)
1921
+ mock_init.assert_called_with(expected_path)
1922
+
1923
+ captured = capsys.readouterr()
1924
+ if input_chat_name == "":
1925
+ assert "What should be the new chat name? " in captured.out
1927
1926
 
1928
1927
 
1929
1928
  def test_do_RERUN(temp_chat_file):
@@ -1956,7 +1955,7 @@ def test_do_CLEAR(temp_chat_file, capsys):
1956
1955
  ):
1957
1956
  chat = Chat(temp_chat_file.name, reset=False)
1958
1957
 
1959
- with patch("builtins.input", return_value="y"):
1958
+ with patch("sys.stdin.readline", return_value="y"):
1960
1959
  chat.do_CLEAR(None)
1961
1960
 
1962
1961
  captured = capsys.readouterr()
@@ -1979,7 +1978,7 @@ def test_do_CLEAR_abort(temp_chat_file, capsys):
1979
1978
  ):
1980
1979
  chat = Chat(temp_chat_file.name, reset=False)
1981
1980
 
1982
- with patch("builtins.input", return_value="n"):
1981
+ with patch("sys.stdin.readline", return_value="n"):
1983
1982
  chat.do_CLEAR(None)
1984
1983
 
1985
1984
  captured = capsys.readouterr()
@@ -2441,7 +2440,7 @@ def test_do_SEND(temp_chat_file):
2441
2440
  with patch.object(chat, "send_message") as mock_send_message:
2442
2441
  chat.do_SEND(None)
2443
2442
  mock_save_message.assert_called_once_with(
2444
- Chat.ROLE_PROMPT, "Message part 1\nMessage part 2"
2443
+ ara_cli.ROLE_PROMPT, "Message part 1\nMessage part 2"
2445
2444
  )
2446
2445
  mock_send_message.assert_called_once()
2447
2446
 
@@ -2631,5 +2630,42 @@ def test_do_LOAD_TEMPLATE_serialize_called_correctly(temp_chat_file):
2631
2630
  # Verify serialize was called exactly once
2632
2631
  mock_artefact.serialize.assert_called_once_with()
2633
2632
 
2634
- # Verify the serialized content was written to file
2635
- mock_file().write.assert_called_once_with(expected_content)
2633
+
2634
+ def test_do_run_pyscript_parsing(temp_chat_file):
2635
+ """Test argument parsing for run_pyscript command."""
2636
+ mock_config = get_default_config()
2637
+ with patch(
2638
+ "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
2639
+ ):
2640
+ chat = Chat(temp_chat_file.name, reset=False)
2641
+
2642
+ chat.script_runner = MagicMock()
2643
+
2644
+ # Test with simple arguments
2645
+ # Note: simulate cmd2 passing a string (Statement behaves like string)
2646
+ chat.do_run_pyscript('script.py arg1 arg2')
2647
+ chat.script_runner.run_script.assert_called_with('script.py', ['arg1', 'arg2'])
2648
+
2649
+ # Test with quoted arguments
2650
+ chat.do_run_pyscript('script.py "arg with spaces" arg2')
2651
+ chat.script_runner.run_script.assert_called_with('script.py', ['arg with spaces', 'arg2'])
2652
+
2653
+ # Test with single argument
2654
+ chat.do_run_pyscript('script.py')
2655
+ chat.script_runner.run_script.assert_called_with('script.py', [])
2656
+
2657
+
2658
+ def test_do_run_pyscript_empty(temp_chat_file):
2659
+ """Test run_pyscript with no arguments."""
2660
+ mock_config = get_default_config()
2661
+ with patch(
2662
+ "ara_cli.prompt_handler.ConfigManager.get_config", return_value=mock_config
2663
+ ):
2664
+ chat = Chat(temp_chat_file.name, reset=False)
2665
+
2666
+ chat.script_runner = MagicMock()
2667
+
2668
+ chat.do_run_pyscript('')
2669
+ # Should not call run_script if no script name provided
2670
+ chat.script_runner.run_script.assert_not_called()
2671
+