From 615645ba9a7e5077690f3f952f178dc97bf0bf49 Mon Sep 17 00:00:00 2001 From: Svjatoslav Agejenko Date: Tue, 21 May 2024 21:44:39 +0300 Subject: [PATCH] Initial implementation of joinfiles command --- doc/index.org | 43 ++++++--- pom.xml | 10 +++ .../eu/svjatoslav/alyverkko_cli/Main.java | 4 +- .../commands/JoinFilesCommand.java | 88 +++++++++++++++++++ .../configuration/Configuration.java | 2 +- 5 files changed, 135 insertions(+), 12 deletions(-) create mode 100644 src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java diff --git a/doc/index.org b/doc/index.org index d87ffd9..91e2d29 100644 --- a/doc/index.org +++ b/doc/index.org @@ -308,18 +308,40 @@ facilities: : sudo systemctl stop alyverkko-cli : sudo systemctl disable alyverkko-cli -* Usage +* Task preparation + +The Älyverkko CLI application expects input files for processing in +the form of plain text files within the specified mail directory +(configured in the [[id:0fcdae48-81c5-4ae1-bdb9-64ae74e87c45][YAML configuration file]]). + +Suggested usage flow is to prepare AI assignments within the Älyverkko +CLI mail directory using normal text editor. Once AI assignment is +ready for processing, you should [[id:883d6e7c-60e0-422b-8c00-5cdc9dfec20d][initiate AI processing]] on that file. + +** joinfiles command + +To simplify appending together multiple files from filesystem for AI +processing in a single query, *joinfiles* command can be used. It +prompts user for the name of the output file (without extension), +thereafter it scans and recursively appends contents of all files in +the current directory and its subdirectories. + +Usage example: +: alyverkko-cli joinfiles + +This is useful for example when you want to submit entire software +project source code directory to AI for processing. + +* Initiate AI processing :PROPERTIES: :ID: 883d6e7c-60e0-422b-8c00-5cdc9dfec20d :END: -The Älyverkko CLI application expects input files for processing in -the form of plain text files within the specified mail directory -(configured in the [[id:0fcdae48-81c5-4ae1-bdb9-64ae74e87c45][YAML configuration file]]). Each file should begin -with a `TOCOMPUTE:` marker on the first line to be considered for +Once your task file is prepared, you should place *TOCOMPUTE:* marker +on the first line of that file, so that it will be considered for processing. -When the application detects a new or modified file in the mail +When the Älyverkko CLI detects a new or modified file in the mail directory: 1. It checks if file has "TOCOMPUTE:" on the first line. If no, file @@ -337,12 +359,13 @@ directory: thread. "TOCOMPUTE:" is removed from the beginning of the file to avoid processing same file again. -Suggested way to use mail processing mode is to prepare assignments -within the Älyverkko CLI mail directory using normal text editor. Feel -free to save intermediary states. Once AI assignment is ready, add +Note: During AI task file preparation, feel free to save intermediary +states as often as needed because AI engine will keep ignoring the +file until *TOCOMPUTE:* line appears. Once AI assignment is ready, add : TOCOMPUTE: to the beginning of the file and save one last time. Älyverkko CLI -will detect new task within one second and will start processing it. +will detect new task approximately within one second after file is +saved and will start processing it. If your text editor automatically reloads file when it was changed by other process in the filesystem, AI response will appear within text diff --git a/pom.xml b/pom.xml index be4cbfd..601d99f 100644 --- a/pom.xml +++ b/pom.xml @@ -50,6 +50,16 @@ jackson-dataformat-yaml 2.13.0 + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.apache.commons + commons-io + 1.3.2 + diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/Main.java b/src/main/java/eu/svjatoslav/alyverkko_cli/Main.java index 4070734..258a751 100644 --- a/src/main/java/eu/svjatoslav/alyverkko_cli/Main.java +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/Main.java @@ -1,5 +1,6 @@ package eu.svjatoslav.alyverkko_cli; +import eu.svjatoslav.alyverkko_cli.commands.JoinFilesCommand; import eu.svjatoslav.alyverkko_cli.commands.ListModelsCommand; import eu.svjatoslav.alyverkko_cli.configuration.Configuration; import eu.svjatoslav.alyverkko_cli.commands.MailCorrespondentCommand; @@ -13,7 +14,8 @@ public class Main { private final java.util.List commands = java.util.Arrays.asList( new ListModelsCommand(), - new MailCorrespondentCommand() + new MailCorrespondentCommand(), + new JoinFilesCommand() ); public static void main(final String[] args) throws IOException, InterruptedException { diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java new file mode 100644 index 0000000..ddd0665 --- /dev/null +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java @@ -0,0 +1,88 @@ +package eu.svjatoslav.alyverkko_cli.commands; + +import eu.svjatoslav.alyverkko_cli.*; +import org.apache.commons.io.FilenameUtils; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; + +import static eu.svjatoslav.alyverkko_cli.Main.configuration; +import static eu.svjatoslav.alyverkko_cli.configuration.Configuration.loadConfiguration; + +public class JoinFilesCommand implements Command { + + @Override + public String getName() { + return "joinfiles"; + } + + public Path baseDirectory; + + @Override + public void execute(String[] cliArguments) throws IOException { + configuration = loadConfiguration(); + if (configuration == null){ + System.out.println("Failed to load configuration file"); + return; + } + + // Ask for file name to be created + System.out.print("Enter the output file name (without extension): "); + String fileName = System.console().readLine(); + + // Create the output file with .org extension + File outputFile = new File(FilenameUtils.normalize(fileName + ".org")); + + // create output file within the mail directory + outputFile = new File(configuration.getMailDirectory(), outputFile.getName()); + + + try (BufferedWriter writer = Files.newBufferedWriter(outputFile.toPath(), StandardCharsets.UTF_8)) { + baseDirectory = Paths.get("."); + joinFilesRecursively(baseDirectory, writer); + } + + System.out.println("Files have been joined into: " + outputFile.getAbsolutePath()); + System.out.println("Opening editor to edit the file..."); + + // TODO: make editor configurable + String command = "emc " + outputFile.getAbsolutePath(); + + // opening editor + Runtime.getRuntime().exec(command); + } + + private void joinFilesRecursively(Path directoryToIndex, BufferedWriter writer) throws IOException { + DirectoryStream stream = Files.newDirectoryStream(directoryToIndex); + + for (Path entry : stream) { + if (Files.isDirectory(entry)) { + joinFilesRecursively(entry, writer); + } else if (Files.isRegularFile(entry)) { + String contentType = Files.probeContentType(entry); + + if (contentType != null && contentType.startsWith("text/")) writeFile(writer, entry); + } + } + } + + private void writeFile(BufferedWriter writer, Path entry) throws IOException { + writeFileHeader(writer, entry); + + String fileContent = new String(Files.readAllBytes(entry)); + + // remove empty lines from the beginning and end of the file + fileContent = fileContent.replaceAll("(?m)^\\s*$", ""); + + writer.write(fileContent + "\n"); + } + + private void writeFileHeader(BufferedWriter writer, Path entry) throws IOException { + String relativePath = baseDirectory.relativize(entry).toString(); + writer.write( "* file: " + relativePath + "\n\n"); + } +} + diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java index 550d03c..46e0b9f 100644 --- a/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java @@ -11,7 +11,7 @@ import java.util.Map; public class Configuration { - private static final String DEFAULT_CONFIG_FILE_PATH = "~/.config/alyverkko-cli.yaml".replaceFirst("^~", System.getProperty("user.home"));; + private static final String DEFAULT_CONFIG_FILE_PATH = "~/.config/alyverkko-cli.yaml".replaceFirst("^~", System.getProperty("user.home")); @JsonProperty("mail_directory") private File mailDirectory; -- 2.20.1