Initial implementation of joinfiles command
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Tue, 21 May 2024 18:44:39 +0000 (21:44 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Tue, 21 May 2024 18:44:39 +0000 (21:44 +0300)
doc/index.org
pom.xml
src/main/java/eu/svjatoslav/alyverkko_cli/Main.java
src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java [new file with mode: 0644]
src/main/java/eu/svjatoslav/alyverkko_cli/configuration/Configuration.java

index d87ffd9..91e2d29 100644 (file)
@@ -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 (file)
--- a/pom.xml
+++ b/pom.xml
             <artifactId>jackson-dataformat-yaml</artifactId>
             <version>2.13.0</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.12.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>1.3.2</version>
+        </dependency>
     </dependencies>
 
     <build>
index 4070734..258a751 100644 (file)
@@ -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<Command> 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 (file)
index 0000000..ddd0665
--- /dev/null
@@ -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<Path> 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");
+    }
+}
+
index 550d03c..46e0b9f 100644 (file)
@@ -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;