diff --git a/release_scripts/APIUpdate/nbactions.xml b/release_scripts/APIUpdate/nbactions.xml
index e3b484bf668bf71fc03c11f9aaba3fd0cbda88e7..0102967d7e010d2bd28457055006d7bb82204a4b 100644
--- a/release_scripts/APIUpdate/nbactions.xml
+++ b/release_scripts/APIUpdate/nbactions.xml
@@ -1,55 +1,55 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<actions>
-        <action>
-            <actionName>run</actionName>
-            <packagings>
-                <packaging>jar</packaging>
-            </packagings>
-            <goals>
-                <goal>process-classes</goal>
-                <goal>org.codehaus.mojo:exec-maven-plugin:3.1.0:exec</goal>
-            </goals>
-            <properties>
-                <exec.vmArgs></exec.vmArgs>
-                <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
-                <exec.appArgs>--help</exec.appArgs>
-                <exec.mainClass>${packageClassName}</exec.mainClass>
-                <exec.executable>java</exec.executable>
-            </properties>
-        </action>
-        <action>
-            <actionName>debug</actionName>
-            <packagings>
-                <packaging>jar</packaging>
-            </packagings>
-            <goals>
-                <goal>process-classes</goal>
-                <goal>org.codehaus.mojo:exec-maven-plugin:3.1.0:exec</goal>
-            </goals>
-            <properties>
-                <exec.vmArgs>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address}</exec.vmArgs>
-                <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
-                <exec.appArgs>--help</exec.appArgs>
-                <exec.mainClass>${packageClassName}</exec.mainClass>
-                <exec.executable>java</exec.executable>
-                <jpda.listen>true</jpda.listen>
-            </properties>
-        </action>
-        <action>
-            <actionName>profile</actionName>
-            <packagings>
-                <packaging>jar</packaging>
-            </packagings>
-            <goals>
-                <goal>process-classes</goal>
-                <goal>org.codehaus.mojo:exec-maven-plugin:3.1.0:exec</goal>
-            </goals>
-            <properties>
-                <exec.vmArgs></exec.vmArgs>
-                <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
-                <exec.mainClass>${packageClassName}</exec.mainClass>
-                <exec.executable>java</exec.executable>
-                <exec.appArgs>--help</exec.appArgs>
-            </properties>
-        </action>
-    </actions>
+<actions> 
+    <action>
+        <actionName>run</actionName>
+        <packagings>
+            <packaging>jar</packaging>
+        </packagings>
+        <goals>
+            <goal>process-classes</goal>
+            <goal>org.codehaus.mojo:exec-maven-plugin:3.1.0:exec</goal>
+        </goals>
+        <properties>
+            <exec.vmArgs></exec.vmArgs>
+            <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
+            <exec.appArgs>--help</exec.appArgs>
+            <exec.mainClass>${packageClassName}</exec.mainClass>
+            <exec.executable>java</exec.executable>
+        </properties>
+    </action>
+    <action>
+        <actionName>debug</actionName>
+        <packagings>
+            <packaging>jar</packaging>
+        </packagings>
+        <goals>
+            <goal>process-classes</goal>
+            <goal>org.codehaus.mojo:exec-maven-plugin:3.1.0:exec</goal>
+        </goals>
+        <properties>
+            <exec.vmArgs>-agentlib:jdwp=transport=dt_socket,server=n,address=${jpda.address}</exec.vmArgs>
+            <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
+            <exec.appArgs>--help</exec.appArgs>
+            <exec.mainClass>${packageClassName}</exec.mainClass>
+            <exec.executable>java</exec.executable>
+            <jpda.listen>true</jpda.listen>
+        </properties>
+    </action>
+    <action>
+        <actionName>profile</actionName>
+        <packagings>
+            <packaging>jar</packaging>
+        </packagings>
+        <goals>
+            <goal>process-classes</goal>
+            <goal>org.codehaus.mojo:exec-maven-plugin:3.1.0:exec</goal>
+        </goals>
+        <properties>
+            <exec.vmArgs></exec.vmArgs>
+            <exec.args>${exec.vmArgs} -classpath %classpath ${exec.mainClass} ${exec.appArgs}</exec.args>
+            <exec.mainClass>${packageClassName}</exec.mainClass>
+            <exec.executable>java</exec.executable>
+            <exec.appArgs>--help</exec.appArgs>
+        </properties>
+    </action>
+</actions>
diff --git a/release_scripts/APIUpdate/pom.xml b/release_scripts/APIUpdate/pom.xml
index 1d38e4f5e2561a59dbd043425a769640e6eeb6f2..0feced83649cd3dd56fc6d5c8ed1fe2b9e11aafc 100644
--- a/release_scripts/APIUpdate/pom.xml
+++ b/release_scripts/APIUpdate/pom.xml
@@ -24,5 +24,39 @@
             <version>1.5.0</version>
         </dependency>
 
+        <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.13.0</version>
+        </dependency>
+
     </dependencies>
+    
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>org.sleuthkit.autopsy.apiupdate.Main</mainClass>
+                        </manifest>
+                    </archive>
+                    <descriptorRefs>
+                        <descriptorRef>jar-with-dependencies</descriptorRef>
+                    </descriptorRefs>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id> <!-- this is used for inheritance merges -->
+                        <phase>package</phase> <!-- bind to the packaging phase -->
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
 </project>
\ No newline at end of file
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c73627d816d35a900c1bbea8d907b2fa3c2df09
--- /dev/null
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/APIDiff.java
@@ -0,0 +1,70 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package org.sleuthkit.autopsy.apiupdate;
+
+import japicmp.cmp.JApiCmpArchive;
+import japicmp.cmp.JarArchiveComparator;
+import japicmp.cmp.JarArchiveComparatorOptions;
+import japicmp.model.JApiClass;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ *
+ * @author gregd
+ */
+public class APIDiff {
+
+    private static final FileFilter JAR_FILTER
+            = (File f) -> f.isFile() && (f.getName().toLowerCase().endsWith(".jar") || f.getName().toLowerCase().endsWith(".nbm"));
+
+    private static List<String> getCommonJars(File prevDir, File currDir) {
+        Set<String> prevJars = getJars(prevDir);
+        Set<String> currJars = getJars(currDir);
+
+        Set<String> commonJars = new HashSet<>(prevJars);
+        commonJars.retainAll(currJars);
+
+        // TODO how to handle different
+        return commonJars.stream().sorted().collect(Collectors.toList());
+    }
+
+    private static Set<String> getJars(File dir) {
+        return Stream.of(dir.listFiles(JAR_FILTER))
+                .map(f -> f.getName())
+                .collect(Collectors.toSet());
+    }
+
+    private static Set<String> getPublicPackages(File jarFile) throws IOException, IllegalStateException {
+        String publicPackageStr = ManifestLoader.loadFromJar(jarFile).getValue("OpenIDE-Module-Public-Packages");
+        if (publicPackageStr == null) {
+            throw new IllegalStateException(MessageFormat.format("Manifest for {0} does not have key of 'OpenIDE-Module-Public-Packages'", jarFile.getAbsolutePath()));
+        } else {
+            return Stream.of(publicPackageStr.split(","))
+                    .map(String::trim)
+                    .map(str -> str.endsWith(".*") ? str.substring(0, str.length() - 2) : str)
+                    .collect(Collectors.toSet());
+        }
+    }
+
+    private static void getComparison(String prevVersion, String curVersion, File prevJar, File curJar) {
+        JarArchiveComparatorOptions comparatorOptions = new JarArchiveComparatorOptions();
+        comparatorOptions.getIgnoreMissingClasses().setIgnoreAllMissingClasses(true);
+        JarArchiveComparator jarArchiveComparator = new JarArchiveComparator(comparatorOptions);
+        List<JApiClass> jApiClasses = jarArchiveComparator.compare(
+                new JApiCmpArchive(prevJar, prevVersion),
+                new JApiCmpArchive(curJar, curVersion)
+        );
+        System.out.println("Comparing " + prevJar.getName());
+        System.out.println(jApiClasses);
+    }
+}
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/CLIProcessor.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java
similarity index 84%
rename from release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/CLIProcessor.java
rename to release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java
index 0ac99e73ed4fe799afcbacc73ad7b5151ff9de1b..5295febd7054219eeaa98f9a57c1f246869dfecd 100644
--- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/CLIProcessor.java
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/CLIProcessor.java
@@ -2,7 +2,7 @@
  * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
  * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
  */
-package org.sleuthkit.autopsy.classpathsimplication.apiupdate;
+package org.sleuthkit.autopsy.apiupdate;
 
 import java.io.File;
 import java.util.Arrays;
@@ -57,12 +57,22 @@ public class CLIProcessor {
             .option("cv")
             .required(true)
             .build();
+    
+        static Option SRC_LOC_OPT = Option.builder()
+            .argName("path")
+            .desc("The path to the root of the autopsy repor")
+            .hasArg(true)
+            .longOpt("src-path")
+            .option("s")
+            .required(true)
+            .build();
 
     static List<Option> ALL_OPTIONS = Arrays.asList(
             PREV_VERS_PATH_OPT,
             CUR_VERS_PATH_OPT,
             PREV_VERS_OPT,
-            CUR_VERS_OPT
+            CUR_VERS_OPT,
+            SRC_LOC_OPT
     );
 
     static Options CLI_OPTIONS = getCliOptions(ALL_OPTIONS);
@@ -103,7 +113,7 @@ static CLIArgs parseCli(String[] args) throws ParseException {
         CommandLine helpCmd = parser.parse(HELP_OPTIONS, args, true);
         boolean isHelp = helpCmd.hasOption(HELP_OPT);
         if (isHelp) {
-            return new CLIArgs(null, null, null, null, true);
+            return new CLIArgs(null, null, null, null, null, true);
         }
 
         CommandLine cmd = parser.parse(CLI_OPTIONS, args);
@@ -111,8 +121,10 @@ static CLIArgs parseCli(String[] args) throws ParseException {
         String prevVers = cmd.getOptionValue(PREV_VERS_OPT);
         String curVersPath = cmd.getOptionValue(CUR_VERS_PATH_OPT);
         String prevVersPath = cmd.getOptionValue(PREV_VERS_PATH_OPT);
+        String srcPath = cmd.getOptionValue(SRC_LOC_OPT);
         File curVersFile = new File(curVersPath);
         File prevVersFile = new File(prevVersPath);
+        File srcPathFile = new File(srcPath);
 
         if (!curVersFile.isDirectory()) {
             throw new ParseException("No directory found at " + curVersFile.getAbsolutePath());
@@ -121,8 +133,12 @@ static CLIArgs parseCli(String[] args) throws ParseException {
         if (!prevVersFile.isDirectory()) {
             throw new ParseException("No directory found at " + prevVersFile.getAbsolutePath());
         }
+        
+                if (!srcPathFile.isDirectory()) {
+            throw new ParseException("No directory found at " + srcPathFile.getAbsolutePath());
+        }
 
-        return new CLIArgs(curVers, prevVers, curVersFile, prevVersFile, false);
+        return new CLIArgs(curVers, prevVers, curVersFile, prevVersFile, srcPathFile, false);
     }
 
 
@@ -133,6 +149,7 @@ public static class CLIArgs {
         private final File currentVersPath;
         private final File previousVersPath;
         private final boolean isHelp;
+        private final File srcPath;
 
         public String getCurrentVersion() {
             return currentVersion;
@@ -154,11 +171,12 @@ public boolean isIsHelp() {
             return isHelp;
         }
 
-        public CLIArgs(String currentVersion, String previousVersion, File currentVersPath, File previousVersPath, boolean isHelp) {
+        public CLIArgs(String currentVersion, String previousVersion, File currentVersPath, File previousVersPath, File srcPath, boolean isHelp) {
             this.currentVersion = currentVersion;
             this.previousVersion = previousVersion;
             this.currentVersPath = currentVersPath;
             this.previousVersPath = previousVersPath;
+            this.srcPath = srcPath;
             this.isHelp = isHelp;
         }
 
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java
new file mode 100644
index 0000000000000000000000000000000000000000..47ada3e089c5c89361291be6aef92c43b1f5ea34
--- /dev/null
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/Main.java
@@ -0,0 +1,82 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ */
+package org.sleuthkit.autopsy.apiupdate;
+
+import japicmp.cmp.JApiCmpArchive;
+import japicmp.cmp.JarArchiveComparator;
+import japicmp.cmp.JarArchiveComparatorOptions;
+import japicmp.model.JApiClass;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.apache.commons.cli.ParseException;
+import org.sleuthkit.autopsy.apiupdate.CLIProcessor.CLIArgs;
+
+/**
+ *
+ * @author gregd
+ */
+public class Main {
+
+    public static void main(String[] args) {
+        args = "-c C:\\Users\\gregd\\Documents\\Source\\autopsy\\build\\cluster\\modules -p C:\\Users\\gregd\\Desktop\\prevVers -cv 4.21.0 -pv 4.20.0".split(" ");
+        CLIArgs cliArgs;
+        try {
+            cliArgs = CLIProcessor.parseCli(args);
+            if (cliArgs.isIsHelp()) {
+                CLIProcessor.printHelp(null);
+                System.exit(0);
+            }
+        } catch (ParseException ex) {
+            CLIProcessor.printHelp(ex);
+            System.exit(-1);
+            return;
+        }
+
+//        for (String commonJarFileName : getCommonJars(cliArgs.getPreviousVersPath(), cliArgs.getCurrentVersPath())) {
+////            getComparison(
+////                    cliArgs.getPreviousVersion(),
+////                    cliArgs.getCurrentVersion(),
+////                    cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile(),
+////                    cliArgs.getCurrentVersPath().toPath().resolve(commonJarFileName).toFile());
+//            try {
+//                Set<String> pubPackages = getPublicPackages(cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile());
+//                System.out.println(pubPackages);
+//            } catch (IOException ex) {
+//                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
+//            } catch (IllegalStateException ex) {
+//                Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
+//            }
+//        }
+
+    }
+
+
+    
+    
+    private static void mainRun() {
+
+        // get public API diff's, for each jar
+        // limit to public packages
+        // one of the following:
+        // generate text output of difference
+        // update version numbers in manifest file/references accordingly
+    }
+
+}
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ManifestLoader.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ManifestLoader.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1d1dbd3fe35833cf444e9aab4f0735aaacffcbc
--- /dev/null
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ManifestLoader.java
@@ -0,0 +1,42 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package org.sleuthkit.autopsy.apiupdate;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ *
+ * @author gregd
+ */
+public class ManifestLoader {
+
+    public static Attributes loadInputStream(InputStream is) throws IOException {
+        Manifest manifest = new Manifest(is);
+        return manifest.getMainAttributes();
+    }
+
+    public static Attributes loadFromJar(File jarFile) throws IOException {
+        ZipFile zipFile = new ZipFile(jarFile);
+
+        Enumeration<? extends ZipEntry> entries = zipFile.entries();
+
+        while (entries.hasMoreElements()) {
+            ZipEntry entry = entries.nextElement();
+            if ("META-INF/MANIFEST.MF".equalsIgnoreCase(entry.getName())) {
+                return loadInputStream(zipFile.getInputStream(entry));
+            }
+        }
+
+        throw new FileNotFoundException("Could not find MANIFEST.MF in " + jarFile.getAbsolutePath());
+    }
+}
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java
new file mode 100644
index 0000000000000000000000000000000000000000..06b3792a8a494accaab3ffe4406a1c637631b0c4
--- /dev/null
+++ b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/apiupdate/ModuleUpdates.java
@@ -0,0 +1,232 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package org.sleuthkit.autopsy.apiupdate;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.util.jar.Attributes;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ *
+ * @author gregd
+ */
+public class ModuleUpdates {
+
+    private static final Logger LOGGER = Logger.getLogger(ModuleUpdates.class.getName());
+//        // Spec
+//            // project.properties
+//                // spec.version.base
+//            // manifest
+//                // OpenIDE-Module-Specification-Version
+//        // Implementation
+//            // manifest
+//                // OpenIDE-Module-Implementation-Version
+//        // Release
+//            // manifest
+//                // OpenIDE-Module (/number)
+//                
+//        // Dependency specification
+//            // project.xml
+//                // project.configuration.data.module-dependencies.dependency.run-dependency:
+//                    // specification-version
+//                    // release-version
+
+    private static final Pattern SPEC_REGEX = Pattern.compile("^\\s*(?<major>\\d*)\\.(?<minor>\\d*)(\\.(?<patch>\\d*))?\\s*$");
+    private static final String SPEC_KEY = "OpenIDE-Module-Specification-Version";
+    private static final String IMPL_KEY = "OpenIDE-Module-Implementation-Version";
+    private static final String RELEASE_KEY = "OpenIDE-Module";
+
+    private static final Pattern RELEASE_REGEX = Pattern.compile("^\\s*(?<releaseName>.+?)(/(?<releaseNum>\\d*))?\\s*$");
+
+    private static final SemVer DEFAULT_SEMVER = new SemVer(1, 0, null);
+    private static final int DEFAULT_VERS_VAL = 1;
+
+    private static SemVer parseSemVer(String semVerStr, SemVer defaultSemVer, String resourceForLogging) {
+        if (StringUtils.isBlank(semVerStr)) {
+            return defaultSemVer;
+        }
+
+        Matcher m = SPEC_REGEX.matcher(semVerStr);
+        if (m.find()) {
+            try {
+                int major = Integer.parseInt(m.group("major"));
+                int minor = Integer.parseInt(m.group("minor"));
+                String patchStr = m.group("patch");
+                Integer patch = StringUtils.isBlank(patchStr) ? null : Integer.parseInt(patchStr);
+                return new SemVer(major, minor, patch);
+            } catch (NullPointerException | NumberFormatException ex) {
+                LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver string {0} for {1}", semVerStr, resourceForLogging), ex);
+                return defaultSemVer;
+            }
+        } else {
+            return defaultSemVer;
+        }
+    }
+
+    private static ReleaseVal parseReleaseVers(String releaseStr, ReleaseVal defaultVal, String resourceForLogging) {
+        if (StringUtils.isBlank(releaseStr)) {
+            return defaultVal;
+        }
+
+        Matcher m = RELEASE_REGEX.matcher(releaseStr);
+        if (m.find()) {
+            try {
+                int major = Integer.parseInt(m.group("major"));
+                int minor = Integer.parseInt(m.group("minor"));
+                String patchStr = m.group("patch");
+                Integer patch = StringUtils.isBlank(patchStr) ? null : Integer.parseInt(patchStr);
+                return new SemVer(major, minor, patch);
+            } catch (NullPointerException | NumberFormatException ex) {
+                LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse semver string {0} for {1}", releaseStr, resourceForLogging), ex);
+                return defaultSemVer;
+            }
+        } else {
+            return defaultSemVer;
+        }
+    }
+    
+    
+
+    private static int tryParse(String str, int defaultVal, String resourceForLogging) {
+        try {
+            return Integer.parseInt(str);
+        } catch (NullPointerException | NumberFormatException ex) {
+            LOGGER.log(Level.SEVERE, MessageFormat.format("Unable to parse version string {0} for {1}", str, resourceForLogging), ex);
+            return defaultVal;
+        }
+    }
+
+    public static SemVer getPrevVersions(File jarFile) throws IOException {
+        Attributes manifest = ManifestLoader.loadFromJar(jarFile);
+        String spec = manifest.getValue(SPEC_KEY);
+        SemVer specSemVer = parseSemVer(spec, DEFAULT_SEMVER,
+                MessageFormat.format("{0} in manifest for {1}", SPEC_KEY, jarFile));
+
+        int implementation = tryParse(manifest.getValue(IMPL_KEY), DEFAULT_VERS_VAL,
+                MessageFormat.format("{0} in manifest for {1}", IMPL_KEY, jarFile));
+
+//        // Spec
+//            // project.properties
+//                // spec.version.base
+//            // manifest
+//                // OpenIDE-Module-Specification-Version
+//        // Implementation
+//            // manifest
+//                // OpenIDE-Module-Implementation-Version
+//        // Release
+//            // manifest
+//                // OpenIDE-Module (/number)
+//                
+//        // Dependency specification
+//            // project.xml
+//                // project.configuration.data.module-dependencies.dependency.run-dependency:
+//                    // specification-version
+//                    // release-version
+    }
+
+    private static void updateVersions() {
+//        [specification major/minor/patch, implementation, release]
+//        assumed defaults???
+//        NON_COMPATIBLE:
+//            specification.major += 1
+//            implementation += 1
+//            release += 1
+//        COMPATIBLE:
+//            specification.minor += 1
+//            implementation += 1
+//        NO_CHANGES:
+//            implementation += 1
+
+    }
+
+    public static class ReleaseVal {
+
+        private final String moduleName;
+        private final Integer releaseVersion;
+
+        public ReleaseVal(String moduleName, Integer releaseVersion) {
+            this.moduleName = moduleName;
+            this.releaseVersion = releaseVersion;
+        }
+
+        public String getModuleName() {
+            return moduleName;
+        }
+
+        public Integer getReleaseVersion() {
+            return releaseVersion;
+        }
+
+        public String getFullReleaseStr() {
+            return this.releaseVersion == null
+                    ? moduleName
+                    : MessageFormat.format("{0}/{1,number,#}", moduleName, releaseVersion);
+        }
+    }
+
+    public static class SemVer {
+
+        private final int major;
+        private final int minor;
+        private final Integer patch;
+
+        public SemVer(int major, int minor, Integer patch) {
+            this.major = major;
+            this.minor = minor;
+            this.patch = patch;
+        }
+
+        public int getMajor() {
+            return major;
+        }
+
+        public int getMinor() {
+            return minor;
+        }
+
+        public Integer getPatch() {
+            return patch;
+        }
+
+        public String getSemVerStr() {
+            return (patch == null)
+                    ? MessageFormat.format("{0,number,#}.{1,number,#}", major, minor)
+                    : MessageFormat.format("{0,number,#}.{1,number,#}.{2,number,#}", major, minor, patch);
+        }
+    }
+
+    public static class ModuleVersionNumbers {
+
+        private final SemVer spec;
+        private final int implementation;
+        private final int release;
+
+        public ModuleVersionNumbers(SemVer spec, int implementation, int release) {
+            this.spec = spec;
+            this.implementation = implementation;
+            this.release = release;
+        }
+
+        public SemVer getSpec() {
+            return spec;
+        }
+
+        public int getImplementation() {
+            return implementation;
+        }
+
+        public int getRelease() {
+            return release;
+        }
+
+    }
+}
diff --git a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/APIUpdate.java b/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/APIUpdate.java
deleted file mode 100644
index e387fd686c880bfdef506e6cc53fb02140560461..0000000000000000000000000000000000000000
--- a/release_scripts/APIUpdate/src/main/java/org/sleuthkit/autopsy/classpathsimplication/apiupdate/APIUpdate.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
- */
-package org.sleuthkit.autopsy.classpathsimplication.apiupdate;
-
-import japicmp.cmp.JApiCmpArchive;
-import japicmp.cmp.JarArchiveComparator;
-import japicmp.cmp.JarArchiveComparatorOptions;
-import japicmp.model.JApiClass;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.text.MessageFormat;
-import java.util.Enumeration;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.jar.Attributes;
-import java.util.jar.Manifest;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
-import org.apache.commons.cli.ParseException;
-import org.sleuthkit.autopsy.classpathsimplication.apiupdate.CLIProcessor.CLIArgs;
-
-/**
- *
- * @author gregd
- */
-public class APIUpdate {
-
-    public static void main(String[] args) {
-        args = "-c C:\\Users\\gregd\\Documents\\Source\\autopsy\\build\\cluster\\modules -p C:\\Users\\gregd\\Desktop\\prevVers -cv 4.21.0 -pv 4.20.0".split(" ");
-        CLIArgs cliArgs;
-        try {
-            cliArgs = CLIProcessor.parseCli(args);
-            if (cliArgs.isIsHelp()) {
-                CLIProcessor.printHelp(null);
-                System.exit(0);
-            }
-        } catch (ParseException ex) {
-            CLIProcessor.printHelp(ex);
-            System.exit(-1);
-            return;
-        }
-
-        for (String commonJarFileName : getCommonJars(cliArgs.getPreviousVersPath(), cliArgs.getCurrentVersPath())) {
-//            getComparison(
-//                    cliArgs.getPreviousVersion(),
-//                    cliArgs.getCurrentVersion(),
-//                    cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile(),
-//                    cliArgs.getCurrentVersPath().toPath().resolve(commonJarFileName).toFile());
-            try {
-                Set<String> pubPackages = getPublicPackages(cliArgs.getPreviousVersPath().toPath().resolve(commonJarFileName).toFile());
-                System.out.println(pubPackages);
-            } catch (IOException ex) {
-                Logger.getLogger(APIUpdate.class.getName()).log(Level.SEVERE, null, ex);
-            } catch (IllegalStateException ex) {
-                Logger.getLogger(APIUpdate.class.getName()).log(Level.SEVERE, null, ex);
-            }
-        }
-
-    }
-
-    private static final FileFilter JAR_FILTER
-            = (File f) -> f.isFile() && (f.getName().toLowerCase().endsWith(".jar") || f.getName().toLowerCase().endsWith(".nbm"));
-
-    private static List<String> getCommonJars(File prevDir, File currDir) {
-        Set<String> prevJars = getJars(prevDir);
-        Set<String> currJars = getJars(currDir);
-
-        Set<String> commonJars = new HashSet<>(prevJars);
-        commonJars.retainAll(currJars);
-
-        // TODO how to handle different
-        return commonJars.stream().sorted().collect(Collectors.toList());
-    }
-
-    private static Set<String> getJars(File dir) {
-        return Stream.of(dir.listFiles(JAR_FILTER))
-                .map(f -> f.getName())
-                .collect(Collectors.toSet());
-    }
-
-    private static Set<String> getPublicPackages(File jarFile) throws IOException, IllegalStateException {
-        ZipFile zipFile = new ZipFile(jarFile);
-
-        Enumeration<? extends ZipEntry> entries = zipFile.entries();
-
-        while (entries.hasMoreElements()) {
-            ZipEntry entry = entries.nextElement();
-            if ("META-INF/MANIFEST.MF".equalsIgnoreCase(entry.getName())) {
-                InputStream stream = zipFile.getInputStream(entry);
-                Manifest manifest = new Manifest(stream);
-                Attributes attributes = manifest.getMainAttributes();
-                String publicPackageStr = attributes.getValue("OpenIDE-Module-Public-Packages");
-                if (publicPackageStr == null) {
-                    throw new IllegalStateException(MessageFormat.format("Manifest for {0} does not have key of 'OpenIDE-Module-Public-Packages'", jarFile.getAbsolutePath()));
-                } else {
-                    return Stream.of(publicPackageStr.split(","))
-                            .map(String::trim)
-                            .map(str -> str.endsWith(".*") ? str.substring(0, str.length() - 2) : str)
-                            .collect(Collectors.toSet());
-                }
-            }
-        }
-
-        throw new FileNotFoundException("Could not find MANIFEST.MF in " + jarFile.getAbsolutePath());
-    }
-
-    private static void getComparison(String prevVersion, String curVersion, File prevJar, File curJar) {
-        JarArchiveComparatorOptions comparatorOptions = new JarArchiveComparatorOptions();
-        comparatorOptions.getIgnoreMissingClasses().setIgnoreAllMissingClasses(true);
-        JarArchiveComparator jarArchiveComparator = new JarArchiveComparator(comparatorOptions);
-        List<JApiClass> jApiClasses = jarArchiveComparator.compare(
-                new JApiCmpArchive(prevJar, prevVersion),
-                new JApiCmpArchive(curJar, curVersion)
-        );
-        System.out.println("Comparing " + prevJar.getName());
-        System.out.println(jApiClasses);
-    }
-
-    private static void mainRun() {
-
-        // get public API diff's, for each jar
-        // limit to public packages
-        // one of the following:
-        // generate text output of difference
-        // update version numbers in manifest file/references accordingly
-    }
-
-}