ara-cli 0.1.9.77__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.
- ara_cli/__init__.py +18 -2
- ara_cli/__main__.py +245 -66
- ara_cli/ara_command_action.py +128 -63
- ara_cli/ara_config.py +201 -177
- ara_cli/ara_subcommands/__init__.py +0 -0
- ara_cli/ara_subcommands/autofix.py +26 -0
- ara_cli/ara_subcommands/chat.py +27 -0
- ara_cli/ara_subcommands/classifier_directory.py +16 -0
- ara_cli/ara_subcommands/common.py +100 -0
- ara_cli/ara_subcommands/create.py +75 -0
- ara_cli/ara_subcommands/delete.py +22 -0
- ara_cli/ara_subcommands/extract.py +22 -0
- ara_cli/ara_subcommands/fetch_templates.py +14 -0
- ara_cli/ara_subcommands/list.py +65 -0
- ara_cli/ara_subcommands/list_tags.py +25 -0
- ara_cli/ara_subcommands/load.py +48 -0
- ara_cli/ara_subcommands/prompt.py +136 -0
- ara_cli/ara_subcommands/read.py +47 -0
- ara_cli/ara_subcommands/read_status.py +20 -0
- ara_cli/ara_subcommands/read_user.py +20 -0
- ara_cli/ara_subcommands/reconnect.py +27 -0
- ara_cli/ara_subcommands/rename.py +22 -0
- ara_cli/ara_subcommands/scan.py +14 -0
- ara_cli/ara_subcommands/set_status.py +22 -0
- ara_cli/ara_subcommands/set_user.py +22 -0
- ara_cli/ara_subcommands/template.py +16 -0
- ara_cli/artefact_autofix.py +214 -28
- ara_cli/artefact_creator.py +5 -8
- ara_cli/artefact_deleter.py +2 -4
- ara_cli/artefact_fuzzy_search.py +13 -6
- ara_cli/artefact_lister.py +29 -55
- ara_cli/artefact_models/artefact_data_retrieval.py +23 -0
- ara_cli/artefact_models/artefact_model.py +106 -25
- ara_cli/artefact_models/artefact_templates.py +23 -13
- ara_cli/artefact_models/epic_artefact_model.py +11 -2
- ara_cli/artefact_models/feature_artefact_model.py +56 -1
- ara_cli/artefact_models/userstory_artefact_model.py +15 -3
- ara_cli/artefact_reader.py +4 -5
- ara_cli/artefact_renamer.py +6 -2
- ara_cli/artefact_scan.py +2 -2
- ara_cli/chat.py +594 -219
- ara_cli/chat_agent/__init__.py +0 -0
- ara_cli/chat_agent/agent_communicator.py +62 -0
- ara_cli/chat_agent/agent_process_manager.py +211 -0
- ara_cli/chat_agent/agent_status_manager.py +73 -0
- ara_cli/chat_agent/agent_workspace_manager.py +76 -0
- ara_cli/commands/__init__.py +0 -0
- ara_cli/commands/command.py +7 -0
- ara_cli/commands/extract_command.py +15 -0
- ara_cli/commands/load_command.py +65 -0
- ara_cli/commands/load_image_command.py +34 -0
- ara_cli/commands/read_command.py +117 -0
- ara_cli/completers.py +144 -0
- ara_cli/directory_navigator.py +37 -4
- ara_cli/error_handler.py +134 -0
- ara_cli/file_classifier.py +3 -2
- ara_cli/file_loaders/__init__.py +0 -0
- ara_cli/file_loaders/binary_file_loader.py +33 -0
- ara_cli/file_loaders/document_file_loader.py +34 -0
- ara_cli/file_loaders/document_reader.py +245 -0
- ara_cli/file_loaders/document_readers.py +233 -0
- ara_cli/file_loaders/file_loader.py +50 -0
- ara_cli/file_loaders/file_loaders.py +123 -0
- ara_cli/file_loaders/image_processor.py +89 -0
- ara_cli/file_loaders/markdown_reader.py +75 -0
- ara_cli/file_loaders/text_file_loader.py +187 -0
- ara_cli/global_file_lister.py +51 -0
- ara_cli/prompt_extractor.py +214 -87
- ara_cli/prompt_handler.py +508 -146
- ara_cli/tag_extractor.py +54 -24
- ara_cli/template_loader.py +245 -0
- ara_cli/template_manager.py +14 -4
- ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
- ara_cli/templates/prompt-modules/commands/extract_general.commands.md +12 -0
- ara_cli/templates/prompt-modules/commands/extract_markdown.commands.md +11 -0
- ara_cli/templates/prompt-modules/commands/extract_python.commands.md +13 -0
- ara_cli/templates/prompt-modules/commands/feature_add_or_modifiy_specified_behavior.commands.md +36 -0
- ara_cli/templates/prompt-modules/commands/feature_generate_initial_specified_bevahior.commands.md +53 -0
- ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
- ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
- ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
- ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
- ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
- ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
- ara_cli/update_config_prompt.py +7 -1
- ara_cli/version.py +1 -1
- ara_cli-0.1.10.8.dist-info/METADATA +241 -0
- {ara_cli-0.1.9.77.dist-info → ara_cli-0.1.10.8.dist-info}/RECORD +104 -59
- tests/test_ara_command_action.py +66 -52
- tests/test_ara_config.py +200 -279
- tests/test_artefact_autofix.py +361 -5
- tests/test_artefact_lister.py +52 -132
- tests/test_artefact_scan.py +1 -1
- tests/test_chat.py +2009 -603
- tests/test_file_classifier.py +23 -0
- tests/test_file_creator.py +3 -5
- tests/test_global_file_lister.py +131 -0
- tests/test_prompt_handler.py +746 -0
- tests/test_tag_extractor.py +19 -13
- tests/test_template_loader.py +192 -0
- tests/test_template_manager.py +5 -4
- ara_cli/ara_command_parser.py +0 -536
- ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
- ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
- ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
- ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
- ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
- ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
- ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
- ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
- ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
- ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
- ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
- ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
- ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
- ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
- ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
- ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
- ara_cli-0.1.9.77.dist-info/METADATA +0 -18
- {ara_cli-0.1.9.77.dist-info → ara_cli-0.1.10.8.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.9.77.dist-info → ara_cli-0.1.10.8.dist-info}/entry_points.txt +0 -0
- {ara_cli-0.1.9.77.dist-info → ara_cli-0.1.10.8.dist-info}/top_level.txt +0 -0
ara_cli/ara_config.py
CHANGED
|
@@ -1,254 +1,278 @@
|
|
|
1
1
|
from typing import List, Dict, Optional, Any
|
|
2
|
-
from pydantic import BaseModel, ValidationError, Field,
|
|
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
|
|
|
13
|
+
|
|
12
14
|
class LLMConfigItem(BaseModel):
|
|
13
15
|
provider: str
|
|
14
16
|
model: str
|
|
15
17
|
temperature: float = Field(ge=0.0, le=1.0)
|
|
16
18
|
max_tokens: Optional[int] = None
|
|
17
|
-
|
|
18
|
-
@field_validator('temperature')
|
|
19
|
-
@classmethod
|
|
20
|
-
def validate_temperature(cls, v: float, info) -> float:
|
|
21
|
-
if not 0.0 <= v <= 1.0:
|
|
22
|
-
print(f"Warning: Temperature is outside the 0.0 to 1.0 range")
|
|
23
|
-
# Return a valid default
|
|
24
|
-
return 0.8
|
|
25
|
-
return v
|
|
19
|
+
max_completion_tokens: Optional[int] = None
|
|
26
20
|
|
|
27
|
-
class ExtCodeDirItem(BaseModel):
|
|
28
|
-
source_dir: str
|
|
29
21
|
|
|
30
22
|
class ARAconfig(BaseModel):
|
|
31
|
-
ext_code_dirs: List[
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
ext_code_dirs: List[Dict[str, str]] = Field(default_factory=lambda: [
|
|
24
|
+
{"source_dir": "./src"},
|
|
25
|
+
{"source_dir": "./tests"}
|
|
34
26
|
])
|
|
27
|
+
global_dirs: Optional[List[Dict[str, str]]] = Field(default=[])
|
|
35
28
|
glossary_dir: str = "./glossary"
|
|
36
29
|
doc_dir: str = "./docs"
|
|
37
30
|
local_prompt_templates_dir: str = "./ara/.araconfig"
|
|
38
31
|
custom_prompt_templates_subdir: Optional[str] = "custom-prompt-modules"
|
|
39
32
|
local_ara_templates_dir: str = "./ara/.araconfig/templates/"
|
|
40
|
-
ara_prompt_given_list_includes: List[str] = Field(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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."""
|
|
109
112
|
critical_fields = {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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",
|
|
114
117
|
}
|
|
115
|
-
|
|
118
|
+
|
|
116
119
|
for field, default_value in critical_fields.items():
|
|
117
120
|
current_value = getattr(self, field)
|
|
118
|
-
if
|
|
119
|
-
(
|
|
120
|
-
|
|
121
|
-
|
|
121
|
+
if not current_value:
|
|
122
|
+
print(
|
|
123
|
+
f"Warning: Value for '{field}' is missing or empty. Using default."
|
|
124
|
+
)
|
|
122
125
|
setattr(self, field, default_value)
|
|
123
|
-
|
|
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
|
+
|
|
124
163
|
return self
|
|
125
164
|
|
|
165
|
+
|
|
126
166
|
# Function to ensure the necessary directories exist
|
|
127
167
|
@lru_cache(maxsize=None)
|
|
128
168
|
def ensure_directory_exists(directory: str):
|
|
169
|
+
"""Creates a directory if it doesn't exist."""
|
|
129
170
|
if not exists(directory):
|
|
130
171
|
os.makedirs(directory)
|
|
131
172
|
print(f"New directory created at {directory}")
|
|
132
173
|
return directory
|
|
133
174
|
|
|
134
|
-
|
|
135
|
-
|
|
175
|
+
|
|
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())
|
|
136
179
|
cleaned_data = {}
|
|
137
180
|
for key, value in data.items():
|
|
138
181
|
if key not in known_fields:
|
|
139
|
-
print(f"Warning: {key}
|
|
182
|
+
print(f"Warning: Unrecognized configuration key '{key}' will be ignored.")
|
|
140
183
|
else:
|
|
141
184
|
cleaned_data[key] = value
|
|
142
185
|
return cleaned_data
|
|
143
186
|
|
|
144
|
-
def fix_llm_temperatures(data: dict) -> dict:
|
|
145
|
-
"""Fix invalid temperatures in LLM configurations"""
|
|
146
|
-
if 'llm_config' in data:
|
|
147
|
-
for model_key, model_config in data['llm_config'].items():
|
|
148
|
-
if isinstance(model_config, dict) and 'temperature' in model_config:
|
|
149
|
-
temp = model_config['temperature']
|
|
150
|
-
if not 0.0 <= temp <= 1.0:
|
|
151
|
-
print(f"Warning: Temperature for model '{model_key}' is outside the 0.0 to 1.0 range")
|
|
152
|
-
model_config['temperature'] = 0.8
|
|
153
|
-
return data
|
|
154
|
-
|
|
155
|
-
def validate_and_fix_config_data(filepath: str) -> dict:
|
|
156
|
-
"""Load, validate, and fix configuration data"""
|
|
157
|
-
try:
|
|
158
|
-
with open(filepath, "r", encoding="utf-8") as file:
|
|
159
|
-
data = json.load(file)
|
|
160
|
-
|
|
161
|
-
# Get known fields from the ARAconfig model
|
|
162
|
-
known_fields = set(ARAconfig.model_fields.keys())
|
|
163
|
-
|
|
164
|
-
# Handle unrecognized keys
|
|
165
|
-
data = handle_unrecognized_keys(data, known_fields)
|
|
166
|
-
|
|
167
|
-
# Fix LLM temperatures before validation
|
|
168
|
-
data = fix_llm_temperatures(data)
|
|
169
|
-
|
|
170
|
-
return data
|
|
171
|
-
except json.JSONDecodeError as e:
|
|
172
|
-
print(f"Error: Invalid JSON in configuration file: {e}")
|
|
173
|
-
print("Creating new configuration with defaults...")
|
|
174
|
-
return {}
|
|
175
|
-
except Exception as e:
|
|
176
|
-
print(f"Error reading configuration file: {e}")
|
|
177
|
-
return {}
|
|
178
187
|
|
|
179
188
|
# Function to read the JSON file and return an ARAconfig model
|
|
180
189
|
@lru_cache(maxsize=1)
|
|
181
190
|
def read_data(filepath: str) -> ARAconfig:
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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))
|
|
185
207
|
|
|
186
208
|
if not exists(filepath):
|
|
187
|
-
|
|
209
|
+
print(f"Configuration file not found. Creating a default one at '{filepath}'.")
|
|
188
210
|
default_config = ARAconfig()
|
|
189
211
|
save_data(filepath, default_config)
|
|
190
|
-
print(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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...")
|
|
222
|
+
default_config = ARAconfig()
|
|
223
|
+
save_data(filepath, default_config)
|
|
224
|
+
return default_config
|
|
225
|
+
|
|
226
|
+
data = handle_unrecognized_keys(data)
|
|
195
227
|
|
|
196
|
-
# Validate and load the existing configuration
|
|
197
|
-
data = validate_and_fix_config_data(filepath)
|
|
198
|
-
|
|
199
228
|
try:
|
|
200
|
-
# Try to create the config with the loaded data
|
|
201
229
|
config = ARAconfig(**data)
|
|
202
|
-
|
|
203
|
-
# Save the potentially fixed configuration back
|
|
204
230
|
save_data(filepath, config)
|
|
205
|
-
|
|
206
231
|
return config
|
|
207
232
|
except ValidationError as e:
|
|
208
|
-
print(
|
|
209
|
-
print(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
233
|
+
print("--- Configuration Error Detected ---")
|
|
234
|
+
print(
|
|
235
|
+
"Some settings in your configuration file are invalid. Attempting to fix them."
|
|
236
|
+
)
|
|
237
|
+
|
|
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
|
|
255
|
+
|
|
229
256
|
|
|
230
257
|
# Function to save the modified configuration back to the JSON file
|
|
231
258
|
def save_data(filepath: str, config: ARAconfig):
|
|
259
|
+
"""Saves the Pydantic config model to a JSON file."""
|
|
232
260
|
with open(filepath, "w", encoding="utf-8") as file:
|
|
233
261
|
json.dump(config.model_dump(), file, indent=4)
|
|
234
262
|
|
|
263
|
+
|
|
235
264
|
# Singleton for configuration management
|
|
236
265
|
class ConfigManager:
|
|
237
266
|
_config_instance = None
|
|
238
267
|
|
|
239
268
|
@classmethod
|
|
240
|
-
def get_config(cls, filepath=DEFAULT_CONFIG_LOCATION):
|
|
269
|
+
def get_config(cls, filepath=DEFAULT_CONFIG_LOCATION) -> ARAconfig:
|
|
241
270
|
if cls._config_instance is None:
|
|
242
|
-
config_dir = dirname(filepath)
|
|
243
|
-
|
|
244
|
-
if not exists(config_dir):
|
|
245
|
-
makedirs(config_dir)
|
|
246
|
-
|
|
247
271
|
cls._config_instance = read_data(filepath)
|
|
248
272
|
return cls._config_instance
|
|
249
|
-
|
|
273
|
+
|
|
250
274
|
@classmethod
|
|
251
275
|
def reset(cls):
|
|
252
|
-
"""Reset the configuration instance (useful for testing)"""
|
|
276
|
+
"""Reset the configuration instance (useful for testing)."""
|
|
253
277
|
cls._config_instance = None
|
|
254
|
-
read_data.cache_clear()
|
|
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)
|