ara-cli 0.1.9.86__tar.gz → 0.1.9.87__tar.gz

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 (168) hide show
  1. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/PKG-INFO +1 -1
  2. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/ara_config.py +53 -105
  3. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/prompt_handler.py +2 -5
  4. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/version.py +1 -1
  5. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli.egg-info/PKG-INFO +1 -1
  6. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli.egg-info/SOURCES.txt +1 -0
  7. ara_cli-0.1.9.87/tests/test_ara_config.py +336 -0
  8. ara_cli-0.1.9.87/tests/test_prompt_handler.py +303 -0
  9. ara_cli-0.1.9.86/tests/test_ara_config.py +0 -443
  10. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/MANIFEST.in +0 -0
  11. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/README.md +0 -0
  12. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/__init__.py +0 -0
  13. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/__main__.py +0 -0
  14. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/ara_command_action.py +0 -0
  15. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/ara_command_parser.py +0 -0
  16. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_autofix.py +0 -0
  17. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_creator.py +0 -0
  18. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_deleter.py +0 -0
  19. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_fuzzy_search.py +0 -0
  20. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_link_updater.py +0 -0
  21. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_lister.py +0 -0
  22. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/__init__.py +0 -0
  23. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/artefact_load.py +0 -0
  24. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/artefact_mapping.py +0 -0
  25. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/artefact_model.py +0 -0
  26. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/artefact_templates.py +0 -0
  27. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/businessgoal_artefact_model.py +0 -0
  28. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/capability_artefact_model.py +0 -0
  29. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/epic_artefact_model.py +0 -0
  30. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/example_artefact_model.py +0 -0
  31. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/feature_artefact_model.py +0 -0
  32. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/issue_artefact_model.py +0 -0
  33. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/keyfeature_artefact_model.py +0 -0
  34. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/serialize_helper.py +0 -0
  35. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/task_artefact_model.py +0 -0
  36. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/userstory_artefact_model.py +0 -0
  37. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_models/vision_artefact_model.py +0 -0
  38. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_reader.py +0 -0
  39. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_renamer.py +0 -0
  40. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/artefact_scan.py +0 -0
  41. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/chat.py +0 -0
  42. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/classifier.py +0 -0
  43. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/codefusionretriever.py +0 -0
  44. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/codehierachieretriever.py +0 -0
  45. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/commandline_completer.py +0 -0
  46. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/commands/__init__.py +0 -0
  47. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/commands/command.py +0 -0
  48. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/commands/extract_command.py +0 -0
  49. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/commands/load_command.py +0 -0
  50. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/commands/load_image_command.py +0 -0
  51. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/directory_navigator.py +0 -0
  52. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/file_classifier.py +0 -0
  53. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/file_lister.py +0 -0
  54. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/file_loaders/__init__.py +0 -0
  55. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/file_loaders/binary_file_loader.py +0 -0
  56. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/file_loaders/document_file_loader.py +0 -0
  57. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/file_loaders/document_reader.py +0 -0
  58. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/file_loaders/file_loader.py +0 -0
  59. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/file_loaders/text_file_loader.py +0 -0
  60. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/filename_validator.py +0 -0
  61. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/list_filter.py +0 -0
  62. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/output_suppressor.py +0 -0
  63. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/prompt_chat.py +0 -0
  64. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/prompt_extractor.py +0 -0
  65. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/prompt_rag.py +0 -0
  66. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/run_file_lister.py +0 -0
  67. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/tag_extractor.py +0 -0
  68. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/template_manager.py +0 -0
  69. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/agile.artefacts +0 -0
  70. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -0
  71. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/blueprints/empty.blueprint.md +0 -0
  72. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/blueprints/task_todo_list_C4_architecture_analysis.blueprint.md +0 -0
  73. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -0
  74. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/architecture_C4_analysis.commands.md +0 -0
  75. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/architecture_radon_cc_score.commands.md +0 -0
  76. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/architecture_radon_halstead_v.commands.md +0 -0
  77. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/architecture_radon_maintainability_score.commands.md +0 -0
  78. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -0
  79. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -0
  80. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -0
  81. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -0
  82. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -0
  83. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -0
  84. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/empty.commands.md +0 -0
  85. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -0
  86. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -0
  87. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -0
  88. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -0
  89. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -0
  90. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -0
  91. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -0
  92. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -0
  93. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/classify_task.intention.md +0 -0
  94. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/empty.intention.md +0 -0
  95. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/error_fixing.intention.md +0 -0
  96. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/feature_fix_steps_for_scenario.intention.md +0 -0
  97. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/feature_formulation.intention.md +0 -0
  98. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/feature_reverse_formulation_from_code.intention.md +0 -0
  99. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/feature_scenario_implementation.intention.md +0 -0
  100. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/feature_scenario_implementation_update.intention.md +0 -0
  101. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/feature_scenario_outline_extension.intention.md +0 -0
  102. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/feature_update_formulation.intention.md +0 -0
  103. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/fibonacci_example_implementation.intention.md +0 -0
  104. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/js_implementation_from_task_description.intention.md +0 -0
  105. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/js_steps_implementation.intention.md +0 -0
  106. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/python_cli_implementation_with_test.intention.md +0 -0
  107. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/python_code_understanding.intention.md +0 -0
  108. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/task_implementation.intention.md +0 -0
  109. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/task_prompt_control_by_status.intention.md +0 -0
  110. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/task_stepwise_implementation_by_number.intention.md +0 -0
  111. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/intentions/task_stepwise_implementation_by_status.intention.md +0 -0
  112. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/rules/architecture_analyst.rules.md +0 -0
  113. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/rules/code_analyst.rules.md +0 -0
  114. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/rules/empty.rules.md +0 -0
  115. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/rules/error_analyst.rules.md +0 -0
  116. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/rules/gherkin_expert.rules.md +0 -0
  117. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/rules/js_expert_developer.rules.md +0 -0
  118. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/rules/product_owner.rules.md +0 -0
  119. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/rules/python_behave.rules.md +0 -0
  120. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/prompt-modules/rules/python_developer.rules.md +0 -0
  121. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/specification_breakdown_files/template.concept.exploration.md +0 -0
  122. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/specification_breakdown_files/template.concept.md +0 -0
  123. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/specification_breakdown_files/template.customer.exploration.md +0 -0
  124. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/specification_breakdown_files/template.customer.md +0 -0
  125. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/specification_breakdown_files/template.persona.exploration.md +0 -0
  126. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/specification_breakdown_files/template.persona.md +0 -0
  127. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/specification_breakdown_files/template.step.exploration.md +0 -0
  128. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/specification_breakdown_files/template.step.md +0 -0
  129. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/specification_breakdown_files/template.technology.exploration.md +0 -0
  130. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/specification_breakdown_files/template.technology.md +0 -0
  131. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.businessgoal.prompt_log.md +0 -0
  132. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.capability.prompt_log.md +0 -0
  133. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.epic.prompt_log.md +0 -0
  134. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.example.prompt_log.md +0 -0
  135. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.feature.prompt_log.md +0 -0
  136. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.issue.prompt_log.md +0 -0
  137. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.keyfeature.prompt_log.md +0 -0
  138. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.steps.prompt_log.md +0 -0
  139. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.task.prompt_log.md +0 -0
  140. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.userstory.prompt_log.md +0 -0
  141. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/templates/template.vision.prompt_log.md +0 -0
  142. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli/update_config_prompt.py +0 -0
  143. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli.egg-info/dependency_links.txt +0 -0
  144. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli.egg-info/entry_points.txt +0 -0
  145. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli.egg-info/requires.txt +0 -0
  146. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/ara_cli.egg-info/top_level.txt +0 -0
  147. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/docker/base/requirements.txt +0 -0
  148. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/setup.cfg +0 -0
  149. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/setup.py +0 -0
  150. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/__init__.py +0 -0
  151. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_ara_command_action.py +0 -0
  152. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_artefact_autofix.py +0 -0
  153. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_artefact_fuzzy_search.py +0 -0
  154. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_artefact_link_updater.py +0 -0
  155. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_artefact_lister.py +0 -0
  156. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_artefact_reader.py +0 -0
  157. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_artefact_renamer.py +0 -0
  158. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_artefact_scan.py +0 -0
  159. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_chat.py +0 -0
  160. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_classifier.py +0 -0
  161. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_directory_navigator.py +0 -0
  162. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_file_classifier.py +0 -0
  163. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_file_creator.py +0 -0
  164. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_file_lister.py +0 -0
  165. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_list_filter.py +0 -0
  166. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_tag_extractor.py +0 -0
  167. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_template_manager.py +0 -0
  168. {ara_cli-0.1.9.86 → ara_cli-0.1.9.87}/tests/test_update_config_prompt.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ara_cli
3
- Version: 0.1.9.86
3
+ Version: 0.1.9.87
4
4
  Summary: Powerful, open source command-line tool for managing, structuring and automating software development artifacts in line with Business-Driven Development (BDD) and AI-assisted processes
5
5
  Description-Content-Type: text/markdown
6
6
  Requires-Dist: litellm
@@ -1,5 +1,5 @@
1
1
  from typing import List, Dict, Optional, Any
2
- from pydantic import BaseModel, ValidationError, Field, field_validator, model_validator
2
+ from pydantic import BaseModel, ValidationError, Field, model_validator
3
3
  import json
4
4
  import os
5
5
  from os.path import exists, dirname
@@ -14,23 +14,11 @@ class LLMConfigItem(BaseModel):
14
14
  model: str
15
15
  temperature: float = Field(ge=0.0, le=1.0)
16
16
  max_tokens: Optional[int] = None
17
-
18
- @field_validator('temperature')
19
- @classmethod
20
- def validate_temperature(cls, v: float, info) -> float:
21
- if not 0.0 <= v <= 1.0:
22
- print(f"Warning: Temperature is outside the 0.0 to 1.0 range")
23
- # Return a valid default
24
- return 0.8
25
- return v
26
-
27
- class ExtCodeDirItem(BaseModel):
28
- source_dir: str
29
17
 
30
18
  class ARAconfig(BaseModel):
31
- ext_code_dirs: List[ExtCodeDirItem] = Field(default_factory=lambda: [
32
- ExtCodeDirItem(source_dir="./src"),
33
- ExtCodeDirItem(source_dir="./tests")
19
+ ext_code_dirs: List[Dict[str, str]] = Field(default_factory=lambda: [
20
+ {"source_dir": "./src"},
21
+ {"source_dir": "./tests"}
34
22
  ])
35
23
  glossary_dir: str = "./glossary"
36
24
  doc_dir: str = "./docs"
@@ -98,27 +86,21 @@ class ARAconfig(BaseModel):
98
86
  )
99
87
  })
100
88
  default_llm: Optional[str] = "gpt-4o"
101
-
102
- model_config = {
103
- "extra": "forbid" # This will help identify unrecognized keys
104
- }
105
89
 
106
90
  @model_validator(mode='after')
107
91
  def check_critical_fields(self) -> 'ARAconfig':
108
- """Check for empty critical fields and use defaults if needed"""
92
+ """Check for empty critical fields and use defaults if needed."""
109
93
  critical_fields = {
110
- 'ext_code_dirs': [ExtCodeDirItem(source_dir="./src"), ExtCodeDirItem(source_dir="./tests")],
94
+ 'ext_code_dirs': [{"source_dir": "./src"}, {"source_dir": "./tests"}],
111
95
  'local_ara_templates_dir': "./ara/.araconfig/templates/",
112
96
  'local_prompt_templates_dir': "./ara/.araconfig",
113
97
  'glossary_dir': "./glossary"
114
98
  }
115
-
99
+
116
100
  for field, default_value in critical_fields.items():
117
101
  current_value = getattr(self, field)
118
- if (not current_value or
119
- (isinstance(current_value, list) and len(current_value) == 0) or
120
- (isinstance(current_value, str) and current_value.strip() == "")):
121
- print(f"Warning: Value for '{field}' is missing or empty.")
102
+ if not current_value:
103
+ print(f"Warning: Value for '{field}' is missing or empty. Using default.")
122
104
  setattr(self, field, default_value)
123
105
 
124
106
  return self
@@ -126,109 +108,80 @@ class ARAconfig(BaseModel):
126
108
  # Function to ensure the necessary directories exist
127
109
  @lru_cache(maxsize=None)
128
110
  def ensure_directory_exists(directory: str):
111
+ """Creates a directory if it doesn't exist."""
129
112
  if not exists(directory):
130
113
  os.makedirs(directory)
131
114
  print(f"New directory created at {directory}")
132
115
  return directory
133
116
 
134
- def handle_unrecognized_keys(data: dict, known_fields: set) -> dict:
135
- """Remove unrecognized keys and warn the user"""
117
+ def handle_unrecognized_keys(data: dict) -> dict:
118
+ """Removes unrecognized keys from the data and warns the user."""
119
+ known_fields = set(ARAconfig.model_fields.keys())
136
120
  cleaned_data = {}
137
121
  for key, value in data.items():
138
122
  if key not in known_fields:
139
- print(f"Warning: {key} is not recognized as a valid configuration option.")
123
+ print(f"Warning: Unrecognized configuration key '{key}' will be ignored.")
140
124
  else:
141
125
  cleaned_data[key] = value
142
126
  return cleaned_data
143
127
 
144
- def fix_llm_temperatures(data: dict) -> dict:
145
- """Fix invalid temperatures in LLM configurations"""
146
- if 'llm_config' in data:
147
- for model_key, model_config in data['llm_config'].items():
148
- if isinstance(model_config, dict) and 'temperature' in model_config:
149
- temp = model_config['temperature']
150
- if not 0.0 <= temp <= 1.0:
151
- print(f"Warning: Temperature for model '{model_key}' is outside the 0.0 to 1.0 range")
152
- model_config['temperature'] = 0.8
153
- return data
154
-
155
- def validate_and_fix_config_data(filepath: str) -> dict:
156
- """Load, validate, and fix configuration data"""
157
- try:
158
- with open(filepath, "r", encoding="utf-8") as file:
159
- data = json.load(file)
160
-
161
- # Get known fields from the ARAconfig model
162
- known_fields = set(ARAconfig.model_fields.keys())
163
-
164
- # Handle unrecognized keys
165
- data = handle_unrecognized_keys(data, known_fields)
166
-
167
- # Fix LLM temperatures before validation
168
- data = fix_llm_temperatures(data)
169
-
170
- return data
171
- except json.JSONDecodeError as e:
172
- print(f"Error: Invalid JSON in configuration file: {e}")
173
- print("Creating new configuration with defaults...")
174
- return {}
175
- except Exception as e:
176
- print(f"Error reading configuration file: {e}")
177
- return {}
178
-
179
128
  # Function to read the JSON file and return an ARAconfig model
180
129
  @lru_cache(maxsize=1)
181
130
  def read_data(filepath: str) -> ARAconfig:
182
- # Ensure the directory for the config file exists
183
- config_dir = dirname(filepath)
184
- ensure_directory_exists(config_dir)
131
+ """
132
+ Reads, validates, and repairs the configuration file.
133
+ If the file doesn't exist, it creates a default one.
134
+ If the file is invalid, it corrects only the broken parts.
135
+ """
136
+ ensure_directory_exists(dirname(filepath))
185
137
 
186
138
  if not exists(filepath):
187
- # If the file does not exist, create it with default values
139
+ print(f"Configuration file not found. Creating a default one at '{filepath}'.")
188
140
  default_config = ARAconfig()
189
141
  save_data(filepath, default_config)
190
- print(
191
- f"ara-cli configuration file '{filepath}' created with default configuration."
192
- f" Please modify it as needed and re-run your command"
193
- )
194
- sys.exit(0) # Exit the application
142
+ print("Please review the default configuration and re-run your command.")
143
+ sys.exit(0)
144
+
145
+ try:
146
+ with open(filepath, "r", encoding="utf-8") as file:
147
+ data = json.load(file)
148
+ except json.JSONDecodeError as e:
149
+ print(f"Error: Invalid JSON in configuration file: {e}")
150
+ print("Creating a new configuration with defaults...")
151
+ default_config = ARAconfig()
152
+ save_data(filepath, default_config)
153
+ return default_config
154
+
155
+ data = handle_unrecognized_keys(data)
195
156
 
196
- # Validate and load the existing configuration
197
- data = validate_and_fix_config_data(filepath)
198
-
199
157
  try:
200
- # Try to create the config with the loaded data
201
158
  config = ARAconfig(**data)
202
-
203
- # Save the potentially fixed configuration back
204
159
  save_data(filepath, config)
205
-
206
160
  return config
207
161
  except ValidationError as e:
208
- print(f"ValidationError: {e}")
209
- print("Correcting configuration with default values...")
162
+ print("--- Configuration Error Detected ---")
163
+ print("Some settings in your configuration file are invalid. Attempting to fix them.")
210
164
 
211
- # Create a default config
212
- default_config = ARAconfig()
165
+ corrected_data = data.copy()
166
+ defaults = ARAconfig().model_dump()
213
167
 
214
- # Try to preserve valid fields from the original data
215
- for field_name, field_value in data.items():
216
- if field_name in ARAconfig.model_fields:
217
- try:
218
- # Attempt to set the field value
219
- setattr(default_config, field_name, field_value)
220
- except:
221
- # If it fails, keep the default
222
- pass
168
+ error_fields = {err['loc'][0] for err in e.errors() if err['loc']}
223
169
 
224
- # Save the corrected configuration
225
- save_data(filepath, default_config)
226
- print("Fixed configuration saved to file.")
170
+ for field_name in error_fields:
171
+ print(f"-> Field '{field_name}' is invalid and will be reverted to its default value.")
172
+ corrected_data[field_name] = defaults.get(field_name)
173
+
174
+ print("--- End of Error Report ---")
175
+
176
+ final_config = ARAconfig(**corrected_data)
177
+ save_data(filepath, final_config)
178
+ print(f"Configuration has been corrected and saved to '{filepath}'.")
227
179
 
228
- return default_config
180
+ return final_config
229
181
 
230
182
  # Function to save the modified configuration back to the JSON file
231
183
  def save_data(filepath: str, config: ARAconfig):
184
+ """Saves the Pydantic config model to a JSON file."""
232
185
  with open(filepath, "w", encoding="utf-8") as file:
233
186
  json.dump(config.model_dump(), file, indent=4)
234
187
 
@@ -237,18 +190,13 @@ class ConfigManager:
237
190
  _config_instance = None
238
191
 
239
192
  @classmethod
240
- def get_config(cls, filepath=DEFAULT_CONFIG_LOCATION):
193
+ def get_config(cls, filepath=DEFAULT_CONFIG_LOCATION) -> ARAconfig:
241
194
  if cls._config_instance is None:
242
- config_dir = dirname(filepath)
243
-
244
- if not exists(config_dir):
245
- makedirs(config_dir)
246
-
247
195
  cls._config_instance = read_data(filepath)
248
196
  return cls._config_instance
249
197
 
250
198
  @classmethod
251
199
  def reset(cls):
252
- """Reset the configuration instance (useful for testing)"""
200
+ """Reset the configuration instance (useful for testing)."""
253
201
  cls._config_instance = None
254
202
  read_data.cache_clear()
@@ -15,8 +15,6 @@ import glob
15
15
  import logging
16
16
 
17
17
 
18
-
19
-
20
18
  class LLMSingleton:
21
19
  _instance = None
22
20
  _model = None
@@ -27,8 +25,7 @@ class LLMSingleton:
27
25
 
28
26
  if not selected_config:
29
27
  raise ValueError(f"No configuration found for the model: {model_id}")
30
- if not isinstance(selected_config, LLMConfigItem):
31
- selected_config = LLMConfigItem(**selected_config)
28
+
32
29
  LLMSingleton._model = model_id
33
30
 
34
31
  # Typesafe for None values inside the config.
@@ -495,7 +492,7 @@ def generate_config_prompt_template_file(prompt_data_path, config_prompt_templat
495
492
  def generate_config_prompt_givens_file(prompt_data_path, config_prompt_givens_name, artefact_to_mark=None):
496
493
  config_prompt_givens_path = os.path.join(prompt_data_path, config_prompt_givens_name)
497
494
  config = ConfigManager.get_config()
498
- dir_list = ["ara"] + [ext.source_dir for ext in config.ext_code_dirs] + [config.doc_dir] + [config.glossary_dir]
495
+ dir_list = ["ara"] + [ext['source_dir'] for ext in config.ext_code_dirs] + [config.doc_dir] + [config.glossary_dir]
499
496
 
500
497
  print(f"used {dir_list} for prompt givens file listing")
501
498
  generate_markdown_listing(dir_list, config.ara_prompt_given_list_includes, config_prompt_givens_path)
@@ -1,2 +1,2 @@
1
1
  # version.py
2
- __version__ = "0.1.9.86" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
2
+ __version__ = "0.1.9.87" # fith parameter like .0 for local install test purposes only. official numbers should be 4 digit numbers
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ara_cli
3
- Version: 0.1.9.86
3
+ Version: 0.1.9.87
4
4
  Summary: Powerful, open source command-line tool for managing, structuring and automating software development artifacts in line with Business-Driven Development (BDD) and AI-assisted processes
5
5
  Description-Content-Type: text/markdown
6
6
  Requires-Dist: litellm
@@ -159,6 +159,7 @@ tests/test_file_classifier.py
159
159
  tests/test_file_creator.py
160
160
  tests/test_file_lister.py
161
161
  tests/test_list_filter.py
162
+ tests/test_prompt_handler.py
162
163
  tests/test_tag_extractor.py
163
164
  tests/test_template_manager.py
164
165
  tests/test_update_config_prompt.py
@@ -0,0 +1,336 @@
1
+ import os
2
+ import json
3
+ import pytest
4
+ from unittest.mock import patch, mock_open, MagicMock
5
+ import sys
6
+ from io import StringIO
7
+ from pydantic import ValidationError
8
+
9
+ # Assuming the test file is structured to import from the production code module
10
+ from ara_cli.ara_config import (
11
+ ensure_directory_exists,
12
+ read_data,
13
+ save_data,
14
+ ARAconfig,
15
+ ConfigManager,
16
+ DEFAULT_CONFIG_LOCATION,
17
+ LLMConfigItem,
18
+ handle_unrecognized_keys,
19
+ )
20
+
21
+
22
+ @pytest.fixture
23
+ def default_config_data():
24
+ """Provides the default configuration as a dictionary."""
25
+ return ARAconfig().model_dump()
26
+
27
+
28
+ @pytest.fixture
29
+ def valid_config_dict():
30
+ """A valid, non-default configuration dictionary for testing."""
31
+ return {
32
+ "ext_code_dirs": [{"source_dir": "./app"}],
33
+ "glossary_dir": "./custom_glossary",
34
+ "doc_dir": "./custom_docs",
35
+ "local_prompt_templates_dir": "./custom_prompts",
36
+ "custom_prompt_templates_subdir": "custom_subdir",
37
+ "local_ara_templates_dir": "./custom_templates/",
38
+ "ara_prompt_given_list_includes": ["*.py", "*.md", "*.json"],
39
+ "llm_config": {
40
+ "gpt-4o-custom": {
41
+ "provider": "openai",
42
+ "model": "openai/gpt-4o",
43
+ "temperature": 0.5,
44
+ "max_tokens": 4096
45
+ }
46
+ },
47
+ "default_llm": "gpt-4o-custom"
48
+ }
49
+
50
+
51
+ @pytest.fixture
52
+ def corrupted_config_dict():
53
+ """A config dictionary with various type errors to test validation and fixing."""
54
+ return {
55
+ "ext_code_dirs": "should_be_a_list",
56
+ "glossary_dir": 123,
57
+ "llm_config": {
58
+ "bad-model": {
59
+ "provider": "test",
60
+ "model": "test/model",
61
+ "temperature": "not_a_float"
62
+ }
63
+ },
64
+ "default_llm": 999
65
+ }
66
+
67
+
68
+ @pytest.fixture(autouse=True)
69
+ def reset_config_manager():
70
+ """Ensures a clean state for each test by resetting the singleton and caches."""
71
+ ConfigManager.reset()
72
+ yield
73
+ ConfigManager.reset()
74
+
75
+ # --- Test Pydantic Models ---
76
+
77
+ class TestLLMConfigItem:
78
+ def test_valid_temperature(self):
79
+ """Tests that a valid temperature is accepted."""
80
+ config = LLMConfigItem(provider="test", model="test/model", temperature=0.7)
81
+ assert config.temperature == 0.7
82
+
83
+ def test_invalid_temperature_too_high_raises_error(self):
84
+ """Tests that temperature > 1.0 raises a ValidationError."""
85
+ with pytest.raises(ValidationError, match="Input should be less than or equal to 1"):
86
+ LLMConfigItem(provider="test", model="test/model", temperature=1.5)
87
+
88
+ def test_invalid_temperature_too_low_raises_error(self):
89
+ """Tests that temperature < 0.0 raises a ValidationError."""
90
+ with pytest.raises(ValidationError, match="Input should be greater than or equal to 0"):
91
+ LLMConfigItem(provider="test", model="test/model", temperature=-0.5)
92
+
93
+
94
+ class TestARAconfig:
95
+ def test_default_values_are_correct(self):
96
+ """Tests that the model initializes with correct default values."""
97
+ config = ARAconfig()
98
+ assert config.ext_code_dirs == [{"source_dir": "./src"}, {"source_dir": "./tests"}]
99
+ assert config.glossary_dir == "./glossary"
100
+ assert config.default_llm == "gpt-4o"
101
+ assert "gpt-4o" in config.llm_config
102
+
103
+ @patch('sys.stdout', new_callable=StringIO)
104
+ def test_check_critical_fields_with_empty_list_reverts_to_default(self, mock_stdout):
105
+ """Tests that an empty list for a critical field is reverted to its default."""
106
+ config = ARAconfig(ext_code_dirs=[])
107
+ assert len(config.ext_code_dirs) == 2
108
+ assert config.ext_code_dirs[0] == {"source_dir": "./src"}
109
+ assert "Warning: Value for 'ext_code_dirs' is missing or empty. Using default." in mock_stdout.getvalue()
110
+
111
+ @patch('sys.stdout', new_callable=StringIO)
112
+ def test_check_critical_fields_with_empty_string_reverts_to_default(self, mock_stdout):
113
+ """Tests that an empty string for a critical field is reverted to its default."""
114
+ config = ARAconfig(glossary_dir="")
115
+ assert config.glossary_dir == "./glossary"
116
+ assert "Warning: Value for 'glossary_dir' is missing or empty. Using default." in mock_stdout.getvalue()
117
+
118
+ # --- Test Helper Functions ---
119
+
120
+ class TestEnsureDirectoryExists:
121
+ @patch('sys.stdout', new_callable=StringIO)
122
+ @patch("os.makedirs")
123
+ @patch("ara_cli.ara_config.exists", return_value=False)
124
+ def test_directory_creation_when_not_exists(self, mock_exists, mock_makedirs, mock_stdout):
125
+ """Tests that a directory is created if it doesn't exist."""
126
+ ensure_directory_exists.cache_clear()
127
+ directory = "/tmp/new/dir"
128
+ result = ensure_directory_exists(directory)
129
+
130
+ mock_exists.assert_called_once_with(directory)
131
+ mock_makedirs.assert_called_once_with(directory)
132
+ assert result == directory
133
+ assert f"New directory created at {directory}" in mock_stdout.getvalue()
134
+
135
+ @patch("os.makedirs")
136
+ @patch("ara_cli.ara_config.exists", return_value=True)
137
+ def test_directory_no_creation_when_exists(self, mock_exists, mock_makedirs):
138
+ """Tests that a directory is not created if it already exists."""
139
+ ensure_directory_exists.cache_clear()
140
+ directory = "/tmp/existing/dir"
141
+ result = ensure_directory_exists(directory)
142
+
143
+ mock_exists.assert_called_once_with(directory)
144
+ mock_makedirs.assert_not_called()
145
+ assert result == directory
146
+
147
+
148
+ class TestHandleUnrecognizedKeys:
149
+ @patch('sys.stdout', new_callable=StringIO)
150
+ def test_removes_unrecognized_keys_and_warns(self, mock_stdout):
151
+ """Tests that unknown keys are removed and a warning is printed."""
152
+ data = {"glossary_dir": "./glossary", "unknown_key": "some_value"}
153
+ cleaned_data = handle_unrecognized_keys(data)
154
+
155
+ assert "unknown_key" not in cleaned_data
156
+ assert "glossary_dir" in cleaned_data
157
+ assert "Warning: Unrecognized configuration key 'unknown_key' will be ignored." in mock_stdout.getvalue()
158
+
159
+ @patch('sys.stdout', new_callable=StringIO)
160
+ def test_no_action_for_valid_data(self, mock_stdout):
161
+ """Tests that no changes are made when there are no unrecognized keys."""
162
+ data = {"glossary_dir": "./glossary", "doc_dir": "./docs"}
163
+ cleaned_data = handle_unrecognized_keys(data)
164
+
165
+ assert cleaned_data == data
166
+ assert mock_stdout.getvalue() == ""
167
+
168
+ # --- Test Core I/O and Logic ---
169
+
170
+ class TestSaveData:
171
+ @patch("builtins.open", new_callable=mock_open)
172
+ def test_save_data_writes_correct_json(self, mock_file, default_config_data):
173
+ """Tests that the config is correctly serialized to a JSON file."""
174
+ config = ARAconfig()
175
+ save_data("config.json", config)
176
+
177
+ mock_file.assert_called_once_with("config.json", "w", encoding="utf-8")
178
+ handle = mock_file()
179
+ written_data = ''.join(call.args[0] for call in handle.write.call_args_list)
180
+
181
+ assert json.loads(written_data) == default_config_data
182
+
183
+
184
+ class TestReadData:
185
+ @patch('sys.stdout', new_callable=StringIO)
186
+ @patch('ara_cli.ara_config.save_data')
187
+ @patch('ara_cli.ara_config.ensure_directory_exists')
188
+ @patch('ara_cli.ara_config.exists', return_value=False)
189
+ def test_file_not_found_creates_default_and_exits(self, mock_exists, mock_ensure_dir, mock_save, mock_stdout):
190
+ """Tests that a default config is created and the program exits if no config file is found."""
191
+ with pytest.raises(SystemExit) as exc_info:
192
+ read_data.cache_clear()
193
+ read_data("config.json")
194
+
195
+ assert exc_info.value.code == 0
196
+ mock_ensure_dir.assert_called_once_with(os.path.dirname("config.json"))
197
+ mock_save.assert_called_once()
198
+
199
+ output = mock_stdout.getvalue()
200
+ assert "Configuration file not found. Creating a default one at 'config.json'." in output
201
+ assert "Please review the default configuration and re-run your command." in output
202
+
203
+ @patch('ara_cli.ara_config.save_data')
204
+ @patch('builtins.open')
205
+ @patch('ara_cli.ara_config.ensure_directory_exists')
206
+ @patch('ara_cli.ara_config.exists', return_value=True)
207
+ def test_valid_config_is_loaded_and_resaved(self, mock_exists, mock_ensure_dir, mock_open_func, mock_save, valid_config_dict):
208
+ """Tests that a valid config is loaded correctly and re-saved (to clean it)."""
209
+ m = mock_open(read_data=json.dumps(valid_config_dict))
210
+ mock_open_func.return_value = m()
211
+ read_data.cache_clear()
212
+
213
+ result = read_data("config.json")
214
+
215
+ assert isinstance(result, ARAconfig)
216
+ assert result.default_llm == "gpt-4o-custom"
217
+ mock_save.assert_called_once()
218
+
219
+ @patch('sys.stdout', new_callable=StringIO)
220
+ @patch('ara_cli.ara_config.save_data')
221
+ @patch('builtins.open', new_callable=mock_open, read_data="this is not json")
222
+ @patch('ara_cli.ara_config.ensure_directory_exists')
223
+ @patch('ara_cli.ara_config.exists', return_value=True)
224
+ def test_invalid_json_creates_default_config(self, mock_exists, mock_ensure_dir, mock_open_func, mock_save, mock_stdout):
225
+ """Tests that a JSON decoding error results in a new default configuration."""
226
+ read_data.cache_clear()
227
+
228
+ result = read_data("config.json")
229
+
230
+ assert isinstance(result, ARAconfig)
231
+ assert result.default_llm == "gpt-4o" # Should be the default config
232
+
233
+ output = mock_stdout.getvalue()
234
+ assert "Error: Invalid JSON in configuration file" in output
235
+ assert "Creating a new configuration with defaults..." in output
236
+ mock_save.assert_called_once()
237
+
238
+ @patch('sys.stdout', new_callable=StringIO)
239
+ @patch('ara_cli.ara_config.save_data')
240
+ @patch('builtins.open')
241
+ @patch('ara_cli.ara_config.ensure_directory_exists')
242
+ @patch('ara_cli.ara_config.exists', return_value=True)
243
+ def test_config_with_validation_errors_is_fixed(self, mock_exists, mock_ensure_dir, mock_open_func, mock_save, mock_stdout, corrupted_config_dict):
244
+ """Tests that a config with invalid fields is automatically corrected to defaults."""
245
+ m = mock_open(read_data=json.dumps(corrupted_config_dict))
246
+ mock_open_func.return_value = m()
247
+ read_data.cache_clear()
248
+
249
+ defaults = ARAconfig()
250
+ result = read_data("config.json")
251
+
252
+ assert isinstance(result, ARAconfig)
253
+ assert result.ext_code_dirs == defaults.ext_code_dirs
254
+ assert result.glossary_dir == defaults.glossary_dir
255
+ assert result.llm_config == defaults.llm_config
256
+ assert result.default_llm == defaults.default_llm
257
+
258
+ output = mock_stdout.getvalue()
259
+ assert "--- Configuration Error Detected ---" in output
260
+ assert "-> Field 'ext_code_dirs' is invalid and will be reverted to its default value." in output
261
+ assert "-> Field 'glossary_dir' is invalid and will be reverted to its default value." in output
262
+ assert "-> Field 'llm_config' is invalid and will be reverted to its default value." in output
263
+ assert "Configuration has been corrected and saved" in output
264
+
265
+ mock_save.assert_called_once_with("config.json", result)
266
+
267
+ @patch('sys.stdout', new_callable=StringIO)
268
+ @patch('ara_cli.ara_config.save_data')
269
+ @patch('builtins.open')
270
+ @patch('ara_cli.ara_config.ensure_directory_exists')
271
+ @patch('ara_cli.ara_config.exists', return_value=True)
272
+ def test_preserves_valid_fields_when_fixing_errors(self, mock_exists, mock_ensure_dir, mock_open_func, mock_save, mock_stdout):
273
+ """Tests that valid, non-default values are preserved during a fix."""
274
+ mixed_config = {
275
+ "glossary_dir": "./my-custom-glossary", # Valid, non-default
276
+ "default_llm": 12345, # Invalid type
277
+ "unrecognized_key": "will_be_ignored" # Unrecognized
278
+ }
279
+ m = mock_open(read_data=json.dumps(mixed_config))
280
+ mock_open_func.return_value = m()
281
+ read_data.cache_clear()
282
+
283
+ defaults = ARAconfig()
284
+ result = read_data("config.json")
285
+
286
+ assert result.glossary_dir == "./my-custom-glossary"
287
+ assert result.default_llm == defaults.default_llm
288
+
289
+ output = mock_stdout.getvalue()
290
+ assert "Warning: Unrecognized configuration key 'unrecognized_key' will be ignored." in output
291
+ assert "-> Field 'default_llm' is invalid" in output
292
+ assert "-> Field 'glossary_dir' is invalid" not in output
293
+
294
+ mock_save.assert_called_once()
295
+ saved_config = mock_save.call_args[0][1]
296
+ assert saved_config.glossary_dir == "./my-custom-glossary"
297
+ assert saved_config.default_llm == defaults.default_llm
298
+
299
+ # --- Test Singleton Manager ---
300
+
301
+ class TestConfigManager:
302
+ @patch('ara_cli.ara_config.read_data')
303
+ def test_get_config_is_singleton(self, mock_read):
304
+ """Tests that get_config returns the same instance on subsequent calls."""
305
+ mock_read.return_value = MagicMock(spec=ARAconfig)
306
+
307
+ config1 = ConfigManager.get_config()
308
+ config2 = ConfigManager.get_config()
309
+
310
+ assert config1 is config2
311
+ mock_read.assert_called_once()
312
+
313
+ @patch('ara_cli.ara_config.read_data')
314
+ def test_reset_clears_instance_and_caches(self, mock_read):
315
+ """Tests that the reset method clears the instance and underlying caches."""
316
+ mock_read.return_value = MagicMock(spec=ARAconfig)
317
+
318
+ ConfigManager.get_config()
319
+ mock_read.assert_called_once()
320
+
321
+ ConfigManager.reset()
322
+ assert ConfigManager._config_instance is None
323
+ mock_read.cache_clear.assert_called_once()
324
+
325
+ ConfigManager.get_config()
326
+ assert mock_read.call_count == 2 # Called again after reset
327
+
328
+ @patch('ara_cli.ara_config.read_data')
329
+ def test_get_config_with_custom_filepath(self, mock_read):
330
+ """Tests that get_config can be called with a custom file path."""
331
+ mock_read.return_value = MagicMock(spec=ARAconfig)
332
+ custom_path = "/custom/path/config.json"
333
+
334
+ ConfigManager.get_config(custom_path)
335
+
336
+ mock_read.assert_called_once_with(custom_path)