From tomitribe-util-dir
Reference for org.tomitribe.util.dir Dir proxy-based filesystem library. TRIGGER when: code imports from org.tomitribe.util.dir or org.tomitribe.util.paths, uses Dir.of(), or user needs strongly-typed filesystem path references via dynamic proxy interfaces. DO NOT TRIGGER when: working with standard java.nio.file.Path or java.io.File directly.
npx claudepluginhub tomitribe/claude-plugins --plugin tomitribe-util-dirThis skill uses the workspace's default tool permissions.
Java utility for compile-time checked, code-completable file path references using dynamic proxies.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Analyzes BMad project state from catalog CSV, configs, artifacts, and query to recommend next skills or answer questions. Useful for help requests, 'what next', or starting BMad.
Java utility for compile-time checked, code-completable file path references using dynamic proxies.
Eliminates string-based Path references by modeling directory structures as interfaces.
Package: org.tomitribe.util.paths
Location: ~/work/tomitribe/tomitribe-util
<groupId>org.tomitribe</groupId>
<artifactId>tomitribe-util</artifactId>
Define an interface whose methods mirror a directory structure. Dir.of() returns a dynamic proxy
that resolves method calls to java.nio.file.Path references.
// Instead of error-prone strings:
Path srcMainResources = moduleDir.resolve("src/main/resources");
Path srcTestResources = moduleDir.resolve("src/test/resource"); // typo!
// Use compile-time checked interfaces:
Module module = Dir.of(Module.class, moduleDir);
Path srcMainResources = module.src().main().resources();
Path srcTestResources = module.src().test().resources();
Methods return Path for leaf entries, or another interface for subdirectories:
public interface Project {
Src src(); // returns another interface (subdirectory)
Path target(); // returns Path
@Name("pom.xml")
Path pomXml(); // @Name for non-Java-identifier filenames
@Name(".gitignore")
Path gitignore(); // works for hidden files too
}
public interface Src {
Section main();
Section test();
Section section(String name); // dynamic subdirectory name
}
public interface Section {
Path java();
Path resources();
}
import org.tomitribe.util.paths.Dir;
Path mydir = java.nio.file.Paths.get("/some/path/to/project");
Project project = Dir.of(Project.class, mydir);
| Annotation | Target | Purpose |
|---|---|---|
@Name("filename") | Method | Override the filename (default: method name) |
@Filter(Predicate.class) | Method | Filter listings by Predicate<Path> (repeatable) |
@Walk | Method | Recursive traversal instead of direct children only |
@Walk(maxDepth=N) | Method | Limit recursion depth |
@Walk(minDepth=N) | Method | Skip results shallower than N levels |
@Walk(minDepth=M, maxDepth=N) | Method | Window into a specific depth range |
@Mkdir | Method | Create directory on access (parent must exist) |
@Mkdirs | Method | Create directory and all parents on access |
@Parent | Method | Navigate to parent directory (1 level up) |
@Parent(N) | Method | Navigate N levels up |
Methods can return:
Path -- resolves to a file or directory pathString arg -- creates a proxy for a dynamically-named subdirectoryPath[] -- lists directory contents as an arrayStream<Path> -- lists directory contents as a streamModule[]) -- lists contents, each wrapped in a Dir proxyStream<Module>) -- streams contents, each wrapped in a Dir proxypublic interface Project extends Dir {
Path[] modules(); // array of all direct children
Stream<Path> modules(); // stream of all direct children
}
@FilterImplement Predicate<Path> and reference it with @Filter:
public static class HasPomXml implements Predicate<Path> {
@Override
public boolean test(final Path path) {
return java.nio.file.Files.exists(path.resolve("pom.xml"));
}
}
public interface Project extends Dir {
@Filter(HasPomXml.class)
Module[] modules();
@Filter(HasPomXml.class)
Stream<Module> modules();
}
@FilterStack multiple @Filter annotations; all must pass:
public interface Work extends Dir {
@Walk
@Filter(IsJunit.class)
@Filter(IsTxt.class)
Stream<Path> junitTxtFiles();
}
@Walkpublic interface Project extends Dir {
@Walk // unlimited depth
@Filter(HasPomXml.class)
Stream<Module> allModules();
@Walk(maxDepth = 3) // limit depth
@Filter(HasPomXml.class)
Stream<Module> nearModules();
@Walk(minDepth = 2, maxDepth = 2) // exact depth
Stream<Path> exactlyTwoDeep();
}
@Walk works with both Stream and array return types.
@Mkdir and @Mkdirspublic interface Section {
@Mkdirs // creates directory AND all missing parents
Path java();
@Mkdir // creates directory only (parent must exist)
Path resources();
}
Directories are created lazily when the method is invoked:
// src/main/java is created (along with src/ and src/main/) at this point:
Path srcMainJava = project.src().main().java();
@Parent for Navigating Uppublic interface Module extends Dir {
@Name("pom.xml") Path pomXml();
Src src();
}
public interface Src {
@Parent Module module(); // 1 level up: src/ -> project/
Section main();
Section test();
}
public interface Section {
@Parent(2) Module module(); // 2 levels up: src/main/ -> project/
Path java();
Path resources();
}
public interface Java extends Dir {
@Parent(3) Module module(); // 3 levels up: src/main/java/ -> project/
}
Dir InterfaceInterfaces can extend Dir to gain built-in methods:
public interface Module extends Dir {
@Name("pom.xml") Path pomXml();
Src src();
}
Built-in Dir methods:
| Method | Returns | Description |
|---|---|---|
dir() | Path | The path for this directory |
get() | Path | Synonym for dir() |
dir(String name) | Dir | A Dir wrapping a named subdirectory |
file(String name) | Path | Path for a named file or subdirectory |
parent() | Path | The parent path |
mkdir() | Path | Create this directory (parent must exist) |
mkdirs() | Path | Create this directory and all parents |
delete() | void | Recursively delete this directory |
walk() | Stream<Path> | All files and directories recursively |
walk(int depth) | Stream<Path> | Walk limited to given depth |
files() | Stream<Path> | Only files (no directories) recursively |
files(int depth) | Stream<Path> | Files limited to given depth |
Methods can return custom classes that wrap a Path. The class needs either:
Path argument, ORPath and returning the class type// Constructor-based
public class Pom {
private final Path file;
public Pom(final Path file) { this.file = file; }
public Path getFile() { return file; }
}
// Factory method-based
public class Archivo {
private final Path path;
private Archivo(final Path path) { this.path = path; }
public static Archivo from(final Path path) { return new Archivo(path); }
}
public interface Module extends Dir {
@Name("pom.xml") Pom pomXml(); // single wrapper
@Walk @Filter(IsJava.class)
Stream<Archivo> javaFiles(); // stream of wrappers
@Walk @Filter(IsJava.class)
Archivo[] javaFileArray(); // array of wrappers
}
Interfaces support default methods for custom logic:
public interface Repository extends Dir {
default Group group(final String name) {
final String path = name.replace(".", "/");
final Group group = Dir.of(Group.class, dir().resolve(path));
group.mkdirs();
return group;
}
}
Repository repo = Dir.of(Repository.class, someDir);
Group group = repo.group("org.color"); // creates org/color/ directory
equals, hashCode, toStringDir proxies implement equals/hashCode based on the underlying Path, and toString returns the absolute path string.
Dir a = Dir.of(Dir.class, Paths.get("/foo/bar"));
Dir b = Dir.of(Dir.class, Paths.get("/foo/bar"));
a.equals(b); // true
a.toString(); // "/foo/bar"