--- /dev/null
+package eu.svjatoslav.alyverkko_cli.commands;
+import eu.svjatoslav.alyverkko_cli.Command;
+import eu.svjatoslav.alyverkko_cli.configuration.Configuration;
+import eu.svjatoslav.alyverkko_cli.configuration.ConfigurationHelper;
+import eu.svjatoslav.alyverkko_cli.configuration.Model;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.*;
+import java.util.*;
+
+import static eu.svjatoslav.alyverkko_cli.Utils.fileHasToComputeMarker;
+
+/**
+ * This command recursively adds a TOCOMPUTE header to all non-hidden files in the current directory
+ * that do not already have one. It prompts the user for skill, model, and priority values,
+ * validates their existence in the configuration, and then processes the files accordingly.
+ * <p>
+ * Usage:
+ * <pre>
+ * alyverkko-cli addheader
+ * </pre>
+ * The command will interactively prompt for:
+ * - Skill name (must exist in skills directory)
+ * - Model alias (must exist in configuration models)
+ * - Priority value (integer, defaults to 0)
+ * <p>
+ * After validation, it will process all non-hidden files in the current directory and subdirectories,
+ * adding the TOCOMPUTE header at the beginning of files that don't already have one.
+ */
+public class AddTaskHeaderCommand implements Command {
+
+ @Override
+ public String getCommandName() {
+ return "addheader";
+ }
+
+ /**
+ * Executes the addheader command. Loads configuration, prompts user for skill, model, and priority,
+ * validates them, and processes all files in the current directory recursively.
+ *
+ * @param cliArguments command-line arguments (unused in this command)
+ * @throws IOException if file operations fail
+ * @throws InterruptedException if interrupted during processing
+ */
+ @Override
+ public void executeCommand(String[] cliArguments) throws IOException, InterruptedException {
+ // Load configuration to validate skills and models
+ Configuration config = ConfigurationHelper.loadConfiguration(ConfigurationHelper.getConfigurationFile(null));
+ if (config == null) {
+ System.err.println("ERROR: Failed to load configuration file");
+ return;
+ }
+
+ Scanner scanner = new Scanner(System.in);
+ String skill = promptForSkill(scanner, config);
+ String model = promptForModel(scanner, config);
+ int priority = promptForPriority(scanner);
+
+ System.out.println("\nProcessing files in current directory...");
+ processDirectory(new File("."), skill, model, priority);
+ System.out.println("\nProcessing complete!");
+ }
+
+ /**
+ * Prompts user for skill name and validates it exists in skills directory.
+ *
+ * @param scanner Scanner for user input
+ * @param config Current configuration
+ * @return Validated skill name
+ */
+ private String promptForSkill(Scanner scanner, Configuration config) {
+ while (true) {
+ System.out.print("Enter skill name: ");
+ String skill = scanner.nextLine().trim();
+ File skillFile = new File(config.getSkillsDirectory(), skill + ".yaml");
+ if (skillFile.exists() && skillFile.isFile()) {
+ return skill;
+ }
+ System.out.println("ERROR: Skill '" + skill + "' not found in " + config.getSkillsDirectory());
+ System.out.println("Available skills: " + Arrays.toString(config.getSkillsDirectory().list((dir, name) -> name.endsWith(".yaml"))));
+ }
+ }
+
+ /**
+ * Prompts user for model alias and validates it exists in configuration.
+ *
+ * @param scanner Scanner for user input
+ * @param config Current configuration
+ * @return Validated model alias
+ */
+ private String promptForModel(Scanner scanner, Configuration config) {
+ List<Model> models = config.getModels();
+ if (models == null || models.isEmpty()) {
+ System.err.println("ERROR: No models configured. Please check your configuration file.");
+ System.exit(1);
+ }
+
+ Map<String, Model> modelMap = new HashMap<>();
+ for (Model model : models) {
+ modelMap.put(model.getAlias(), model);
+ }
+
+ while (true) {
+ System.out.print("Enter model alias: ");
+ String alias = scanner.nextLine().trim();
+ if (modelMap.containsKey(alias)) {
+ return alias;
+ }
+ System.out.println("ERROR: Model '" + alias + "' not found. Available models: " + String.join(", ", modelMap.keySet()));
+ }
+ }
+
+ /**
+ * Prompts user for priority value with validation.
+ *
+ * @param scanner Scanner for user input
+ * @return Validated priority integer
+ */
+ private int promptForPriority(Scanner scanner) {
+ while (true) {
+ System.out.print("Enter priority (integer, default 0): ");
+ String input = scanner.nextLine().trim();
+ if (input.isEmpty()) {
+ return 0;
+ }
+ try {
+ int priority = Integer.parseInt(input);
+ if (priority < 0) {
+ System.out.println("Priority must be non-negative. Using 0 as default.");
+ return 0;
+ }
+ return priority;
+ } catch (NumberFormatException e) {
+ System.out.println("ERROR: Invalid priority. Please enter an integer.");
+ }
+ }
+ }
+
+ /**
+ * Recursively processes all files in a directory, skipping hidden files.
+ *
+ * @param dir Directory to process
+ * @param skill Skill name to include in TOCOMPUTE header
+ * @param model Model alias to include in TOCOMPUTE header
+ * @param priority Priority value to include in TOCOMPUTE header
+ * @throws IOException if file operations fail
+ */
+ private void processDirectory(File dir, String skill, String model, int priority) throws IOException {
+ File[] files = dir.listFiles();
+ if (files == null) return;
+
+ for (File file : files) {
+ if (file.isDirectory()) {
+ processDirectory(file, skill, model, priority);
+ } else if (file.isFile() && !file.getName().startsWith(".")) {
+ processFile(file, skill, model, priority);
+ }
+ }
+ }
+
+ /**
+ * Processes a single file by adding TOCOMPUTE header if not already present.
+ *
+ * @param file File to process
+ * @param skill Skill name for TOCOMPUTE header
+ * @param model Model alias for TOCOMPUTE header
+ * @param priority Priority value for TOCOMPUTE header
+ * @throws IOException if file operations fail
+ */
+ private void processFile(File file, String skill, String model, int priority) throws IOException {
+
+ if (fileHasToComputeMarker(file)) {
+ System.out.println("Skipped (already has header): " + file.getAbsolutePath());
+ return;
+ }
+
+ String content = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8);
+ String newContent = String.format("TOCOMPUTE: skill=%s model=%s priority=%d\n%s", skill, model, priority, content);
+
+ Files.write(file.toPath(), newContent.getBytes(StandardCharsets.UTF_8));
+ System.out.println("Added header: " + file.getAbsolutePath());
+ }
+
+
+}