A collection of Vaadin tools that AI agents (and humans) can run to inspect and validate Vaadin projects.
It ships two ways, both from this one repository:
- A self-contained native CLI — no Node.js and no JVM required at runtime, and it never searches the machine for one.
- A Claude Code / Codex plugin that exposes the tools as agent skills.
A Vaadin machine is not guaranteed to have Node.js, and reaching into
~/.vaadin/node to borrow Vaadin's copy makes a launcher look like it's hunting
for something to execute. The tools are therefore compiled ahead of time, per
platform, into small self-contained binaries (Go, ~2 MB each). The plugin bundles
those binaries and a tiny selector that picks the right one by uname — it only
ever runs code inside this repository, and needs no runtime install, no network,
and no $HOME probing.
This repository is the plugin checkout, with Claude Code and Codex manifests, the Go sources, and the shared test fixtures alongside each other:
.
├── .codex-plugin/
│ └── plugin.json # Codex plugin manifest
├── .claude-plugin/
│ ├── plugin.json # Claude Code plugin manifest
│ └── marketplace.json # self-marketplace, for trying it from a checkout
├── bin/
│ ├── vaadin-agent-tools # POSIX arch selector (run via ${CLAUDE_PLUGIN_ROOT})
│ ├── vaadin-agent-tools.bat # Windows selector
│ └── platform/ # native binaries (built by go/build.sh, committed)
├── skills/ # one SKILL.md per tool (the agent surface)
│ ├── vaadin-check-theme-mixing/SKILL.md
│ └── vaadin-create-project/SKILL.md
├── hooks/ # Claude Code hooks shipped by the plugin
│ └── hooks.json # PostToolUse: theme-mixing check after styling edits
├── go/ # the CLI implementation (source of the binaries)
│ ├── main.go build.sh go.mod
│ └── internal/{cli,tool,tools,lib}/…
└── test/fixtures/ # sample projects, shared by the Go tests
For real distribution the plugin is published through
vaadin/agent-marketplace
alongside vaadin-skills. To try it from a local checkout, this repo doubles as
a one-plugin dev marketplace:
/plugin marketplace add /path/to/agent-tools
/plugin install vaadin-agent-tools@vaadin-agent-tools-devThen just ask — the skills trigger by description (e.g. "create a new Vaadin project" or "check this project for theme mixing") and the agent runs the tool.
The prebuilt binaries are committed under bin/platform/, so a fresh checkout is
ready to run — no build step needed to try it.
For real distribution, publish the plugin through
vaadin/agent-marketplace
alongside vaadin-skills. After the vaadin-agent-tools entry is published
there, install it from that marketplace:
codex plugin marketplace add vaadin/agent-marketplace --ref main
codex plugin add vaadin-agent-tools@vaadin-marketplaceTo pick up later changes, refresh Git-backed marketplace snapshots:
codex plugin marketplace upgradeCodex reads this repository's .codex-plugin/plugin.json and loads the shared
skills/ directory from the installed plugin. The prebuilt binaries are
committed under bin/platform/, so a fresh install is ready to run without a
build step.
The plugin is a thin wrapper around a normal command line, which you can also run by hand or in CI:
bin/vaadin-agent-tools <tool> [args] [--json]
bin/vaadin-agent-tools listEvery tool supports --json for machine-readable output (recommended for agents)
and exits non-zero when it finds a problem, so it slots into CI and agent
workflows.
Bootstraps a new Vaadin project by downloading a skeleton from
start.vaadin.com and extracting it — the core of
create-vaadin (npm init vaadin), minus the interactive prompts and IDE launch.
bin/vaadin-agent-tools create-project ./my-app
bin/vaadin-agent-tools create-project ./my-app --example=flow --pre --jsonFlags:
--name=<id>— Maven artifactId (default: sanitized basename of the target dir)--example=flow|none— include the "Task List" Flow example view (default:flow)--pre— use the pre-release Vaadin platform version--overwrite— if the target exists and is non-empty, replace its contents
This tool reaches the network and writes files. Exit codes: 0 created · 1
download or extraction failed · 2 usage error.
Detects whether a Vaadin project mixes the Aura and Lumo themes, which leads to conflicting styles and CSS custom properties that fail to resolve.
bin/vaadin-agent-tools check-theme-mixing ./my-project
bin/vaadin-agent-tools check-theme-mixing ./my-project --jsonDetection signals:
@StyleSheet(Aura.STYLESHEET)/@StyleSheet(Lumo.STYLESHEET)in Java sources@importofaura/aura.cssorlumo/lumo.cssin reusable-theme stylesheets- Usage of
--aura-*vs--lumo-*CSS custom properties - Usage of the
LumoUtilityclass in Java, whose utility CSS classes only work under the Lumo theme (not Aura)
If no base theme is explicitly loaded, the active theme cannot be determined (it
may be a custom or base-styles theme). In that case the theme-dependent checks
are a no-op — the tool reports a THEME_INDETERMINATE info finding and exits
0 rather than guessing. Ensuring correctness there is outside the tool's scope.
Exit codes: 0 no error-level findings · 1 mixing detected · 2 usage error.
The plugin ships one Claude Code hook, wired up in
hooks/hooks.json and referenced from the plugin manifest.
PostToolUse → theme-mixing check. After the agent edits a file (Edit,
Write, or MultiEdit), the hook runs the theme-mixing check and, only when it
finds error-level mixing, feeds the findings back to the agent so it can fix
them. The scope is deliberately narrow so the hook stays quiet — it speaks only
when all three gates pass:
- The edited file is
.cssor.java(anything else is ignored). - For
.java, the text the edit introduced mentions a Vaadin styling API (getStyle(,*ClassName(s),@CssImport/@StyleSheet/@Theme,LumoUtility,--lumo-/--aura-,*ThemeName(s),getThemeList(). Any.cssedit qualifies. - The theme-mixing check reports an error-level finding for the edited file's project. Clean projects, warnings, and indeterminate results produce no output, and the edit is never blocked.
The hook logic lives in the native binary (vaadin-agent-tools hook post-tool-use, reading the PostToolUse event on stdin), so it runs identically
on macOS, Linux, and Windows with no bash, jq, or PowerShell dependency. The
plugin isn't on PATH, so hooks/hooks.json invokes the
selector through ${CLAUDE_PLUGIN_ROOT}, the plugin's install directory:
"command": "\"${CLAUDE_PLUGIN_ROOT}/bin/vaadin-agent-tools\" hook post-tool-use"You can test it by hand:
echo '{"tool_input":{"file_path":"/abs/path/App.java","new_string":"btn.getStyle().set(\"color\",\"red\");"}}' \
| bin/vaadin-agent-tools hook post-tool-useThe command-line surface is the stable interface — treat it as the contract, not the implementation behind it:
- Invocation:
vaadin-agent-tools <tool> [args] [--json] [-h|--help] [-v|--version] - JSON output (
--json):{ "tool", "ok", ...result }, or{ "tool", "ok": false, "usageError" }for a usage error. - Exit codes:
0no error-level findings ·1findings/error ·2usage error.
The implementation is Go today, reached only through this contract (the command
name, args, JSON shape, and exit codes) so a future reimplementation could drop
in behind the same vaadin-agent-tools command without changing how agents or CI
call it.
Cross-compile the binaries for every supported platform into bin/platform/:
sh go/build.shTargets: linux/amd64, linux/arm64, windows/amd64, and a universal macOS
binary (darwin, arm64 + amd64 via lipo; falls back to per-arch binaries when
lipo is unavailable). Binaries are built with CGO_ENABLED=0 and stripped
(-ldflags "-s -w" -trimpath) — small and self-contained, with no packing.
- Create
go/internal/tools/<name>.godefining atool.Descriptor(Name,Summary,Usage, and aRun(tool.Args) tool.Resultfunction). - Register it in the
registryslice ingo/internal/cli/cli.go. - Add a
skills/vaadin-<name>/SKILL.mdso agents discover and run it by description. Prefix the skillnamewithvaadin-so it does not collide with skills from other plugins (the CLI subcommand stays unprefixed).
Use the shared helpers in go/internal/lib — Walk for
scanning project files, NewFinding / NewEvidence for the standard finding
shape — to keep output consistent across tools.
cd go
go test ./...
go vet ./...The tests reuse the sample projects in test/fixtures.
The native binaries in bin/platform/ are committed to the repository so that
a git-source install (a fresh clone, or the marketplace) has a runnable plugin
with no build step. When you change anything under go/, rebuild and commit the
binaries in the same change so they stay in sync with the source:
sh go/build.sh && git add bin/platformBecause the plugin manifest is at the repository root, a marketplace entry can use
a plain url source pointing at this repo — the same pattern vaadin-skills
uses. (If the committed binaries ever become too heavy for the repo, the
alternative is to keep them out and ship a release archive referenced from the
marketplace entry with { "source": "archive", "url": "…", "sha256": "…" }.)
Apache-2.0