Class DevCliInstaller
vaadin-dev CLI and its agent skills into a project.
The payload ships as classpath resources of this module - the one that already holds logic shared by the Maven and Gradle plugins - so a future Gradle task reads the same bytes from the same jar. Nothing generated and no jars are copied: the CLI resolves the dev-loop daemon from the project's own dependencies at first use.
provisionHotswapAgent(java.nio.file.Path, java.util.function.Consumer<java.lang.String>, java.util.function.Consumer<java.lang.String>) is the second half of an install, and the only
part that needs the network: it puts the one asset the loop cannot resolve
from a Maven repository into the machine-level cache, so that a machine
prepared here - a container image built while it had network access, say -
can run the loop later with none. It runs the provisioning code out of the
daemon jar the project resolves, which is deliberately not a dependency of
this module; see there for why.
Everything is installed relative to one directory, and the two skill trees are installed as siblings, because the Claude adapter reaches the shared reference by relative path.
All of it is meant to be committed by the developer: it is project
tooling, like mvnw, and the point is that every agent and every
developer on the repository gets the same instructions.
For internal use only. May be renamed or removed in a future release.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordWhat an installation did, so a caller can report it. -
Method Summary
Modifier and TypeMethodDescriptionstatic DevCliInstaller.ResultInstalls the CLI and the skills under the given directory.Provisions the HotswapAgent jar into the machine-level dev-loop cache, which is the one thing the loop would otherwise download the first time someone runsvaadin-dev start.static voidreport(DevCliInstaller.Result result, Path targetDirectory, Consumer<String> info) Reports what an installation did, in the order a reader wants it: what changed, then what was left alone and why.
-
Method Details
-
install
Installs the CLI and the skills under the given directory.The CLI goes into
.vaadin/, the directory Vaadin already owns in a project: the dev-loop daemon writes its handshake there, so keeping the scripts beside it means one hidden directory rather than a second visible one at the project root.A file is written whenever it is absent or differs from the shipped copy, and left alone when it already holds exactly those bytes. There is deliberately no "you edited this, so I will keep it" case: these files are project tooling, like
mvnw, and regenerating them is what the goal is for. Anyone who needs project-specific agent instructions should add their own skill beside these rather than edit them, and the goal is unbound, so it only ever runs when someone asks for it.That also makes it self-healing. A checkout is free to hand a committed text file back with the platform's line endings, and a
vaadin-devwhose shebang ends\r\ndoes not run on Linux or macOS; because any difference is replaced, the next run repairs it.- Parameters:
targetDirectory- the directory to install into, normally the application module's base directory- Returns:
- what was written and what was already up to date
- Throws:
IOException- if a file cannot be read or written
-
provisionHotswapAgent
public static Optional<Path> provisionHotswapAgent(Path daemonJar, Consumer<String> info, Consumer<String> warn) throws IOException Provisions the HotswapAgent jar into the machine-level dev-loop cache, which is the one thing the loop would otherwise download the first time someone runsvaadin-dev start.Doing it here rather than there is what makes the loop usable on a machine that has no network access by the time anyone develops on it. The daemon still provisions on demand, through the same code and into the same cache, so this only ever moves the download earlier - it never becomes a second copy or a second version.
Nothing is written into the project: the cache is under
~/.vaadin/devloop, so one download serves every application on the machine and amvn cleandoes not throw it away.The work is done by
HotswapAgentJarinsidedaemonJar, reached through a throwaway class loader rather than by depending on the daemon from here. Two reasons, and the second is the one that decides it. A plugin dependency is resolved into the plugin realm before any goal runs, so every build --Pproductionincluded, and every Gradle buildscript - would fetch dev-loop tooling nothing in it can call. And the version that matters is the one the project resolves: the CLI runs that daemon, so pre-downloading whatever version another copy of the daemon pins would leave the running daemon asking for a different one and reaching for the network anyway - in exactly the air-gapped container this feature exists for. Reading the pin out of the jar that will run makes the two agree by construction.Reflection is safe here because the daemon is dependency-free, by an enforcer rule in its own module: the platform class loader is a sufficient parent, and using that rather than this module loader also means nothing already in the plugin realm can shadow what is loaded.
- Parameters:
daemonJar- theflow-devloop-daemonjar the project resolvesinfo- where progress and the outcome are reportedwarn- where a warning is reported when the daemon cannot provision- Returns:
- the provisioned jar, or empty when that daemon is too old to
carry the provisioning code - in which case a warning has been
reported and the first
startdownloads as before - Throws:
IOException- if provisioning was attempted and the jar could neither be found in the cache nor downloaded
-
report
public static void report(DevCliInstaller.Result result, Path targetDirectory, Consumer<String> info) Reports what an installation did, in the order a reader wants it: what changed, then what was left alone and why.Through the caller's build log rather than a logger of its own, so the output lands where the developer is already reading.
- Parameters:
result- the installation resulttargetDirectory- the directory installed into, for relative reportinginfo- where the report is written
-