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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.

## Unreleased
- **Fix: Hints Name the Binary You Ran:** User-facing hints such as "Use 'lucli server new' …", "Run 'lucli secrets init' …" or "run 'lucli modules list' …" always said `lucli`, also under an aliased binary (e.g. `wheels`). They now use `LuCLI.cliName()`, the active profile's name. The bundled `lang` module's "Use 'lucli lang help'" hint does the same. picocli `@Command` help text (annotation constants) is unchanged. Adds `CliNameTest`.

- **Fix: The Bundled `lang` Module Compiles:** eight help strings in `modules/lang/Module.cfc` contained an unescaped `#` (`# e.g., …`), which CFML reads as an expression delimiter, so the module failed to compile ("Invalid identifier") and `lang` was unusable. They are escaped as `##`. Upstream: cybersonic/LuCLI#146 (PR cybersonic/LuCLI#147).

- **Fix: `lucli run <file>.cfm` No Longer Prints the Built-in Variables Setup:** For a `.cfm` (a template, where text outside `<cfscript>` is output), the built-in variables setup was prepended as plain text, so about 20 lines of setup code and an `// === Original Script Content ===` marker were printed before the template's output. The setup now runs inside `<cfscript>` ahead of the unchanged template, so the output is exactly what the template renders, and the built-in variables (`__scriptPath`, `ARGS`, …) are still available. `.cfs` scripts were not affected. Upstream: cybersonic/LuCLI#137. Adds `TemplatePreludeTest`.

- **Fix: Root Flags Before a Root Subcommand Under an Aliased Binary:** `wheels --timing cfml 'x'` (any root option such as `--timing`, `--verbose`, `-e prod`, `--timeout=30` before a reserved root subcommand) was handed to the module, because the reserved-token rule treated the subcommand as a module positional when it wasn't at index 1. The leading position now skips root options and their separate values, so the root subcommand runs with the flags as it does without them. Reserved tokens later in a module command are still rewritten as before. Adds `LuCLIModuleHelpPreprocessTest` cases.
Expand Down
1 change: 1 addition & 0 deletions FORK.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ No release is tagged for this queue yet; tagging needs the Wheels release owner'
| 11 | wheels-dev/LuCLI [#23](https://github.com/wheels-dev/LuCLI/pull/23) (issue: upstream [cybersonic/LuCLI#139](https://github.com/cybersonic/LuCLI/issues/139)) | `server start --dry-run` no longer writes a default `lucee.json`; the preview shows `--name` |
| 12 | wheels-dev/LuCLI [#24](https://github.com/wheels-dev/LuCLI/pull/24) (fork-only: follows row 2 / upstream PR [cybersonic/LuCLI#124](https://github.com/cybersonic/LuCLI/pull/124)) | Root options before a reserved root subcommand under an aliased binary run that subcommand instead of the module (`wheels --timing cfml 'x'`) |
| 13 | wheels-dev/LuCLI [#26](https://github.com/wheels-dev/LuCLI/pull/26) (issue: upstream [cybersonic/LuCLI#137](https://github.com/cybersonic/LuCLI/issues/137)) | `lucli run <file>.cfm` runs the built-in variables setup inside `<cfscript>` instead of printing it |
| 14 | wheels-dev/LuCLI [#27](https://github.com/wheels-dev/LuCLI/pull/27) (fork-only polish, no upstream issue) | User-facing hints name the active binary (`wheels server new`, not `lucli server new`) via `LuCLI.cliName()` |

Upstream (row 8): issue [cybersonic/LuCLI#133](https://github.com/cybersonic/LuCLI/issues/133),
PR [cybersonic/LuCLI#134](https://github.com/cybersonic/LuCLI/pull/134) (a `cherry-pick -x` of
Expand Down
20 changes: 15 additions & 5 deletions src/main/java/org/lucee/lucli/LuCLI.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,16 @@ public class LuCLI implements Callable<Integer> {

public static Map<String, String> scriptEnvironment = new HashMap<>(System.getenv());

/**
* The command name to show in user-facing hints ("Use 'wheels server new' ..."):
* the active profile's name, so an aliased binary doesn't tell users to run
* {@code lucli}. Falls back to {@code lucli}.
*/
public static String cliName() {
String name = activeProfile != null ? activeProfile.name() : null;
return (name == null || name.isBlank()) ? "lucli" : name;
}

/**
* The active CLI profile, determined by the binary name at startup.
* Controls branding, home directory name, and prompt prefix.
Expand Down Expand Up @@ -354,7 +364,7 @@ private Integer routeCommand(String arg, String[] args) throws Exception {
// Check if it's a CFML file (.cfm, .cfc, .cfs)
if (file.exists() && arg.endsWith(".cfc")) {
StringOutput.Quick.error("Executing .cfc files directly is not supported.");
StringOutput.getInstance().println("Use a module entry point instead (e.g. 'lucli modules run <module>').");
StringOutput.getInstance().println("Use a module entry point instead (e.g. '" + cliName() + " modules run <module>').");
return 1;
}
if (file.exists() && (arg.endsWith(".cfm") ||
Expand All @@ -380,8 +390,8 @@ private Integer routeCommand(String arg, String[] args) throws Exception {
spec.commandLine(),
"Unknown command, file, or module: '" + arg + "'\n" +
" - If it's a file, check the path and extension (.cfm, .cfs, .lucli)\n" +
" - If it's a module, run 'lucli modules list' to see available modules\n" +
" - Run 'lucli --help' to see available commands"
" - If it's a module, run '" + cliName() + " modules list' to see available modules\n" +
" - Run '" + cliName() + " --help' to see available commands"
);
}

Expand Down Expand Up @@ -415,7 +425,7 @@ private Integer executeViaModulesCommand(String moduleName, String[] args) throw
}

verbose("Executing module shortcut: " + moduleName +
" (equivalent to 'lucli modules run " + moduleName + " " + String.join(" ", args) + "')");
" (equivalent to '" + cliName() + " modules run " + moduleName + " " + String.join(" ", args) + "')");

List<String> cmdArgs = moduleShortcutArgs(moduleName, args, isVerbose(), isDebug(), envOption, envFileOption);
return spec.commandLine().execute(cmdArgs.toArray(new String[0]));
Expand Down Expand Up @@ -1114,7 +1124,7 @@ private static void initializeLucliScriptSecretStore() throws Exception {
Path storePath = Paths.get(home).resolve("secrets").resolve("local.json");

if (!Files.exists(storePath)) {
throw new IllegalStateException("Script references ${secret:...} but local secret store does not exist. Run 'lucli secrets init' and define the required secrets.");
throw new IllegalStateException("Script references ${secret:...} but local secret store does not exist. Run '" + cliName() + " secrets init' and define the required secrets.");
}

// Prefer non-interactive passphrase from environment when available
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/lucee/lucli/cflint/CFLintCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private boolean ensureCFLintAvailable() {
return true;
} catch (Exception e) {
System.err.println("❌ Error downloading or loading CFLint: " + e.getMessage());
System.err.println("💡 Try running 'lucli lint status' to check CFLint availability");
System.err.println("💡 Try running '" + org.lucee.lucli.LuCLI.cliName() + " lint status' to check CFLint availability");
return false;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/lucee/lucli/cli/commands/AiCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,7 @@ public Integer call() throws Exception {

String endpoint = firstNonBlank(endpointName, config.defaultEndpoint);
if (isBlank(endpoint)) {
StringOutput.Quick.error("No endpoint specified. Use --endpoint or set --default-endpoint via 'lucli ai config'.");
StringOutput.Quick.error("No endpoint specified. Use --endpoint or set --default-endpoint via '" + org.lucee.lucli.LuCLI.cliName() + " ai config'.");
return 1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ private Integer runLspDaemon() throws Exception {

if (!ModuleCommand.moduleExists(lspModuleName)) {
LuCLI.error("LSP module '" + lspModuleName + "' not found under ~/.lucli/modules.");
LuCLI.error("Install or create it with 'lucli modules init " + lspModuleName + "'.");
LuCLI.error("Install or create it with '" + org.lucee.lucli.LuCLI.cliName() + " modules init " + lspModuleName + "'.");
return 1;
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/lucee/lucli/cli/commands/RunCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ else if (fileName.endsWith(".cfm") ) {
}
}
else if (fileName.endsWith(".cfc") ) {
StringOutput.Quick.error("Executing .cfc files via 'lucli run' is not supported.");
StringOutput.getInstance().println("Use a module entry point instead (e.g. 'lucli modules run <module>').");
StringOutput.Quick.error("Executing .cfc files via '" + org.lucee.lucli.LuCLI.cliName() + " run' is not supported.");
StringOutput.getInstance().println("Use a module entry point instead (e.g. '" + org.lucee.lucli.LuCLI.cliName() + " modules run <module>').");
return 1;
}
else if (fileName.endsWith(".cfs") ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ static class GetCommand implements Callable<Integer> {
@Override
public Integer call() throws Exception {
if (!show) {
System.err.println("By default, 'lucli secrets get' does not print raw values. Use --show if you really need to see it.");
System.err.println("By default, '" + org.lucee.lucli.LuCLI.cliName() + " secrets get' does not print raw values. Use --show if you really need to see it.");
return 1;
}
String provider = selectedProvider();
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/lucee/lucli/cli/commands/ServerCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public Integer call() throws Exception {
}

if (invokedAsTart) {
System.out.println("Tip: 'lucli server tart' also starts a server. Sweet.");
System.out.println("Tip: '" + org.lucee.lucli.LuCLI.cliName() + " server tart' also starts a server. Sweet.");
System.out.println(" .-\"\"-.");
System.out.println(" .' .-. '.");
System.out.println(" / ( ) \\");
Expand Down Expand Up @@ -1164,7 +1164,7 @@ public Integer call() throws Exception {
if (name != null && !name.trim().isEmpty()) {
System.err.println("No running Lucee server found with name '" + name + "'.");
} else {
System.err.println("No running Lucee server found for this directory. Start one with 'lucli server start'.");
System.err.println("No running Lucee server found for this directory. Start one with '" + org.lucee.lucli.LuCLI.cliName() + " server start'.");
}
return 1;
}
Expand Down Expand Up @@ -1257,7 +1257,7 @@ public Integer call() throws Exception {

if (!Files.exists(cfg)) {
System.err.println("❌ Config file not found: " + cfg.toAbsolutePath());
System.err.println(" Use 'lucli server new' to create a new lucee.json.");
System.err.println(" Use '" + org.lucee.lucli.LuCLI.cliName() + " server new' to create a new lucee.json.");
return 1;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private void listModules() throws IOException {

if (!Files.exists(modulesDir)) {
StringOutput.getInstance().println("${EMOJI_INFO} No modules directory found at: " + modulesDir);
StringOutput.getInstance().println("${EMOJI_BULB} Use 'lucli modules init <module-name>' to create your first module.");
StringOutput.getInstance().println("${EMOJI_BULB} Use '" + org.lucee.lucli.LuCLI.cliName() + " modules init <module-name>' to create your first module.");
return;
}

Expand All @@ -70,7 +70,7 @@ private void listModules() throws IOException {

if (installed.isEmpty() && repoModules.isEmpty()) {
StringOutput.getInstance().println("${EMOJI_INFO} No modules found.");
StringOutput.getInstance().println("${EMOJI_BULB} Use 'lucli modules init <module-name>' to create a new module.");
StringOutput.getInstance().println("${EMOJI_BULB} Use '" + org.lucee.lucli.LuCLI.cliName() + " modules init <module-name>' to create a new module.");
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ private LocalSecretStore getLocalSecretStore(boolean required) throws Exception
if (required) {
throw new IllegalStateException(
"Local secret store not found at " + storePath +
". Run 'lucli secrets init' to create it."
". Run '" + org.lucee.lucli.LuCLI.cliName() + " secrets init' to create it."
);
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ private static ConnectionDetails resolveConnectionDetails(String serverName, Str
if (serverName != null) {
LuceeServerManager.ServerInfo serverInfo = serverManager.getServerInfoByName(serverName);
if (serverInfo == null) {
lastErrorMessage = "❌ Server '" + serverName + "' not found.\n💡 Use 'lucli server list' to see available servers.";
lastErrorMessage = "❌ Server '" + serverName + "' not found.\n💡 Use '" + org.lucee.lucli.LuCLI.cliName() + " server list' to see available servers.";
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public static String readSecretFromLocalStore(
if (required) {
throw new IllegalStateException(
"Local secret store not found at " + getLocalStorePath() +
". Run 'lucli secrets init' first."
". Run '" + org.lucee.lucli.LuCLI.cliName() + " secrets init' first."
);
}
return null;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/lucee/lucli/server/LogCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public static void executeLog(String[] args) {
LuceeServerManager.ServerInstance serverInstance = serverManager.getRunningServer(currentDir);
if (serverInstance == null) {
System.err.println("No running server found for the current directory.");
System.err.println("Use 'lucli server start' to start a server first.");
System.err.println("Use '" + org.lucee.lucli.LuCLI.cliName() + " server start' to start a server first.");
System.exit(1);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1719,7 +1719,7 @@ public static void resolveSecretPlaceholders(ServerConfig config, Path projectDi
if (!LucliSecretProviderSupport.hasLocalStoreFile()) {
throw new IOException(
"Configuration references secret placeholders but local secret store does not exist. " +
"Run 'lucli secrets init' and define the required secrets."
"Run '" + org.lucee.lucli.LuCLI.cliName() + " secrets init' and define the required secrets."
);
}
localPassphrase = LucliSecretProviderSupport.resolvePassphrase(
Expand Down
14 changes: 7 additions & 7 deletions src/main/java/org/lucee/lucli/server/ServerCommandHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ private String handleServerStart(LuceeServerManager serverManager, String[] args
result.append(" - ").append(certsDir.resolve("keystore.pass")).append("\n");
}

result.append("\nYou can now inspect this instance or start it later with 'lucli server start' (without --create-config).\n");
result.append("\nYou can now inspect this instance or start it later with '" + org.lucee.lucli.LuCLI.cliName() + " server start' (without --create-config).\n");
return formatOutput(result.toString(), false);
}

Expand Down Expand Up @@ -1627,7 +1627,7 @@ private String handleServerInfo(LuceeServerManager serverManager, String[] args)
missing.append("❌ Config file not found: ")
.append(cfgPath.toAbsolutePath())
.append("\n");
missing.append(" Use 'lucli server new' to create a new lucee.json for this project.\n");
missing.append(" Use '" + org.lucee.lucli.LuCLI.cliName() + " server new' to create a new lucee.json for this project.\n");
return formatOutput(missing.toString(), true);
}

Expand Down Expand Up @@ -1776,7 +1776,7 @@ private String handleServerEnv(String[] args) throws Exception {
missing.append("❌ Config file not found: ")
.append(cfgPath.toAbsolutePath())
.append("\n");
missing.append(" Use 'lucli server new' to create a new lucee.json for this project.\n");
missing.append(" Use '" + org.lucee.lucli.LuCLI.cliName() + " server new' to create a new lucee.json for this project.\n");
return formatOutput(missing.toString(), true);
}

Expand Down Expand Up @@ -2135,9 +2135,9 @@ private String handleServerLock(String[] args) throws Exception {
if (existing != null && existing.locked && !update) {
StringBuilder msg = new StringBuilder();
msg.append("❌ Server configuration is already locked for env '").append(envKey).append("'.\n");
msg.append(" Use 'lucli server lock --env=").append("_default".equals(envKey) ? "" : envKey)
msg.append(" Use '" + org.lucee.lucli.LuCLI.cliName() + " server lock --env=").append("_default".equals(envKey) ? "" : envKey)
.append(" --update' to refresh from the current ").append(cfgFile).append(".\n");
msg.append(" Or 'lucli server unlock");
msg.append(" Or '" + org.lucee.lucli.LuCLI.cliName() + " server unlock");
if (!"_default".equals(envKey)) {
msg.append(" --env=").append(envKey);
}
Expand All @@ -2159,7 +2159,7 @@ private String handleServerLock(String[] args) throws Exception {
StringBuilder result = new StringBuilder();
result.append("🔒 Server configuration locked for env '").append(envKey).append("'.\n");
result.append(" Source file: ").append(cfgFile).append("\n");
result.append(" Future 'lucli server start");
result.append(" Future '" + org.lucee.lucli.LuCLI.cliName() + " server start");
if (!"_default".equals(envKey)) {
result.append(" --env=").append(envKey);
}
Expand Down Expand Up @@ -2202,7 +2202,7 @@ private String handleServerUnlock(String[] args) throws Exception {

StringBuilder result = new StringBuilder();
result.append("🔓 Server configuration unlocked for env '").append(envKey).append("'.\n");
result.append(" Future 'lucli server start");
result.append(" Future '" + org.lucee.lucli.LuCLI.cliName() + " server start");
if (!"_default".equals(envKey)) {
result.append(" --env=").append(envKey);
}
Expand Down
Loading
Loading