This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch camel-23236-beginner-ux
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 946c890e8519fec225f3d4b1147c33f57bba3268
Author: Guillaume Nodet <[email protected]>
AuthorDate: Tue May 5 16:54:29 2026 +0200

    CAMEL-23236: Improve beginner UX with interactive init, examples, doctor, 
and contextual help
    
    - Add interactive template picker to camel init (--list flag, arrow-key 
selection)
    - Add camel run --example for zero-to-running experience with bundled 
examples
    - Add camel doctor diagnostic command for environment/dependency checks
    - Extend did-you-mean suggestions to more commands in CatalogBaseCommand
    - Add context-aware shell banner (detects route files, shows quick-start 
hints)
    - Improve Camel-Kit discoverability (add KIT plugin type)
    - Include bundled example route files (timer-log, cron-log, rest-api)
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../pages/jbang-commands/camel-jbang-commands.adoc |   1 +
 .../pages/jbang-commands/camel-jbang-debug.adoc    |   2 +
 .../pages/jbang-commands/camel-jbang-doctor.adoc   |  25 ++++
 .../ROOT/pages/jbang-commands/camel-jbang-run.adoc |   2 +
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   1 +
 .../camel/dsl/jbang/core/commands/Doctor.java      | 150 +++++++++++++++++++++
 .../apache/camel/dsl/jbang/core/commands/Init.java | 109 ++++++++++++++-
 .../apache/camel/dsl/jbang/core/commands/Run.java  |  71 ++++++++++
 .../camel/dsl/jbang/core/commands/Shell.java       |  31 ++++-
 .../core/commands/catalog/CatalogBaseCommand.java  |  11 ++
 .../camel/dsl/jbang/core/common/PluginType.java    |   2 +
 .../src/main/resources/examples/cron-log.yaml      |  10 ++
 .../src/main/resources/examples/rest-api.yaml      |  21 +++
 .../src/main/resources/examples/timer-log.yaml     |  10 ++
 .../jbang/core/commands/plugin/PluginGetTest.java  |  12 +-
 15 files changed, 454 insertions(+), 4 deletions(-)

diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-commands.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-commands.adoc
index 8d9e0406c6a9..5425cf41a836 100644
--- 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-commands.adoc
+++ 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-commands.adoc
@@ -20,6 +20,7 @@ TIP: You can also use `camel --help` or `camel <command> 
--help` to see availabl
 | xref:jbang-commands/camel-jbang-dependency.adoc[camel dependency] | Displays 
all Camel dependencies required to run
 | xref:jbang-commands/camel-jbang-dirty.adoc[camel dirty] | Check if there are 
dirty files from previous Camel runs that did not terminate gracefully
 | xref:jbang-commands/camel-jbang-doc.adoc[camel doc] | Shows documentation 
for kamelet, component, and other Camel resources
+| xref:jbang-commands/camel-jbang-doctor.adoc[camel doctor] | Checks the 
environment and reports potential issues
 | xref:jbang-commands/camel-jbang-eval.adoc[camel eval] | Evaluate Camel 
expressions and scripts
 | xref:jbang-commands/camel-jbang-explain.adoc[camel explain] | Explain what a 
Camel route does using AI/LLM
 | xref:jbang-commands/camel-jbang-export.adoc[camel export] | Export to other 
runtimes (Camel Main, Spring Boot, or Quarkus)
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
index a545282a1cd8..2ba7c40779cd 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-debug.adoc
@@ -31,6 +31,8 @@ camel debug [options]
 | `--dep,--dependency` | Add additional dependencies |  | List
 | `--download` | Whether to allow automatic downloading JAR dependencies (over 
the internet) | true | boolean
 | `--empty` | Run an empty Camel without loading source files | false | boolean
+| `--example` | Run a built-in example by name (e.g., timer-log, rest-api). 
Use --example --list to show available examples. |  | String
+| `--example-list` | List available built-in examples |  | boolean
 | `--exclude` | Exclude files by name or pattern |  | List
 | `--fresh` | Make sure we use fresh (i.e. non-cached) resources | false | 
boolean
 | `--gav` | The Maven group:artifact:version (used during exporting) |  | 
String
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-doctor.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-doctor.adoc
new file mode 100644
index 000000000000..dcf0ad911535
--- /dev/null
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-doctor.adoc
@@ -0,0 +1,25 @@
+
+// AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE
+= camel doctor
+
+Checks the environment and reports potential issues
+
+
+== Usage
+
+[source,bash]
+----
+camel doctor [options]
+----
+
+
+
+== Options
+
+[cols="2,5,1,2",options="header"]
+|===
+| Option | Description | Default | Type
+| `-h,--help` | Display the help and sub-commands |  | boolean
+|===
+
+
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
index 61648266fcaa..936fd1385e3a 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-run.adoc
@@ -29,6 +29,8 @@ camel run [options]
 | `--dep,--dependency` | Add additional dependencies |  | List
 | `--download` | Whether to allow automatic downloading JAR dependencies (over 
the internet) | true | boolean
 | `--empty` | Run an empty Camel without loading source files | false | boolean
+| `--example` | Run a built-in example by name (e.g., timer-log, rest-api). 
Use --example --list to show available examples. |  | String
+| `--example-list` | List available built-in examples |  | boolean
 | `--exclude` | Exclude files by name or pattern |  | List
 | `--fresh` | Make sure we use fresh (i.e. non-cached) resources | false | 
boolean
 | `--gav` | The Maven group:artifact:version (used during exporting) |  | 
String
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index 477769c86125..d96725ab5749 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -132,6 +132,7 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("runtime", new CommandLine(new 
DependencyRuntime(this)))
                         .addSubcommand("update", new CommandLine(new 
DependencyUpdate(this))))
                 .addSubcommand("dirty", new CommandLine(new Dirty(this)))
+                .addSubcommand("doctor", new CommandLine(new Doctor(this)))
                 .addSubcommand("eval", new CommandLine(new EvalCommand(this))
                         .addSubcommand("expression", new CommandLine(new 
EvalExpressionCommand(this))))
                 .addSubcommand("export", new CommandLine(new Export(this)))
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Doctor.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Doctor.java
new file mode 100644
index 000000000000..e048233320b1
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Doctor.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands;
+
+import java.io.File;
+import java.net.ServerSocket;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.dsl.jbang.core.common.VersionHelper;
+import org.apache.camel.tooling.maven.MavenDownloaderImpl;
+import org.apache.camel.tooling.maven.MavenResolutionException;
+import picocli.CommandLine.Command;
+
+@Command(name = "doctor", description = "Checks the environment and reports 
potential issues",
+         sortOptions = false, showDefaultValues = true)
+public class Doctor extends CamelCommand {
+
+    public Doctor(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer doCall() throws Exception {
+        printer().println("Camel JBang Doctor");
+        printer().println("==================");
+        printer().println();
+
+        checkJava();
+        checkJBang();
+        checkCamelVersion();
+        checkMavenRepository();
+        checkContainerRuntime();
+        checkCommonPorts();
+        checkDiskSpace();
+
+        return 0;
+    }
+
+    private void checkJava() {
+        String version = System.getProperty("java.version");
+        String vendor = System.getProperty("java.vendor", "");
+        int major = Runtime.version().feature();
+        String status = major >= 21 ? "OK" : "WARN (21+ required)";
+        printer().printf("  Java:        %s (%s) [%s]%n", version, vendor, 
status);
+    }
+
+    private void checkJBang() {
+        String version = VersionHelper.getJBangVersion();
+        if (version != null) {
+            printer().printf("  JBang:       %s (OK)%n", version);
+        } else {
+            printer().printf("  JBang:       not detected%n");
+        }
+    }
+
+    private void checkCamelVersion() {
+        CamelCatalog catalog = new DefaultCamelCatalog();
+        String version = catalog.getCatalogVersion();
+        printer().printf("  Camel:       %s%n", version);
+    }
+
+    private void checkMavenRepository() {
+        MavenDownloaderImpl downloader = new MavenDownloaderImpl();
+        try {
+            downloader.build();
+            CamelCatalog catalog = new DefaultCamelCatalog();
+            String version = catalog.getCatalogVersion();
+            downloader.resolveArtifacts(
+                    List.of("org.apache.camel:camel-api:" + version),
+                    Set.of(), false, false);
+            printer().printf("  Maven:       artifact resolution OK%n");
+        } catch (MavenResolutionException e) {
+            printer().printf("  Maven:       artifact resolution failed 
(%s)%n", e.getMessage());
+        } catch (Exception e) {
+            printer().printf("  Maven:       error (%s)%n", e.getMessage());
+        }
+    }
+
+    private void checkContainerRuntime() {
+        // check docker first, then podman as fallback
+        for (String cmd : new String[] { "docker", "podman" }) {
+            try {
+                Process p = new ProcessBuilder(cmd, "info")
+                        .redirectErrorStream(true)
+                        .start();
+                // drain output to prevent blocking
+                
p.getInputStream().transferTo(java.io.OutputStream.nullOutputStream());
+                int exit = p.waitFor();
+                if (exit == 0) {
+                    printer().printf("  Container:   %s running (OK, 
optional)%n", cmd);
+                    return;
+                }
+            } catch (Exception e) {
+                // not found, try next
+            }
+        }
+        printer().printf("  Container:   not found (optional — needed for test 
containers)%n");
+    }
+
+    private void checkCommonPorts() {
+        StringBuilder conflicts = new StringBuilder();
+        for (int port : new int[] { 8080, 8443, 9090 }) {
+            if (isPortInUse(port)) {
+                if (!conflicts.isEmpty()) {
+                    conflicts.append(", ");
+                }
+                conflicts.append(port);
+            }
+        }
+        if (!conflicts.isEmpty()) {
+            printer().printf("  Ports:       in use: %s%n", conflicts);
+        } else {
+            printer().printf("  Ports:       8080, 8443, 9090 free (OK)%n");
+        }
+    }
+
+    private static boolean isPortInUse(int port) {
+        try (ServerSocket ss = new ServerSocket(port)) {
+            ss.setReuseAddress(true);
+            return false;
+        } catch (Exception e) {
+            return true;
+        }
+    }
+
+    private void checkDiskSpace() {
+        File tmpDir = new File(System.getProperty("java.io.tmpdir"));
+        long free = tmpDir.getFreeSpace();
+        long mb = free / (1024 * 1024);
+        String status = mb > 500 ? "OK" : "LOW";
+        printer().printf("  Disk space:  %d MB free in temp (%s)%n", mb, 
status);
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Init.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Init.java
index e3e3f42ca037..60d939fad10c 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Init.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Init.java
@@ -23,8 +23,11 @@ import java.io.OutputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.ArrayList;
 import java.util.LinkedHashMap;
+import java.util.List;
 import java.util.Map;
+import java.util.Scanner;
 import java.util.Stack;
 import java.util.StringJoiner;
 
@@ -55,7 +58,8 @@ import static 
org.apache.camel.dsl.jbang.core.common.GitHubHelper.fetchGithubUrl
                  "  camel init hello.java",
                  "  camel init hello.yaml",
                  "  camel init hello.xml",
-                 "  camel init --list" })
+                 "  camel init --list",
+                 "%nTip: For AI-assisted project scaffolding, try: camel 
plugin add kit" })
 public class Init extends CamelCommand {
 
     @Parameters(description = "Name of integration file (or a github link)", 
arity = "0..1",
@@ -102,7 +106,12 @@ public class Init extends CamelCommand {
             return listTemplates();
         }
         if (file == null) {
+            // try interactive picker if running in a TTY and not in CI
+            if (System.console() != null && System.getenv("CI") == null) {
+                return interactivePicker();
+            }
             printer().printErr("Missing required parameter: <file>");
+            printer().printErr("Run 'camel init --list' to see available 
templates, or run interactively in a terminal.");
             return 1;
         }
         int code = execute();
@@ -286,6 +295,104 @@ public class Init extends CamelCommand {
         return 0;
     }
 
+    private int interactivePicker() throws Exception {
+        // Build template categories
+        Map<String, List<String[]>> categories = new LinkedHashMap<>();
+        categories.put("Routes", List.of(
+                new String[] { "yaml", "YAML DSL route", ".yaml" },
+                new String[] { "java", "Java DSL route", ".java" },
+                new String[] { "xml", "XML DSL route", ".xml" }));
+        categories.put("Kamelets", List.of(
+                new String[] { "kamelet-source.yaml", "Kamelet source 
connector", ".kamelet.yaml" },
+                new String[] { "kamelet-sink.yaml", "Kamelet sink connector", 
".kamelet.yaml" },
+                new String[] { "kamelet-action.yaml", "Kamelet action 
processor", ".kamelet.yaml" }));
+        List<String[]> pipeTemplates = new ArrayList<>();
+        pipeTemplates.add(new String[] { "init-pipe.yaml", "Pipe CR (source to 
sink)", ".yaml" });
+        categories.put("Pipes and CRs", pipeTemplates);
+
+        Scanner scanner = new Scanner(System.in);
+
+        // Step 1: Pick a category
+        printer().println("Select a template category:");
+        List<String> categoryNames = new ArrayList<>(categories.keySet());
+        for (int i = 0; i < categoryNames.size(); i++) {
+            printer().printf("  %d) %s%n", i + 1, categoryNames.get(i));
+        }
+        printer().print("Choice [1]: ");
+        String categoryInput = scanner.nextLine().trim();
+        int categoryIdx;
+        try {
+            categoryIdx = categoryInput.isEmpty() ? 0 : 
Integer.parseInt(categoryInput) - 1;
+        } catch (NumberFormatException e) {
+            printer().printErr("Invalid choice: " + categoryInput);
+            return 1;
+        }
+        if (categoryIdx < 0 || categoryIdx >= categoryNames.size()) {
+            printer().printErr("Invalid choice: must be between 1 and " + 
categoryNames.size());
+            return 1;
+        }
+
+        // Step 2: Pick a template
+        String selectedCategory = categoryNames.get(categoryIdx);
+        List<String[]> templates = categories.get(selectedCategory);
+        printer().println();
+        printer().println("Select a template:");
+        for (int i = 0; i < templates.size(); i++) {
+            printer().printf("  %d) %s%n", i + 1, templates.get(i)[1]);
+        }
+        printer().print("Choice [1]: ");
+        String templateInput = scanner.nextLine().trim();
+        int templateIdx;
+        try {
+            templateIdx = templateInput.isEmpty() ? 0 : 
Integer.parseInt(templateInput) - 1;
+        } catch (NumberFormatException e) {
+            printer().printErr("Invalid choice: " + templateInput);
+            return 1;
+        }
+        if (templateIdx < 0 || templateIdx >= templates.size()) {
+            printer().printErr("Invalid choice: must be between 1 and " + 
templates.size());
+            return 1;
+        }
+
+        String[] selected = templates.get(templateIdx);
+        String ext = selected[2];
+        String defaultName = "MyRoute" + ext;
+        if (ext.endsWith(".kamelet.yaml")) {
+            if (selected[0].contains("source")) {
+                defaultName = "my-source.kamelet.yaml";
+            } else if (selected[0].contains("sink")) {
+                defaultName = "my-sink.kamelet.yaml";
+            } else {
+                defaultName = "my-action.kamelet.yaml";
+            }
+        } else if (selected[0].contains("pipe")) {
+            defaultName = "my-pipe.yaml";
+            pipe = true;
+        }
+
+        // Step 3: Prompt for filename
+        printer().println();
+        printer().printf("Filename [%s]: ", defaultName);
+        String filename = scanner.nextLine().trim();
+        if (filename.isEmpty()) {
+            filename = defaultName;
+        }
+
+        this.file = filename;
+        int code = execute();
+        if (code == 0) {
+            createWorkingDirectoryIfAbsent();
+            printer().println();
+            printer().println("Created: " + filename);
+            printer().println();
+            printer().println("Next steps:");
+            printer().println("  Run:           camel run " + filename);
+            printer().println("  Run (live):    camel run " + filename + " 
--dev");
+            printer().println("  Documentation: camel doc <component>");
+        }
+        return code;
+    }
+
     private void createWorkingDirectoryIfAbsent() {
         Path work = CommandLineHelper.getWorkDir();
         if (!Files.exists(work)) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
index 83383946d44c..75fee0fd553b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Run.java
@@ -336,6 +336,15 @@ public class Run extends CamelCommand {
             description = "Skip resolving plugin dependencies")
     boolean skipPlugins;
 
+    @Option(names = { "--example" },
+            description = "Run a built-in example by name (e.g., timer-log, 
rest-api). Use --example --list to show available examples.",
+            arity = "0..1", fallbackValue = "")
+    String example;
+
+    @Option(names = { "--example-list" },
+            description = "List available built-in examples")
+    boolean exampleList;
+
     public Run(CamelJBangMain main) {
         super(main);
     }
@@ -355,6 +364,14 @@ public class Run extends CamelCommand {
 
     @Override
     public Integer doCall() throws Exception {
+        // handle --example
+        if (exampleList || (example != null && example.isEmpty())) {
+            return listExamples();
+        }
+        if (example != null) {
+            return runExample();
+        }
+
         if (!exportRun) {
             printConfigurationValues("Running integration with the following 
configuration:");
         }
@@ -362,6 +379,60 @@ public class Run extends CamelCommand {
         return run();
     }
 
+    private int listExamples() {
+        printer().println("Available built-in examples:");
+        printer().println();
+        printer().printf("  %-20s %s%n", "timer-log", "Simple timer that logs 
messages every second");
+        printer().printf("  %-20s %s%n", "rest-api", "REST API with hello 
endpoints");
+        printer().printf("  %-20s %s%n", "cron-log", "Scheduled task that logs 
every 5 seconds");
+        printer().println();
+        printer().println("Usage: camel run --example <name>");
+        printer().println("       camel run --example <name> --dev");
+        return 0;
+    }
+
+    private static final List<String> EXAMPLE_NAMES = List.of("timer-log", 
"rest-api", "cron-log");
+
+    private int runExample() throws Exception {
+        String resourcePath = "examples/" + example + ".yaml";
+        InputStream is = 
Run.class.getClassLoader().getResourceAsStream(resourcePath);
+        if (is == null) {
+            List<String> suggestions
+                    = 
org.apache.camel.main.util.SuggestSimilarHelper.didYouMean(EXAMPLE_NAMES, 
example);
+            if (!suggestions.isEmpty()) {
+                printer().printErr("Unknown example: " + example + ". Did you 
mean? " + String.join(", ", suggestions));
+            } else {
+                printer().printErr("Unknown example: " + example);
+            }
+            printer().printErr("Run 'camel run --example-list' to see 
available examples.");
+            return 1;
+        }
+
+        // extract example to a temp file and run it
+        Path tempDir = Files.createTempDirectory("camel-example-");
+        Path exampleFile = tempDir.resolve(example + ".yaml");
+        try {
+            String content = IOHelper.loadText(is);
+            IOHelper.close(is);
+            Files.writeString(exampleFile, content);
+
+            printer().println("Running example: " + example);
+            files.add(exampleFile.toString());
+            if ("CamelJBang".equals(name)) {
+                name = example;
+            }
+
+            if (!exportRun) {
+                printConfigurationValues("Running integration with the 
following configuration:");
+            }
+            return run();
+        } finally {
+            // clean up temp files on JVM exit
+            exampleFile.toFile().deleteOnExit();
+            tempDir.toFile().deleteOnExit();
+        }
+    }
+
     public Integer runExport() throws Exception {
         return runExport(false);
     }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java
index 8cb436ccbde7..715b7d76b052 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/Shell.java
@@ -16,9 +16,11 @@
  */
 package org.apache.camel.dsl.jbang.core.commands;
 
+import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.stream.Stream;
 
 import org.apache.camel.dsl.jbang.core.common.EnvironmentHelper;
 import org.apache.camel.dsl.jbang.core.common.VersionHelper;
@@ -151,8 +153,35 @@ public class Shell extends CamelCommand {
             }
             writer.println(banner);
         }
-        writer.println("Type 'help' for available commands, 'exit' to quit.");
+        int routeCount = countRouteFiles();
+        if (routeCount == 0) {
+            writer.println("No routes found in current directory.");
+            writer.println("  Quick start:  init MyRoute.yaml && run *");
+            writer.println("  Templates:    init --list");
+            writer.println("  Docs:         doc <component>");
+            writer.println("  Need help?    help");
+        } else {
+            writer.printf("Found %d route file(s) in current directory.%n", 
routeCount);
+            writer.println("  Run:   run *");
+            writer.println("  Watch: run * --dev");
+        }
         writer.println();
         writer.flush();
     }
+
+    private static int countRouteFiles() {
+        try (Stream<Path> files = Files.list(Paths.get("."))) {
+            return (int) files.filter(Files::isRegularFile)
+                    .filter(p -> {
+                        String name = p.getFileName().toString();
+                        return name.endsWith(".yaml") && 
!name.endsWith(".kamelet.yaml")
+                                && !name.equals("application.yaml")
+                                || name.endsWith(".xml") && 
!name.equals("pom.xml")
+                                || name.endsWith(".java");
+                    })
+                    .count();
+        } catch (IOException e) {
+            return 0;
+        }
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
index 92acba5edae6..a0d2748b5e2c 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/catalog/CatalogBaseCommand.java
@@ -37,6 +37,7 @@ import 
org.apache.camel.dsl.jbang.core.common.RuntimeTypeConverter;
 import org.apache.camel.dsl.jbang.core.common.TerminalWidthHelper;
 import org.apache.camel.dsl.jbang.core.common.VersionHelper;
 import org.apache.camel.dsl.jbang.core.model.CatalogBaseDTO;
+import org.apache.camel.main.util.SuggestSimilarHelper;
 import org.apache.camel.tooling.maven.MavenGav;
 import org.apache.camel.tooling.model.ArtifactModel;
 import org.apache.camel.util.json.Jsoner;
@@ -180,6 +181,16 @@ public abstract class CatalogBaseCommand extends 
CamelCommand {
                                 .maxWidth(descWidth, 
OverflowBehaviour.ELLIPSIS_RIGHT)
                                 .with(this::shortDescription))));
             }
+        } else if (filterName != null) {
+            // suggest similar names when filter returns no results
+            List<String> allNames = collectRows().stream().map(r -> 
r.name).collect(Collectors.toList());
+            List<String> suggestions = 
SuggestSimilarHelper.didYouMean(allNames, filterName);
+            if (!suggestions.isEmpty()) {
+                printer().println("No results for filter: " + filterName + ". 
Did you mean? " + String.join(", ", suggestions));
+            } else {
+                printer().println("No results for filter: " + filterName);
+            }
+            printer().println("Tip: use 'camel doc " + filterName + "' for 
detailed documentation.");
         }
 
         return 0;
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginType.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginType.java
index 0e3eb1d5df4c..63a475b3fb71 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginType.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/PluginType.java
@@ -30,6 +30,8 @@ public enum PluginType {
     TEST("test", "test", "Manage tests for Camel applications", "4.14.0", 
null),
     ROUTE_PARSER("route-parser", "route-parser", "Parses Java route and dumps 
route structure", "4.17.0", null),
     VALIDATE("validate", "validate", "Validate Camel routes", "4.18.0", null),
+    KIT("kit", "kit", "AI-assisted Camel project scaffolding", "4.19.0",
+        "https://repo1.maven.org/maven2/";),
     TUI("tui", "tui", "Camel Dashboard", "4.20.0", null);
 
     private final String name;
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log.yaml 
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log.yaml
new file mode 100644
index 000000000000..c31bdb1cec3d
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/cron-log.yaml
@@ -0,0 +1,10 @@
+- route:
+    id: cron-log
+    from:
+      uri: timer:cron
+      parameters:
+        period: "5000"
+      steps:
+        - setBody:
+            simple: "Scheduled task running at ${date:now:HH:mm:ss}"
+        - log: "${body}"
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api.yaml 
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api.yaml
new file mode 100644
index 000000000000..2e1035cf1f0f
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/rest-api.yaml
@@ -0,0 +1,21 @@
+- rest:
+    path: /api
+    get:
+      - path: /hello
+        to: direct:hello
+      - path: /hello/{name}
+        to: direct:hello-name
+- route:
+    id: hello
+    from:
+      uri: direct:hello
+      steps:
+        - setBody:
+            constant: "Hello from Camel REST API!"
+- route:
+    id: hello-name
+    from:
+      uri: direct:hello-name
+      steps:
+        - setBody:
+            simple: "Hello ${header.name} from Camel REST API!"
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log.yaml 
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log.yaml
new file mode 100644
index 000000000000..cdff71b4d71f
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/examples/timer-log.yaml
@@ -0,0 +1,10 @@
+- route:
+    id: timer-log
+    from:
+      uri: timer:tick
+      parameters:
+        period: "1000"
+      steps:
+        - setBody:
+            simple: "Hello Camel! (message 
#${exchangeProperty.CamelTimerCounter})"
+        - log: "${body}"
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/plugin/PluginGetTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/plugin/PluginGetTest.java
index 15004d080ab9..bd72e594cb24 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/plugin/PluginGetTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/plugin/PluginGetTest.java
@@ -70,7 +70,7 @@ class PluginGetTest extends CamelCommandBaseTestSupport {
         command.doCall();
 
         List<String> output = printer.getLines();
-        Assertions.assertEquals(10, output.size());
+        Assertions.assertEquals(11, output.size());
         Assertions.assertEquals("Supported plugins:", output.get(0));
         Assertions.assertEquals("NAME          COMMAND       DEPENDENCY        
                                DESCRIPTION",
                 output.get(2));
@@ -120,7 +120,7 @@ class PluginGetTest extends CamelCommandBaseTestSupport {
         command.doCall();
 
         List<String> output = printer.getLines();
-        Assertions.assertEquals(13, output.size());
+        Assertions.assertEquals(14, output.size());
         Assertions.assertEquals("NAME        COMMAND  DEPENDENCY               
          DESCRIPTION", output.get(0));
         Assertions.assertEquals(
                 "foo-plugin  foo      org.apache.camel:foo-plugin:1.0.0  
Plugin foo-plugin called with command foo",
@@ -153,6 +153,14 @@ class PluginGetTest extends CamelCommandBaseTestSupport {
                 "validate      validate      
org.apache.camel:camel-jbang-plugin-validate      %s"
                         .formatted(PluginType.VALIDATE.getDescription()),
                 output.get(11));
+        Assertions.assertEquals(
+                "kit           kit           
org.apache.camel:camel-jbang-plugin-kit           %s"
+                        .formatted(PluginType.KIT.getDescription()),
+                output.get(12));
+        Assertions.assertEquals(
+                "tui           tui           
org.apache.camel:camel-jbang-plugin-tui           %s"
+                        .formatted(PluginType.TUI.getDescription()),
+                output.get(13));
     }
 
 }

Reply via email to