From f93e138ce38d3382ff01b4e7e17b4bbf77bd30ca Mon Sep 17 00:00:00 2001 From: Svjatoslav Agejenko Date: Mon, 24 Mar 2025 00:45:38 +0200 Subject: [PATCH] Improve code comments --- .../eu/svjatoslav/alyverkko_cli/Command.java | 16 +++ .../commands/JoinFilesCommand.java | 129 ++++++++++++++---- .../configuration/ConfigurationModel.java | 19 ++- 3 files changed, 137 insertions(+), 27 deletions(-) diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/Command.java b/src/main/java/eu/svjatoslav/alyverkko_cli/Command.java index 18767b9..30748ca 100644 --- a/src/main/java/eu/svjatoslav/alyverkko_cli/Command.java +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/Command.java @@ -2,9 +2,25 @@ package eu.svjatoslav.alyverkko_cli; import java.io.IOException; +/** + * A simple interface for all subcommands used by the Älyverkko CLI. + * Implementing classes define a unique name (e.g., "wizard") and an + * {@code execute} method for the command's logic. + */ public interface Command { + /** + * @return the subcommand's name. + */ String getName(); + /** + * Called to carry out the specific subcommand. Typically reads + * command-line arguments and performs the desired action. + * + * @param args arguments passed after the subcommand name. + * @throws IOException if I/O operations fail. + * @throws InterruptedException if the operation is interrupted. + */ void execute(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 index 7d4c1e6..b72b440 100644 --- a/src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/commands/JoinFilesCommand.java @@ -1,6 +1,6 @@ package eu.svjatoslav.alyverkko_cli.commands; -import eu.svjatoslav.alyverkko_cli.*; +import eu.svjatoslav.alyverkko_cli.Command; import eu.svjatoslav.commons.cli_helper.parameter_parser.Parser; import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.DirectoryOption; import eu.svjatoslav.commons.cli_helper.parameter_parser.parameter.NullOption; @@ -17,34 +17,80 @@ import java.nio.file.*; import static eu.svjatoslav.alyverkko_cli.Main.configuration; import static eu.svjatoslav.alyverkko_cli.configuration.Configuration.loadConfiguration; +/** + * The JoinFilesCommand aggregates multiple files (optionally matching + * a specific pattern) into a single file for AI processing, typically + * in the mail directory. + * + * Usage Example: + *
+ *   alyverkko-cli joinfiles -s /path/to/source -p "*.java" -t "my_topic" --edit
+ * 
+ */ public class JoinFilesCommand implements Command { + /** + * A command-line parser to handle joinfiles arguments. + */ final Parser parser = new Parser(); + /** + * Directory from which files will be joined. + */ public DirectoryOption sourceDirectoryOption = parser.add(new DirectoryOption("Directory to join files from")) - .addAliases("--src-dir", "-s").mustExist(); + .addAliases("--src-dir", "-s") + .mustExist(); + /** + * Pattern for matching files, such as "*.java". + */ public StringOption patternOption = parser.add(new StringOption("Pattern to match files")) .addAliases("--pattern", "-p"); + /** + * Topic name, used as the basis for the output file name. + */ public StringOption topic = parser.add(new StringOption("Topic of the joined files")) - .addAliases("--topic", "-t").setMandatory(); + .addAliases("--topic", "-t") + .setMandatory(); + /** + * If present, open the joined file using a text editor afterward. + */ public NullOption editOption = parser.add(new NullOption("Edit the joined file using text editor")) .addAliases("--edit", "-e"); + /** + * The base directory for recursion when joining files. + */ + public Path sourceBaseDirectory; + /** + * The pattern used to filter files for joining, e.g. "*.java". + */ + public String fileNamePattern = null; + + /** + * The resulting output file that aggregates all matched files. + */ + File outputFile; + + /** + * @return the name of this command, i.e., "joinfiles". + */ @Override public String getName() { return "joinfiles"; } - public Path baseDirectory; - - public String pattern = null; - - File outputFile; - + /** + * Executes the command that joins files from a specified directory + * (matching an optional pattern) into one output file in the mail + * directory. Optionally, it can open the output file in an editor. + * + * @param cliArguments the command-line arguments after "joinfiles". + * @throws IOException if any IO operations fail. + */ @Override public void execute(String[] cliArguments) throws IOException { configuration = loadConfiguration(); @@ -54,51 +100,69 @@ public class JoinFilesCommand implements Command { } if (!parser.parse(cliArguments)) { - System.out.println("Failed to parse commandline arguments"); + System.out.println("Failed to parse command-line arguments"); parser.showHelp(); return; } - // build the path to the target file that is relative to the mail directory + // Build the path to the target file that is relative to the mail directory outputFile = configuration.getMailDirectory().toPath().resolve(topic.getValue() + ".org").toFile(); if (patternOption.isPresent()) { - pattern = patternOption.getValue(); + fileNamePattern = patternOption.getValue(); joinFiles(); } - if (editOption.isPresent()) openFileWithEditor(); - + if (editOption.isPresent()) { + openFileWithEditor(); + } } + /** + * Opens the joined file with a text editor. Currently uses a + * command "emc" as an example—adapt as needed. + * + * @throws IOException if the launch of the editor fails. + */ private void openFileWithEditor() throws IOException { - String [] cmd = {"emc", outputFile.getAbsolutePath()}; + String[] cmd = {"emc", outputFile.getAbsolutePath()}; Runtime.getRuntime().exec(cmd); } + /** + * Joins the matching files from the configured source directory + * into a single file named {@code .org} in the mail directory. + * + * @throws IOException if reading or writing files fails. + */ private void joinFiles() throws IOException { - - // Create or append to the target file boolean appendToFile = outputFile.exists(); if (sourceDirectoryOption.isPresent()) { - baseDirectory = sourceDirectoryOption.getValue().toPath(); + sourceBaseDirectory = sourceDirectoryOption.getValue().toPath(); } else { - baseDirectory = Paths.get("."); + sourceBaseDirectory = Paths.get("."); } try (BufferedWriter writer = Files.newBufferedWriter( outputFile.toPath(), StandardCharsets.UTF_8, appendToFile ? StandardOpenOption.APPEND : StandardOpenOption.CREATE)) { - - // Join files that match the pattern in the specified directory - joinFilesRecursively(sourceDirectoryOption.getValue().toPath(), writer); + // Recursively join files that match the pattern + joinFilesRecursively(sourceBaseDirectory, writer); } System.out.println("Files have been joined into: " + outputFile.getAbsolutePath()); } + /** + * Recursively traverses the specified directory and writes the contents + * of files that match the specified {@link #fileNamePattern}. + * + * @param directoryToIndex the directory to be searched recursively. + * @param writer the writer to which file contents are appended. + * @throws IOException if file reading fails. + */ private void joinFilesRecursively(Path directoryToIndex, BufferedWriter writer) throws IOException { try (DirectoryStream stream = Files.newDirectoryStream(directoryToIndex)) { for (Path entry : stream) { @@ -107,7 +171,7 @@ public class JoinFilesCommand implements Command { } else if (Files.isRegularFile(entry)) { String fileName = entry.getFileName().toString(); - boolean match = GlobMatcher.match(fileName, pattern); + boolean match = GlobMatcher.match(fileName, fileNamePattern); if (match) { System.out.println("Joining file: " + fileName); writeFile(writer, entry); @@ -117,10 +181,18 @@ public class JoinFilesCommand implements Command { } } + /** + * Writes the contents of a single file to the specified writer, + * including a small header containing the file path. + * + * @param writer the writer to which file contents are appended. + * @param entry the file to read and write. + * @throws IOException if file reading or writing fails. + */ private void writeFile(BufferedWriter writer, Path entry) throws IOException { writeFileHeader(writer, entry); - String fileContent = new String(Files.readAllBytes(entry)); + String fileContent = new String(Files.readAllBytes(entry), StandardCharsets.UTF_8); // remove empty lines from the beginning and end of the file fileContent = fileContent.replaceAll("(?m)^\\s*$", ""); @@ -128,8 +200,15 @@ public class JoinFilesCommand implements Command { writer.write(fileContent + "\n"); } + /** + * Writes a small header line to indicate which file is being appended. + * + * @param writer the writer to which the header is appended. + * @param entry the path of the current file. + * @throws IOException if writing fails. + */ private void writeFileHeader(BufferedWriter writer, Path entry) throws IOException { - String relativePath = baseDirectory.relativize(entry).toString(); + String relativePath = sourceBaseDirectory.relativize(entry).toString(); writer.write("* file: " + relativePath + "\n\n"); } } diff --git a/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationModel.java b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationModel.java index 5116d4a..604f327 100644 --- a/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationModel.java +++ b/src/main/java/eu/svjatoslav/alyverkko_cli/configuration/ConfigurationModel.java @@ -3,21 +3,36 @@ package eu.svjatoslav.alyverkko_cli.configuration; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +/** + * Represents a single AI model configuration entry, including alias, + * path to the model file, token context size, and an optional + * end-of-text marker. + */ @Data public class ConfigurationModel { + /** + * A short name for the model, e.g., "default" or "mistral". + */ private String alias; /** - * Absolute path in the file system where the model is stored. + * The path to the model file (GGUF, etc.), relative to + * {@link Configuration#getModelsDirectory()} or fully qualified. */ @JsonProperty("filesystem_path") private String filesystemPath; + /** + * The maximum context size the model supports, in tokens. + */ @JsonProperty("context_size_tokens") private int contextSizeTokens; + /** + * Optional text marker signifying the end of text for this model. + * If non-null, it will be used to strip trailing tokens from the AI response. + */ @JsonProperty("end_of_text_marker") private String endOfTextMarker; - } -- 2.20.1