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/ara_config.py CHANGED
@@ -1,119 +1,264 @@
1
- from typing import List, Dict, Union, Optional
2
- from pydantic import BaseModel
1
+ from typing import List, Dict, Optional, Any
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
6
6
  from os import makedirs
7
7
  from functools import lru_cache
8
-
8
+ import sys
9
+ import warnings
9
10
 
10
11
  DEFAULT_CONFIG_LOCATION = "./ara/.araconfig/ara_config.json"
11
12
 
12
13
 
14
+ class LLMConfigItem(BaseModel):
15
+ provider: str
16
+ model: str
17
+ temperature: float = Field(ge=0.0, le=1.0)
18
+ max_tokens: Optional[int] = None
19
+ max_completion_tokens: Optional[int] = None
20
+
21
+
13
22
  class ARAconfig(BaseModel):
14
- ext_code_dirs: List[Dict[str, str]] = [
15
- {"source_dir_1": "./src"},
16
- {"source_dir_2": "./tests"},
17
- ]
23
+ ext_code_dirs: List[Dict[str, str]] = Field(default_factory=lambda: [
24
+ {"source_dir": "./src"},
25
+ {"source_dir": "./tests"}
26
+ ])
27
+ global_dirs: Optional[List[Dict[str, str]]] = Field(default=[])
18
28
  glossary_dir: str = "./glossary"
19
29
  doc_dir: str = "./docs"
20
30
  local_prompt_templates_dir: str = "./ara/.araconfig"
21
31
  custom_prompt_templates_subdir: Optional[str] = "custom-prompt-modules"
22
32
  local_ara_templates_dir: str = "./ara/.araconfig/templates/"
23
- ara_prompt_given_list_includes: List[str] = [
24
- "*.businessgoal",
25
- "*.vision",
26
- "*.capability",
27
- "*.keyfeature",
28
- "*.epic",
29
- "*.userstory",
30
- "*.example",
31
- "*.feature",
32
- "*.task",
33
- "*.py",
34
- "*.md",
35
- "*.png",
36
- "*.jpg",
37
- "*.jpeg",
38
- ]
39
- llm_config: Dict[str, Dict[str, Union[str, float]]] = {
40
- "gpt-4o": {
41
- "provider": "openai",
42
- "model": "openai/gpt-4o",
43
- "temperature": 0.8
44
- },
45
- "gpt-4.1": {
46
- "provider": "openai",
47
- "model": "openai/gpt-4.1",
48
- "temperature": 0.8,
49
- },
50
- "o3-mini": {
51
- "provider": "openai",
52
- "model": "openai/o3-mini",
53
- "temperature": 1.0,
54
- },
55
- "opus-4": {
56
- "provider": "anthropic",
57
- "model": "anthropic/claude-opus-4-20250514",
58
- "temperature": 0.8,
59
- },
60
- "sonnet-4": {
61
- "provider": "anthropic",
62
- "model": "anthropic/claude-sonnet-4-20250514",
63
- "temperature": 0.8,
64
- },
65
- "together-ai-llama-2": {
66
- "provider": "together_ai",
67
- "model": "together_ai/togethercomputer/llama-2-70b",
68
- "temperature": 0.8,
69
- },
70
- "groq-llama-3": {
71
- "provider": "groq",
72
- "model": "groq/llama3-70b-8192",
73
- "temperature": 0.8,
74
- },
75
- }
76
- default_llm: Optional[str] = "gpt-4o"
33
+ ara_prompt_given_list_includes: List[str] = Field(
34
+ default_factory=lambda: [
35
+ "*.businessgoal",
36
+ "*.vision",
37
+ "*.capability",
38
+ "*.keyfeature",
39
+ "*.epic",
40
+ "*.userstory",
41
+ "*.example",
42
+ "*.feature",
43
+ "*.task",
44
+ "*.py",
45
+ "*.md",
46
+ "*.png",
47
+ "*.jpg",
48
+ "*.jpeg",
49
+ ]
50
+ )
51
+ llm_config: Dict[str, LLMConfigItem] = Field(
52
+ default_factory=lambda: {
53
+ "gpt-5": LLMConfigItem(
54
+ provider="openai",
55
+ model="openai/gpt-5",
56
+ temperature=1,
57
+ max_completion_tokens=16000,
58
+ ),
59
+ "gpt-5-mini": LLMConfigItem(
60
+ provider="openai", model="openai/gpt-5-mini-2025-08-07", temperature=1
61
+ ),
62
+ "gpt-4o": LLMConfigItem(
63
+ provider="openai",
64
+ model="openai/gpt-4o",
65
+ temperature=0.8,
66
+ max_tokens=16000,
67
+ ),
68
+ "gpt-4.1": LLMConfigItem(
69
+ provider="openai",
70
+ model="openai/gpt-4.1",
71
+ temperature=0.8,
72
+ max_tokens=16000,
73
+ ),
74
+ "o3-mini": LLMConfigItem(
75
+ provider="openai",
76
+ model="openai/o3-mini",
77
+ temperature=1.0,
78
+ max_tokens=8000,
79
+ ),
80
+ "opus-4": LLMConfigItem(
81
+ provider="anthropic",
82
+ model="anthropic/claude-opus-4-20250514",
83
+ temperature=0.5,
84
+ max_tokens=32000,
85
+ ),
86
+ "sonnet-4": LLMConfigItem(
87
+ provider="anthropic",
88
+ model="anthropic/claude-sonnet-4-20250514",
89
+ temperature=0.5,
90
+ max_tokens=32000,
91
+ ),
92
+ "together-ai-llama-2": LLMConfigItem(
93
+ provider="together_ai",
94
+ model="together_ai/togethercomputer/llama-2-70b",
95
+ temperature=0.8,
96
+ max_tokens=4000,
97
+ ),
98
+ "groq-llama-3": LLMConfigItem(
99
+ provider="groq",
100
+ model="groq/llama3-70b-8192",
101
+ temperature=0.8,
102
+ max_tokens=4000,
103
+ ),
104
+ }
105
+ )
106
+ default_llm: Optional[str] = None
107
+ extraction_llm: Optional[str] = None
108
+
109
+ @model_validator(mode="after")
110
+ def check_critical_fields(self) -> "ARAconfig":
111
+ """Check for empty critical fields and validate default_llm and extraction_llm."""
112
+ critical_fields = {
113
+ "ext_code_dirs": [{"source_dir": "./src"}, {"source_dir": "./tests"}],
114
+ "local_ara_templates_dir": "./ara/.araconfig/templates/",
115
+ "local_prompt_templates_dir": "./ara/.araconfig",
116
+ "glossary_dir": "./glossary",
117
+ }
118
+
119
+ for field, default_value in critical_fields.items():
120
+ current_value = getattr(self, field)
121
+ if not current_value:
122
+ print(
123
+ f"Warning: Value for '{field}' is missing or empty. Using default."
124
+ )
125
+ setattr(self, field, default_value)
126
+
127
+ if not self.llm_config:
128
+ print(
129
+ "Warning: 'llm_config' is empty. 'default_llm' and 'extraction_llm' cannot be set."
130
+ )
131
+ self.default_llm = None
132
+ self.extraction_llm = None
133
+ return self
134
+
135
+ first_available_llm = next(iter(self.llm_config))
136
+
137
+ if not self.default_llm:
138
+ print(
139
+ f"Warning: 'default_llm' is not set. Defaulting to the first available model: '{first_available_llm}'."
140
+ )
141
+ self.default_llm = first_available_llm
142
+ elif self.default_llm not in self.llm_config:
143
+ print(
144
+ f"Warning: The configured 'default_llm' ('{self.default_llm}') does not exist in 'llm_config'."
145
+ )
146
+ print(
147
+ f"-> Reverting to the first available model: '{first_available_llm}'."
148
+ )
149
+ self.default_llm = first_available_llm
150
+
151
+ if not self.extraction_llm:
152
+ print(
153
+ f"Warning: 'extraction_llm' is not set. Setting it to the same as 'default_llm': '{self.default_llm}'."
154
+ )
155
+ self.extraction_llm = self.default_llm
156
+ elif self.extraction_llm not in self.llm_config:
157
+ print(
158
+ f"Warning: The configured 'extraction_llm' ('{self.extraction_llm}') does not exist in 'llm_config'."
159
+ )
160
+ print(f"-> Reverting to the 'default_llm' value: '{self.default_llm}'.")
161
+ self.extraction_llm = self.default_llm
162
+
163
+ return self
77
164
 
78
165
 
79
166
  # Function to ensure the necessary directories exist
80
167
  @lru_cache(maxsize=None)
81
168
  def ensure_directory_exists(directory: str):
169
+ """Creates a directory if it doesn't exist."""
82
170
  if not exists(directory):
83
171
  os.makedirs(directory)
84
172
  print(f"New directory created at {directory}")
85
173
  return directory
86
174
 
87
175
 
88
- def validate_config_data(filepath: str):
89
- with open(filepath, "r") as file:
90
- data = json.load(file)
91
- return data
176
+ def handle_unrecognized_keys(data: dict) -> dict:
177
+ """Removes unrecognized keys from the data and warns the user."""
178
+ known_fields = set(ARAconfig.model_fields.keys())
179
+ cleaned_data = {}
180
+ for key, value in data.items():
181
+ if key not in known_fields:
182
+ print(f"Warning: Unrecognized configuration key '{key}' will be ignored.")
183
+ else:
184
+ cleaned_data[key] = value
185
+ return cleaned_data
92
186
 
93
187
 
94
188
  # Function to read the JSON file and return an ARAconfig model
95
189
  @lru_cache(maxsize=1)
96
190
  def read_data(filepath: str) -> ARAconfig:
191
+ """
192
+ Reads, validates, and repairs the configuration file.
193
+ If the file doesn't exist, it creates a default one.
194
+ If the file is invalid, it corrects only the broken parts.
195
+ """
196
+
197
+ def warn_on_duplicate_llm_dict_key(ordered_pairs):
198
+ """Reject duplicate keys."""
199
+ d = {}
200
+ for k, v in ordered_pairs:
201
+ if k in d:
202
+ warnings.warn(f"Duplicate LLM configuration identifier '{k}'. The previous entry will be removed.", UserWarning)
203
+ d[k] = v
204
+ return d
205
+
206
+ ensure_directory_exists(dirname(filepath))
207
+
97
208
  if not exists(filepath):
98
- # If file does not exist, create it with default values
209
+ print(f"Configuration file not found. Creating a default one at '{filepath}'.")
210
+ default_config = ARAconfig()
211
+ save_data(filepath, default_config)
212
+ print("Please review the default configuration and re-run your command.")
213
+ sys.exit(0)
214
+
215
+ try:
216
+ with open(filepath, "r", encoding="utf-8") as file:
217
+ content = file.read()
218
+ data = json.loads(content, object_pairs_hook=warn_on_duplicate_llm_dict_key)
219
+ except json.JSONDecodeError as e:
220
+ print(f"Error: Invalid JSON in configuration file: {e}")
221
+ print("Creating a new configuration with defaults...")
99
222
  default_config = ARAconfig()
223
+ save_data(filepath, default_config)
224
+ return default_config
100
225
 
101
- with open(filepath, "w") as file:
102
- json.dump(default_config.model_dump(), file, indent=4)
226
+ data = handle_unrecognized_keys(data)
103
227
 
228
+ try:
229
+ config = ARAconfig(**data)
230
+ save_data(filepath, config)
231
+ return config
232
+ except ValidationError as e:
233
+ print("--- Configuration Error Detected ---")
104
234
  print(
105
- f"ara-cli configuration file '{filepath}' created with default configuration. Please modify it as needed and re-run your command"
235
+ "Some settings in your configuration file are invalid. Attempting to fix them."
106
236
  )
107
- exit() # Exit the application
108
237
 
109
- data = validate_config_data(filepath)
110
- return ARAconfig(**data)
238
+ corrected_data = data.copy()
239
+ defaults = ARAconfig().model_dump()
240
+
241
+ error_fields = {err["loc"][0] for err in e.errors() if err["loc"]}
242
+
243
+ for field_name in error_fields:
244
+ print(f"-> Field '{field_name}' is invalid and will be reverted to its default value.")
245
+ if field_name in corrected_data:
246
+ corrected_data[field_name] = defaults.get(field_name)
247
+
248
+ print("--- End of Error Report ---")
249
+
250
+ final_config = ARAconfig(**corrected_data)
251
+ save_data(filepath, final_config)
252
+ print(f"Configuration has been corrected and saved to '{filepath}'.")
253
+
254
+ return final_config
111
255
 
112
256
 
113
257
  # Function to save the modified configuration back to the JSON file
114
258
  def save_data(filepath: str, config: ARAconfig):
115
- with open(filepath, "w") as file:
116
- json.dump(config.dict(), file, indent=4)
259
+ """Saves the Pydantic config model to a JSON file."""
260
+ with open(filepath, "w", encoding="utf-8") as file:
261
+ json.dump(config.model_dump(), file, indent=4)
117
262
 
118
263
 
119
264
  # Singleton for configuration management
@@ -121,12 +266,13 @@ class ConfigManager:
121
266
  _config_instance = None
122
267
 
123
268
  @classmethod
124
- def get_config(cls, filepath=DEFAULT_CONFIG_LOCATION):
269
+ def get_config(cls, filepath=DEFAULT_CONFIG_LOCATION) -> ARAconfig:
125
270
  if cls._config_instance is None:
126
- config_dir = dirname(filepath)
127
-
128
- if not exists(config_dir):
129
- makedirs(config_dir)
130
-
131
271
  cls._config_instance = read_data(filepath)
132
272
  return cls._config_instance
273
+
274
+ @classmethod
275
+ def reset(cls):
276
+ """Reset the configuration instance (useful for testing)."""
277
+ cls._config_instance = None
278
+ read_data.cache_clear()
File without changes
@@ -0,0 +1,26 @@
1
+ import typer
2
+ from .common import MockArgs
3
+ from ara_cli.ara_command_action import autofix_action
4
+
5
+
6
+ def autofix_main(
7
+ single_pass: bool = typer.Option(False, "--single-pass", help="Run the autofix once for every scanned file"),
8
+ deterministic: bool = typer.Option(False, "-d", "--deterministic", help="Run only deterministic fixes e.g Title-FileName Mismatch fix"),
9
+ non_deterministic: bool = typer.Option(False, "-nd", "--non-deterministic", help="Run only non-deterministic fixes")
10
+ ):
11
+ """Fix ARA tree with llm models for scanned artefacts with ara scan command."""
12
+ if deterministic and non_deterministic:
13
+ typer.echo("Error: --deterministic and --non-deterministic are mutually exclusive", err=True)
14
+ raise typer.Exit(1)
15
+
16
+ args = MockArgs(
17
+ single_pass=single_pass,
18
+ deterministic=deterministic,
19
+ non_deterministic=non_deterministic
20
+ )
21
+ autofix_action(args)
22
+
23
+
24
+ def register(parent: typer.Typer):
25
+ help_text = "Fix ARA tree with llm models for scanned artefacts"
26
+ parent.command(name="autofix", help=help_text)(autofix_main)
@@ -0,0 +1,27 @@
1
+ import typer
2
+ from typing import Optional, List
3
+ from .common import MockArgs, ChatNameArgument
4
+ from ara_cli.ara_command_action import chat_action
5
+
6
+
7
+ def chat_main(
8
+ chat_name: Optional[str] = ChatNameArgument("Optional name for a specific chat. Pass the .md file to continue an existing chat", None),
9
+ reset: Optional[bool] = typer.Option(None, "-r", "--reset/--no-reset", help="Reset the chat file if it exists"),
10
+ output_mode: bool = typer.Option(False, "--out", help="Output the contents of the chat file instead of entering interactive chat mode"),
11
+ append: Optional[List[str]] = typer.Option(None, "--append", help="Append strings to the chat file"),
12
+ restricted: Optional[bool] = typer.Option(None, "--restricted/--no-restricted", help="Start with a limited set of commands")
13
+ ):
14
+ """Command line chatbot. Chat control with SEND/s | RERUN/r | QUIT/q"""
15
+ args = MockArgs(
16
+ chat_name=chat_name,
17
+ reset=reset,
18
+ output_mode=output_mode,
19
+ append=append,
20
+ restricted=restricted
21
+ )
22
+ chat_action(args)
23
+
24
+
25
+ def register(parent: typer.Typer):
26
+ help_text = "Command line chatbot. Chat control with SEND/s | RERUN/r | QUIT/q"
27
+ parent.command(name="chat", help=help_text)(chat_main)
@@ -0,0 +1,16 @@
1
+ import typer
2
+ from .common import ClassifierEnum, MockArgs, ClassifierArgument
3
+ from ara_cli.ara_command_action import classifier_directory_action
4
+
5
+
6
+ def classifier_directory_main(
7
+ classifier: ClassifierEnum = ClassifierArgument("Classifier of the artefact type")
8
+ ):
9
+ """Print the ara subdirectory for an artefact classifier."""
10
+ args = MockArgs(classifier=classifier.value)
11
+ classifier_directory_action(args)
12
+
13
+
14
+ def register(parent: typer.Typer):
15
+ help_text = "Print the ara subdirectory for an artefact classifier"
16
+ parent.command(name="classifier-directory", help=help_text)(classifier_directory_main)
@@ -0,0 +1,100 @@
1
+ from typing import Optional
2
+ from enum import Enum
3
+ import typer
4
+ from ara_cli.classifier import Classifier
5
+ from ara_cli.template_manager import SpecificationBreakdownAspects
6
+ from ara_cli.completers import DynamicCompleters
7
+
8
+
9
+ # Get classifiers and aspects
10
+ classifiers = Classifier.ordered_classifiers()
11
+ aspects = SpecificationBreakdownAspects.VALID_ASPECTS
12
+
13
+
14
+ # Create enums for better type safety
15
+ ClassifierEnum = Enum('ClassifierEnum', {c: c for c in classifiers})
16
+ AspectEnum = Enum('AspectEnum', {a: a for a in aspects})
17
+ TemplateTypeEnum = Enum('TemplateTypeEnum', {
18
+ 'rules': 'rules',
19
+ 'intention': 'intention',
20
+ 'commands': 'commands',
21
+ 'blueprint': 'blueprint'
22
+ })
23
+
24
+
25
+ # Create typed arguments and options with autocompletion
26
+ def ClassifierArgument(help_text: str, default=...):
27
+ """Create a classifier argument with autocompletion."""
28
+ return typer.Argument(
29
+ default,
30
+ help=help_text,
31
+ autocompletion=DynamicCompleters.create_classifier_completer()
32
+ )
33
+
34
+
35
+ def ClassifierOption(help_text: str, *names):
36
+ """Create a classifier option with autocompletion."""
37
+ return typer.Option(
38
+ None,
39
+ *names,
40
+ help=help_text,
41
+ autocompletion=DynamicCompleters.create_classifier_completer()
42
+ )
43
+
44
+
45
+ def ArtefactNameArgument(help_text: str, default=...):
46
+ """Create an artefact name argument with autocompletion."""
47
+ return typer.Argument(
48
+ default,
49
+ help=help_text,
50
+ autocompletion=DynamicCompleters.create_artefact_name_completer()
51
+ )
52
+
53
+
54
+
55
+ def ParentNameArgument(help_text: str):
56
+ """Create a parent name argument with autocompletion."""
57
+ return typer.Argument(
58
+ help=help_text,
59
+ autocompletion=DynamicCompleters.create_parent_name_completer()
60
+ )
61
+
62
+
63
+ def AspectArgument(help_text: str):
64
+ """Create an aspect argument with autocompletion."""
65
+ return typer.Argument(
66
+ help=help_text,
67
+ autocompletion=DynamicCompleters.create_aspect_completer()
68
+ )
69
+
70
+
71
+ def StatusArgument(help_text: str):
72
+ """Create a status argument with autocompletion."""
73
+ return typer.Argument(
74
+ help=help_text,
75
+ autocompletion=DynamicCompleters.create_status_completer()
76
+ )
77
+
78
+
79
+ def TemplateTypeArgument(help_text: str):
80
+ """Create a template type argument with autocompletion."""
81
+ return typer.Argument(
82
+ help=help_text,
83
+ autocompletion=DynamicCompleters.create_template_type_completer()
84
+ )
85
+
86
+
87
+ def ChatNameArgument(help_text: str, default=None):
88
+ """Create a chat name argument with autocompletion."""
89
+ return typer.Argument(
90
+ default,
91
+ help=help_text,
92
+ autocompletion=DynamicCompleters.create_chat_file_completer()
93
+ )
94
+
95
+
96
+ # Mock args class to maintain compatibility with existing action functions
97
+ class MockArgs:
98
+ def __init__(self, **kwargs):
99
+ for key, value in kwargs.items():
100
+ setattr(self, key, value)
@@ -0,0 +1,75 @@
1
+ import typer
2
+ from typing import Optional
3
+ from .common import (
4
+ ClassifierEnum, AspectEnum, MockArgs,
5
+ ClassifierArgument, ArtefactNameArgument, ParentNameArgument, AspectArgument
6
+ )
7
+ from ara_cli.ara_command_action import create_action
8
+
9
+
10
+ def create_contributes_to(
11
+ ctx: typer.Context,
12
+ parent_classifier: ClassifierEnum = ClassifierArgument("Classifier of the parent"),
13
+ parent_name: str = ParentNameArgument("Name of a parent artefact"),
14
+ rule: Optional[str] = typer.Option(None, "-r", "--rule", help="Rule for contribution")
15
+ ):
16
+ """Create an artefact that contributes to a parent artefact."""
17
+ # Get classifier and parameter from parent context
18
+ parent_params = ctx.parent.params
19
+ classifier = parent_params['classifier']
20
+ parameter = parent_params['parameter']
21
+
22
+ args = MockArgs(
23
+ classifier=classifier,
24
+ parameter=parameter,
25
+ option="contributes-to",
26
+ parent_classifier=parent_classifier.value,
27
+ parent_name=parent_name,
28
+ rule=rule
29
+ )
30
+ create_action(args)
31
+
32
+
33
+ def create_aspect(
34
+ ctx: typer.Context,
35
+ aspect: AspectEnum = AspectArgument("Adds additional specification breakdown aspects")
36
+ ):
37
+ """Create an artefact with additional specification breakdown aspects."""
38
+ # Get classifier and parameter from parent context
39
+ parent_params = ctx.parent.params
40
+ classifier = parent_params['classifier']
41
+ parameter = parent_params['parameter']
42
+
43
+ args = MockArgs(
44
+ classifier=classifier,
45
+ parameter=parameter,
46
+ option="aspect",
47
+ aspect=aspect.value
48
+ )
49
+ create_action(args)
50
+
51
+
52
+ def create_main(
53
+ ctx: typer.Context,
54
+ classifier: ClassifierEnum = ClassifierArgument("Classifier that also serves as file extension"),
55
+ parameter: str = ArtefactNameArgument("Artefact name that serves as filename")
56
+ ):
57
+ """Create a classified artefact with data directory."""
58
+ if ctx.invoked_subcommand is None:
59
+ args = MockArgs(
60
+ classifier=classifier.value,
61
+ parameter=parameter,
62
+ option=None
63
+ )
64
+ create_action(args)
65
+
66
+
67
+ def register(parent: typer.Typer):
68
+ create_app = typer.Typer(
69
+ help="Create a classified artefact with data directory",
70
+ add_completion=False # Disable completion on subcommand
71
+ )
72
+ create_app.command("contributes-to")(create_contributes_to)
73
+ create_app.command("aspect")(create_aspect)
74
+ create_app.callback(invoke_without_command=True)(create_main)
75
+ parent.add_typer(create_app, name="create")
@@ -0,0 +1,22 @@
1
+ import typer
2
+ from .common import ClassifierEnum, MockArgs, ClassifierArgument, ArtefactNameArgument
3
+ from ara_cli.ara_command_action import delete_action
4
+
5
+
6
+ def delete_main(
7
+ classifier: ClassifierEnum = ClassifierArgument("Classifier of the artefact to be deleted"),
8
+ parameter: str = ArtefactNameArgument("Filename of artefact"),
9
+ force: bool = typer.Option(False, "-f", "--force", help="ignore nonexistent files and arguments, never prompt")
10
+ ):
11
+ """Delete an artefact file including its data directory."""
12
+ args = MockArgs(
13
+ classifier=classifier.value,
14
+ parameter=parameter,
15
+ force=force
16
+ )
17
+ delete_action(args)
18
+
19
+
20
+ def register(parent: typer.Typer):
21
+ help_text = "Delete an artefact file including its data directory"
22
+ parent.command(name="delete", help=help_text)(delete_main)
@@ -0,0 +1,22 @@
1
+ import typer
2
+ from .common import MockArgs
3
+ from ara_cli.ara_command_action import extract_action
4
+
5
+
6
+ def extract_main(
7
+ filename: str = typer.Argument(help="Input file to extract from"),
8
+ force: bool = typer.Option(False, "-f", "--force", help="Answer queries with yes when extracting"),
9
+ write: bool = typer.Option(False, "-w", "--write", help="Overwrite existing files without using LLM for merging")
10
+ ):
11
+ """Extract blocks of marked content from a given file."""
12
+ args = MockArgs(
13
+ filename=filename,
14
+ force=force,
15
+ write=write
16
+ )
17
+ extract_action(args)
18
+
19
+
20
+ def register(parent: typer.Typer):
21
+ help_text = "Extract blocks of marked content from a given file"
22
+ parent.command(name="extract", help=help_text)(extract_main)
@@ -0,0 +1,14 @@
1
+ import typer
2
+ from .common import MockArgs
3
+ from ara_cli.ara_command_action import fetch_templates_action
4
+
5
+
6
+ def fetch_templates_main():
7
+ """Fetches templates and stores them in .araconfig."""
8
+ args = MockArgs()
9
+ fetch_templates_action(args)
10
+
11
+
12
+ def register(parent: typer.Typer):
13
+ help_text = "Fetches templates and stores them in .araconfig"
14
+ parent.command(name="fetch-templates", help=help_text)(fetch_templates_main)