@Override
	public void launch(GhidraApplicationLayout layout, String[] args) throws Exception {

		if (args.length != 1) {
			System.out.println("Expected 1 argument but got " + args.length);
			System.exit(1);
		}

		String appName = args[0];
		System.out.println("\nDiscovering " + appName + " artifact directories....");

		// Discover directories
		Set<File> discoveredSet = new LinkedHashSet<>();
		discoveredSet.addAll(findSettingsDirs(appName, layout));
		discoveredSet.addAll(findCacheDirs(appName, layout));
		discoveredSet.addAll(findTempDirs(appName, layout));
		List<File> discoveredDirs = new ArrayList<>(discoveredSet);

		// Exit if we didn't discover any directories
		if (discoveredDirs.isEmpty()) {
			System.out.println("NONE FOUND");
			return;
		}

		// Output discovered directories and prompt user
		File potentialParentDir = null;
		for (int i = 0; i < discoveredDirs.size(); i++) {
			File d = discoveredDirs.get(i);
			File parentDir = d.getParentFile();
			boolean indent = parentDir.equals(potentialParentDir);
			System.out.println("%2d)%s %s".formatted(i + 1, indent ? "   " : "", d));
			if (!indent) {
				potentialParentDir = d;
			}
		}
		System.out.println("*) All");
		System.out.println("0) Exit");
		System.out.print("Enter a directory to delete: ");

		// Get user choice and delete
		String choice = null;
		try (Scanner scanner = new Scanner(System.in)){ 
			List<File> failures = new ArrayList<>();
			choice = scanner.nextLine().trim();
			switch (choice) {
				case "0":
					System.out.println("Exiting...");
					return;
				case "*":
					for (File dir : discoveredDirs) {
						if (dir.isDirectory()) {
							if (!FileUtilities.deleteDir(dir)) {
								failures.add(dir);
							}
						}
					}
					break;
				default:
					File dir = discoveredDirs.get(Integer.parseInt(choice) - 1);
					if (!FileUtilities.deleteDir(dir)) {
						failures.add(dir);
					}
			}
			System.out.println(failures.isEmpty() ? "SUCCESS" : "Failed to delete:");
			failures.forEach(dir -> System.out.println("   " + dir));
		}
		catch (NoSuchElementException e) {
			// User likely hit ctrl+c to exit
		}
		catch (NumberFormatException | IndexOutOfBoundsException e) {
			System.out.println("Invalid entry: \"" + choice + "\"");
		}
	}
Launches the {@link AppCleaner}

@param layout The application layout to use for the launch
@param args One argument is expected: the name of the application to clean.  All other
  arguments are ignored.
@throws Exception if there was a problem with the launch
Ghidra/Framework/Utility/src/main/java/utility/application/AppCleaner.java