Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ User-facing `fix`/`feat` PRs add a **fragment file**, never a direct `CHANGELOG.

There is no `wheels mcp setup` command — copy the JSON above into `.mcp.json` manually (see the MCP integration guide for OpenCode/Cursor variants).

Tools are auto-discovered from `cli/lucli/Module.cfc` public functions, prefixed with the module name (`wheels_generate`, `wheels_migrate`, `wheels_test`, `wheels_reload`, `wheels_seed`, `wheels_analyze`, `wheels_validate`, `wheels_routes`, `wheels_info`, `wheels_destroy`, `wheels_doctor`, `wheels_stats`, `wheels_notes`, `wheels_db`, `wheels_upgrade`, `wheels_create`, `wheels_deploy`, `wheels_packages`). CLI-only tools (`main`, `mcp`, `d`, `g`, `new`, `console`, `start`, `stop`, `browser`) are hidden via `mcpHiddenTools()`.
Tools are auto-discovered from `cli/lucli/Module.cfc` public functions. Names in `tools/list` are the bare function names — NOT `wheels_*`-prefixed (live-verified on the released 4.0.3 CLI): `analyze`, `create`, `db`, `deploy`, `destroy`, `doctor`, `generate`, `info`, `migrate`, `notes`, `packages`, `reload`, `routes`, `seed`, `stats`, `test`, `upgrade`, `validate` (18 tools; the `wheels` server entry in `.mcp.json` namespaces them per client). CLI-only tools (`main`, `mcp`, `d`, `g`, `new`, `console`, `start`, `stop`, `browser`) are hidden via `mcpHiddenTools()`.

**Deprecated:** the in-dev-server HTTP endpoint at `/wheels/mcp`. Emits a deprecation notice on first request. Migrate to the stdio surface.

Expand All @@ -725,13 +725,13 @@ Prefer MCP tools when the Wheels MCP server is available. Fall back to CLI other

| Task | MCP | CLI |
|------|-----|-----|
| Generate | `wheels_generate(type, name, attributes)` | `wheels g model/controller/scaffold Name attrs` |
| Migrate | `wheels_migrate(action="latest\|up\|down\|info\|doctor")` | `wheels migrate latest\|up\|down\|info\|doctor` |
| Generate | `generate(type, name, attributes)` | `wheels g model/controller/scaffold Name attrs` |
| Migrate | `migrate(action="latest\|up\|down\|info\|doctor")` | `wheels migrate latest\|up\|down\|info\|doctor` |
| Migrator reconciliation | — | `wheels migrate forget\|pretend <version> --yes` (shared dev DB orphan cleanup; see #2780) |
| Test | `wheels_test()` | `wheels test` |
| Reload | `wheels_reload()` | `?reload=true&password=...` |
| Test | `test()` | `wheels test` |
| Reload | `reload()` | `?reload=true&password=...` |
| Server | — | `wheels start\|stop` |
| Analyze | `wheels_analyze(target="all")` | — |
| Analyze | `analyze(target="all")` | — |
| Admin | — | `wheels g admin ModelName` |
| Seed | — | `wheels seed` |

Expand Down
3 changes: 2 additions & 1 deletion cli/lucli/Module.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -1384,7 +1384,8 @@ component extends="modules.BaseModule" {
out(" https://guides.wheels.dev/v4-0-0/command-line-tools/mcp-integration");
out("");
out("All public commands in this module are auto-discovered as MCP tools.");
out("Tools are prefixed with the module name: wheels_generate, wheels_migrate, etc.");
out("Tool names match the command names: generate, migrate, etc. (unprefixed");
out("in tools/list — the server entry in .mcp.json namespaces them per client).");
out("Stateful/interactive commands (start, stop, new, console, ...) are hidden");
out("from MCP tools/list via mcpHiddenTools() — they remain CLI-only.");
return "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ The package-manager installs do extra work on your behalf that this path skips.
- **`LUCLI_HOME` isolation.** Package installs export `LUCLI_HOME=$HOME/.wheels` so installed modules live in `~/.wheels/modules/wheels/` and stay out of the way of any other LuCLI work. Standalone LuCLI uses `~/.lucli/modules/wheels/` — fine on its own, but a previously installed `wheels` module (e.g. v3.1.0 from the older registry entry) will shadow your new install unless you pass `--force`.
- **JDK resolution.** Package installs probe for a Java 21 install and export `JAVA_HOME` inside their `wheels` wrapper. Standalone LuCLI uses whatever `java` is on your `PATH` — if that's a different major version, the runtime will refuse to start. Make sure `java -version` reports 21.x before installing.
- **First-run SQLite shim.** The package wrappers auto-stage `sqlite-jdbc-*.jar` into Lucee Express's `lib/ext/` so `wheels start` against a SQLite datasource works out of the box. With a bare LuCLI install you'll need to drop the JAR there yourself the first time you create a SQLite-backed app.
- **Channel awareness.** `wheels --version` from a packaged install reports `(stable)` or `(bleeding-edge)`. From a bare LuCLI install, the module reports its own version but there's no channel marker — you have to remember which tarball URL you installed from.
- **Channel awareness.** `wheels version` from a packaged install reports `(stable)` or `(bleeding-edge)`. From a bare LuCLI install, the module reports its own version but there's no channel marker — you have to remember which tarball URL you installed from.
- **No `scoop update` / `brew upgrade` story.** Upgrades are manual: re-run `lucli modules install wheels --force --url=…` with a newer release URL.

This path exists for people who already run LuCLI for other modules and don't want a second binary on `PATH`. For everyone else, the Scoop / Homebrew / Linux package paths are the better default.
Expand All @@ -208,12 +208,17 @@ wheels --version
You should see something like:

``` title="illustrative — example version output"
Wheels 4.0.0-SNAPSHOT+1523 (snapshot)
LuCLI 0.3.7
Java 21.0.8
Wheels Version: 4.0.3
```

The three-line format tells you the Wheels Module version, the underlying LuCLI version, and the JVM the wrapper picked up. If any of those lines is missing or reports an unexpected value, jump to troubleshooting below.
followed by the Wheels ASCII-art banner. The richer check is `wheels version` (no dashes), which reports the release channel and the JVM the wrapper picked up:

``` title="illustrative — example wheels version output"
Wheels 4.0.3 (stable)
Java 21.0.11
```

If either line is missing or reports an unexpected value — say a Java major version other than 21 — jump to troubleshooting below.

Running `wheels` with no arguments is also a quick sanity check — it prints the same Wheels help banner as `wheels --help`. (`wheels help` falls through to LuCLI's own generic help rather than the Wheels banner, so prefer `wheels --help`.) If you see a `Component [modules.wheels.Module] has no function with name [main]` error instead, you are running a build that predates this fix — upgrade to the latest 4.0.x release to restore the expected behavior.

Expand All @@ -233,7 +238,7 @@ On Linux, the `.deb`/`.rpm` package installs `/usr/bin/wheels`, which should be

### Conflict with a standalone LuCLI install sharing `~/.lucli/`

The wheels formula deliberately isolates runtime state under `~/.wheels/` (via `LUCLI_HOME`) so a standalone `lucli` install — which uses `~/.lucli/` — stays out of the way. If you previously had LuCLI installed directly and see odd module-resolution errors, check that the wrapper set `LUCLI_HOME` correctly (`wheels system env` will dump the resolved environment) and that `~/.wheels/modules/wheels/` contains a current `Module.cfc` and `.module-version` file.
The wheels formula deliberately isolates runtime state under `~/.wheels/` (via `LUCLI_HOME`) so a standalone `lucli` install — which uses `~/.lucli/` — stays out of the way. If you previously had LuCLI installed directly and see odd module-resolution errors, check that the wrapper set `LUCLI_HOME` correctly (`wheels system paths` prints the resolved home directory and where it came from) and that `~/.wheels/modules/wheels/` contains a current `Module.cfc` and `.module-version` file.

### Windows: `there is no Resource provider available with the name [c]`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The `wheels` binary can run as a Model Context Protocol (MCP) server over stdio,

## How it works

LuCLI ships a generic `mcp` subcommand that wraps any installed module's public functions as MCP tools over stdio JSON-RPC. Running `wheels mcp wheels` starts that server bound to the Wheels Module — every public function in `cli/lucli/Module.cfc` becomes a tool named `wheels_<function>` (for example, `wheels_generate`, `wheels_migrate`). The AI IDE spawns `wheels` as a subprocess, speaks newline-delimited JSON-RPC over stdin/stdout, and receives tool results the same way. No network listener is involved.
LuCLI ships a generic `mcp` subcommand that wraps any installed module's public functions as MCP tools over stdio JSON-RPC. Running `wheels mcp wheels` starts that server bound to the Wheels Module — every public function in `cli/lucli/Module.cfc` becomes a tool named after the function itself (for example, `generate`, `migrate`). Tool names appear unprefixed in `tools/list`; the `wheels` server entry in your MCP config is what namespaces them per client. The AI IDE spawns `wheels` as a subprocess, speaks newline-delimited JSON-RPC over stdin/stdout, and receives tool results the same way. No network listener is involved.

## Setup

Expand All @@ -37,7 +37,7 @@ Add an `.mcp.json` file to your project root (or wherever your IDE expects it).

That's the whole configuration. The first `wheels` is the binary name; the second is the module name to expose. The IDE launches the subprocess on demand — you don't need to keep `wheels mcp wheels` running yourself, and the server exits when the IDE disconnects.

Running `wheels mcp` on its own (no module name) prints the same snippet and a short pointer to configuration docs. It is a help command, not a setup wizard — there is no generated file today.
Don't try `wheels mcp` on its own to see this snippet: on the released launcher, LuCLI reserves the `mcp` verb and a bare invocation exits with `mcp: missing module name` instead of reaching the Wheels Module's help text. To check the server from a shell, use the `--once` smoke test below. There is no setup wizard and no generated file — add the `.mcp.json` snippet manually.

<Aside type="note" title="IDE-specific paths">
Claude Code reads `.mcp.json` from the project root. OpenCode uses `.opencode.json` with the same `mcpServers` shape. Cursor reads from its settings UI. Check your IDE's MCP documentation for the exact location; the server definition itself is identical across clients.
Expand All @@ -55,28 +55,28 @@ That runs a single MCP method and exits — useful for smoke-testing without ope

## Tools exposed

Every public function in the Wheels Module is auto-discovered as an MCP tool, prefixed with the module name:
Every public function in the Wheels Module is auto-discovered as an MCP tool. Names in `tools/list` are the bare function names — 18 tools on a 4.0.3 install:

| Tool | Purpose |
|---|---|
| `wheels_generate` | Scaffold a model, controller, view, migration, scaffold, route, test, property, or helper. |
| `wheels_destroy` | Remove a previously generated component. The default `resource` type cascades (model + controller + views + tests + route + drop-table migration); `model`, `controller`, and `view` are scoped to that artefact only. |
| `wheels_migrate` | Run, roll back, or inspect database migrations. |
| `wheels_seed` | Run convention-based seeds against the configured environment. |
| `wheels_db` | Database utility operations (schema, reset, status). |
| `wheels_test` | Run the app or core test suite and return results. |
| `wheels_reload` | Reload the app via the running dev server. |
| `wheels_routes` | List registered routes. |
| `wheels_info` | Print project, framework, and environment info. |
| `wheels_analyze` | Run the codebase analyzer (conventions, anti-patterns, drift). |
| `wheels_validate` | Run configuration and schema validation checks. |
| `wheels_doctor` | Diagnose install, project, and runtime issues. |
| `wheels_stats` | Count models, controllers, routes, tests, migrations. |
| `wheels_notes` | Surface `TODO`, `FIXME`, `HACK`, `OPTIMIZE` annotations from the codebase. |
| `wheels_create` | Create an application (currently only `wheels create app <name>`; forwards to `wheels new`). |
| `wheels_upgrade` | Scan the project for breaking changes against a target framework version (read-only). |
| `wheels_deploy` | Kamal-style production deploys (full deploy, rollback, config, setup, bootstrap, exec). |
| `wheels_packages` | Search, show, add, update, and remove Wheels packages; manage the registry cache. |
| `generate` | Scaffold a model, controller, view, migration, scaffold, route, test, property, or helper. |
| `destroy` | Remove a previously generated component. The default `resource` type cascades (model + controller + views + tests + route + drop-table migration); `model`, `controller`, and `view` are scoped to that artefact only. |
| `migrate` | Run, roll back, or inspect database migrations. |
| `seed` | Run convention-based seeds against the configured environment. |
| `db` | Database utility operations (schema, reset, status). |
| `test` | Run the app or core test suite and return results. |
| `reload` | Reload the app via the running dev server. |
| `routes` | List registered routes. |
| `info` | Print project, framework, and environment info. |
| `analyze` | Run the codebase analyzer (conventions, anti-patterns, drift). |
| `validate` | Run configuration and schema validation checks. |
| `doctor` | Diagnose install, project, and runtime issues. |
| `stats` | Count models, controllers, routes, tests, migrations. |
| `notes` | Surface `TODO`, `FIXME`, `HACK`, `OPTIMIZE` annotations from the codebase. |
| `create` | Create an application (currently only `wheels create app <name>`; forwards to `wheels new`). |
| `upgrade` | Scan the project for breaking changes against a target framework version (read-only). |
| `deploy` | Kamal-style production deploys (full deploy, rollback, config, setup, bootstrap, exec). |
| `packages` | Search, show, add, update, and remove Wheels packages; manage the registry cache. |

### Tools deliberately hidden

Expand All @@ -87,7 +87,7 @@ A handful of public CLI commands are excluded from MCP discovery via `mcpHiddenT
- `new` — scaffolds an entire new project. Destructive and requires interactive prompts for project context.
- `browser` — multi-step browser testing flow with its own subcommand tree.
- `mcp` — the meta command itself; exposing it would let an agent recursively launch MCP servers.
- `d` — short alias for `destroy`, excluded to avoid a duplicate tool entry (the canonical `wheels_destroy` is exposed).
- `d` — short alias for `destroy`, excluded to avoid a duplicate tool entry (the canonical `destroy` is exposed).
- `g` — short alias for `generate`, excluded for the same duplicate-entry reason as `d`.
- `main` — the bare `wheels` no-args dispatch target; it is not a subcommand.
- Any function whose name begins with `$` — internal helpers kept `public` only so unit tests can reach them (a CFML testing carve-out). `mcpHiddenTools()` discovers these structurally via `getMetaData(this)`, so a future `$helper` added to the module cannot accidentally surface as a callable MCP tool without a manual denylist update.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ With the server still running in the background:
wheels test posts
```

The trailing positional scopes the test run to specs whose path matches the filter string. The scaffold generated a `PostsSpec.cfc` under `tests/specs/`, so the filter narrows the run to just that file's specs. Omit it to run everything. A run with failing or erroring specs exits non-zero; a fully-passing run exits `0`.
The trailing positional is a directory scope, not a filename match — `posts` expands to `tests.specs.posts`. The scaffold generated `PostSpec.cfc` under `tests/specs/models/` and `PostsControllerSpec.cfc` under `tests/specs/controllers/`, so on a fresh scaffold the `posts` scope matches neither directory and the run reports `0 passed`. Use `wheels test models` or `wheels test controllers` to scope to the generated specs — though note that plain `wheels test` also reports `0 passed` here, because the generated specs are empty `describe` stubs until you write expectations into them. A run with failing or erroring specs exits non-zero; a run with no failures exits `0`.

## 7. What's next

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ A bare positional argument is treated as the filter directory — `wheels test m
| `--directory=<dir>` | Alias for `--filter` (tutorial chapter 7). When both are supplied, `--directory` wins. |
| `--db=<engine>` | Database engine for `--core` matrix runs only. Ignored for app tests (with a warning) — see [below](#testing-against-different-engines). |
| `--reporter=<name>` | `simple` (default, colourful), `json` (raw runner JSON), `tap` (TAP v13 for CI consumers). |
| `--verbose`, `-v` | Print per-spec output instead of the summary line. |
| `--ci` | CI mode: tightens exit codes and output for GitHub Actions and similar runners. |
| `--verbose`, `-v` | Accepted but currently inert — output is identical to a plain run; no per-spec output is printed for passing specs. Wiring tracked in [#3113](https://github.com/wheels-dev/wheels/issues/3113). |
| `--ci` | Accepted but currently inert — output is byte-identical to a plain run, and exit codes are already non-zero on failure without it. Intended to tighten output for GitHub Actions and similar runners; tracked in [#3113](https://github.com/wheels-dev/wheels/issues/3113). |
| `--core` | Run framework self-tests (`vendor/wheels/tests/specs/`) instead of your app suite. App tests are the default; `--core` is the explicit opt-in. |
| `--no-test-db` | Disable the auto-swap to `<datasource>_test`. App tests run against your dev datasource, with whatever data is already in it. |
| `--base-path=<path>` | URL prefix the app is mounted under (e.g. `/myapp`). Auto-derived from `WHEELS_SUBPATH` or `set(subpath=...)` in `config/settings.cfm` when omitted. Leave unset for root-mounted apps (the default). |
Expand Down Expand Up @@ -87,10 +87,10 @@ The engine identifiers are `postgres` (not `postgresql`) and `sqlserver` (not `m
#### Example

```bash title="example"
wheels test --filter=models --verbose
wheels test --filter=models
```

Runs every spec under `tests/specs/models/` with per-spec output. Exit code is non-zero if any spec fails or errors.
Runs every spec under `tests/specs/models/`. Exit code is non-zero if any spec fails or errors.

#### Testing against different engines

Expand Down Expand Up @@ -155,8 +155,9 @@ wheels test
# Narrow to one area while iterating
wheels test --filter=models

# CI-shaped run — tight output, firm exit codes
wheels test --ci --db=sqlite
# CI run — exit codes are already firm without extra flags; for machine-readable
# output use a reporter (--ci itself is inert today, #3113)
wheels test --reporter=tap

# First-time browser setup, then exercise the browser specs
wheels browser setup
Expand Down
Loading