ara-cli 0.1.10.0__py3-none-any.whl → 0.1.13.3__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.
- ara_cli/__init__.py +51 -6
- ara_cli/__main__.py +270 -103
- ara_cli/ara_command_action.py +106 -63
- ara_cli/ara_config.py +187 -128
- 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/config.py +221 -0
- ara_cli/ara_subcommands/convert.py +43 -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.py +41 -0
- ara_cli/ara_subcommands/fetch_agents.py +22 -0
- ara_cli/ara_subcommands/fetch_scripts.py +19 -0
- ara_cli/ara_subcommands/fetch_templates.py +19 -0
- ara_cli/ara_subcommands/list.py +139 -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 +154 -63
- ara_cli/artefact_converter.py +256 -0
- ara_cli/artefact_models/artefact_model.py +106 -25
- ara_cli/artefact_models/artefact_templates.py +20 -10
- ara_cli/artefact_models/epic_artefact_model.py +11 -2
- ara_cli/artefact_models/feature_artefact_model.py +31 -1
- ara_cli/artefact_models/userstory_artefact_model.py +15 -3
- ara_cli/artefact_scan.py +2 -2
- ara_cli/chat.py +283 -80
- ara_cli/chat_agent/__init__.py +0 -0
- ara_cli/chat_agent/agent_process_manager.py +155 -0
- ara_cli/chat_script_runner/__init__.py +0 -0
- ara_cli/chat_script_runner/script_completer.py +23 -0
- ara_cli/chat_script_runner/script_finder.py +41 -0
- ara_cli/chat_script_runner/script_lister.py +36 -0
- ara_cli/chat_script_runner/script_runner.py +36 -0
- ara_cli/chat_web_search/__init__.py +0 -0
- ara_cli/chat_web_search/web_search.py +263 -0
- ara_cli/commands/agent_run_command.py +98 -0
- ara_cli/commands/fetch_agents_command.py +106 -0
- ara_cli/commands/fetch_scripts_command.py +43 -0
- ara_cli/commands/fetch_templates_command.py +39 -0
- ara_cli/commands/fetch_templates_commands.py +39 -0
- ara_cli/commands/list_agents_command.py +39 -0
- ara_cli/commands/read_command.py +17 -4
- ara_cli/completers.py +180 -0
- ara_cli/constants.py +2 -0
- ara_cli/directory_navigator.py +37 -4
- ara_cli/file_loaders/text_file_loader.py +2 -2
- ara_cli/global_file_lister.py +5 -15
- ara_cli/llm_utils.py +58 -0
- ara_cli/prompt_chat.py +20 -4
- ara_cli/prompt_extractor.py +199 -76
- ara_cli/prompt_handler.py +160 -59
- ara_cli/tag_extractor.py +38 -18
- ara_cli/template_loader.py +3 -2
- ara_cli/template_manager.py +52 -21
- ara_cli/templates/global-scripts/hello_global.py +1 -0
- ara_cli/templates/prompt-modules/commands/add_scenarios_for_new_behaviour.feature_creation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/align_feature_with_implementation_changes.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/analyze_codebase_and_plan_tasks.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/choose_best_parent_artefact.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/create_tasks_from_artefact_content.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/create_tests_for_uncovered_modules.test_generation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/derive_features_from_video_description.feature_creation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/describe_agent_capabilities.agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
- ara_cli/templates/prompt-modules/commands/execute_scoped_todos_in_task.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/explain_single_file_purpose.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/extract_file_information_bullets.interview_agent.commands.md +1 -0
- 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/fix_failing_behave_step_definitions.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/fix_failing_pytest_tests.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/general_instruction_policy.commands.md +47 -0
- ara_cli/templates/prompt-modules/commands/generate_and_fix_pytest_tests.test_generation_agent.commands.md +1 -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/templates/prompt-modules/commands/suggest_next_story_child_tasks.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/summarize_or_transcribe_media.interview_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/update_feature_to_match_implementation.feature_creation_agent.commands.md +1 -0
- ara_cli/templates/prompt-modules/commands/update_user_story_with_requirements.interview_agent.commands.md +1 -0
- ara_cli/version.py +1 -1
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/METADATA +34 -1
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/RECORD +123 -54
- tests/test_ara_command_action.py +31 -19
- tests/test_ara_config.py +177 -90
- tests/test_artefact_autofix.py +170 -97
- tests/test_artefact_autofix_integration.py +495 -0
- tests/test_artefact_converter.py +357 -0
- tests/test_artefact_extraction.py +564 -0
- tests/test_artefact_scan.py +1 -1
- tests/test_chat.py +162 -126
- tests/test_chat_givens_images.py +603 -0
- tests/test_chat_script_runner.py +454 -0
- tests/test_global_file_lister.py +1 -1
- tests/test_llm_utils.py +164 -0
- tests/test_prompt_chat.py +343 -0
- tests/test_prompt_extractor.py +683 -0
- tests/test_prompt_handler.py +12 -4
- tests/test_tag_extractor.py +19 -13
- tests/test_web_search.py +467 -0
- ara_cli/ara_command_parser.py +0 -605
- 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.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/WHEEL +0 -0
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/entry_points.txt +0 -0
- {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/top_level.txt +0 -0
ara_cli/ara_config.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from typing import List, Dict, Optional
|
|
1
|
+
from typing import List, Dict, Optional
|
|
2
2
|
from pydantic import BaseModel, ValidationError, Field, model_validator
|
|
3
3
|
import json
|
|
4
4
|
import os
|
|
5
|
-
from os.path import exists, dirname
|
|
6
5
|
from os import makedirs
|
|
6
|
+
from os.path import exists, dirname
|
|
7
7
|
from functools import lru_cache
|
|
8
8
|
import sys
|
|
9
9
|
import warnings
|
|
@@ -14,21 +14,92 @@ DEFAULT_CONFIG_LOCATION = "./ara/.araconfig/ara_config.json"
|
|
|
14
14
|
class LLMConfigItem(BaseModel):
|
|
15
15
|
provider: str
|
|
16
16
|
model: str
|
|
17
|
-
temperature: float = Field(ge=0.0, le=
|
|
17
|
+
temperature: Optional[float] = Field(ge=0.0, le=2.0)
|
|
18
18
|
max_tokens: Optional[int] = None
|
|
19
19
|
max_completion_tokens: Optional[int] = None
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
def get_default_llm_config() -> Dict[str, "LLMConfigItem"]:
|
|
23
|
+
"""Returns the default LLM configuration."""
|
|
24
|
+
return {
|
|
25
|
+
"gpt-5.2": LLMConfigItem(
|
|
26
|
+
provider="openai",
|
|
27
|
+
model="openai/gpt-5.2",
|
|
28
|
+
temperature=1,
|
|
29
|
+
max_completion_tokens=16000,
|
|
30
|
+
),
|
|
31
|
+
"gpt-5-mini": LLMConfigItem(
|
|
32
|
+
provider="openai", model="openai/gpt-5-mini", temperature=1
|
|
33
|
+
),
|
|
34
|
+
"gpt-5-web": LLMConfigItem(
|
|
35
|
+
provider="openai",
|
|
36
|
+
model="openai/gpt-5-search-api",
|
|
37
|
+
temperature=1,
|
|
38
|
+
max_completion_tokens=16000,
|
|
39
|
+
),
|
|
40
|
+
"gpt-4o": LLMConfigItem(
|
|
41
|
+
provider="openai",
|
|
42
|
+
model="openai/gpt-4o",
|
|
43
|
+
temperature=0.8,
|
|
44
|
+
max_tokens=16000,
|
|
45
|
+
),
|
|
46
|
+
"gpt-4o-search-preview": LLMConfigItem(
|
|
47
|
+
provider="openai",
|
|
48
|
+
model="openai/gpt-4o-search-preview",
|
|
49
|
+
temperature=None,
|
|
50
|
+
max_tokens=None,
|
|
51
|
+
max_completion_tokens=None,
|
|
52
|
+
),
|
|
53
|
+
"opus-4.5-advanced": LLMConfigItem(
|
|
54
|
+
provider="anthropic",
|
|
55
|
+
model="anthropic/claude-opus-4-5-20251101",
|
|
56
|
+
temperature=0.5,
|
|
57
|
+
max_tokens=32000,
|
|
58
|
+
),
|
|
59
|
+
"opus-4.1-exceptional": LLMConfigItem(
|
|
60
|
+
provider="anthropic",
|
|
61
|
+
model="anthropic/claude-opus-4-1-20250805",
|
|
62
|
+
temperature=0.5,
|
|
63
|
+
max_tokens=32000,
|
|
64
|
+
),
|
|
65
|
+
"sonnet-4.5-coding": LLMConfigItem(
|
|
66
|
+
provider="anthropic",
|
|
67
|
+
model="anthropic/claude-sonnet-4-5-20250929",
|
|
68
|
+
temperature=0.5,
|
|
69
|
+
max_tokens=32000,
|
|
70
|
+
),
|
|
71
|
+
"haiku-4-5": LLMConfigItem(
|
|
72
|
+
provider="anthropic",
|
|
73
|
+
model="anthropic/claude-haiku-4-5-20251001",
|
|
74
|
+
temperature=0.8,
|
|
75
|
+
max_tokens=32000,
|
|
76
|
+
),
|
|
77
|
+
"together-ai-llama-2": LLMConfigItem(
|
|
78
|
+
provider="together_ai",
|
|
79
|
+
model="together_ai/togethercomputer/llama-2-70b",
|
|
80
|
+
temperature=0.8,
|
|
81
|
+
max_tokens=4000,
|
|
82
|
+
),
|
|
83
|
+
"groq-llama-3": LLMConfigItem(
|
|
84
|
+
provider="groq",
|
|
85
|
+
model="groq/llama3-70b-8192",
|
|
86
|
+
temperature=0.8,
|
|
87
|
+
max_tokens=4000,
|
|
88
|
+
),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
22
92
|
class ARAconfig(BaseModel):
|
|
23
|
-
ext_code_dirs: List[Dict[str, str]] = Field(
|
|
24
|
-
{"source_dir": "./src"},
|
|
25
|
-
|
|
26
|
-
])
|
|
93
|
+
ext_code_dirs: List[Dict[str, str]] = Field(
|
|
94
|
+
default_factory=lambda: [{"source_dir": "./src"}, {"source_dir": "./tests"}]
|
|
95
|
+
)
|
|
27
96
|
global_dirs: Optional[List[Dict[str, str]]] = Field(default=[])
|
|
28
97
|
glossary_dir: str = "./glossary"
|
|
29
98
|
doc_dir: str = "./docs"
|
|
30
99
|
local_prompt_templates_dir: str = "./ara/.araconfig"
|
|
31
100
|
custom_prompt_templates_subdir: Optional[str] = "custom-prompt-modules"
|
|
101
|
+
local_scripts_dir: str = "./ara/.araconfig"
|
|
102
|
+
custom_scripts_subdir: Optional[str] = "custom-scripts"
|
|
32
103
|
local_ara_templates_dir: str = "./ara/.araconfig/templates/"
|
|
33
104
|
ara_prompt_given_list_includes: List[str] = Field(
|
|
34
105
|
default_factory=lambda: [
|
|
@@ -48,63 +119,26 @@ class ARAconfig(BaseModel):
|
|
|
48
119
|
"*.jpeg",
|
|
49
120
|
]
|
|
50
121
|
)
|
|
51
|
-
llm_config
|
|
52
|
-
|
|
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
|
-
)
|
|
122
|
+
# llm_config defaults to the standard set of models
|
|
123
|
+
llm_config: Dict[str, LLMConfigItem] = Field(default_factory=get_default_llm_config)
|
|
106
124
|
default_llm: Optional[str] = None
|
|
107
125
|
extraction_llm: Optional[str] = None
|
|
126
|
+
conversion_llm: Optional[str] = None
|
|
127
|
+
|
|
128
|
+
def _validate_llm_field(
|
|
129
|
+
self, field: str, fallback: str, missing_msg: str, invalid_msg: str
|
|
130
|
+
):
|
|
131
|
+
"""Helper to validate and set fallback for LLM fields."""
|
|
132
|
+
value = getattr(self, field)
|
|
133
|
+
if not value:
|
|
134
|
+
print(f"Warning: '{field}' is not set. {missing_msg}: '{fallback}'.")
|
|
135
|
+
setattr(self, field, fallback)
|
|
136
|
+
elif value not in self.llm_config:
|
|
137
|
+
print(
|
|
138
|
+
f"Warning: The configured '{field}' ('{value}') does not exist in 'llm_config'."
|
|
139
|
+
)
|
|
140
|
+
print(f"-> Reverting to {invalid_msg}: '{fallback}'.")
|
|
141
|
+
setattr(self, field, fallback)
|
|
108
142
|
|
|
109
143
|
@model_validator(mode="after")
|
|
110
144
|
def check_critical_fields(self) -> "ARAconfig":
|
|
@@ -113,12 +147,12 @@ class ARAconfig(BaseModel):
|
|
|
113
147
|
"ext_code_dirs": [{"source_dir": "./src"}, {"source_dir": "./tests"}],
|
|
114
148
|
"local_ara_templates_dir": "./ara/.araconfig/templates/",
|
|
115
149
|
"local_prompt_templates_dir": "./ara/.araconfig",
|
|
150
|
+
"local_scripts_dir": "./ara/.araconfig",
|
|
116
151
|
"glossary_dir": "./glossary",
|
|
117
152
|
}
|
|
118
153
|
|
|
119
154
|
for field, default_value in critical_fields.items():
|
|
120
|
-
|
|
121
|
-
if not current_value:
|
|
155
|
+
if not getattr(self, field):
|
|
122
156
|
print(
|
|
123
157
|
f"Warning: Value for '{field}' is missing or empty. Using default."
|
|
124
158
|
)
|
|
@@ -132,33 +166,25 @@ class ARAconfig(BaseModel):
|
|
|
132
166
|
self.extraction_llm = None
|
|
133
167
|
return self
|
|
134
168
|
|
|
135
|
-
|
|
169
|
+
first_available = next(iter(self.llm_config))
|
|
170
|
+
self._validate_llm_field(
|
|
171
|
+
"default_llm",
|
|
172
|
+
first_available,
|
|
173
|
+
"Defaulting to the first available model",
|
|
174
|
+
"the first available model",
|
|
175
|
+
)
|
|
136
176
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
177
|
+
# Now used as fallback for others
|
|
178
|
+
fallback_val = self.default_llm
|
|
179
|
+
fallback_missing_msg = "Setting it to the same as 'default_llm'"
|
|
180
|
+
fallback_invalid_msg = "the 'default_llm' value"
|
|
150
181
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
|
182
|
+
self._validate_llm_field(
|
|
183
|
+
"extraction_llm", fallback_val, fallback_missing_msg, fallback_invalid_msg
|
|
184
|
+
)
|
|
185
|
+
self._validate_llm_field(
|
|
186
|
+
"conversion_llm", fallback_val, fallback_missing_msg, fallback_invalid_msg
|
|
187
|
+
)
|
|
162
188
|
|
|
163
189
|
return self
|
|
164
190
|
|
|
@@ -186,71 +212,101 @@ def handle_unrecognized_keys(data: dict) -> dict:
|
|
|
186
212
|
|
|
187
213
|
|
|
188
214
|
# Function to read the JSON file and return an ARAconfig model
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _create_default_config(filepath: str) -> ARAconfig:
|
|
218
|
+
"""Create and save a default configuration."""
|
|
219
|
+
print(f"Configuration file not found. Creating a default one at '{filepath}'.")
|
|
220
|
+
default_config = ARAconfig(llm_config=get_default_llm_config())
|
|
221
|
+
save_data(filepath, default_config)
|
|
222
|
+
print("Please review the default configuration and re-run your command.")
|
|
223
|
+
sys.exit(0)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _load_json_config(filepath: str) -> dict:
|
|
227
|
+
"""Load and parse JSON configuration file."""
|
|
196
228
|
|
|
197
229
|
def warn_on_duplicate_llm_dict_key(ordered_pairs):
|
|
198
230
|
"""Reject duplicate keys."""
|
|
199
231
|
d = {}
|
|
200
232
|
for k, v in ordered_pairs:
|
|
201
233
|
if k in d:
|
|
202
|
-
warnings.warn(
|
|
234
|
+
warnings.warn(
|
|
235
|
+
f"Duplicate LLM configuration identifier '{k}'. The previous entry will be removed.",
|
|
236
|
+
UserWarning,
|
|
237
|
+
)
|
|
203
238
|
d[k] = v
|
|
204
239
|
return d
|
|
205
240
|
|
|
241
|
+
with open(filepath, "r", encoding="utf-8") as file:
|
|
242
|
+
content = file.read()
|
|
243
|
+
return json.loads(content, object_pairs_hook=warn_on_duplicate_llm_dict_key)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _correct_validation_errors(data: dict, errors: list) -> ARAconfig:
|
|
247
|
+
"""Correct validation errors and return a valid config."""
|
|
248
|
+
print("--- Configuration Error Detected ---")
|
|
249
|
+
print(
|
|
250
|
+
"Some settings in your configuration file are invalid. Attempting to fix them."
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
corrected_data = data.copy()
|
|
254
|
+
defaults = ARAconfig(llm_config=get_default_llm_config()).model_dump()
|
|
255
|
+
error_fields = {err["loc"][0] for err in errors if err["loc"]}
|
|
256
|
+
|
|
257
|
+
for field_name in error_fields:
|
|
258
|
+
print(
|
|
259
|
+
f"-> Field '{field_name}' is invalid and will be reverted to its default value."
|
|
260
|
+
)
|
|
261
|
+
if field_name in corrected_data:
|
|
262
|
+
corrected_data[field_name] = defaults.get(field_name)
|
|
263
|
+
|
|
264
|
+
print("--- End of Error Report ---")
|
|
265
|
+
return ARAconfig(**corrected_data)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@lru_cache(maxsize=1)
|
|
269
|
+
def read_data(filepath: str) -> ARAconfig:
|
|
270
|
+
"""
|
|
271
|
+
Reads, validates, and repairs the configuration file.
|
|
272
|
+
If the file doesn't exist, it creates a default one.
|
|
273
|
+
If the file is invalid, it corrects only the broken parts.
|
|
274
|
+
"""
|
|
206
275
|
ensure_directory_exists(dirname(filepath))
|
|
207
276
|
|
|
208
277
|
if not exists(filepath):
|
|
209
|
-
|
|
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)
|
|
278
|
+
return _create_default_config(filepath)
|
|
214
279
|
|
|
215
280
|
try:
|
|
216
|
-
|
|
217
|
-
content = file.read()
|
|
218
|
-
data = json.loads(content, object_pairs_hook=warn_on_duplicate_llm_dict_key)
|
|
281
|
+
data = _load_json_config(filepath)
|
|
219
282
|
except json.JSONDecodeError as e:
|
|
220
283
|
print(f"Error: Invalid JSON in configuration file: {e}")
|
|
221
284
|
print("Creating a new configuration with defaults...")
|
|
222
|
-
default_config = ARAconfig()
|
|
285
|
+
default_config = ARAconfig(llm_config=get_default_llm_config())
|
|
223
286
|
save_data(filepath, default_config)
|
|
224
287
|
return default_config
|
|
225
288
|
|
|
226
289
|
data = handle_unrecognized_keys(data)
|
|
227
290
|
|
|
291
|
+
needs_save = False
|
|
292
|
+
if "llm_config" not in data or not data.get("llm_config"):
|
|
293
|
+
print(
|
|
294
|
+
"Info: 'llm_config' is missing or empty. Populating with default LLM configurations."
|
|
295
|
+
)
|
|
296
|
+
data["llm_config"] = {
|
|
297
|
+
k: v.model_dump() for k, v in get_default_llm_config().items()
|
|
298
|
+
}
|
|
299
|
+
needs_save = True
|
|
300
|
+
|
|
228
301
|
try:
|
|
229
302
|
config = ARAconfig(**data)
|
|
230
|
-
|
|
303
|
+
if needs_save:
|
|
304
|
+
save_data(filepath, config)
|
|
231
305
|
return config
|
|
232
306
|
except ValidationError as e:
|
|
233
|
-
|
|
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)
|
|
307
|
+
final_config = _correct_validation_errors(data, e.errors())
|
|
251
308
|
save_data(filepath, final_config)
|
|
252
309
|
print(f"Configuration has been corrected and saved to '{filepath}'.")
|
|
253
|
-
|
|
254
310
|
return final_config
|
|
255
311
|
|
|
256
312
|
|
|
@@ -266,9 +322,12 @@ class ConfigManager:
|
|
|
266
322
|
_config_instance = None
|
|
267
323
|
|
|
268
324
|
@classmethod
|
|
269
|
-
def get_config(cls, filepath=
|
|
325
|
+
def get_config(cls, filepath=None) -> ARAconfig:
|
|
326
|
+
if filepath:
|
|
327
|
+
return read_data(filepath)
|
|
328
|
+
|
|
270
329
|
if cls._config_instance is None:
|
|
271
|
-
cls._config_instance = read_data(
|
|
330
|
+
cls._config_instance = read_data(DEFAULT_CONFIG_LOCATION)
|
|
272
331
|
return cls._config_instance
|
|
273
332
|
|
|
274
333
|
@classmethod
|
|
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.constants import VALID_ASPECTS
|
|
6
|
+
from ara_cli.completers import DynamicCompleters
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Get classifiers and aspects
|
|
10
|
+
classifiers = Classifier.ordered_classifiers()
|
|
11
|
+
aspects = 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)
|