ara-cli 0.1.9.69__tar.gz → 0.1.9.71__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.
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/PKG-INFO +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/ara_command_action.py +16 -12
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/ara_config.py +24 -10
- ara_cli-0.1.9.71/ara_cli/artefact_autofix.py +446 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_creator.py +3 -3
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_fuzzy_search.py +9 -4
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_link_updater.py +4 -4
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/artefact_model.py +14 -7
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/artefact_templates.py +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/feature_artefact_model.py +72 -18
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/serialize_helper.py +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_reader.py +16 -38
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_renamer.py +2 -2
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_scan.py +28 -3
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/chat.py +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/file_classifier.py +3 -3
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/file_lister.py +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/list_filter.py +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/output_suppressor.py +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/prompt_extractor.py +3 -3
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/prompt_handler.py +9 -10
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/prompt_rag.py +2 -2
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/template_manager.py +2 -2
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/update_config_prompt.py +2 -2
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/version.py +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli.egg-info/PKG-INFO +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli.egg-info/SOURCES.txt +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/setup.py +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_ara_command_action.py +7 -7
- ara_cli-0.1.9.69/tests/test_ara_autofix.py → ara_cli-0.1.9.71/tests/test_artefact_autofix.py +163 -29
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_artefact_link_updater.py +3 -3
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_artefact_renamer.py +2 -2
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_artefact_scan.py +52 -19
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_file_classifier.py +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_file_lister.py +1 -1
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_list_filter.py +2 -2
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_update_config_prompt.py +2 -2
- ara_cli-0.1.9.69/ara_cli/artefact_autofix.py +0 -191
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/MANIFEST.in +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/README.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/__init__.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/__main__.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/ara_command_parser.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_deleter.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_lister.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/__init__.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/artefact_load.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/artefact_mapping.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/businessgoal_artefact_model.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/capability_artefact_model.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/epic_artefact_model.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/example_artefact_model.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/issue_artefact_model.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/keyfeature_artefact_model.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/task_artefact_model.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/userstory_artefact_model.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/artefact_models/vision_artefact_model.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/classifier.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/codefusionretriever.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/codehierachieretriever.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/commandline_completer.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/directory_navigator.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/filename_validator.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/prompt_chat.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/run_file_lister.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/tag_extractor.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/agile.artefacts +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/blueprints/empty.blueprint.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/blueprints/task_todo_list_C4_architecture_analysis.blueprint.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/architecture_C4_analysis.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/architecture_radon_cc_score.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/architecture_radon_halstead_v.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/architecture_radon_maintainability_score.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/empty.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/classify_task.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/empty.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/error_fixing.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/feature_fix_steps_for_scenario.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/feature_formulation.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/feature_reverse_formulation_from_code.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/feature_scenario_implementation.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/feature_scenario_implementation_update.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/feature_scenario_outline_extension.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/feature_update_formulation.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/fibonacci_example_implementation.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/js_implementation_from_task_description.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/js_steps_implementation.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/python_cli_implementation_with_test.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/python_code_understanding.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/task_implementation.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/task_prompt_control_by_status.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/task_stepwise_implementation_by_number.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/intentions/task_stepwise_implementation_by_status.intention.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/rules/architecture_analyst.rules.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/rules/code_analyst.rules.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/rules/empty.rules.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/rules/error_analyst.rules.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/rules/gherkin_expert.rules.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/rules/js_expert_developer.rules.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/rules/product_owner.rules.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/rules/python_behave.rules.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/prompt-modules/rules/python_developer.rules.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/specification_breakdown_files/template.concept.exploration.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/specification_breakdown_files/template.concept.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/specification_breakdown_files/template.customer.exploration.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/specification_breakdown_files/template.customer.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/specification_breakdown_files/template.persona.exploration.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/specification_breakdown_files/template.persona.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/specification_breakdown_files/template.step.exploration.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/specification_breakdown_files/template.step.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/specification_breakdown_files/template.technology.exploration.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/specification_breakdown_files/template.technology.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.businessgoal +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.businessgoal.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.capability +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.capability.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.epic +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.epic.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.example +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.example.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.feature +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.feature.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.issue +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.issue.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.keyfeature +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.keyfeature.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.steps.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.task +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.task.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.userstory +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.userstory.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.vision +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli/templates/template.vision.prompt_log.md +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli.egg-info/dependency_links.txt +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli.egg-info/entry_points.txt +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli.egg-info/requires.txt +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/ara_cli.egg-info/top_level.txt +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/docker/base/requirements.txt +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/setup.cfg +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/__init__.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_ara_config.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_artefact_fuzzy_search.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_artefact_lister.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_artefact_reader.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_chat.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_classifier.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_directory_navigator.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_file_creator.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_tag_extractor.py +0 -0
- {ara_cli-0.1.9.69 → ara_cli-0.1.9.71}/tests/test_template_manager.py +0 -0
|
@@ -33,7 +33,7 @@ def create_action(args):
|
|
|
33
33
|
if parent_classifier and parent_name and rule:
|
|
34
34
|
check_validity(Classifier.is_valid_classifier(parent_classifier), invalid_classifier_message)
|
|
35
35
|
check_validity(is_valid_filename(parent_name), invalid_name_message)
|
|
36
|
-
parent_artefact = ArtefactReader.
|
|
36
|
+
parent_artefact = ArtefactReader.read_artefact(artefact_name=parent_name, classifier=parent_classifier)
|
|
37
37
|
rule = find_closest_rule(parent_artefact, rule)
|
|
38
38
|
return parent_classifier, parent_name, rule
|
|
39
39
|
if parent_classifier and parent_name:
|
|
@@ -359,7 +359,7 @@ def reconnect_action(args):
|
|
|
359
359
|
|
|
360
360
|
feedback_message = f"Updated contribution of {classifier} '{artefact_name}' to {parent_classifier} '{parent_name}'"
|
|
361
361
|
|
|
362
|
-
content, artefact_info = ArtefactReader.
|
|
362
|
+
content, artefact_info = ArtefactReader.read_artefact_data(
|
|
363
363
|
artefact_name=artefact_name,
|
|
364
364
|
classifier=classifier
|
|
365
365
|
)
|
|
@@ -367,7 +367,7 @@ def reconnect_action(args):
|
|
|
367
367
|
print(read_error_message)
|
|
368
368
|
return
|
|
369
369
|
|
|
370
|
-
parent_content, parent_info = ArtefactReader.
|
|
370
|
+
parent_content, parent_info = ArtefactReader.read_artefact_data(
|
|
371
371
|
artefact_name=parent_name,
|
|
372
372
|
classifier=parent_classifier
|
|
373
373
|
)
|
|
@@ -399,7 +399,7 @@ def reconnect_action(args):
|
|
|
399
399
|
exit(1)
|
|
400
400
|
|
|
401
401
|
artefact.contribution = contribution
|
|
402
|
-
with open(artefact.file_path, 'w') as file:
|
|
402
|
+
with open(artefact.file_path, 'w', encoding='utf-8') as file:
|
|
403
403
|
artefact_content = artefact.serialize()
|
|
404
404
|
file.write(artefact_content)
|
|
405
405
|
|
|
@@ -426,7 +426,7 @@ def read_status_action(args):
|
|
|
426
426
|
lambda x: x["title"] == artefact_name, artefact_info_dicts
|
|
427
427
|
))
|
|
428
428
|
|
|
429
|
-
with open(artefact_info["file_path"], 'r') as file:
|
|
429
|
+
with open(artefact_info["file_path"], 'r', encoding='utf-8') as file:
|
|
430
430
|
content = file.read()
|
|
431
431
|
artefact = artefact_from_content(content)
|
|
432
432
|
|
|
@@ -458,7 +458,7 @@ def read_user_action(args):
|
|
|
458
458
|
lambda x: x["title"] == artefact_name, artefact_info_dicts
|
|
459
459
|
))
|
|
460
460
|
|
|
461
|
-
with open(artefact_info["file_path"], 'r') as file:
|
|
461
|
+
with open(artefact_info["file_path"], 'r', encoding='utf-8') as file:
|
|
462
462
|
content = file.read()
|
|
463
463
|
artefact = artefact_from_content(content)
|
|
464
464
|
|
|
@@ -500,14 +500,14 @@ def set_status_action(args):
|
|
|
500
500
|
lambda x: x["title"] == artefact_name, classified_artefact_dict
|
|
501
501
|
))
|
|
502
502
|
|
|
503
|
-
with open(artefact_info["file_path"], 'r') as file:
|
|
503
|
+
with open(artefact_info["file_path"], 'r', encoding='utf-8') as file:
|
|
504
504
|
content = file.read()
|
|
505
505
|
artefact = artefact_from_content(content)
|
|
506
506
|
|
|
507
507
|
artefact.status = new_status
|
|
508
508
|
|
|
509
509
|
serialized_content = artefact.serialize()
|
|
510
|
-
with open(f"{artefact_info['file_path']}", 'w') as file:
|
|
510
|
+
with open(f"{artefact_info['file_path']}", 'w', encoding='utf-8') as file:
|
|
511
511
|
file.write(serialized_content)
|
|
512
512
|
|
|
513
513
|
print(f"Status of task '{artefact_name}' has been updated to '{new_status}'.")
|
|
@@ -537,7 +537,7 @@ def set_user_action(args):
|
|
|
537
537
|
lambda x: x["title"] == artefact_name, classified_artefact_dict
|
|
538
538
|
))
|
|
539
539
|
|
|
540
|
-
with open(artefact_info["file_path"], 'r') as file:
|
|
540
|
+
with open(artefact_info["file_path"], 'r', encoding='utf-8') as file:
|
|
541
541
|
content = file.read()
|
|
542
542
|
artefact = artefact_from_content(content)
|
|
543
543
|
|
|
@@ -545,7 +545,7 @@ def set_user_action(args):
|
|
|
545
545
|
|
|
546
546
|
serialized_content = artefact.serialize()
|
|
547
547
|
|
|
548
|
-
with open(artefact_info["file_path"], 'w') as file:
|
|
548
|
+
with open(artefact_info["file_path"], 'w', encoding='utf-8') as file:
|
|
549
549
|
file.write(serialized_content)
|
|
550
550
|
|
|
551
551
|
print(f"User of task '{artefact_name}' has been updated to '{new_user}'.")
|
|
@@ -575,6 +575,7 @@ def scan_action(args):
|
|
|
575
575
|
|
|
576
576
|
def autofix_action(args):
|
|
577
577
|
from ara_cli.artefact_autofix import parse_report, apply_autofix, read_report_file
|
|
578
|
+
from ara_cli.file_classifier import FileClassifier
|
|
578
579
|
|
|
579
580
|
# If the user passes --non-deterministic, only_deterministic_fix becomes False.
|
|
580
581
|
# If the user passes --deterministic, only_non_deterministic_fix becomes False.
|
|
@@ -591,6 +592,9 @@ def autofix_action(args):
|
|
|
591
592
|
print("No issues found in the report. Nothing to fix.")
|
|
592
593
|
return
|
|
593
594
|
|
|
595
|
+
file_classifier = FileClassifier(os)
|
|
596
|
+
classified_artefact_info = file_classifier.classify_files()
|
|
597
|
+
|
|
594
598
|
# print("\nStarting autofix process...")
|
|
595
599
|
for classifier, files in issues.items():
|
|
596
600
|
print(f"\nClassifier: {classifier}")
|
|
@@ -600,8 +604,8 @@ def autofix_action(args):
|
|
|
600
604
|
classifier,
|
|
601
605
|
reason,
|
|
602
606
|
deterministic=run_deterministic,
|
|
603
|
-
non_deterministic=run_non_deterministic
|
|
607
|
+
non_deterministic=run_non_deterministic,
|
|
608
|
+
classified_artefact_info=classified_artefact_info
|
|
604
609
|
)
|
|
605
610
|
|
|
606
611
|
print("\nAutofix process completed. Please review the changes.")
|
|
607
|
-
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import List, Dict,
|
|
1
|
+
from typing import List, Dict, Optional
|
|
2
2
|
from pydantic import BaseModel
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
@@ -10,6 +10,13 @@ from functools import lru_cache
|
|
|
10
10
|
DEFAULT_CONFIG_LOCATION = "./ara/.araconfig/ara_config.json"
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
class LLMConfigItem(BaseModel):
|
|
14
|
+
provider: str
|
|
15
|
+
model: str
|
|
16
|
+
temperature: float
|
|
17
|
+
max_tokens: Optional[int] = None
|
|
18
|
+
|
|
19
|
+
|
|
13
20
|
class ARAconfig(BaseModel):
|
|
14
21
|
ext_code_dirs: List[Dict[str, str]] = [
|
|
15
22
|
{"source_dir_1": "./src"},
|
|
@@ -36,42 +43,49 @@ class ARAconfig(BaseModel):
|
|
|
36
43
|
"*.jpg",
|
|
37
44
|
"*.jpeg",
|
|
38
45
|
]
|
|
39
|
-
llm_config: Dict[str,
|
|
46
|
+
llm_config: Dict[str, LLMConfigItem] = {
|
|
40
47
|
"gpt-4o": {
|
|
41
48
|
"provider": "openai",
|
|
42
49
|
"model": "openai/gpt-4o",
|
|
43
|
-
"temperature": 0.8
|
|
50
|
+
"temperature": 0.8,
|
|
51
|
+
"max_tokens": 16384
|
|
44
52
|
},
|
|
45
53
|
"gpt-4.1": {
|
|
46
54
|
"provider": "openai",
|
|
47
55
|
"model": "openai/gpt-4.1",
|
|
48
56
|
"temperature": 0.8,
|
|
57
|
+
"max_tokens": 1024
|
|
49
58
|
},
|
|
50
59
|
"o3-mini": {
|
|
51
60
|
"provider": "openai",
|
|
52
61
|
"model": "openai/o3-mini",
|
|
53
62
|
"temperature": 1.0,
|
|
63
|
+
"max_tokens": 1024
|
|
54
64
|
},
|
|
55
65
|
"opus-4": {
|
|
56
66
|
"provider": "anthropic",
|
|
57
67
|
"model": "anthropic/claude-opus-4-20250514",
|
|
58
68
|
"temperature": 0.8,
|
|
69
|
+
"max_tokens": 32000
|
|
59
70
|
},
|
|
60
71
|
"sonnet-4": {
|
|
61
72
|
"provider": "anthropic",
|
|
62
73
|
"model": "anthropic/claude-sonnet-4-20250514",
|
|
63
74
|
"temperature": 0.8,
|
|
75
|
+
"max_tokens": 1024
|
|
64
76
|
},
|
|
65
77
|
"together-ai-llama-2": {
|
|
66
78
|
"provider": "together_ai",
|
|
67
79
|
"model": "together_ai/togethercomputer/llama-2-70b",
|
|
68
80
|
"temperature": 0.8,
|
|
81
|
+
"max_tokens": 1024
|
|
69
82
|
},
|
|
70
83
|
"groq-llama-3": {
|
|
71
84
|
"provider": "groq",
|
|
72
85
|
"model": "groq/llama3-70b-8192",
|
|
73
86
|
"temperature": 0.8,
|
|
74
|
-
|
|
87
|
+
"max_tokens": 1024
|
|
88
|
+
}
|
|
75
89
|
}
|
|
76
90
|
default_llm: Optional[str] = "gpt-4o"
|
|
77
91
|
|
|
@@ -86,7 +100,7 @@ def ensure_directory_exists(directory: str):
|
|
|
86
100
|
|
|
87
101
|
|
|
88
102
|
def validate_config_data(filepath: str):
|
|
89
|
-
with open(filepath, "r") as file:
|
|
103
|
+
with open(filepath, "r", encoding="utf-8") as file:
|
|
90
104
|
data = json.load(file)
|
|
91
105
|
return data
|
|
92
106
|
|
|
@@ -98,8 +112,8 @@ def read_data(filepath: str) -> ARAconfig:
|
|
|
98
112
|
# If file does not exist, create it with default values
|
|
99
113
|
default_config = ARAconfig()
|
|
100
114
|
|
|
101
|
-
with open(filepath, "w") as file:
|
|
102
|
-
json.dump(default_config.model_dump(), file, indent=4)
|
|
115
|
+
with open(filepath, "w", encoding="utf-8") as file:
|
|
116
|
+
json.dump(default_config.model_dump(mode='json'), file, indent=4)
|
|
103
117
|
|
|
104
118
|
print(
|
|
105
119
|
f"ara-cli configuration file '{filepath}' created with default configuration. Please modify it as needed and re-run your command"
|
|
@@ -112,8 +126,8 @@ def read_data(filepath: str) -> ARAconfig:
|
|
|
112
126
|
|
|
113
127
|
# Function to save the modified configuration back to the JSON file
|
|
114
128
|
def save_data(filepath: str, config: ARAconfig):
|
|
115
|
-
with open(filepath, "w") as file:
|
|
116
|
-
json.dump(config.
|
|
129
|
+
with open(filepath, "w", encoding="utf-8") as file:
|
|
130
|
+
json.dump(config.model_dump(mode='json'), file, indent=4)
|
|
117
131
|
|
|
118
132
|
|
|
119
133
|
# Singleton for configuration management
|
|
@@ -129,4 +143,4 @@ class ConfigManager:
|
|
|
129
143
|
makedirs(config_dir)
|
|
130
144
|
|
|
131
145
|
cls._config_instance = read_data(filepath)
|
|
132
|
-
return cls._config_instance
|
|
146
|
+
return cls._config_instance
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
from ara_cli.artefact_fuzzy_search import (
|
|
2
|
+
find_closest_name_matches,
|
|
3
|
+
extract_artefact_names_of_classifier,
|
|
4
|
+
)
|
|
5
|
+
from ara_cli.file_classifier import FileClassifier
|
|
6
|
+
from ara_cli.artefact_reader import ArtefactReader
|
|
7
|
+
from ara_cli.artefact_models.artefact_load import artefact_from_content
|
|
8
|
+
from ara_cli.artefact_models.artefact_model import Artefact
|
|
9
|
+
from typing import Optional, Dict, List, Tuple
|
|
10
|
+
import difflib
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def read_report_file():
|
|
15
|
+
file_path = "incompatible_artefacts_report.md"
|
|
16
|
+
try:
|
|
17
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
18
|
+
content = f.read()
|
|
19
|
+
except OSError:
|
|
20
|
+
print(
|
|
21
|
+
'Artefact scan results file not found. Did you run the "ara scan" command?'
|
|
22
|
+
)
|
|
23
|
+
return None
|
|
24
|
+
return content
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def parse_report(content: str) -> Dict[str, List[Tuple[str, str]]]:
|
|
28
|
+
"""
|
|
29
|
+
Parses the incompatible artefacts report and returns structured data.
|
|
30
|
+
Returns a dictionary where keys are artefact classifiers, and values are lists of (file_path, reason) tuples.
|
|
31
|
+
"""
|
|
32
|
+
lines = content.splitlines()
|
|
33
|
+
issues = {}
|
|
34
|
+
current_classifier = None
|
|
35
|
+
|
|
36
|
+
if not lines or lines[0] != "# Artefact Check Report":
|
|
37
|
+
return issues
|
|
38
|
+
return issues
|
|
39
|
+
|
|
40
|
+
if len(lines) >= 3 and lines[2] == "No problems found.":
|
|
41
|
+
return issues
|
|
42
|
+
return issues
|
|
43
|
+
|
|
44
|
+
for line in lines[1:]:
|
|
45
|
+
line = line.strip()
|
|
46
|
+
if not line:
|
|
47
|
+
continue
|
|
48
|
+
|
|
49
|
+
if line.startswith("## "):
|
|
50
|
+
current_classifier = line[3:].strip()
|
|
51
|
+
issues[current_classifier] = []
|
|
52
|
+
|
|
53
|
+
elif line.startswith("- ") and current_classifier is not None:
|
|
54
|
+
parts = line.split("`", 2)
|
|
55
|
+
if len(parts) < 3:
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
file_path = parts[1]
|
|
59
|
+
reason = parts[2].split(":", 1)[1].strip() if ":" in parts[2] else ""
|
|
60
|
+
issues[current_classifier].append((file_path, reason))
|
|
61
|
+
|
|
62
|
+
return issues
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def read_artefact(file_path):
|
|
66
|
+
"""Reads the artefact text from the given file path."""
|
|
67
|
+
try:
|
|
68
|
+
with open(file_path, "r", encoding="utf-8") as file:
|
|
69
|
+
return file.read()
|
|
70
|
+
except FileNotFoundError:
|
|
71
|
+
print(f"File not found: {file_path}")
|
|
72
|
+
return None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def determine_artefact_type_and_class(classifier):
|
|
76
|
+
from ara_cli.artefact_models.artefact_mapping import artefact_type_mapping
|
|
77
|
+
from ara_cli.artefact_models.artefact_model import ArtefactType
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
artefact_type = ArtefactType(classifier)
|
|
81
|
+
except ValueError:
|
|
82
|
+
print(f"Invalid classifier: {classifier}")
|
|
83
|
+
return None, None
|
|
84
|
+
|
|
85
|
+
artefact_class = artefact_type_mapping.get(artefact_type)
|
|
86
|
+
if not artefact_class:
|
|
87
|
+
print(f"No artefact class found for {artefact_type}")
|
|
88
|
+
return None, None
|
|
89
|
+
|
|
90
|
+
return artefact_type, artefact_class
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def construct_prompt(artefact_type, reason, file_path, artefact_text):
|
|
94
|
+
from ara_cli.artefact_models.artefact_model import ArtefactType
|
|
95
|
+
|
|
96
|
+
prompt = (
|
|
97
|
+
f"Correct the following {artefact_type.value} artefact to fix the issue: {reason}. "
|
|
98
|
+
"Provide the corrected artefact. Do not reformulate the artefact, "
|
|
99
|
+
"just fix the pydantic model errors, use correct grammar. "
|
|
100
|
+
"You should follow the name of the file "
|
|
101
|
+
f"from its path {file_path} for naming the artefact's title. "
|
|
102
|
+
"You are not allowed to use file extention in the artefact title. "
|
|
103
|
+
"You are not allowed to modify, delete or add tags. "
|
|
104
|
+
"User tag should be '@user_<username>'. The pydantic model already provides the '@user_' prefix. "
|
|
105
|
+
"So you should be careful to not make it @user_user_<username>. "
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if artefact_type == ArtefactType.task:
|
|
109
|
+
prompt += (
|
|
110
|
+
"For task artefacts, if the action items looks like template or empty "
|
|
111
|
+
"then just delete those action items."
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
prompt += "\nThe current artefact is:\n" "```\n" f"{artefact_text}\n" "```"
|
|
115
|
+
|
|
116
|
+
return prompt
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def run_agent(prompt, artefact_class):
|
|
120
|
+
from pydantic_ai import Agent
|
|
121
|
+
|
|
122
|
+
# gpt-4o
|
|
123
|
+
# anthropic:claude-3-7-sonnet-20250219
|
|
124
|
+
# anthropic:claude-4-sonnet-20250514
|
|
125
|
+
agent = Agent(
|
|
126
|
+
model="anthropic:claude-4-sonnet-20250514",
|
|
127
|
+
result_type=artefact_class,
|
|
128
|
+
instrument=True,
|
|
129
|
+
)
|
|
130
|
+
result = agent.run_sync(prompt)
|
|
131
|
+
return result.data
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def write_corrected_artefact(file_path, corrected_text):
|
|
135
|
+
with open(file_path, "w", encoding="utf-8") as file:
|
|
136
|
+
file.write(corrected_text)
|
|
137
|
+
print(f"Fixed artefact at {file_path}")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def ask_for_correct_contribution(
|
|
141
|
+
artefact_info: Optional[tuple[str, str]] = None
|
|
142
|
+
) -> tuple[str, str]:
|
|
143
|
+
"""
|
|
144
|
+
Ask the user to provide a valid contribution when no match can be found.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
artefact_info: Optional tuple containing (artefact_name, artefact_classifier)
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
A tuple of (name, classifier) for the contribution
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
artefact_name, artefact_classifier = (
|
|
154
|
+
artefact_info if artefact_info else (None, None)
|
|
155
|
+
)
|
|
156
|
+
contribution_message = (
|
|
157
|
+
f"of {artefact_classifier} artefact '{artefact_name}'" if artefact_name else ""
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
print(
|
|
161
|
+
f"Can not determine a match for contribution {contribution_message}. "
|
|
162
|
+
f"Please provide a valid contribution or contribution will be empty (<classifier> <file_name>)."
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
user_input = input().strip()
|
|
166
|
+
|
|
167
|
+
if not user_input:
|
|
168
|
+
return None, None
|
|
169
|
+
|
|
170
|
+
parts = user_input.split(maxsplit=1)
|
|
171
|
+
if len(parts) != 2:
|
|
172
|
+
print("Invalid input format. Expected: <classifier> <file_name>")
|
|
173
|
+
return None, None
|
|
174
|
+
|
|
175
|
+
classifier, name = parts
|
|
176
|
+
return name, classifier
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def ask_for_contribution_choice(
|
|
180
|
+
choices, artefact_info: Optional[tuple[str, str]] = None
|
|
181
|
+
) -> Optional[str]:
|
|
182
|
+
artefact_name, artefact_classifier = artefact_info
|
|
183
|
+
message = "Found multiple close matches for the contribution"
|
|
184
|
+
if artefact_name and artefact_classifier:
|
|
185
|
+
message += f" of the {artefact_classifier} '{artefact_name}'"
|
|
186
|
+
print(f"{message}.")
|
|
187
|
+
for i, contribution in enumerate(choices):
|
|
188
|
+
print(f"{i + 1}: {contribution}")
|
|
189
|
+
choice_number = input(
|
|
190
|
+
"Please choose the artefact to use for contribution (enter number): "
|
|
191
|
+
)
|
|
192
|
+
try:
|
|
193
|
+
choice_index = int(choice_number) - 1
|
|
194
|
+
if choice_index < 0 or choice_index >= len(choices):
|
|
195
|
+
print("Invalid choice. Aborting contribution choice.")
|
|
196
|
+
return None
|
|
197
|
+
choice = choices[choice_index]
|
|
198
|
+
except ValueError:
|
|
199
|
+
print("Invalid input. Aborting contribution choice.")
|
|
200
|
+
return None
|
|
201
|
+
return choice
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _has_valid_contribution(artefact: Artefact) -> bool:
|
|
205
|
+
contribution = artefact.contribution
|
|
206
|
+
return contribution and contribution.artefact_name and contribution.classifier
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _update_rule(
|
|
210
|
+
artefact: Artefact, name: str, classifier: str, classified_file_info: dict
|
|
211
|
+
) -> None:
|
|
212
|
+
"""Updates the rule in the contribution if a close match is found."""
|
|
213
|
+
rule = artefact.contribution.rule
|
|
214
|
+
|
|
215
|
+
content, artefact_data = ArtefactReader.read_artefact_data(
|
|
216
|
+
artefact_name=name,
|
|
217
|
+
classifier=classifier,
|
|
218
|
+
classified_file_info=classified_file_info,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
parent = artefact_from_content(content=content)
|
|
222
|
+
rules = parent.rules
|
|
223
|
+
|
|
224
|
+
closest_rule_match = difflib.get_close_matches(rule, rules, cutoff=0.5)
|
|
225
|
+
if closest_rule_match:
|
|
226
|
+
artefact.contribution.rule = closest_rule_match[0]
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _set_contribution_multiple_matches(
|
|
230
|
+
artefact: Artefact,
|
|
231
|
+
closest_matches: list,
|
|
232
|
+
artefact_tuple: tuple,
|
|
233
|
+
classified_file_info: dict,
|
|
234
|
+
) -> tuple[Artefact, bool]:
|
|
235
|
+
contribution = artefact.contribution
|
|
236
|
+
classifier = contribution.classifier
|
|
237
|
+
original_name = contribution.artefact_name
|
|
238
|
+
|
|
239
|
+
closest_match = closest_matches[0]
|
|
240
|
+
if len(closest_matches) > 1:
|
|
241
|
+
closest_match = ask_for_contribution_choice(closest_matches, artefact_tuple)
|
|
242
|
+
|
|
243
|
+
if not closest_match:
|
|
244
|
+
print(
|
|
245
|
+
f"Contribution of {artefact_tuple[1]} '{artefact_tuple[0]}' will be empty."
|
|
246
|
+
)
|
|
247
|
+
artefact.contribution = None
|
|
248
|
+
return artefact, True
|
|
249
|
+
|
|
250
|
+
print(
|
|
251
|
+
f"Updating contribution of {artefact_tuple[1]} '{artefact_tuple[0]}' to {classifier} '{closest_match}'"
|
|
252
|
+
)
|
|
253
|
+
contribution.artefact_name = closest_match
|
|
254
|
+
artefact.contribution = contribution
|
|
255
|
+
|
|
256
|
+
if contribution.rule:
|
|
257
|
+
_update_rule(artefact, original_name, classifier, classified_file_info)
|
|
258
|
+
|
|
259
|
+
return artefact, True
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def set_closest_contribution(
|
|
263
|
+
artefact: Artefact, classified_file_info=None
|
|
264
|
+
) -> tuple[Artefact, bool]:
|
|
265
|
+
if not _has_valid_contribution(artefact):
|
|
266
|
+
return artefact, False
|
|
267
|
+
contribution = artefact.contribution
|
|
268
|
+
name = contribution.artefact_name
|
|
269
|
+
classifier = contribution.classifier
|
|
270
|
+
rule = contribution.rule
|
|
271
|
+
|
|
272
|
+
if not classified_file_info:
|
|
273
|
+
file_classifier = FileClassifier(os)
|
|
274
|
+
classified_file_info = file_classifier.classify_files()
|
|
275
|
+
|
|
276
|
+
all_artefact_names = extract_artefact_names_of_classifier(
|
|
277
|
+
classified_files=classified_file_info, classifier=classifier
|
|
278
|
+
)
|
|
279
|
+
closest_matches = find_closest_name_matches(
|
|
280
|
+
artefact_name=name, all_artefact_names=all_artefact_names
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
artefact_tuple = (artefact.title, artefact._artefact_type().value)
|
|
284
|
+
|
|
285
|
+
if not closest_matches:
|
|
286
|
+
name, classifier = ask_for_correct_contribution(artefact_tuple)
|
|
287
|
+
if not name or not classifier:
|
|
288
|
+
artefact.contribution = None
|
|
289
|
+
return artefact, True
|
|
290
|
+
print(f"Updating contribution of {artefact._artefact_type().value} '{artefact.title}' to {classifier} '{name}'")
|
|
291
|
+
contribution.artefact_name = name
|
|
292
|
+
contribution.classifier = classifier
|
|
293
|
+
artefact.contribution = contribution
|
|
294
|
+
return artefact, True
|
|
295
|
+
|
|
296
|
+
if closest_matches[0] == name:
|
|
297
|
+
return artefact, False
|
|
298
|
+
|
|
299
|
+
return _set_contribution_multiple_matches(
|
|
300
|
+
artefact=artefact,
|
|
301
|
+
closest_matches=closest_matches,
|
|
302
|
+
artefact_tuple=artefact_tuple,
|
|
303
|
+
classified_file_info=classified_file_info,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
print(
|
|
307
|
+
f"Updating contribution of {artefact._artefact_type().value} '{artefact.title}' to {classifier} '{closest_match}'"
|
|
308
|
+
)
|
|
309
|
+
contribution.artefact_name = closest_match
|
|
310
|
+
artefact.contribution = contribution
|
|
311
|
+
|
|
312
|
+
if not rule:
|
|
313
|
+
return artefact, True
|
|
314
|
+
|
|
315
|
+
content, artefact = ArtefactReader.read_artefact_data(
|
|
316
|
+
artefact_name=name,
|
|
317
|
+
classifier=classifier,
|
|
318
|
+
classified_file_info=classified_file_info,
|
|
319
|
+
)
|
|
320
|
+
parent = artefact_from_content(content=content)
|
|
321
|
+
rules = parent.rules
|
|
322
|
+
|
|
323
|
+
closest_rule_match = difflib.get_close_matches(rule, rules, cutoff=0.5)
|
|
324
|
+
if closest_rule_match:
|
|
325
|
+
contribution.rule = closest_rule_match
|
|
326
|
+
artefact.contribution = contribution
|
|
327
|
+
return artefact, True
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def fix_title_mismatch(
|
|
331
|
+
file_path: str, artefact_text: str, artefact_class, **kwargs
|
|
332
|
+
) -> str:
|
|
333
|
+
"""
|
|
334
|
+
Deterministically fixes the title in the artefact text to match the filename.
|
|
335
|
+
"""
|
|
336
|
+
base_name = os.path.basename(file_path)
|
|
337
|
+
correct_title_underscores, _ = os.path.splitext(base_name)
|
|
338
|
+
correct_title_spaces = correct_title_underscores.replace("_", " ")
|
|
339
|
+
|
|
340
|
+
title_prefix = artefact_class._title_prefix()
|
|
341
|
+
|
|
342
|
+
lines = artefact_text.splitlines()
|
|
343
|
+
new_lines = []
|
|
344
|
+
title_found_and_replaced = False
|
|
345
|
+
|
|
346
|
+
for line in lines:
|
|
347
|
+
if not title_found_and_replaced and line.strip().startswith(title_prefix):
|
|
348
|
+
new_lines.append(f"{title_prefix} {correct_title_spaces}")
|
|
349
|
+
title_found_and_replaced = True
|
|
350
|
+
else:
|
|
351
|
+
new_lines.append(line)
|
|
352
|
+
|
|
353
|
+
if not title_found_and_replaced:
|
|
354
|
+
print(
|
|
355
|
+
f"Warning: Title prefix '{title_prefix}' not found in {file_path}. Title could not be fixed."
|
|
356
|
+
)
|
|
357
|
+
return artefact_text
|
|
358
|
+
|
|
359
|
+
return "\n".join(new_lines)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def fix_contribution(
|
|
363
|
+
file_path: str,
|
|
364
|
+
artefact_text: str,
|
|
365
|
+
artefact_class: str,
|
|
366
|
+
classified_artefact_info: dict,
|
|
367
|
+
**kwargs,
|
|
368
|
+
):
|
|
369
|
+
if not classified_artefact_info:
|
|
370
|
+
file_classifier = FileClassifier(os)
|
|
371
|
+
classified_artefact_info = file_classifier.classify_files()
|
|
372
|
+
artefact = artefact_class.deserialize(artefact_text)
|
|
373
|
+
artefact, _ = set_closest_contribution(artefact)
|
|
374
|
+
artefact_text = artefact.serialize()
|
|
375
|
+
return artefact_text
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def apply_autofix(
|
|
379
|
+
file_path: str,
|
|
380
|
+
classifier: str,
|
|
381
|
+
reason: str,
|
|
382
|
+
deterministic: bool = True,
|
|
383
|
+
non_deterministic: bool = True,
|
|
384
|
+
classified_artefact_info: Optional[Dict[str, List[Dict[str, str]]]] = None,
|
|
385
|
+
) -> bool:
|
|
386
|
+
artefact_text = read_artefact(file_path)
|
|
387
|
+
if artefact_text is None:
|
|
388
|
+
return False
|
|
389
|
+
|
|
390
|
+
artefact_type, artefact_class = determine_artefact_type_and_class(classifier)
|
|
391
|
+
if artefact_type is None or artefact_class is None:
|
|
392
|
+
return False
|
|
393
|
+
|
|
394
|
+
if classified_artefact_info is None:
|
|
395
|
+
file_classifier = FileClassifier(os)
|
|
396
|
+
classified_file_info = file_classifier.classified_files()
|
|
397
|
+
|
|
398
|
+
deterministic_markers_to_functions = {
|
|
399
|
+
"Filename-Title Mismatch": fix_title_mismatch,
|
|
400
|
+
"Invalid Contribution Reference": fix_contribution,
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
try:
|
|
404
|
+
deterministic_issue = next(
|
|
405
|
+
(
|
|
406
|
+
marker
|
|
407
|
+
for marker in deterministic_markers_to_functions.keys()
|
|
408
|
+
if marker in reason
|
|
409
|
+
),
|
|
410
|
+
None,
|
|
411
|
+
)
|
|
412
|
+
except StopIteration:
|
|
413
|
+
pass
|
|
414
|
+
is_deterministic_issue = deterministic_issue is not None
|
|
415
|
+
|
|
416
|
+
if deterministic and is_deterministic_issue:
|
|
417
|
+
print(f"Attempting deterministic fix for {file_path}...")
|
|
418
|
+
corrected_text = deterministic_markers_to_functions[deterministic_issue](
|
|
419
|
+
file_path=file_path,
|
|
420
|
+
artefact_text=artefact_text,
|
|
421
|
+
artefact_class=artefact_class,
|
|
422
|
+
classified_artefact_info=classified_artefact_info,
|
|
423
|
+
)
|
|
424
|
+
write_corrected_artefact(file_path, corrected_text)
|
|
425
|
+
return True
|
|
426
|
+
|
|
427
|
+
# Attempt non-deterministic fix if requested and the issue is NOT deterministic
|
|
428
|
+
if non_deterministic and not is_deterministic_issue:
|
|
429
|
+
print(f"Attempting non-deterministic (LLM) fix for {file_path}...")
|
|
430
|
+
prompt = construct_prompt(artefact_type, reason, file_path, artefact_text)
|
|
431
|
+
try:
|
|
432
|
+
corrected_artefact = run_agent(prompt, artefact_class)
|
|
433
|
+
corrected_text = corrected_artefact.serialize()
|
|
434
|
+
write_corrected_artefact(file_path, corrected_text)
|
|
435
|
+
return True
|
|
436
|
+
except Exception as e:
|
|
437
|
+
print(f"LLM agent failed to fix artefact at {file_path}: {e}")
|
|
438
|
+
return False
|
|
439
|
+
|
|
440
|
+
# Log if a fix was skipped due to flags
|
|
441
|
+
if is_deterministic_issue and not deterministic:
|
|
442
|
+
print(f"Skipping deterministic fix for {file_path} as per request.")
|
|
443
|
+
elif not is_deterministic_issue and not non_deterministic:
|
|
444
|
+
print(f"Skipping non-deterministic fix for {file_path} as per request.")
|
|
445
|
+
|
|
446
|
+
return False
|