import java.lang.module.ModuleFinder;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;

/** Bach's init program. */
class init {

  public static void main(String... args) throws Exception {
    var version = args.length == 0 ? "17-ea" : args[0];
    System.out.printf("Initialize Bach %s in %s%n", version, Path.of("").toAbsolutePath());

    var bin = Path.of(".bach/bin");
    if (Files.isDirectory(bin)) {
      var module = ModuleFinder.of(bin).find("com.github.sormuras.bach");
      if (module.isPresent()) Files.delete(Path.of(module.get().location().orElseThrow()));
    } else {
      Files.createDirectories(bin);
    }

    loadScript("bach").toFile().setExecutable(true);
    loadScript("bach.bat");
    loadScript("boot.java");
    loadScript("boot.jsh");
    loadScript("init.java");
    loadModule("com.github.sormuras.bach", version);

    System.out.printf("%nBach %s initialized.%n", version);
  }

  static Path loadScript(String name) throws Exception {
    var uri = "https://github.com/sormuras/bach/raw/main/.bach/bin/" + name;
    return copy(uri, name);
  }

  static void loadModule(String name, String version) throws Exception {
    var jar = name + "@" + version + ".jar";
    var uri = "https://github.com/sormuras/bach/releases/download/" + version + "/" + jar;
    copy(uri, jar);
  }

  static Path copy(String uri, String name) throws Exception {
    var file = Path.of(".bach", "bin", name);
    try (var stream = new URL(uri).openStream()) {
      var size = Files.copy(stream, file, StandardCopyOption.REPLACE_EXISTING);
      System.out.printf("%,7d %s%n", size, file);
    }
    return file;
  }
}

@@ -1,20 +1,41 @@
+import java.io.File;%0A
 import java.lang.mod
@@ -168,16 +168,39 @@
 yOption;
+%0Aimport java.util.List;
 %0A%0A/** Ba
@@ -235,16 +235,66 @@
 init %7B%0A%0A
+  public static Path BIN = Path.of(%22.bach/bin%22);%0A%0A
   public
@@ -338,32 +338,124 @@
 ows Exception %7B%0A
+    var update = Files.isDirectory(BIN);%0A    var action = update ? %22Update%22 : %22Initialize%22;%0A
     var version 
@@ -494,16 +494,17 @@
 rgs%5B0%5D;%0A
+%0A
     Syst
@@ -518,26 +518,18 @@
 printf(%22
-Initialize
+%25s
  Bach %25s
@@ -534,24 +534,32 @@
 %25s in %25s%25n%22,
+ action,
  version, Pa
@@ -596,70 +596,18 @@
     
-var bin = Path.of(%22.bach/bin%22);%0A    if (Files.isDirectory(bin)
+if (update
 ) %7B%0A
@@ -641,19 +641,19 @@
 nder.of(
-bin
+BIN
 ).find(%22
@@ -815,19 +815,19 @@
 ctories(
-bin
+BIN
 );%0A    %7D
@@ -1057,61 +1057,728 @@
     
-System.out.printf(%22%25nBach %25s initialized.%25n%22, version
+checkPath();%0A%0A    System.out.printf(%0A        %22%22%22%0A        %25sd Bach %25s.%0A        %0A        bach boot%0A          Launch JShell with Bach booted into it.%0A        bach build%0A          Build the current modular Java project.%0A        %22%22%22, action, version);%0A  %7D%0A%0A  static void checkPath() %7B%0A    var path = System.getenv(%22PATH%22);%0A    if (path == null) return;%0A    var target = BIN.toString();%0A    var elements = List.of(path.split(File.pathSeparator));%0A    for (var element : elements) if (element.strip().equals(target)) return;%0A%0A    System.out.printf(%0A        %22%22%22%0A        Append %60%25s%60 to the PATH environment variable in order to call%0A        Bach's launch script without using that path prefix every time.%0A        %22%22%22,%0A        BIN
 );%0A 
@@ -2264,32 +2264,20 @@
 e = 
-Path.of(%22.bach%22, %22bin%22, 
+BIN.resolve(
 name

Print more helpful messages on init/update