ara-cli 0.1.9.69__py3-none-any.whl → 0.1.10.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ara-cli might be problematic. Click here for more details.

Files changed (150) hide show
  1. ara_cli/__init__.py +18 -2
  2. ara_cli/__main__.py +248 -62
  3. ara_cli/ara_command_action.py +155 -86
  4. ara_cli/ara_config.py +226 -80
  5. ara_cli/ara_subcommands/__init__.py +0 -0
  6. ara_cli/ara_subcommands/autofix.py +26 -0
  7. ara_cli/ara_subcommands/chat.py +27 -0
  8. ara_cli/ara_subcommands/classifier_directory.py +16 -0
  9. ara_cli/ara_subcommands/common.py +100 -0
  10. ara_cli/ara_subcommands/create.py +75 -0
  11. ara_cli/ara_subcommands/delete.py +22 -0
  12. ara_cli/ara_subcommands/extract.py +22 -0
  13. ara_cli/ara_subcommands/fetch_templates.py +14 -0
  14. ara_cli/ara_subcommands/list.py +65 -0
  15. ara_cli/ara_subcommands/list_tags.py +25 -0
  16. ara_cli/ara_subcommands/load.py +48 -0
  17. ara_cli/ara_subcommands/prompt.py +136 -0
  18. ara_cli/ara_subcommands/read.py +47 -0
  19. ara_cli/ara_subcommands/read_status.py +20 -0
  20. ara_cli/ara_subcommands/read_user.py +20 -0
  21. ara_cli/ara_subcommands/reconnect.py +27 -0
  22. ara_cli/ara_subcommands/rename.py +22 -0
  23. ara_cli/ara_subcommands/scan.py +14 -0
  24. ara_cli/ara_subcommands/set_status.py +22 -0
  25. ara_cli/ara_subcommands/set_user.py +22 -0
  26. ara_cli/ara_subcommands/template.py +16 -0
  27. ara_cli/artefact_autofix.py +649 -68
  28. ara_cli/artefact_creator.py +8 -11
  29. ara_cli/artefact_deleter.py +2 -4
  30. ara_cli/artefact_fuzzy_search.py +22 -10
  31. ara_cli/artefact_link_updater.py +4 -4
  32. ara_cli/artefact_lister.py +29 -55
  33. ara_cli/artefact_models/artefact_data_retrieval.py +23 -0
  34. ara_cli/artefact_models/artefact_load.py +11 -3
  35. ara_cli/artefact_models/artefact_model.py +146 -39
  36. ara_cli/artefact_models/artefact_templates.py +70 -44
  37. ara_cli/artefact_models/businessgoal_artefact_model.py +23 -25
  38. ara_cli/artefact_models/epic_artefact_model.py +34 -26
  39. ara_cli/artefact_models/feature_artefact_model.py +203 -64
  40. ara_cli/artefact_models/keyfeature_artefact_model.py +21 -24
  41. ara_cli/artefact_models/serialize_helper.py +1 -1
  42. ara_cli/artefact_models/task_artefact_model.py +83 -15
  43. ara_cli/artefact_models/userstory_artefact_model.py +37 -27
  44. ara_cli/artefact_models/vision_artefact_model.py +23 -42
  45. ara_cli/artefact_reader.py +92 -91
  46. ara_cli/artefact_renamer.py +8 -4
  47. ara_cli/artefact_scan.py +66 -3
  48. ara_cli/chat.py +622 -162
  49. ara_cli/chat_agent/__init__.py +0 -0
  50. ara_cli/chat_agent/agent_communicator.py +62 -0
  51. ara_cli/chat_agent/agent_process_manager.py +211 -0
  52. ara_cli/chat_agent/agent_status_manager.py +73 -0
  53. ara_cli/chat_agent/agent_workspace_manager.py +76 -0
  54. ara_cli/commands/__init__.py +0 -0
  55. ara_cli/commands/command.py +7 -0
  56. ara_cli/commands/extract_command.py +15 -0
  57. ara_cli/commands/load_command.py +65 -0
  58. ara_cli/commands/load_image_command.py +34 -0
  59. ara_cli/commands/read_command.py +117 -0
  60. ara_cli/completers.py +144 -0
  61. ara_cli/directory_navigator.py +37 -4
  62. ara_cli/error_handler.py +134 -0
  63. ara_cli/file_classifier.py +6 -5
  64. ara_cli/file_lister.py +1 -1
  65. ara_cli/file_loaders/__init__.py +0 -0
  66. ara_cli/file_loaders/binary_file_loader.py +33 -0
  67. ara_cli/file_loaders/document_file_loader.py +34 -0
  68. ara_cli/file_loaders/document_reader.py +245 -0
  69. ara_cli/file_loaders/document_readers.py +233 -0
  70. ara_cli/file_loaders/file_loader.py +50 -0
  71. ara_cli/file_loaders/file_loaders.py +123 -0
  72. ara_cli/file_loaders/image_processor.py +89 -0
  73. ara_cli/file_loaders/markdown_reader.py +75 -0
  74. ara_cli/file_loaders/text_file_loader.py +187 -0
  75. ara_cli/global_file_lister.py +51 -0
  76. ara_cli/list_filter.py +1 -1
  77. ara_cli/output_suppressor.py +1 -1
  78. ara_cli/prompt_extractor.py +215 -88
  79. ara_cli/prompt_handler.py +521 -134
  80. ara_cli/prompt_rag.py +2 -2
  81. ara_cli/tag_extractor.py +83 -38
  82. ara_cli/template_loader.py +245 -0
  83. ara_cli/template_manager.py +18 -13
  84. ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
  85. ara_cli/templates/prompt-modules/commands/extract_general.commands.md +12 -0
  86. ara_cli/templates/prompt-modules/commands/extract_markdown.commands.md +11 -0
  87. ara_cli/templates/prompt-modules/commands/extract_python.commands.md +13 -0
  88. ara_cli/templates/prompt-modules/commands/feature_add_or_modifiy_specified_behavior.commands.md +36 -0
  89. ara_cli/templates/prompt-modules/commands/feature_generate_initial_specified_bevahior.commands.md +53 -0
  90. ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
  91. ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
  92. ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
  93. ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
  94. ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
  95. ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
  96. ara_cli/update_config_prompt.py +9 -3
  97. ara_cli/version.py +1 -1
  98. ara_cli-0.1.10.8.dist-info/METADATA +241 -0
  99. ara_cli-0.1.10.8.dist-info/RECORD +193 -0
  100. tests/test_ara_command_action.py +73 -59
  101. tests/test_ara_config.py +341 -36
  102. tests/test_artefact_autofix.py +1060 -0
  103. tests/test_artefact_link_updater.py +3 -3
  104. tests/test_artefact_lister.py +52 -132
  105. tests/test_artefact_renamer.py +2 -2
  106. tests/test_artefact_scan.py +327 -33
  107. tests/test_chat.py +2063 -498
  108. tests/test_file_classifier.py +24 -1
  109. tests/test_file_creator.py +3 -5
  110. tests/test_file_lister.py +1 -1
  111. tests/test_global_file_lister.py +131 -0
  112. tests/test_list_filter.py +2 -2
  113. tests/test_prompt_handler.py +746 -0
  114. tests/test_tag_extractor.py +19 -13
  115. tests/test_template_loader.py +192 -0
  116. tests/test_template_manager.py +5 -4
  117. tests/test_update_config_prompt.py +2 -2
  118. ara_cli/ara_command_parser.py +0 -327
  119. ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
  120. ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
  121. ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
  122. ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
  123. ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
  124. ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
  125. ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
  126. ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
  127. ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
  128. ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
  129. ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
  130. ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
  131. ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
  132. ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
  133. ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
  134. ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
  135. ara_cli/templates/template.businessgoal +0 -10
  136. ara_cli/templates/template.capability +0 -10
  137. ara_cli/templates/template.epic +0 -15
  138. ara_cli/templates/template.example +0 -6
  139. ara_cli/templates/template.feature +0 -26
  140. ara_cli/templates/template.issue +0 -14
  141. ara_cli/templates/template.keyfeature +0 -15
  142. ara_cli/templates/template.task +0 -6
  143. ara_cli/templates/template.userstory +0 -17
  144. ara_cli/templates/template.vision +0 -14
  145. ara_cli-0.1.9.69.dist-info/METADATA +0 -16
  146. ara_cli-0.1.9.69.dist-info/RECORD +0 -158
  147. tests/test_ara_autofix.py +0 -219
  148. {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/WHEEL +0 -0
  149. {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/entry_points.txt +0 -0
  150. {ara_cli-0.1.9.69.dist-info → ara_cli-0.1.10.8.dist-info}/top_level.txt +0 -0
ara_cli/completers.py ADDED
@@ -0,0 +1,144 @@
1
+ import os
2
+ from typing import List, Optional
3
+ from pathlib import Path
4
+ import typer
5
+
6
+ from ara_cli.classifier import Classifier
7
+ from ara_cli.template_manager import SpecificationBreakdownAspects
8
+
9
+
10
+ def complete_classifier(incomplete: str) -> List[str]:
11
+ """Complete classifier names."""
12
+ classifiers = Classifier.ordered_classifiers()
13
+ return [c for c in classifiers if c.startswith(incomplete)]
14
+
15
+
16
+ def complete_aspect(incomplete: str) -> List[str]:
17
+ """Complete aspect names."""
18
+ aspects = SpecificationBreakdownAspects.VALID_ASPECTS
19
+ return [a for a in aspects if a.startswith(incomplete)]
20
+
21
+
22
+ def complete_status(incomplete: str) -> List[str]:
23
+ """Complete task status values."""
24
+ statuses = ["to-do", "in-progress", "review", "done", "closed"]
25
+ return [s for s in statuses if s.startswith(incomplete)]
26
+
27
+
28
+ def complete_template_type(incomplete: str) -> List[str]:
29
+ """Complete template type values."""
30
+ template_types = ["rules", "intention", "commands", "blueprint"]
31
+ return [t for t in template_types if t.startswith(incomplete)]
32
+
33
+
34
+ def complete_artefact_name(classifier: str) -> List[str]:
35
+ """Complete artefact names for a given classifier."""
36
+ try:
37
+ # Get the directory for the classifier
38
+ classifier_dir = f"ara/{Classifier.get_sub_directory(classifier)}"
39
+
40
+ if not os.path.exists(classifier_dir):
41
+ return []
42
+
43
+ # Find all files with the classifier extension
44
+ artefacts = []
45
+ for file in os.listdir(classifier_dir):
46
+ if file.endswith(f'.{classifier}'):
47
+ # Remove the extension to get the artefact name
48
+ name = file[:-len(f'.{classifier}')]
49
+ artefacts.append(name)
50
+
51
+ return sorted(artefacts)
52
+ except Exception:
53
+ return []
54
+
55
+
56
+ def complete_artefact_name_for_classifier(classifier: str):
57
+ """Create a completer function for artefact names of a specific classifier."""
58
+ def completer(incomplete: str) -> List[str]:
59
+ artefacts = complete_artefact_name(classifier)
60
+ return [a for a in artefacts if a.startswith(incomplete)]
61
+ return completer
62
+
63
+
64
+ def complete_chat_files(incomplete: str) -> List[str]:
65
+ """Complete chat file names (without .md extension)."""
66
+ try:
67
+ chat_files = []
68
+ current_dir = Path.cwd()
69
+
70
+ # Look for .md files in current directory
71
+ for file in current_dir.glob("*.md"):
72
+ name = file.stem
73
+ if name.startswith(incomplete):
74
+ chat_files.append(name)
75
+
76
+ return sorted(chat_files)
77
+ except Exception:
78
+ return []
79
+
80
+
81
+ # Dynamic completers that need context
82
+ class DynamicCompleters:
83
+ @staticmethod
84
+ def create_classifier_completer():
85
+ """Create a completer for classifiers."""
86
+ def completer(ctx: typer.Context, incomplete: str) -> List[str]:
87
+ return complete_classifier(incomplete)
88
+ return completer
89
+
90
+ @staticmethod
91
+ def create_aspect_completer():
92
+ """Create a completer for aspects."""
93
+ def completer(ctx: typer.Context, incomplete: str) -> List[str]:
94
+ return complete_aspect(incomplete)
95
+ return completer
96
+
97
+ @staticmethod
98
+ def create_status_completer():
99
+ """Create a completer for status values."""
100
+ def completer(ctx: typer.Context, incomplete: str) -> List[str]:
101
+ return complete_status(incomplete)
102
+ return completer
103
+
104
+ @staticmethod
105
+ def create_template_type_completer():
106
+ """Create a completer for template types."""
107
+ def completer(ctx: typer.Context, incomplete: str) -> List[str]:
108
+ return complete_template_type(incomplete)
109
+ return completer
110
+
111
+ @staticmethod
112
+ def create_artefact_name_completer():
113
+ """Create a completer for artefact names based on classifier context."""
114
+ def completer(ctx: typer.Context, incomplete: str) -> List[str]:
115
+ # Try to get classifier from context
116
+ if hasattr(ctx, 'params') and 'classifier' in ctx.params:
117
+ classifier = ctx.params['classifier']
118
+ if hasattr(classifier, 'value'):
119
+ classifier = classifier.value
120
+ artefacts = complete_artefact_name(classifier)
121
+ return [a for a in artefacts if a.startswith(incomplete)]
122
+ return []
123
+ return completer
124
+
125
+ @staticmethod
126
+ def create_parent_name_completer():
127
+ """Create a completer for parent artefact names based on parent classifier context."""
128
+ def completer(ctx: typer.Context, incomplete: str) -> List[str]:
129
+ # Try to get parent_classifier from context
130
+ if hasattr(ctx, 'params') and 'parent_classifier' in ctx.params:
131
+ parent_classifier = ctx.params['parent_classifier']
132
+ if hasattr(parent_classifier, 'value'):
133
+ parent_classifier = parent_classifier.value
134
+ artefacts = complete_artefact_name(parent_classifier)
135
+ return [a for a in artefacts if a.startswith(incomplete)]
136
+ return []
137
+ return completer
138
+
139
+ @staticmethod
140
+ def create_chat_file_completer():
141
+ """Create a completer for chat files."""
142
+ def completer(ctx: typer.Context, incomplete: str) -> List[str]:
143
+ return complete_chat_files(incomplete)
144
+ return completer
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import sys
2
3
  from os.path import join, exists, isdir, dirname, basename
3
4
  # from ara_cli.directory_searcher import DirectorySearcher
4
5
 
@@ -23,7 +24,8 @@ class DirectoryNavigator:
23
24
  return original_directory
24
25
 
25
26
  current_directory = original_directory
26
- while current_directory != dirname(current_directory): # Ensure loop breaks at root
27
+ # Ensure loop breaks at root
28
+ while current_directory != dirname(current_directory):
27
29
  potential_path = join(current_directory, self.target_directory)
28
30
  if self.exists(potential_path):
29
31
  os.chdir(potential_path)
@@ -31,7 +33,8 @@ class DirectoryNavigator:
31
33
  current_directory = dirname(current_directory)
32
34
 
33
35
  # If the loop completes, the target directory was not found
34
- user_input = input(f"Unable to locate the '{self.target_directory}' directory. Do you want to create an 'ara' folder in the working directory? (y/N): ").strip().lower()
36
+ user_input = input(
37
+ f"Unable to locate the '{self.target_directory}' directory. Do you want to create an 'ara' folder in the working directory? (y/N): ").strip().lower()
35
38
 
36
39
  if user_input == '' or user_input == 'y':
37
40
  ara_folder_path = join(original_directory, 'ara')
@@ -40,7 +43,8 @@ class DirectoryNavigator:
40
43
  os.chdir(ara_folder_path)
41
44
  return original_directory
42
45
  else:
43
- print(f"Unable to locate the '{self.target_directory}' directory and user declined to create 'ara' folder.")
46
+ print(
47
+ f"Unable to locate the '{self.target_directory}' directory and user declined to create 'ara' folder.")
44
48
  sys.exit(0)
45
49
 
46
50
  def navigate_to_relative(self, relative_path):
@@ -56,7 +60,36 @@ class DirectoryNavigator:
56
60
  if self.exists(path):
57
61
  os.chdir(path)
58
62
  else:
59
- raise Exception(f"Unable to navigate to '{relative_path}' relative to the target directory.")
63
+ raise Exception(
64
+ f"Unable to navigate to '{relative_path}' relative to the target directory.")
65
+
66
+ @staticmethod
67
+ def find_ara_directory_root():
68
+ """Find the root ara directory by traversing up the directory tree."""
69
+ current_dir = os.getcwd()
70
+
71
+ # Check if we're already inside an ara directory structure
72
+ path_parts = current_dir.split(os.sep)
73
+
74
+ # Look for 'ara' in the path parts
75
+ if 'ara' in path_parts:
76
+ ara_index = path_parts.index('ara')
77
+ # Reconstruct path up to and including 'ara'
78
+ ara_root_parts = path_parts[:ara_index + 1]
79
+ potential_ara_root = os.sep.join(ara_root_parts)
80
+ if os.path.exists(potential_ara_root) and os.path.isdir(potential_ara_root):
81
+ return potential_ara_root
82
+
83
+ # If not inside ara directory, check current directory and parents
84
+ check_dir = current_dir
85
+ # Stop at filesystem root
86
+ while check_dir != os.path.dirname(check_dir):
87
+ ara_path = os.path.join(check_dir, 'ara')
88
+ if os.path.exists(ara_path) and os.path.isdir(ara_path):
89
+ return ara_path
90
+ check_dir = os.path.dirname(check_dir)
91
+
92
+ return None
60
93
 
61
94
  # debug version
62
95
  # def get_ara_directory(self):
@@ -0,0 +1,134 @@
1
+ import sys
2
+ import traceback
3
+ from typing import Optional
4
+ from enum import Enum
5
+ from functools import wraps
6
+
7
+
8
+ RED = '\033[91m'
9
+ RESET = '\033[0m'
10
+
11
+
12
+ class ErrorLevel(Enum):
13
+ INFO = "INFO"
14
+ WARNING = "WARNING"
15
+ ERROR = "ERROR"
16
+ CRITICAL = "CRITICAL"
17
+
18
+
19
+ class AraError(Exception):
20
+ """Base exception class for ARA CLI errors"""
21
+
22
+ def __init__(
23
+ self, message: str, error_code: int = 1, level: ErrorLevel = ErrorLevel.ERROR
24
+ ):
25
+ self.message = message
26
+ self.error_code = error_code
27
+ self.level = level
28
+ super().__init__(self.message)
29
+
30
+
31
+ class AraValidationError(AraError):
32
+ """Raised when validation fails"""
33
+
34
+ def __init__(self, message: str):
35
+ super().__init__(message, error_code=2, level=ErrorLevel.ERROR)
36
+
37
+
38
+ class AraConfigurationError(AraError):
39
+ """Raised when configuration is invalid"""
40
+
41
+ def __init__(self, message: str):
42
+ super().__init__(message, error_code=4, level=ErrorLevel.ERROR)
43
+
44
+
45
+ class ErrorHandler:
46
+ """Centralized error handler for ARA CLI"""
47
+
48
+ def __init__(self, debug_mode: bool = False):
49
+ self.debug_mode = debug_mode
50
+
51
+ def handle_error(self, error: Exception, context: Optional[str] = None) -> None:
52
+ """Handle any error with standardized output"""
53
+ if isinstance(error, AraError):
54
+ self._handle_ara_error(error, context)
55
+ else:
56
+ self._handle_generic_error(error, context)
57
+
58
+ def _handle_ara_error(self, error: AraError, context: Optional[str] = None) -> None:
59
+ """Handle ARA-specific errors"""
60
+ self._report_ara_error(error, context)
61
+
62
+ sys.exit(error.error_code)
63
+
64
+ def _handle_generic_error(
65
+ self, error: Exception, context: Optional[str] = None
66
+ ) -> None:
67
+ """Handle generic Python errors"""
68
+ self._report_generic_error(error, context)
69
+
70
+ sys.exit(1)
71
+
72
+
73
+ def report_error(self, error: Exception, context: Optional[str] = None) -> None:
74
+ """Report error with standardized formatting but don't exit"""
75
+ if isinstance(error, AraError):
76
+ self._report_ara_error(error, context)
77
+ else:
78
+ self._report_generic_error(error, context)
79
+
80
+
81
+ def _report_ara_error(self, error: AraError, context: Optional[str] = None) -> None:
82
+ """Report ARA-specific errors without exiting"""
83
+ error_prefix = f"[{error.level.value}]"
84
+
85
+ if context:
86
+ print(f"{RED}{error_prefix} {context}: {error.message}{RESET}", file=sys.stderr)
87
+ else:
88
+ print(f"{RED}{error_prefix} {error.message}{RESET}", file=sys.stderr)
89
+
90
+ if self.debug_mode:
91
+ traceback.print_exc()
92
+
93
+
94
+ def _report_generic_error(self, error: Exception, context: Optional[str] = None) -> None:
95
+ """Report generic Python errors without exiting"""
96
+ error_type = type(error).__name__
97
+
98
+ if context:
99
+ print(f"{RED}[ERROR] {context}: {error_type}: {str(error)}{RESET}", file=sys.stderr)
100
+ else:
101
+ print(f"{RED}[ERROR] {error_type}: {str(error)}{RESET}", file=sys.stderr)
102
+
103
+ if self.debug_mode:
104
+ traceback.print_exc()
105
+
106
+
107
+ def validate_and_exit(
108
+ self, condition: bool, message: str, error_code: int = 1
109
+ ) -> None:
110
+ """Validate condition and exit with error if false"""
111
+ if not condition:
112
+ raise AraValidationError(message)
113
+
114
+
115
+ def handle_errors(_func=None, context: Optional[str] = None, error_handler: Optional[ErrorHandler] = None):
116
+ """Decorator to handle errors in action functions"""
117
+
118
+ def decorator(func):
119
+ @wraps(func)
120
+ def wrapper(*args, **kwargs):
121
+ nonlocal error_handler
122
+ if error_handler is None:
123
+ error_handler = ErrorHandler()
124
+
125
+ try:
126
+ return func(*args, **kwargs)
127
+ except Exception as e:
128
+ error_handler.handle_error(e, context or func.__name__)
129
+
130
+ return wrapper
131
+
132
+ if callable(_func):
133
+ return decorator(_func)
134
+ return decorator
@@ -1,6 +1,7 @@
1
+ from . import error_handler
1
2
  from ara_cli.classifier import Classifier
2
3
  from ara_cli.artefact_models.artefact_model import Artefact
3
- from ara_cli.artefact_fuzzy_search import find_closest_name_match
4
+ from ara_cli.artefact_fuzzy_search import find_closest_name_matches
4
5
  from functools import lru_cache
5
6
  from typing import Optional
6
7
  import textwrap
@@ -17,7 +18,7 @@ class FileClassifier:
17
18
  info["title"] for info in classified_artefacts.get(classifier, [])]
18
19
  if name in all_artefact_names:
19
20
  return name
20
- return find_closest_name_match(name, all_artefact_names)
21
+ return find_closest_name_matches(name, all_artefact_names)
21
22
 
22
23
  @lru_cache(maxsize=None)
23
24
  def read_file_content(self, file_path):
@@ -33,8 +34,8 @@ class FileClassifier:
33
34
  if byte > 127:
34
35
  return True
35
36
  except Exception as e:
36
- # Handle unexpected errors while reading the file in binary mode
37
- print(f"Error while checking if file is binary: {e}")
37
+ error_handler.report_error(e, "checking if file is binary")
38
+ # print(f"Error while checking if file is binary: {e}")
38
39
  return False
39
40
 
40
41
  def read_file_with_fallback(self, file_path):
@@ -68,7 +69,7 @@ class FileClassifier:
68
69
  files_by_classifier = {classifier: [] for classifier in Classifier.ordered_classifiers()}
69
70
 
70
71
  for root, _, files in self.file_system.walk("."):
71
- if root.endswith(".data"):
72
+ if root.endswith(".data") or root.endswith("templates"):
72
73
  continue
73
74
  for file in files:
74
75
  file_path = self.file_system.path.join(root, file)
ara_cli/file_lister.py CHANGED
@@ -28,7 +28,7 @@ def generate_markdown_listing(directories, file_types_to_be_listed, output_file_
28
28
  if any(fnmatch.fnmatch(file, pattern) for pattern in file_types_to_be_listed):
29
29
  markdown_lines.append(f"{indent} - [] {file}")
30
30
 
31
- with open(output_file_path, "w") as md_file:
31
+ with open(output_file_path, "w", encoding="utf-8") as md_file:
32
32
  md_file.write('\n'.join(markdown_lines))
33
33
 
34
34
 
File without changes
@@ -0,0 +1,33 @@
1
+ import base64
2
+ import os
3
+ from ara_cli.file_loaders.file_loader import FileLoader
4
+
5
+
6
+ class BinaryFileLoader(FileLoader):
7
+ """Loads binary files (images)"""
8
+
9
+ def load(
10
+ self,
11
+ file_path: str,
12
+ mime_type: str,
13
+ prefix: str = "",
14
+ suffix: str = "",
15
+ block_delimiter: str = "",
16
+ extract_images: bool = False
17
+ ) -> bool:
18
+ """Load binary file as base64"""
19
+
20
+ with open(file_path, 'rb') as file:
21
+ file_content = file.read()
22
+
23
+ base64_image = base64.b64encode(file_content).decode("utf-8")
24
+
25
+ if block_delimiter:
26
+ file_content = f"{block_delimiter}\n{file_content}\n{block_delimiter}"
27
+
28
+ write_content = f"{prefix}![{os.path.basename(file_path)}](data:{mime_type};base64,{base64_image}){suffix}\n"
29
+
30
+ with open(self.chat.chat_name, 'a', encoding='utf-8') as chat_file:
31
+ chat_file.write(write_content)
32
+
33
+ return True
@@ -0,0 +1,34 @@
1
+ from ara_cli.file_loaders.document_reader import DocumentReaderFactory
2
+ from ara_cli.file_loaders.file_loader import FileLoader
3
+
4
+
5
+ class DocumentFileLoader(FileLoader):
6
+ """Loads document files (PDF, DOCX, ODT)"""
7
+
8
+ def load(
9
+ self,
10
+ file_path: str,
11
+ prefix: str = "",
12
+ suffix: str = "",
13
+ block_delimiter: str = "```",
14
+ extract_images: bool = False
15
+ ) -> bool:
16
+ """Load document file with optional image extraction"""
17
+
18
+ reader = DocumentReaderFactory.create_reader(file_path)
19
+
20
+ if not reader:
21
+ print("Unsupported document type.")
22
+ return False
23
+
24
+ text_content = reader.read(extract_images=extract_images)
25
+
26
+ if block_delimiter:
27
+ text_content = f"{block_delimiter}\n{text_content}\n{block_delimiter}"
28
+
29
+ write_content = f"{prefix}{text_content}{suffix}\n"
30
+
31
+ with open(self.chat.chat_name, 'a', encoding='utf-8') as chat_file:
32
+ chat_file.write(write_content)
33
+
34
+ return True