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.
Files changed (140) hide show
  1. ara_cli/__init__.py +51 -6
  2. ara_cli/__main__.py +270 -103
  3. ara_cli/ara_command_action.py +106 -63
  4. ara_cli/ara_config.py +187 -128
  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/config.py +221 -0
  11. ara_cli/ara_subcommands/convert.py +43 -0
  12. ara_cli/ara_subcommands/create.py +75 -0
  13. ara_cli/ara_subcommands/delete.py +22 -0
  14. ara_cli/ara_subcommands/extract.py +22 -0
  15. ara_cli/ara_subcommands/fetch.py +41 -0
  16. ara_cli/ara_subcommands/fetch_agents.py +22 -0
  17. ara_cli/ara_subcommands/fetch_scripts.py +19 -0
  18. ara_cli/ara_subcommands/fetch_templates.py +19 -0
  19. ara_cli/ara_subcommands/list.py +139 -0
  20. ara_cli/ara_subcommands/list_tags.py +25 -0
  21. ara_cli/ara_subcommands/load.py +48 -0
  22. ara_cli/ara_subcommands/prompt.py +136 -0
  23. ara_cli/ara_subcommands/read.py +47 -0
  24. ara_cli/ara_subcommands/read_status.py +20 -0
  25. ara_cli/ara_subcommands/read_user.py +20 -0
  26. ara_cli/ara_subcommands/reconnect.py +27 -0
  27. ara_cli/ara_subcommands/rename.py +22 -0
  28. ara_cli/ara_subcommands/scan.py +14 -0
  29. ara_cli/ara_subcommands/set_status.py +22 -0
  30. ara_cli/ara_subcommands/set_user.py +22 -0
  31. ara_cli/ara_subcommands/template.py +16 -0
  32. ara_cli/artefact_autofix.py +154 -63
  33. ara_cli/artefact_converter.py +256 -0
  34. ara_cli/artefact_models/artefact_model.py +106 -25
  35. ara_cli/artefact_models/artefact_templates.py +20 -10
  36. ara_cli/artefact_models/epic_artefact_model.py +11 -2
  37. ara_cli/artefact_models/feature_artefact_model.py +31 -1
  38. ara_cli/artefact_models/userstory_artefact_model.py +15 -3
  39. ara_cli/artefact_scan.py +2 -2
  40. ara_cli/chat.py +283 -80
  41. ara_cli/chat_agent/__init__.py +0 -0
  42. ara_cli/chat_agent/agent_process_manager.py +155 -0
  43. ara_cli/chat_script_runner/__init__.py +0 -0
  44. ara_cli/chat_script_runner/script_completer.py +23 -0
  45. ara_cli/chat_script_runner/script_finder.py +41 -0
  46. ara_cli/chat_script_runner/script_lister.py +36 -0
  47. ara_cli/chat_script_runner/script_runner.py +36 -0
  48. ara_cli/chat_web_search/__init__.py +0 -0
  49. ara_cli/chat_web_search/web_search.py +263 -0
  50. ara_cli/commands/agent_run_command.py +98 -0
  51. ara_cli/commands/fetch_agents_command.py +106 -0
  52. ara_cli/commands/fetch_scripts_command.py +43 -0
  53. ara_cli/commands/fetch_templates_command.py +39 -0
  54. ara_cli/commands/fetch_templates_commands.py +39 -0
  55. ara_cli/commands/list_agents_command.py +39 -0
  56. ara_cli/commands/read_command.py +17 -4
  57. ara_cli/completers.py +180 -0
  58. ara_cli/constants.py +2 -0
  59. ara_cli/directory_navigator.py +37 -4
  60. ara_cli/file_loaders/text_file_loader.py +2 -2
  61. ara_cli/global_file_lister.py +5 -15
  62. ara_cli/llm_utils.py +58 -0
  63. ara_cli/prompt_chat.py +20 -4
  64. ara_cli/prompt_extractor.py +199 -76
  65. ara_cli/prompt_handler.py +160 -59
  66. ara_cli/tag_extractor.py +38 -18
  67. ara_cli/template_loader.py +3 -2
  68. ara_cli/template_manager.py +52 -21
  69. ara_cli/templates/global-scripts/hello_global.py +1 -0
  70. ara_cli/templates/prompt-modules/commands/add_scenarios_for_new_behaviour.feature_creation_agent.commands.md +1 -0
  71. ara_cli/templates/prompt-modules/commands/align_feature_with_implementation_changes.interview_agent.commands.md +1 -0
  72. ara_cli/templates/prompt-modules/commands/analyze_codebase_and_plan_tasks.interview_agent.commands.md +1 -0
  73. ara_cli/templates/prompt-modules/commands/choose_best_parent_artefact.interview_agent.commands.md +1 -0
  74. ara_cli/templates/prompt-modules/commands/create_tasks_from_artefact_content.interview_agent.commands.md +1 -0
  75. ara_cli/templates/prompt-modules/commands/create_tests_for_uncovered_modules.test_generation_agent.commands.md +1 -0
  76. ara_cli/templates/prompt-modules/commands/derive_features_from_video_description.feature_creation_agent.commands.md +1 -0
  77. ara_cli/templates/prompt-modules/commands/describe_agent_capabilities.agent.commands.md +1 -0
  78. ara_cli/templates/prompt-modules/commands/empty.commands.md +2 -12
  79. ara_cli/templates/prompt-modules/commands/execute_scoped_todos_in_task.interview_agent.commands.md +1 -0
  80. ara_cli/templates/prompt-modules/commands/explain_single_file_purpose.interview_agent.commands.md +1 -0
  81. ara_cli/templates/prompt-modules/commands/extract_file_information_bullets.interview_agent.commands.md +1 -0
  82. ara_cli/templates/prompt-modules/commands/extract_general.commands.md +12 -0
  83. ara_cli/templates/prompt-modules/commands/extract_markdown.commands.md +11 -0
  84. ara_cli/templates/prompt-modules/commands/extract_python.commands.md +13 -0
  85. ara_cli/templates/prompt-modules/commands/feature_add_or_modifiy_specified_behavior.commands.md +36 -0
  86. ara_cli/templates/prompt-modules/commands/feature_generate_initial_specified_bevahior.commands.md +53 -0
  87. ara_cli/templates/prompt-modules/commands/fix_failing_behave_step_definitions.interview_agent.commands.md +1 -0
  88. ara_cli/templates/prompt-modules/commands/fix_failing_pytest_tests.interview_agent.commands.md +1 -0
  89. ara_cli/templates/prompt-modules/commands/general_instruction_policy.commands.md +47 -0
  90. ara_cli/templates/prompt-modules/commands/generate_and_fix_pytest_tests.test_generation_agent.commands.md +1 -0
  91. ara_cli/templates/prompt-modules/commands/prompt_template_tech_stack_transformer.commands.md +95 -0
  92. ara_cli/templates/prompt-modules/commands/python_bug_fixing_code.commands.md +34 -0
  93. ara_cli/templates/prompt-modules/commands/python_generate_code.commands.md +27 -0
  94. ara_cli/templates/prompt-modules/commands/python_refactoring_code.commands.md +39 -0
  95. ara_cli/templates/prompt-modules/commands/python_step_definitions_generation_and_fixing.commands.md +40 -0
  96. ara_cli/templates/prompt-modules/commands/python_unittest_generation_and_fixing.commands.md +48 -0
  97. ara_cli/templates/prompt-modules/commands/suggest_next_story_child_tasks.interview_agent.commands.md +1 -0
  98. ara_cli/templates/prompt-modules/commands/summarize_or_transcribe_media.interview_agent.commands.md +1 -0
  99. ara_cli/templates/prompt-modules/commands/update_feature_to_match_implementation.feature_creation_agent.commands.md +1 -0
  100. ara_cli/templates/prompt-modules/commands/update_user_story_with_requirements.interview_agent.commands.md +1 -0
  101. ara_cli/version.py +1 -1
  102. {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/METADATA +34 -1
  103. {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/RECORD +123 -54
  104. tests/test_ara_command_action.py +31 -19
  105. tests/test_ara_config.py +177 -90
  106. tests/test_artefact_autofix.py +170 -97
  107. tests/test_artefact_autofix_integration.py +495 -0
  108. tests/test_artefact_converter.py +357 -0
  109. tests/test_artefact_extraction.py +564 -0
  110. tests/test_artefact_scan.py +1 -1
  111. tests/test_chat.py +162 -126
  112. tests/test_chat_givens_images.py +603 -0
  113. tests/test_chat_script_runner.py +454 -0
  114. tests/test_global_file_lister.py +1 -1
  115. tests/test_llm_utils.py +164 -0
  116. tests/test_prompt_chat.py +343 -0
  117. tests/test_prompt_extractor.py +683 -0
  118. tests/test_prompt_handler.py +12 -4
  119. tests/test_tag_extractor.py +19 -13
  120. tests/test_web_search.py +467 -0
  121. ara_cli/ara_command_parser.py +0 -605
  122. ara_cli/templates/prompt-modules/blueprints/complete_pytest_unittest.blueprint.md +0 -27
  123. ara_cli/templates/prompt-modules/blueprints/task_todo_list_implement_feature_BDD_way.blueprint.md +0 -30
  124. ara_cli/templates/prompt-modules/commands/artefact_classification.commands.md +0 -9
  125. ara_cli/templates/prompt-modules/commands/artefact_extension.commands.md +0 -17
  126. ara_cli/templates/prompt-modules/commands/artefact_formulation.commands.md +0 -14
  127. ara_cli/templates/prompt-modules/commands/behave_step_generation.commands.md +0 -102
  128. ara_cli/templates/prompt-modules/commands/code_generation_complex.commands.md +0 -20
  129. ara_cli/templates/prompt-modules/commands/code_generation_simple.commands.md +0 -13
  130. ara_cli/templates/prompt-modules/commands/error_fixing.commands.md +0 -20
  131. ara_cli/templates/prompt-modules/commands/feature_file_update.commands.md +0 -18
  132. ara_cli/templates/prompt-modules/commands/feature_formulation.commands.md +0 -43
  133. ara_cli/templates/prompt-modules/commands/js_code_generation_simple.commands.md +0 -13
  134. ara_cli/templates/prompt-modules/commands/refactoring.commands.md +0 -15
  135. ara_cli/templates/prompt-modules/commands/refactoring_analysis.commands.md +0 -9
  136. ara_cli/templates/prompt-modules/commands/reverse_engineer_feature_file.commands.md +0 -15
  137. ara_cli/templates/prompt-modules/commands/reverse_engineer_program_flow.commands.md +0 -19
  138. {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/WHEEL +0 -0
  139. {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/entry_points.txt +0 -0
  140. {ara_cli-0.1.10.0.dist-info → ara_cli-0.1.13.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,221 @@
1
+ """
2
+ Config subcommand for ara-cli.
3
+ Provides commands for managing ara configuration.
4
+ """
5
+ import typer
6
+ import os
7
+ from typing import Optional
8
+
9
+ config_app = typer.Typer(
10
+ help="Manage ara configuration",
11
+ no_args_is_help=True,
12
+ )
13
+
14
+
15
+ def _show_config_status(config_path: str, current_data: dict) -> None:
16
+ """Display current configuration status."""
17
+ import typer
18
+ typer.echo("Current configuration status:")
19
+ typer.echo(f" Config file: {config_path}")
20
+ typer.echo(
21
+ f" LLM configs: {len(current_data.get('llm_config', {}))} models defined")
22
+ typer.echo(f" Default LLM: {current_data.get('default_llm', 'not set')}")
23
+ typer.echo(
24
+ f" Extraction LLM: {current_data.get('extraction_llm', 'not set')}")
25
+ typer.echo("")
26
+ typer.echo("Use flags to reset specific parts:")
27
+ typer.echo(" --all Reset everything")
28
+ typer.echo(" --llm-config Reset LLM configurations")
29
+ typer.echo(" --default-llm Reset default LLM selection")
30
+ typer.echo(" --paths Reset directory paths")
31
+
32
+
33
+ def _prepare_changes(
34
+ all_config: bool, llm_config: bool, default_llm: bool,
35
+ extraction_llm: bool, paths: bool, current_data: dict, defaults: dict
36
+ ) -> tuple[list, dict]:
37
+ """Prepare the list of changes and new data."""
38
+ changes = []
39
+ new_data = current_data.copy()
40
+
41
+ if all_config:
42
+ changes.append("All configuration values")
43
+ return changes, defaults.copy()
44
+
45
+ if llm_config:
46
+ changes.append("llm_config (LLM configurations)")
47
+ new_data["llm_config"] = defaults["llm_config"]
48
+
49
+ if default_llm:
50
+ first_llm = next(
51
+ iter(new_data.get("llm_config", defaults["llm_config"])))
52
+ changes.append(f"default_llm -> '{first_llm}'")
53
+ new_data["default_llm"] = first_llm
54
+
55
+ if extraction_llm:
56
+ target_llm = new_data.get("default_llm") or next(
57
+ iter(new_data.get("llm_config", defaults["llm_config"])))
58
+ changes.append(f"extraction_llm -> '{target_llm}'")
59
+ new_data["extraction_llm"] = target_llm
60
+
61
+ if paths:
62
+ path_fields = [
63
+ "ext_code_dirs", "global_dirs", "glossary_dir", "doc_dir",
64
+ "local_prompt_templates_dir", "local_scripts_dir", "local_ara_templates_dir",
65
+ ]
66
+ for field in path_fields:
67
+ if field in defaults:
68
+ new_data[field] = defaults[field]
69
+ changes.append(
70
+ "Directory paths (ext_code_dirs, glossary_dir, doc_dir, etc.)")
71
+
72
+ return changes, new_data
73
+
74
+
75
+ def _apply_config_changes(config_path: str, new_data: dict) -> None:
76
+ """Validate and save configuration changes."""
77
+ import typer
78
+ from ara_cli.ara_config import ARAconfig, save_data, ConfigManager
79
+
80
+ validated_config = ARAconfig(**new_data)
81
+ save_data(config_path, validated_config)
82
+ ConfigManager.reset()
83
+
84
+ typer.echo("")
85
+ typer.echo("✓ Configuration reset successfully.")
86
+ typer.echo(f" Saved to: {config_path}")
87
+
88
+
89
+ @config_app.command("reset")
90
+ def reset_config(
91
+ all_config: bool = typer.Option(
92
+ False, "--all", "-a", help="Reset entire configuration to defaults"),
93
+ llm_config: bool = typer.Option(
94
+ False, "--llm-config", help="Reset only llm_config to defaults"),
95
+ default_llm: bool = typer.Option(
96
+ False, "--default-llm", help="Reset only default_llm to first available LLM"),
97
+ extraction_llm: bool = typer.Option(
98
+ False, "--extraction-llm", help="Reset only extraction_llm to match default_llm"),
99
+ paths: bool = typer.Option(
100
+ False, "--paths", help="Reset directory paths to defaults"),
101
+ dry_run: bool = typer.Option(
102
+ False, "--dry-run", help="Show what would be reset without making changes"),
103
+ yes: bool = typer.Option(False, "--yes", "-y",
104
+ help="Skip confirmation prompt"),
105
+ ):
106
+ """
107
+ Reset ara configuration to default values.
108
+
109
+ If no flags are specified, shows current configuration status.
110
+ Use specific flags to reset only certain parts of the configuration.
111
+
112
+ Examples:
113
+ ara config reset --llm-config # Reset only LLM configurations
114
+ ara config reset --all # Reset everything to defaults
115
+ ara config reset --paths --dry-run # Preview path reset without applying
116
+ """
117
+ from ara_cli.ara_config import DEFAULT_CONFIG_LOCATION, ARAconfig, get_default_llm_config
118
+ import json
119
+
120
+ config_path = DEFAULT_CONFIG_LOCATION
121
+
122
+ if not os.path.exists(config_path):
123
+ typer.echo(f"Configuration file not found at '{config_path}'.")
124
+ typer.echo("Run any ara command to create a default configuration.")
125
+ raise typer.Exit(1)
126
+
127
+ try:
128
+ with open(config_path, "r", encoding="utf-8") as f:
129
+ current_data = json.load(f)
130
+ except json.JSONDecodeError as e:
131
+ typer.echo(f"Error reading configuration: {e}")
132
+ raise typer.Exit(1)
133
+
134
+ no_flags = not any(
135
+ [all_config, llm_config, default_llm, extraction_llm, paths])
136
+ if no_flags:
137
+ _show_config_status(config_path, current_data)
138
+ return
139
+
140
+ default_config = ARAconfig(llm_config=get_default_llm_config())
141
+ defaults = default_config.model_dump()
142
+
143
+ changes, new_data = _prepare_changes(
144
+ all_config, llm_config, default_llm, extraction_llm, paths, current_data, defaults
145
+ )
146
+
147
+ typer.echo("The following will be reset to defaults:")
148
+ for change in changes:
149
+ typer.echo(f" • {change}")
150
+
151
+ if dry_run:
152
+ typer.echo("")
153
+ typer.echo("[Dry run - no changes made]")
154
+ return
155
+
156
+ if not yes:
157
+ typer.echo("")
158
+ if not typer.confirm("Proceed with reset?"):
159
+ typer.echo("Reset cancelled.")
160
+ raise typer.Exit(0)
161
+
162
+ try:
163
+ _apply_config_changes(config_path, new_data)
164
+ except Exception as e:
165
+ typer.echo(f"Error saving configuration: {e}", err=True)
166
+ raise typer.Exit(1)
167
+
168
+
169
+ @config_app.command("show")
170
+ def show_config(
171
+ llm_only: bool = typer.Option(
172
+ False,
173
+ "--llm",
174
+ help="Show only LLM configurations"
175
+ ),
176
+ json_output: bool = typer.Option(
177
+ False,
178
+ "--json",
179
+ help="Output as JSON"
180
+ ),
181
+ ):
182
+ """
183
+ Show current ara configuration.
184
+
185
+ Examples:
186
+ ara config show # Show full configuration
187
+ ara config show --llm # Show only LLM configurations
188
+ ara config show --json # Output as JSON
189
+ """
190
+ from ara_cli.ara_config import DEFAULT_CONFIG_LOCATION
191
+ import json
192
+
193
+ config_path = DEFAULT_CONFIG_LOCATION
194
+
195
+ if not os.path.exists(config_path):
196
+ typer.echo(f"Configuration file not found at '{config_path}'.")
197
+ raise typer.Exit(1)
198
+
199
+ with open(config_path, "r", encoding="utf-8") as f:
200
+ config_data = json.load(f)
201
+
202
+ if llm_only:
203
+ output_data = {
204
+ "llm_config": config_data.get("llm_config", {}),
205
+ "default_llm": config_data.get("default_llm"),
206
+ "extraction_llm": config_data.get("extraction_llm"),
207
+ }
208
+ else:
209
+ output_data = config_data
210
+
211
+ if json_output:
212
+ typer.echo(json.dumps(output_data, indent=2))
213
+ else:
214
+ typer.echo(f"Configuration file: {config_path}")
215
+ typer.echo("")
216
+ typer.echo(json.dumps(output_data, indent=2))
217
+
218
+
219
+ def register(app: typer.Typer):
220
+ """Register the config command group with the main app."""
221
+ app.add_typer(config_app, name="config")
@@ -0,0 +1,43 @@
1
+ import typer
2
+
3
+ from ara_cli import error_handler
4
+ from ara_cli.completers import DynamicCompleters
5
+
6
+
7
+ def register(app: typer.Typer):
8
+ @app.command()
9
+ def convert(
10
+ old_classifier: str = typer.Argument(
11
+ ...,
12
+ help=" The classifier of the source artefact",
13
+ autocompletion=DynamicCompleters.create_classifier_completer(),
14
+ ),
15
+ artefact_name: str = typer.Argument(
16
+ ...,
17
+ help="The name of the artefact to convert",
18
+ autocompletion=DynamicCompleters.create_convert_source_artefact_name_completer(),
19
+ ),
20
+ new_classifier: str = typer.Argument(
21
+ ...,
22
+ help="The target classifier",
23
+ autocompletion=DynamicCompleters.create_classifier_completer(),
24
+ ),
25
+ merge: bool = typer.Option(
26
+ False, "--merge", help="Merge with existing artefact if it exists"
27
+ ),
28
+ override: bool = typer.Option(
29
+ False, "--override", help="Override existing artefact if it exists"
30
+ ),
31
+ ):
32
+ """
33
+ Convert an existing artefact from one classifier to another.
34
+ """
35
+ try:
36
+ from ara_cli.artefact_converter import AraArtefactConverter
37
+
38
+ converter = AraArtefactConverter()
39
+ converter.convert(
40
+ old_classifier, artefact_name, new_classifier, merge, override
41
+ )
42
+ except Exception as e:
43
+ error_handler.handle_error(e)
@@ -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,41 @@
1
+ import typer
2
+
3
+
4
+ def register(app: typer.Typer):
5
+ @app.command(
6
+ name="fetch",
7
+ help="Fetch templates, scripts, or agents. If no flags provided, fetches all.",
8
+ )
9
+ def fetch(
10
+ templates: bool = typer.Option(
11
+ False, "--templates", "-t", help="Fetch prompt templates only."
12
+ ),
13
+ scripts: bool = typer.Option(
14
+ False, "--scripts", "-s", help="Fetch scripts only."
15
+ ),
16
+ agents: bool = typer.Option(False, "--agents", "-a", help="Fetch agents only."),
17
+ ):
18
+ from ara_cli.commands.fetch_templates_command import FetchTemplatesCommand
19
+ from ara_cli.commands.fetch_scripts_command import FetchScriptsCommand
20
+ from ara_cli.commands.fetch_agents_command import FetchAgentsCommand
21
+
22
+ if not any([templates, scripts, agents]):
23
+ templates = True
24
+ scripts = True
25
+ agents = True
26
+ typer.echo("Fetching all resources (templates, scripts, agents)...")
27
+
28
+ if templates:
29
+ typer.echo("Fetching templates...")
30
+ command = FetchTemplatesCommand()
31
+ command.execute()
32
+
33
+ if scripts:
34
+ typer.echo("Fetching scripts...")
35
+ command = FetchScriptsCommand()
36
+ command.execute()
37
+
38
+ if agents:
39
+ typer.echo("Fetching agents...")
40
+ command = FetchAgentsCommand()
41
+ command.execute()
@@ -0,0 +1,22 @@
1
+ import typer
2
+
3
+
4
+ def register(app: typer.Typer):
5
+ """Register the fetch-agents command with the typer app."""
6
+
7
+ @app.command(
8
+ name="fetch-agents",
9
+ help="Fetch binary agents from templates to project.",
10
+ deprecated=True,
11
+ )
12
+ def fetch_agents():
13
+ """Fetch binary agents from ara_cli/templates/agents to ara/.araconfig/agents."""
14
+ from ara_cli.commands.fetch_agents_command import FetchAgentsCommand
15
+
16
+ typer.secho(
17
+ "WARNING: 'fetch-agents' is deprecated. Please use 'ara fetch --agents' or just 'ara fetch' instead.",
18
+ fg=typer.colors.YELLOW,
19
+ err=True,
20
+ )
21
+ command = FetchAgentsCommand()
22
+ command.execute()
@@ -0,0 +1,19 @@
1
+ import typer
2
+
3
+
4
+ def register(app: typer.Typer):
5
+ @app.command(
6
+ name="fetch-scripts",
7
+ help="Fetch global scripts into your config directory.",
8
+ deprecated=True,
9
+ )
10
+ def fetch_scripts():
11
+ from ara_cli.commands.fetch_scripts_command import FetchScriptsCommand
12
+
13
+ typer.secho(
14
+ "WARNING: 'fetch-scripts' is deprecated. Please use 'ara fetch --scripts' or just 'ara fetch' instead.",
15
+ fg=typer.colors.YELLOW,
16
+ err=True,
17
+ )
18
+ command = FetchScriptsCommand()
19
+ command.execute()
@@ -0,0 +1,19 @@
1
+ import typer
2
+
3
+
4
+ def register(app: typer.Typer):
5
+ @app.command(
6
+ name="fetch-templates",
7
+ help="Fetch global prompt templates into your config directory.",
8
+ deprecated=True,
9
+ )
10
+ def fetch_templates():
11
+ from ara_cli.commands.fetch_templates_command import FetchTemplatesCommand
12
+
13
+ typer.secho(
14
+ "WARNING: 'fetch-templates' is deprecated. Please use 'ara fetch --templates' or just 'ara fetch' instead.",
15
+ fg=typer.colors.YELLOW,
16
+ err=True,
17
+ )
18
+ command = FetchTemplatesCommand()
19
+ command.execute()
@@ -0,0 +1,139 @@
1
+ import typer
2
+ from ara_cli.error_handler import AraError
3
+ from typing import Optional, List, Tuple
4
+ from .common import MockArgs
5
+ from ara_cli.completers import DynamicCompleters
6
+ from ara_cli.ara_command_action import list_action
7
+
8
+
9
+ def _validate_extension_options(
10
+ include_extension: Optional[List[str]], exclude_extension: Optional[List[str]]
11
+ ) -> None:
12
+ """Validate that include and exclude extension options are mutually exclusive."""
13
+ if include_extension and exclude_extension:
14
+ raise AraError(
15
+ "--include-extension/-i and --exclude-extension/-e are mutually exclusive"
16
+ )
17
+
18
+
19
+ def _validate_exclusive_options(
20
+ branch: bool,
21
+ children: bool,
22
+ data: bool,
23
+ ) -> None:
24
+ """Validate that branch, children, and data options are mutually exclusive."""
25
+ exclusive_options = [branch, children, data]
26
+ true_options = [opt for opt in exclusive_options if opt]
27
+ if len(true_options) > 1:
28
+ raise AraError("--branch, --children, and --data are mutually exclusive")
29
+
30
+
31
+ def list_main(
32
+ classifier: Optional[str] = typer.Argument(
33
+ None,
34
+ help="The classifier of the artefact",
35
+ autocompletion=DynamicCompleters.create_classifier_completer(),
36
+ ),
37
+ artefact_name: Optional[str] = typer.Argument(
38
+ None,
39
+ help="The name of the artefact",
40
+ autocompletion=DynamicCompleters.create_artefact_name_completer(),
41
+ ),
42
+ include_content: Optional[List[str]] = typer.Option(
43
+ None,
44
+ "-I",
45
+ "--include-content",
46
+ help="filter for files which include given content",
47
+ ),
48
+ exclude_content: Optional[List[str]] = typer.Option(
49
+ None,
50
+ "-E",
51
+ "--exclude-content",
52
+ help="filter for files which do not include given content",
53
+ ),
54
+ include_tags: Optional[List[str]] = typer.Option(
55
+ None, "--include-tags", help="filter for files which include given tags"
56
+ ),
57
+ exclude_tags: Optional[List[str]] = typer.Option(
58
+ None, "--exclude-tags", help="filter for files which do not include given tags"
59
+ ),
60
+ include_extension: Optional[List[str]] = typer.Option(
61
+ None,
62
+ "-i",
63
+ "--include-extension",
64
+ "--include-classifier",
65
+ help="list of extensions to include in listing",
66
+ ),
67
+ exclude_extension: Optional[List[str]] = typer.Option(
68
+ None,
69
+ "-e",
70
+ "--exclude-extension",
71
+ "--exclude-classifier",
72
+ help="list of extensions to exclude from listing",
73
+ ),
74
+ branch: bool = typer.Option(
75
+ False,
76
+ "-b",
77
+ "--branch",
78
+ help="List artefacts in the parent chain (requires classifier and artefact_name)",
79
+ ),
80
+ children: bool = typer.Option(
81
+ False,
82
+ "-c",
83
+ "--children",
84
+ help="List child artefacts (requires classifier and artefact_name)",
85
+ ),
86
+ data: bool = typer.Option(
87
+ False,
88
+ "-d",
89
+ "--data",
90
+ help="List file in the data directory (requires classifier and artefact_name)",
91
+ ),
92
+ ):
93
+ """List files with optional tags.
94
+
95
+ Examples:
96
+ ara list feature my_feature --data --include-extension .md
97
+ ara list --include-extension .feature
98
+ ara list userstory my_story --children
99
+ ara list userstory my_story --branch --include-extension .businessgoal
100
+ ara list --include-content "example content" --include-extension .task
101
+ """
102
+ _validate_extension_options(include_extension, exclude_extension)
103
+ _validate_exclusive_options(branch, children, data)
104
+
105
+ # If classifier is provided but no artefact_name, and no other specific flags are set,
106
+ # treat it as a filter by classifier (extension).
107
+ # This supports "ara list feature" -> lists all features.
108
+ if classifier and not artefact_name and not (branch or children or data):
109
+ # We append the classifier (prefixed with '.') to include_extension
110
+ # This assumes classifier names correspond to extensions (e.g. 'feature' -> '.feature')
111
+ # existing logic usually expects the extension format.
112
+ ext = f".{classifier}"
113
+ if include_extension:
114
+ include_extension.append(ext)
115
+ else:
116
+ include_extension = [ext]
117
+ # Clear classifier from args so it doesn't trigger other logic
118
+ classifier = None
119
+
120
+ args = MockArgs(
121
+ classifier=classifier,
122
+ artefact_name=artefact_name,
123
+ include_content=include_content,
124
+ exclude_content=exclude_content,
125
+ include_tags=include_tags,
126
+ exclude_tags=exclude_tags,
127
+ include_extension=include_extension,
128
+ exclude_extension=exclude_extension,
129
+ branch=branch,
130
+ children=children,
131
+ data=data,
132
+ )
133
+
134
+ list_action(args)
135
+
136
+
137
+ def register(parent: typer.Typer):
138
+ help_text = "List files with optional tags"
139
+ parent.command(name="list", help=help_text)(list_main)
@@ -0,0 +1,25 @@
1
+ import typer
2
+ from typing import Optional
3
+ from .common import ClassifierEnum, MockArgs, ClassifierOption
4
+ from ara_cli.ara_command_action import list_tags_action
5
+
6
+
7
+ def list_tags_main(
8
+ json_output: bool = typer.Option(False, "-j", "--json/--no-json", help="Output tags as JSON"),
9
+ include_classifier: Optional[ClassifierEnum] = ClassifierOption("Show tags for an artefact type", "--include-classifier"),
10
+ exclude_classifier: Optional[ClassifierEnum] = ClassifierOption("Show tags for an artefact type", "--exclude-classifier"),
11
+ filtered_extra_column: bool = typer.Option(False, "--filtered-extra-column", help="Filter tags for extra column")
12
+ ):
13
+ """Show tags."""
14
+ args = MockArgs(
15
+ json=json_output,
16
+ include_classifier=include_classifier.value if include_classifier else None,
17
+ exclude_classifier=exclude_classifier.value if exclude_classifier else None,
18
+ filtered_extra_column=filtered_extra_column
19
+ )
20
+ list_tags_action(args)
21
+
22
+
23
+ def register(parent: typer.Typer):
24
+ help_text = "Show tags"
25
+ parent.command(name="list-tags", help=help_text)(list_tags_main)