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

luigidemasi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git

commit db66a09cb474129119205c28b64618c358af8ce3
Author: Luigi De Masi <[email protected]>
AuthorDate: Sat Oct 10 08:14:43 2026 +0200

    CAMEL-25489: Address semantic CLI review feedback
    
    Group inspection and evaluation under camel semantic, with get as the
    default. Preserve text usage help and failed lookup exit codes, reject
    unsupported runtime versions early, and use tables for human output.
    
    Share request-file handling with the TUI and existing RuntimeHelper
    clients. Add regression coverage, help examples, and updated generated
    command and language documentation.
    
    Co-authored-by: OpenAI Codex <[email protected]>
    Signed-off-by: Luigi De Masi <[email protected]>
---
 .../camel/catalog/docs/semantic-language.adoc      |   3 +
 .../src/main/docs/semantic-language.adoc           |   3 +
 .../modules/ROOT/pages/camel-jbang-managing.adoc   |  35 ++---
 .../ROOT/pages/jbang-commands/camel-jbang-cmd.adoc |   1 -
 .../pages/jbang-commands/camel-jbang-commands.adoc |   1 +
 .../ROOT/pages/jbang-commands/camel-jbang-get.adoc |   1 -
 ...valuate.adoc => camel-jbang-semantic-eval.adoc} |   4 +-
 ...semantic.adoc => camel-jbang-semantic-get.adoc} |   4 +-
 ...get-semantic.adoc => camel-jbang-semantic.adoc} |  14 +-
 .../META-INF/camel-jbang-commands-metadata.json    |   5 +-
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   5 +-
 .../jbang/core/commands/action/CamelSemantic.java  |  51 +++++---
 .../commands/action/SemanticActionCommand.java     |  46 +++----
 .../core/commands/action/SemanticEvaluate.java     |  12 +-
 .../jbang/core/commands/action/SemanticGet.java    |  33 +++++
 .../MissingPluginParameterExceptionHandler.java    |   8 +-
 .../exceptionhandler/UsageErrorHandler.java        |  26 ++++
 .../camel/dsl/jbang/core/common/RuntimeHelper.java |  76 ++++++++---
 .../core/commands/action/SemanticCommandTest.java  | 143 ++++++++++++++++++--
 .../dsl/jbang/core/common/RuntimeHelperTest.java   | 145 +++++++++++++++++++++
 .../jbang/core/commands/tui/MonitorContext.java    |  16 +--
 21 files changed, 514 insertions(+), 118 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/semantic-language.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/semantic-language.adoc
index 9ebafdf2564e..ca271702ccc3 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/semantic-language.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/semantic-language.adoc
@@ -910,6 +910,9 @@ include::partial$language-options.adoc[]
 
 == Developer consoles
 
+Use `camel semantic` to inspect definitions and `camel semantic eval` to 
evaluate them from the
+xref:manual::camel-jbang-managing.adoc#_semantic_definitions_and_evaluation[Camel
 CLI].
+
 `semantic-metadata` exposes definitions and expert contracts without 
performing inference.
 `semantic-evaluate` evaluates a named definition against a sample exchange or 
calls an expert
 operation directly. It uses the configured provider and its normal validation, 
without sending
diff --git 
a/components/camel-ai/camel-semantic/src/main/docs/semantic-language.adoc 
b/components/camel-ai/camel-semantic/src/main/docs/semantic-language.adoc
index 9ebafdf2564e..ca271702ccc3 100644
--- a/components/camel-ai/camel-semantic/src/main/docs/semantic-language.adoc
+++ b/components/camel-ai/camel-semantic/src/main/docs/semantic-language.adoc
@@ -910,6 +910,9 @@ include::partial$language-options.adoc[]
 
 == Developer consoles
 
+Use `camel semantic` to inspect definitions and `camel semantic eval` to 
evaluate them from the
+xref:manual::camel-jbang-managing.adoc#_semantic_definitions_and_evaluation[Camel
 CLI].
+
 `semantic-metadata` exposes definitions and expert contracts without 
performing inference.
 `semantic-evaluate` evaluates a named definition against a sample exchange or 
calls an expert
 operation directly. It uses the configured provider and its normal validation, 
without sending
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-managing.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang-managing.adoc
index 06bf53b9e529..ae6f8f8413c1 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-managing.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-managing.adoc
@@ -283,27 +283,30 @@ TIP: Separate multiple IDs with commas: 
`--id=log1,log2,setBody2`.
 
 The CLI can inspect and try the running application's
 xref:next@components:languages:semantic-language.adoc[semantic definitions and 
experts].
-The application must include `camel-semantic` with the `semantic-metadata` and
-`semantic-evaluate` developer consoles and have the local CLI connector 
enabled.
+The application must run Camel 4.23 or newer, include `camel-semantic` with the
+`semantic-metadata` and `semantic-evaluate` developer consoles, and have the 
local
+CLI connector enabled.
 These commands use the connector; enabling the HTTP console is unnecessary.
 
 [source,bash]
 ----
-camel get semantic my-app
-camel get semantic my-app --json
-camel get semantic my-app --expert=guard
+camel semantic my-app
+camel semantic get my-app
+camel semantic get my-app --json
+camel semantic get my-app --expert=guard
 ----
 
-`get semantic` lists definition names, experts, operations, state selectors, 
result types
-and resolution errors, followed by the known experts and default expert. 
`--expert`
+`camel semantic` defaults to `camel semantic get`. The text output uses tables 
listing
+definition names, experts, operations, state selectors, result types and 
resolution
+errors, followed by the known experts and default expert. `--expert`
 shows one expert's operations, input and result semantics, and parameter 
contract.
-Inspection does not perform inference.
+Inspection does not perform inference. Use `--json` for machine-readable 
output.
 
 Evaluate a named definition with a sample exchange:
 
 [source,bash]
 ----
-camel cmd semantic-evaluate my-app --evaluation=safe \
+camel semantic eval my-app --evaluation=safe \
   --body='Please summarize this message' --header=subject=example \
   --variable=language=en --json
 ----
@@ -312,14 +315,14 @@ The definition's state selector reads the supplied body, 
headers and exchange-lo
 variables. Variables with repository prefixes such as `global:` are rejected. 
The
 sample is evaluated directly; it is not sent through the application's routes.
 
-Alternatively, invoke an expert operation directly. Use `get semantic 
--expert` to
+Alternatively, invoke an expert operation directly. Use `semantic get 
--expert` to
 find the operation names and parameters supported by that expert:
 
 [source,bash]
 ----
-camel cmd semantic-evaluate my-app --expert=guard --operation=detect \
+camel semantic eval my-app --expert=guard --operation=detect \
   --input='Please summarize this message' --json
-camel cmd semantic-evaluate my-app --expert=grader --operation=score \
+camel semantic eval my-app --expert=grader --operation=score \
   --input='Sample answer' 
--parameter='criteria=json:["poor","good","excellent"]' --json
 ----
 
@@ -343,6 +346,8 @@ native inference may continue. Configure provider-specific 
request timeouts as w
 
 Both commands accept an integration name or PID. With no name they select the 
only
 running integration; ambiguous selection fails and lists the matching PIDs.
+Known versions older than 4.23 fail immediately with exit code 3. If the 
runtime version
+is unavailable, the CLI attempts the request and applies the normal timeout.
 Each request has its own reply file, so concurrent commands do not consume each
 other's results.
 
@@ -362,11 +367,11 @@ result fields, such as `error` and `elapsedMillis`. 
Diagnostics go to stderr.
 | 2 | Invalid command arguments or sample values.
 | 3 | No matching integration, ambiguous selection, unavailable semantic 
tooling or expert operations.
 | 4 | No reply before the CLI timeout.
-| 70 | Unexpected internal error; stderr includes the stack trace.
+| 70 | Unexpected local CLI error; stderr includes the stack trace.
 |===
 
-See xref:jbang-commands/camel-jbang-get-semantic.adoc[camel get semantic] and
-xref:jbang-commands/camel-jbang-cmd-semantic-evaluate.adoc[camel cmd 
semantic-evaluate]
+See xref:jbang-commands/camel-jbang-semantic-get.adoc[camel semantic get] and
+xref:jbang-commands/camel-jbang-semantic-eval.adoc[camel semantic eval]
 for the complete option reference.
 
 == Developer Console
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd.adoc
index 943602405d14..e6edad675139 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd.adoc
@@ -35,7 +35,6 @@ camel cmd [options]
 | xref:jbang-commands/camel-jbang-cmd-route-dump.adoc[route-dump] | Dump Camel 
route in XML, YAML, or Java DSL format
 | xref:jbang-commands/camel-jbang-cmd-route-structure.adoc[route-structure] | 
Dump Camel route structure
 | xref:jbang-commands/camel-jbang-cmd-route-topology.adoc[route-topology] | 
Display inter-route topology connections
-| 
xref:jbang-commands/camel-jbang-cmd-semantic-evaluate.adoc[semantic-evaluate] | 
Evaluate a semantic definition or expert operation
 | xref:jbang-commands/camel-jbang-cmd-send.adoc[send] | Send messages to 
endpoints
 | xref:jbang-commands/camel-jbang-cmd-span.adoc[span] | Display OpenTelemetry 
spans from running Camel integrations
 | xref:jbang-commands/camel-jbang-cmd-sql.adoc[sql] | Execute SQL query on a 
DataSource
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 cbf10685bbbd..304f8d109ecd 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
@@ -41,6 +41,7 @@ TIP: You can also use `camel --help` or `camel <command> 
--help` to see availabl
 | xref:jbang-commands/camel-jbang-run.adoc[camel run] | Run as local Camel 
integration
 | xref:jbang-commands/camel-jbang-sbom.adoc[camel sbom] | Generate a CycloneDX 
or SPDX SBOM for a specific project
 | xref:jbang-commands/camel-jbang-script.adoc[camel script] | Run Camel 
integration as shell script for terminal scripting
+| xref:jbang-commands/camel-jbang-semantic.adoc[camel semantic] | List 
semantic definitions and expert contracts
 | xref:jbang-commands/camel-jbang-shell.adoc[camel shell] | Interactive Camel 
CLI shell.
 | xref:jbang-commands/camel-jbang-stop.adoc[camel stop] | Shuts down running 
Camel integrations
 | xref:jbang-commands/camel-jbang-top.adoc[camel top] | Top status of Camel 
integrations
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc
index d15d01264c16..1e4f3601dfc9 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc
@@ -44,7 +44,6 @@ camel get [options]
 | xref:jbang-commands/camel-jbang-get-rest.adoc[rest] | Get REST services of 
Camel integrations
 | xref:jbang-commands/camel-jbang-get-route.adoc[route] | Get status of Camel 
routes
 | xref:jbang-commands/camel-jbang-get-route-controller.adoc[route-controller] 
| List status of route controller
-| xref:jbang-commands/camel-jbang-get-semantic.adoc[semantic] | List semantic 
definitions and expert contracts
 | xref:jbang-commands/camel-jbang-get-service.adoc[service] | Get services of 
Camel integrations
 | xref:jbang-commands/camel-jbang-get-source.adoc[source] | Display Camel 
route source code
 | xref:jbang-commands/camel-jbang-get-sql-trace.adoc[sql-trace] | Get SQL 
query trace data
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-semantic-evaluate.adoc
 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-semantic-eval.adoc
similarity index 94%
rename from 
docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-semantic-evaluate.adoc
rename to 
docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-semantic-eval.adoc
index bdb5e5089a3c..46a10dfd9c03 100644
--- 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-semantic-evaluate.adoc
+++ 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-semantic-eval.adoc
@@ -1,6 +1,6 @@
 
 // AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE
-= camel cmd semantic-evaluate
+= camel semantic eval
 
 Evaluate a semantic definition or expert operation
 
@@ -9,7 +9,7 @@ Evaluate a semantic definition or expert operation
 
 [source,bash]
 ----
-camel cmd semantic-evaluate [options]
+camel semantic eval [options]
 ----
 
 
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-semantic.adoc
 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-semantic-get.adoc
similarity index 91%
copy from 
docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-semantic.adoc
copy to 
docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-semantic-get.adoc
index ba1b6d8db73e..78378a9f5756 100644
--- 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-semantic.adoc
+++ 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-semantic-get.adoc
@@ -1,6 +1,6 @@
 
 // AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE
-= camel get semantic
+= camel semantic get
 
 List semantic definitions and expert contracts
 
@@ -9,7 +9,7 @@ List semantic definitions and expert contracts
 
 [source,bash]
 ----
-camel get semantic [options]
+camel semantic get [options]
 ----
 
 
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-semantic.adoc
 b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-semantic.adoc
similarity index 61%
rename from 
docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-semantic.adoc
rename to 
docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-semantic.adoc
index ba1b6d8db73e..ddfdae7e4972 100644
--- 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-semantic.adoc
+++ 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-semantic.adoc
@@ -1,6 +1,6 @@
 
 // AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE
-= camel get semantic
+= camel semantic
 
 List semantic definitions and expert contracts
 
@@ -9,10 +9,20 @@ List semantic definitions and expert contracts
 
 [source,bash]
 ----
-camel get semantic [options]
+camel semantic [options]
 ----
 
 
+== Subcommands
+
+[cols="2,5",options="header"]
+|===
+| Subcommand | Description
+| xref:jbang-commands/camel-jbang-semantic-eval.adoc[eval] | Evaluate a 
semantic definition or expert operation
+| xref:jbang-commands/camel-jbang-semantic-get.adoc[get] | List semantic 
definitions and expert contracts
+|===
+
+
 
 == Options
 
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
 
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
index 3b83afa8e364..b89a9e4721d0 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
@@ -3,7 +3,7 @@
     { "name": "ask", "fullName": "ask", "description": "Ask a question about a 
running Camel application using AI", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Ask", "options": [ { "names": 
"--api-key", "description": "API key. Also reads ANTHROPIC_API_KEY, 
OPENAI_API_KEY, WATSONX_APIKEY, or LLM_API_KEY env vars", "javaType": 
"java.lang.String", "type": "string" }, { "names": "--api-type", "description": 
"API type: 'ollama', 'openai', 'anthropic', or 'watsonx'", "javaType":  [...]
     { "name": "bind", "fullName": "bind", "description": "DEPRECATED: Bind 
source and sink Kamelets as a new Camel integration", "deprecated": true, 
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.bind.Bind", "options": 
[ { "names": "--error-handler", "description": "Add error handler 
(none|log|sink:<endpoint>). Sink endpoints are expected in the format 
[[apigroup\/]version:]kind:[namespace\/]name, plain Camel URIs or Kamelet 
name.", "javaType": "java.lang.String", "type": "stri [...]
     { "name": "catalog", "fullName": "catalog", "description": "List artifacts 
from Camel Catalog", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.catalog.CatalogCommand", "options": [ 
{ "names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"component", "fullName": "catalog component", "description": "List components 
from the Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.co [...]
-    { "name": "cmd", "fullName": "cmd", "description": "Performs commands in 
the running Camel integrations, such as start\/stop route, or change logging 
levels.", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"browse", "fullName": "cmd browse", "description": "Browse pending messages on 
endpoints [...]
+    { "name": "cmd", "fullName": "cmd", "description": "Performs commands in 
the running Camel integrations, such as start\/stop route, or change logging 
levels.", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"browse", "fullName": "cmd browse", "description": "Browse pending messages on 
endpoints [...]
     { "name": "completion", "fullName": "completion", "description": "Generate 
completion script for bash\/zsh", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Complete", "options": [ { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ] },
     { "name": "config", "fullName": "config", "description": "Get and set user 
configuration values", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.config.ConfigCommand", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get", 
"fullName": "config get", "description": "Display user configuration value", 
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.config. [...]
     { "name": "debug", "fullName": "debug", "description": "Debug local Camel 
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug", 
"options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd 
HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background", "description": "Run in the background", "defaultValue": 
"false", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background-wait", "description": "To  [...]
@@ -15,7 +15,7 @@
     { "name": "eval", "fullName": "eval", "description": "Evaluate Camel 
expressions and scripts", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.EvalCommand", "options": [ { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ], "subcommands": [ { "name": "expression", 
"fullName": "eval expression", "description": "Evaluates Camel expression", 
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.EvalEx [...]
     { "name": "explain", "fullName": "explain", "description": "Explain what a 
Camel route does using AI\/LLM", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Explain", "options": [ { "names": 
"--api-key", "description": "API key for authentication. Also reads 
ANTHROPIC_API_KEY, OPENAI_API_KEY, WATSONX_APIKEY, or LLM_API_KEY env vars", 
"javaType": "java.lang.String", "type": "string" }, { "names": "--api-type", 
"description": "API type: 'ollama', 'openai' (OpenAI-compatible), ' [...]
     { "name": "export", "fullName": "export", "description": "Export to other 
runtimes (Camel Main, Spring Boot, or Quarkus)", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Export", "options": [ { "names": 
"--build-property", "description": "Maven build properties, ex. 
--build-property=prop1=foo", "javaType": "java.util.List", "type": "array" }, { 
"names": "--camel-spring-boot-version", "description": "Camel version to use 
with Spring Boot", "javaType": "java.lang.String", "ty [...]
-    { "name": "get", "fullName": "get", "description": "Get status of Camel 
integrations", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.process.CamelStatus", "options": [ { 
"names": "--watch", "description": "Execute periodically and showing output 
fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ], "subcommands": [ { "name": "activity", 
"fullName": " [...]
+    { "name": "get", "fullName": "get", "description": "Get status of Camel 
integrations", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.process.CamelStatus", "options": [ { 
"names": "--watch", "description": "Execute periodically and showing output 
fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ], "subcommands": [ { "name": "activity", 
"fullName": " [...]
     { "name": "harden", "fullName": "harden", "description": "Suggest security 
hardening for Camel routes using AI\/LLM", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Harden", "options": [ { "names": 
"--api-key", "description": "API key for authentication. Also reads 
OPENAI_API_KEY or LLM_API_KEY env vars", "javaType": "java.lang.String", 
"type": "string" }, { "names": "--api-type", "description": "API type: 'ollama' 
or 'openai' (OpenAI-compatible)", "defaultValue": "ollama", [...]
     { "name": "hawtio", "fullName": "hawtio", "description": "Launch Hawtio 
web console", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.process.Hawtio", "options": [ { 
"names": "--host", "description": "Hostname to bind the Hawtio web console to", 
"defaultValue": "127.0.0.1", "javaType": "java.lang.String", "type": "string" 
}, { "names": "--openUrl", "description": "To automatic open Hawtio web console 
in the web browser", "defaultValue": "true", "javaType": "boolean", "type": 
[...]
     { "name": "infra", "fullName": "infra", "description": "List and Run 
external services for testing and prototyping", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.infra.InfraCommand", "options": [ { 
"names": "--json", "description": "Output in JSON Format", "javaType": 
"boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display 
the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], 
"subcommands": [ { "name": "get", "fullName": "infra  [...]
@@ -30,6 +30,7 @@
     { "name": "run", "fullName": "run", "description": "Run as local Camel 
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Run", 
"options": [ { "names": "--background", "description": "Run in the background", 
"defaultValue": "false", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background-wait", "description": "To wait for run in background to startup 
successfully, before returning", "defaultValue": "true", "javaType": "boolean", 
"type": "boolean" }, { [...]
     { "name": "sbom", "fullName": "sbom", "description": "Generate a CycloneDX 
or SPDX SBOM for a specific project", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.SBOMGenerator", "options": [ { 
"names": "--build-property", "description": "Maven build properties, ex. 
--build-property=prop1=foo", "javaType": "java.util.List", "type": "array" }, { 
"names": "--camel-spring-boot-version", "description": "Camel version to use 
with Spring Boot", "javaType": "java.lang.String", "type" [...]
     { "name": "script", "fullName": "script", "description": "Run Camel 
integration as shell script for terminal scripting", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Script", "options": [ { "names": 
"--logging", "description": "Can be used to turn on logging (logs to file in 
<user home>\/.camel directory)", "defaultValue": "false", "javaType": 
"boolean", "type": "boolean" }, { "names": "--logging-level", "description": 
"Logging level (ERROR, WARN, INFO, DEBUG, TRACE)", "d [...]
+    { "name": "semantic", "fullName": "semantic", "description": "List 
semantic definitions and expert contracts", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.action.CamelSemantic", "options": [ { 
"names": "--expert", "description": "Show the operations and parameter contract 
of this expert", "javaType": "java.lang.String", "type": "string" }, { "names": 
"--json", "description": "Output a single JSON document", "javaType": 
"boolean", "type": "boolean" }, { "names": "--timeou [...]
     { "name": "shell", "fullName": "shell", "description": "Interactive Camel 
CLI shell.", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Shell", 
"options": [ { "names": "-h,--help", "description": "Display the help and 
sub-commands", "javaType": "boolean", "type": "boolean" } ] },
     { "name": "stop", "fullName": "stop", "description": "Shuts down running 
Camel integrations", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.process.StopProcess", "options": [ { 
"names": "--kill", "description": "To force killing the process (SIGKILL)", 
"javaType": "boolean", "type": "boolean" }, { "names": "-h,--help", 
"description": "Display the help and sub-commands", "javaType": "boolean", 
"type": "boolean" } ] },
     { "name": "top", "fullName": "top", "description": "Top status of Camel 
integrations", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.process.CamelTop", "options": [ { 
"names": "--watch", "description": "Execute periodically and showing output 
fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ], "subcommands": [ { "name": "context", 
"fullName": "top  [...]
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 6b2d2bee8eca..41b63bd7a972 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
@@ -123,7 +123,6 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("route-dump", new CommandLine(new 
CamelRouteDumpAction(this)))
                         .addSubcommand("route-structure", new CommandLine(new 
CamelRouteStructureAction(this)))
                         .addSubcommand("route-topology", new CommandLine(new 
CamelRouteTopologyAction(this)))
-                        .addSubcommand("semantic-evaluate", new 
CommandLine(new SemanticEvaluate(this)))
                         .addSubcommand("send", new CommandLine(new 
CamelSendAction(this)))
                         .addSubcommand("sql", new CommandLine(new 
CamelSqlQueryAction(this)))
                         .addSubcommand("span", new CommandLine(new 
CamelSpanAction(this)))
@@ -184,7 +183,6 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("rest", new CommandLine(new 
ListRest(this)))
                         .addSubcommand("route", new CommandLine(new 
CamelRouteStatus(this)))
                         .addSubcommand("route-controller", new CommandLine(new 
RouteControllerAction(this)))
-                        .addSubcommand("semantic", new CommandLine(new 
CamelSemantic(this)))
                         .addSubcommand("service", new CommandLine(new 
ListService(this)))
                         .addSubcommand("source", new CommandLine(new 
CamelSourceAction(this)))
                         .addSubcommand("sql-trace", new CommandLine(new 
ListSqlTrace(this)))
@@ -215,6 +213,9 @@ public class CamelJBangMain implements Callable<Integer> {
                 .addSubcommand("run", new CommandLine(new Run(this)))
                 .addSubcommand("sbom", new CommandLine(new 
SBOMGenerator(this)))
                 .addSubcommand("script", new CommandLine(new Script(this)))
+                .addSubcommand("semantic", new CommandLine(new 
CamelSemantic(this))
+                        .addSubcommand("get", new CommandLine(new 
SemanticGet(this)))
+                        .addSubcommand("eval", new CommandLine(new 
SemanticEvaluate(this))))
                 .addSubcommand("shell", new CommandLine(new Shell(this)))
                 .addSubcommand("stop", new CommandLine(new StopProcess(this)))
                 .addSubcommand("top", new CommandLine(new CamelTop(this))
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSemantic.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSemantic.java
index b9c167ba7ee1..8f6a22246022 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSemantic.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelSemantic.java
@@ -16,6 +16,12 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.action;
 
+import java.util.List;
+import java.util.stream.IntStream;
+
+import com.github.freva.asciitable.AsciiTable;
+import com.github.freva.asciitable.Column;
+import com.github.freva.asciitable.HorizontalAlign;
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
@@ -23,7 +29,12 @@ import org.apache.camel.util.json.Jsoner;
 import picocli.CommandLine;
 
 @CommandLine.Command(name = "semantic", description = "List semantic 
definitions and expert contracts",
-                     sortOptions = false, showDefaultValues = true)
+                     sortOptions = false, showDefaultValues = true,
+                     footer = {
+                             "%nExamples:",
+                             "  camel semantic my-app",
+                             "  camel semantic get my-app --expert=guard",
+                             "  camel semantic eval my-app --evaluation=safe 
--body='Sample message' --json" })
 public class CamelSemantic extends SemanticActionCommand {
 
     @CommandLine.Option(names = "--expert", description = "Show the operations 
and parameter contract of this expert")
@@ -62,34 +73,36 @@ public class CamelSemantic extends SemanticActionCommand {
     @Override
     protected void render(JsonObject response) {
         if (expert != null) {
-            printer().println("Expert: " + expert);
-            renderOperations(response.getCollection("operations"));
+            JsonArray operations = response.getCollection("operations");
+            if (operations != null) {
+                printer().println("Expert: " + expert);
+                renderOperations(operations);
+            }
             return;
         }
         printer().println("Definitions:");
-        printer().println("NAME\tEXPERT\tOPERATION\tSTATE\tRESULT 
TYPE\tERROR");
-        JsonArray evaluations = response.getCollection("evaluations");
-        if (evaluations != null) {
-            for (int i = 0; i < evaluations.size(); i++) {
-                JsonObject value = evaluations.getMap(i);
-                printer().println(String.join("\t", text(value, "name"), 
text(value, "expert"), text(value, "operation"),
-                        text(value, "state"), text(value, "resultType"), 
text(value, "error")));
-            }
-        }
+        printer().println(AsciiTable.getTable(AsciiTable.NO_BORDERS, 
rows(response.getCollection("evaluations")), List.of(
+                new 
Column().header("NAME").dataAlign(HorizontalAlign.LEFT).with(r -> text(r, 
"name")),
+                new 
Column().header("EXPERT").dataAlign(HorizontalAlign.LEFT).with(r -> text(r, 
"expert")),
+                new 
Column().header("OPERATION").dataAlign(HorizontalAlign.LEFT).with(r -> text(r, 
"operation")),
+                new 
Column().header("STATE").dataAlign(HorizontalAlign.LEFT).with(r -> text(r, 
"state")),
+                new Column().header("RESULT 
TYPE").dataAlign(HorizontalAlign.LEFT).with(r -> text(r, "resultType")),
+                new 
Column().header("ERROR").dataAlign(HorizontalAlign.LEFT).with(r -> text(r, 
"error")))));
         printer().println("Experts:");
-        JsonArray experts = response.getCollection("experts");
-        if (experts != null) {
-            for (int i = 0; i < experts.size(); i++) {
-                JsonObject value = experts.getMap(i);
-                printer().println(text(value, "reference") + "\t" + 
text(value, "name") + "\t" + text(value, "error"));
-            }
-        }
+        printer().println(AsciiTable.getTable(AsciiTable.NO_BORDERS, 
rows(response.getCollection("experts")), List.of(
+                new 
Column().header("REFERENCE").dataAlign(HorizontalAlign.LEFT).with(r -> text(r, 
"reference")),
+                new 
Column().header("NAME").dataAlign(HorizontalAlign.LEFT).with(r -> text(r, 
"name")),
+                new 
Column().header("ERROR").dataAlign(HorizontalAlign.LEFT).with(r -> text(r, 
"error")))));
         printer().println("Default expert: " + text(response, 
"defaultExpert"));
         if (response.get("defaultError") != null) {
             printer().println("Default expert error: " + 
response.get("defaultError"));
         }
     }
 
+    private static List<JsonObject> rows(JsonArray values) {
+        return values == null ? List.of() : IntStream.range(0, 
values.size()).<JsonObject> mapToObj(values::getMap).toList();
+    }
+
     private void renderOperations(JsonArray operations) {
         for (int i = 0; i < operations.size(); i++) {
             JsonObject operation = operations.getMap(i);
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticActionCommand.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticActionCommand.java
index f142c1ec01eb..6f0f9dff6d3b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticActionCommand.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticActionCommand.java
@@ -16,19 +16,17 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.action;
 
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
 import java.util.Arrays;
 import java.util.List;
-import java.util.UUID;
 
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
-import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
+import 
org.apache.camel.dsl.jbang.core.commands.exceptionhandler.UsageErrorHandler;
+import org.apache.camel.dsl.jbang.core.common.RuntimeHelper;
+import org.apache.camel.dsl.jbang.core.common.VersionHelper;
 import org.apache.camel.util.json.JsonObject;
 import picocli.CommandLine;
 
-public abstract class SemanticActionCommand extends ActionBaseCommand {
+public abstract class SemanticActionCommand extends ActionBaseCommand 
implements UsageErrorHandler {
 
     @CommandLine.Parameters(description = "Name or pid of a running Camel 
integration", arity = "0..1")
     String name = "*";
@@ -66,7 +64,16 @@ public abstract class SemanticActionCommand extends 
ActionBaseCommand {
             if (pids.size() != 1) {
                 return error(3, "Multiple Camel integrations match " + name + 
": " + pids + ". Specify a pid.", null);
             }
-            JsonObject response = execute(pids.get(0), request);
+            long pid = pids.get(0);
+            JsonObject status = loadStatus(pid);
+            JsonObject context = status == null ? null : 
status.getMap("context");
+            String version = context == null ? null : 
context.getString("version");
+            if (version != null && version.matches("\\d+\\.\\d+(?:\\..*)?") && 
!VersionHelper.isGE(version, "4.23")) {
+                return error(3, "Semantic tooling requires Camel 4.23 or 
newer; integration " + pid
+                                + " runs Camel " + version,
+                        null);
+            }
+            JsonObject response = RuntimeHelper.executeAction(pid, request, 
timeout);
             if (response == null) {
                 return error(4, "No reply from integration within " + timeout 
+ " milliseconds", null);
             }
@@ -74,7 +81,7 @@ public abstract class SemanticActionCommand extends 
ActionBaseCommand {
                 return error(3, "Semantic tooling is unavailable; the 
integration needs a matching camel-semantic version",
                         null);
             }
-            if ("failed".equals(response.getString("status")) || 
"error".equals(response.getString("status"))) {
+            if ("failed".equals(response.getString("status"))) {
                 String message = response.getString("error");
                 if (message == null || message.isBlank()) {
                     message = "Semantic action failed";
@@ -95,28 +102,11 @@ public abstract class SemanticActionCommand extends 
ActionBaseCommand {
                 && 
ProcessHandle.of(pid).filter(ProcessHandle::isAlive).isPresent();
     }
 
-    private JsonObject execute(long pid, JsonObject request) throws Exception {
-        String id = UUID.randomUUID().toString();
-        Path directory = CommandLineHelper.getCamelDir();
-        Path action = directory.resolve(pid + "-action-" + id + ".json");
-        Path output = directory.resolve(pid + "-output-" + id + ".json");
-        Path temporary = directory.resolve(pid + "-" + id + ".tmp");
-        try {
-            Files.writeString(temporary, request.toJson());
-            Files.move(temporary, action, StandardCopyOption.ATOMIC_MOVE);
-            return getJsonObject(output, timeout);
-        } finally {
-            // Removing an unfinished request asks the connector to cancel the 
evaluation.
-            Files.deleteIfExists(action);
-            Files.deleteIfExists(output);
-            Files.deleteIfExists(temporary);
-        }
-    }
-
-    public int usageError(String message, String[] args) {
+    @Override
+    public Integer usageError(String message, String[] args) {
         json = Arrays.stream(args).takeWhile(arg -> !"--".equals(arg))
                 .anyMatch(arg -> "--json".equals(arg) || 
"--json=true".equals(arg));
-        return error(2, message, null);
+        return json ? error(2, message, null) : null;
     }
 
     protected int error(int code, String message, JsonObject result) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticEvaluate.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticEvaluate.java
index b58777a3849f..cee694a82653 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticEvaluate.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticEvaluate.java
@@ -29,10 +29,15 @@ import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
 import picocli.CommandLine;
 
[email protected](name = "semantic-evaluate", description = "Evaluate a 
semantic definition or expert operation",
-                     sortOptions = false, showDefaultValues = true)
[email protected](name = "eval", description = "Evaluate a semantic 
definition or expert operation",
+                     sortOptions = false, showDefaultValues = true,
+                     footer = {
+                             "%nExamples:",
+                             "  camel semantic eval my-app --evaluation=safe 
--body='Sample message' --json",
+                             "  camel semantic eval my-app --expert=guard 
--operation=detect --input='Sample message'" })
 public class SemanticEvaluate extends SemanticActionCommand {
 
+    // Jsoner accepts trailing commas; command input must use strict JSON 
parsing.
     private static final ObjectReader JSON_VALUE = new 
ObjectMapper().readerFor(Object.class)
             .with(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
 
@@ -98,6 +103,9 @@ public class SemanticEvaluate extends SemanticActionCommand {
                 request.put("variables", sampleVariables);
             }
         } else {
+            if (expert == null && operation == null && input == null) {
+                throw new IllegalArgumentException("Choose 
--evaluation=<name>, or --expert with --operation and --input");
+            }
             requireText(expert, "--expert");
             requireText(operation, "--operation");
             if (input == null) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticGet.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticGet.java
new file mode 100644
index 000000000000..cc4212b27aef
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticGet.java
@@ -0,0 +1,33 @@
+/*
+ * 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.action;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import picocli.CommandLine;
+
[email protected](name = "get", description = "List semantic definitions 
and expert contracts",
+                     sortOptions = false, showDefaultValues = true,
+                     footer = {
+                             "%nExamples:",
+                             "  camel semantic get my-app --json",
+                             "  camel semantic get my-app --expert=guard" })
+public class SemanticGet extends CamelSemantic {
+
+    public SemanticGet(CamelJBangMain main) {
+        super(main);
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/exceptionhandler/MissingPluginParameterExceptionHandler.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/exceptionhandler/MissingPluginParameterExceptionHandler.java
index 2d16706bcde5..cd7c46624b7a 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/exceptionhandler/MissingPluginParameterExceptionHandler.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/exceptionhandler/MissingPluginParameterExceptionHandler.java
@@ -19,7 +19,6 @@ package 
org.apache.camel.dsl.jbang.core.commands.exceptionhandler;
 import java.io.PrintWriter;
 import java.util.Optional;
 
-import org.apache.camel.dsl.jbang.core.commands.action.SemanticActionCommand;
 import org.apache.camel.dsl.jbang.core.common.PluginHelper;
 import org.apache.camel.dsl.jbang.core.common.PluginType;
 import picocli.CommandLine;
@@ -34,8 +33,11 @@ public class MissingPluginParameterExceptionHandler 
implements IParameterExcepti
     public int handleParseException(ParameterException ex, String[] args) 
throws Exception {
         CommandLine cmd = ex.getCommandLine();
         PrintWriter err = cmd.getErr();
-        if (cmd.getCommand() instanceof SemanticActionCommand semantic) {
-            return semantic.usageError(ex.getMessage(), args);
+        if (cmd.getCommand() instanceof UsageErrorHandler handler) {
+            Integer exitCode = handler.usageError(ex.getMessage(), args);
+            if (exitCode != null) {
+                return exitCode;
+            }
         }
 
         if (ex.getMessage().startsWith("Unmatched argument at index 0") && 
args.length > 0) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/exceptionhandler/UsageErrorHandler.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/exceptionhandler/UsageErrorHandler.java
new file mode 100644
index 000000000000..9a3787b73735
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/exceptionhandler/UsageErrorHandler.java
@@ -0,0 +1,26 @@
+/*
+ * 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.exceptionhandler;
+
+/** Optional command-specific rendering of usage errors. */
+public interface UsageErrorHandler {
+
+    /**
+     * @return the exit code when handled, or {@code null} to use the standard 
picocli diagnostics
+     */
+    Integer usageError(String message, String[] args);
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelper.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelper.java
index a492e4e57d72..4ed9da5462d7 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelper.java
@@ -17,12 +17,15 @@
 package org.apache.camel.dsl.jbang.core.common;
 
 import java.io.File;
+import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 import org.apache.camel.support.PatternHelper;
 import org.apache.camel.util.FileUtil;
@@ -37,7 +40,7 @@ import org.apache.camel.util.json.Jsoner;
  * to {@code {pid}-action-{requestId}.json} and reading the response from 
{@code {pid}-output-{requestId}.json}. Each
  * request gets a unique ID so concurrent callers (CLI, MCP server, etc.) 
don't interfere with each other.
  * <p>
- * This class is used by both the {@code camel ask} CLI command and the MCP 
server's {@code RuntimeService}.
+ * This class is used by CLI commands, the TUI and the MCP server's {@code 
RuntimeService}.
  *
  * @since 4.21
  */
@@ -187,39 +190,74 @@ public final class RuntimeHelper {
     }
 
     public static String executeAction(long pid, String action, 
Consumer<JsonObject> configure, long timeoutMs) {
-        String requestId = UUID.randomUUID().toString().substring(0, 8);
-        Path camelDir = CommandLineHelper.getCamelDir();
-        Path outputFile = camelDir.resolve(pid + "-output-" + requestId + 
".json");
-        PathUtils.deleteFile(outputFile);
-
-        JsonObject root = new JsonObject();
-        root.put("action", action);
+        JsonObject request = new JsonObject();
+        request.put("action", action);
         if (configure != null) {
-            configure.accept(root);
+            configure.accept(request);
+        }
+        try {
+            String response = executeAction(pid, request, timeoutMs, 
Function.identity());
+            if (response != null) {
+                return response;
+            }
+        } catch (IOException e) {
+            // Preserve the existing text API's diagnostic when the request 
cannot be exchanged.
         }
+        return "Timeout waiting for response from PID " + pid + " for action: 
" + action;
+    }
 
-        Path actionFile = camelDir.resolve(pid + "-action-" + requestId + 
".json");
-        PathUtils.writeTextSafely(root.toJson(), actionFile);
+    /**
+     * Executes a request and waits for a complete JSON object. Each request 
uses independent files, published
+     * atomically, and removing an unfinished request asks the connector to 
cancel it.
+     *
+     * @return             the response, or {@code null} on timeout or 
interruption
+     * @throws IOException if publishing or cleaning up the request fails
+     */
+    public static JsonObject executeAction(long pid, JsonObject request, long 
timeoutMs) throws IOException {
+        return executeAction(pid, request, timeoutMs, text -> 
Jsoner.deserialize(text, (JsonObject) null));
+    }
 
+    private static <T> T executeAction(long pid, JsonObject request, long 
timeoutMs, Function<String, T> reader)
+            throws IOException {
+        String requestId = UUID.randomUUID().toString();
+        Path directory = CommandLineHelper.getCamelDir();
+        Path actionFile = directory.resolve(pid + "-action-" + requestId + 
".json");
+        Path outputFile = directory.resolve(pid + "-output-" + requestId + 
".json");
+        Path temporary = directory.resolve(pid + "-" + requestId + ".tmp");
         try {
+            Files.writeString(temporary, request.toJson());
+            Files.move(temporary, actionFile, StandardCopyOption.ATOMIC_MOVE);
             StopWatch watch = new StopWatch();
             while (watch.taken() < timeoutMs) {
                 try {
-                    Thread.sleep(POLL_INTERVAL_MS);
-                    if (Files.exists(outputFile) && 
outputFile.toFile().length() > 0) {
-                        return Files.readString(outputFile);
+                    Thread.sleep(Math.min(POLL_INTERVAL_MS, Math.max(1, 
timeoutMs - watch.taken())));
+                    if (Files.exists(outputFile)) {
+                        String text = Files.readString(outputFile);
+                        if (!text.isEmpty()) {
+                            T response = reader.apply(text);
+                            if (response != null) {
+                                return response;
+                            }
+                        }
                     }
                 } catch (InterruptedException e) {
                     Thread.currentThread().interrupt();
                     break;
-                } catch (Exception e) {
-                    // retry
+                } catch (IOException e) {
+                    // The runtime writes output in place; retry until the 
response is complete.
                 }
             }
-            return "Timeout waiting for response from PID " + pid + " for 
action: " + action;
+            return null;
         } finally {
-            PathUtils.deleteFile(outputFile);
-            PathUtils.deleteFile(actionFile);
+            try {
+                Files.deleteIfExists(actionFile);
+            } finally {
+                try {
+                    Files.deleteIfExists(outputFile);
+                } finally {
+                    Files.deleteIfExists(temporary);
+                }
+            }
         }
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticCommandTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticCommandTest.java
index 2ee93a075b0a..b6329a9a869f 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticCommandTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/SemanticCommandTest.java
@@ -27,8 +27,10 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Function;
+import java.util.function.IntSupplier;
 
 import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
 import 
org.apache.camel.dsl.jbang.core.commands.exceptionhandler.MissingPluginParameterExceptionHandler;
@@ -42,6 +44,7 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 import org.junit.jupiter.params.provider.ValueSource;
 import picocli.CommandLine;
 
@@ -94,7 +97,10 @@ class SemanticCommandTest {
         }));
         assertEquals("semantic-metadata", request.get().get("action"));
         assertEquals(true, request.get().get("overview"));
-        
assertTrue(printer.getOutput().contains("safe\tguard\tdetect\t${body}\tboolean\tdefinition
 error"));
+        assertTrue(printer.getOutput().contains("RESULT TYPE"));
+        assertTrue(printer.getOutput().lines().anyMatch(line -> 
line.trim().matches(
+                "safe +guard +detect +\\$\\{body} +boolean +definition 
error")));
+        assertFalse(printer.getOutput().contains("\t"));
         assertTrue(printer.getOutput().contains("Safety guard"));
         assertTrue(printer.getOutput().contains("expert error"));
         assertTrue(printer.getOutput().contains("No default expert"));
@@ -133,6 +139,33 @@ class SemanticCommandTest {
         assertError(3, "unknown");
     }
 
+    @ParameterizedTest
+    @ValueSource(booleans = { false, true })
+    void failedExpertLookupRetainsFailureExitCode(boolean json) throws 
Exception {
+        assertEquals(1, respond(new CamelSemantic(main()), r -> jsonUnchecked(
+                "{\"status\":\"failed\",\"error\":\"metadata dispatch 
failed\"}"),
+                "--expert=guard", "--json=" + json));
+        assertFalse(errors.toString().contains("NullPointerException"));
+        if (json) {
+            assertError(1, "metadata dispatch failed");
+        } else {
+            assertEquals("metadata dispatch failed" + System.lineSeparator(), 
errors.toString());
+            assertEquals("", printer.getOutput());
+        }
+    }
+
+    @ParameterizedTest
+    @ValueSource(booleans = { false, true })
+    void textParseErrorsKeepSuggestionsSynopsisAndHelp(boolean evaluate) 
throws Exception {
+        SemanticActionCommand action = evaluate ? new SemanticEvaluate(main()) 
: new CamelSemantic(main());
+        assertEquals(2, command(action).execute("--exper=guard"));
+        assertTrue(errors.toString().contains("--expert"));
+        assertTrue(errors.toString().contains("Usage:"));
+        assertTrue(errors.toString().contains("--help'"));
+        assertEquals("", printer.getOutput());
+        assertTrue(actionFiles().isEmpty());
+    }
+
     @Test
     void evaluatesNamedDefinitionWithSampleExchangeWithoutDoubleEscaping() 
throws Exception {
         String body = "A \"quote\"\nwith a backslash \\ and = sign";
@@ -219,7 +252,7 @@ class SemanticCommandTest {
     void textUsageErrorsGoOnlyToStderr() throws Exception {
         assertEquals(2, command(new 
SemanticEvaluate(main())).execute(Long.toString(pid)));
         assertEquals("", printer.getOutput());
-        assertTrue(errors.toString().contains("--expert is required"));
+        assertTrue(errors.toString().contains("Choose --evaluation=<name>, or 
--expert with --operation and --input"));
         assertTrue(actionFiles().isEmpty());
     }
 
@@ -332,8 +365,12 @@ class SemanticCommandTest {
         CamelJBangMain main = new CamelJBangMain() {
             @Override
             public void postAddCommands(CommandLine command, String[] args) {
-                
assertNotNull(command.getSubcommands().get("get").getSubcommands().get("semantic"));
-                
assertNotNull(command.getSubcommands().get("cmd").getSubcommands().get("semantic-evaluate"));
+                CommandLine semantic = 
command.getSubcommands().get("semantic");
+                assertNotNull(semantic);
+                assertNotNull(semantic.getSubcommands().get("get"));
+                assertNotNull(semantic.getSubcommands().get("eval"));
+                
assertFalse(command.getSubcommands().get("get").getSubcommands().containsKey("semantic"));
+                
assertFalse(command.getSubcommands().get("cmd").getSubcommands().containsKey("semantic-evaluate"));
             }
 
             @Override
@@ -347,10 +384,94 @@ class SemanticCommandTest {
             }
         };
         main.withPrinter(printer);
-        main.execute("cmd", "semantic-evaluate", "--bad-option", "--json");
+        main.execute("semantic", "eval", "--bad-option", "--json");
         assertError(2, "Unknown option");
     }
 
+    @ParameterizedTest
+    @CsvSource({ "false, true", "false, false", "true, true", "true, false" })
+    void jsonParseErrorsWorkBeforeAndAfterInvalidOption(boolean evaluate, 
boolean jsonFirst) throws Exception {
+        SemanticActionCommand action = evaluate ? new SemanticEvaluate(main()) 
: new CamelSemantic(main());
+        String[] args = jsonFirst
+                ? new String[] { "--json", "--exper=guard" }
+                : new String[] { "--exper=guard", "--json" };
+        assertEquals(2, command(action).execute(args));
+        assertError(2, "Unknown option");
+        assertFalse(errors.toString().contains("Usage:"));
+        assertTrue(actionFiles().isEmpty());
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = { "--json=false", "--" })
+    void textParseErrorsDoNotEnableJson(String option) throws Exception {
+        assertEquals(2, command(new 
SemanticEvaluate(main())).execute("--exper=guard", option,
+                "--".equals(option) ? "--json" : "--json=false"));
+        assertTrue(errors.toString().contains("Usage:"));
+        assertEquals("", printer.getOutput());
+        assertTrue(actionFiles().isEmpty());
+    }
+
+    @ParameterizedTest
+    @CsvSource({ "3.22.0,false", "4.9.9,true", "4.21.0,false", "4.22.1,true", 
"4.22.0.redhat-00001,false" })
+    void rejectsOlderRuntimesWithoutPublishingRequest(String version, boolean 
evaluate) throws Exception {
+        writeVersion(version);
+        SemanticActionCommand action = evaluate ? new SemanticEvaluate(main()) 
: new CamelSemantic(main());
+        String[] args = evaluate
+                ? new String[] { Long.toString(pid), "--evaluation=safe", 
"--json" }
+                : new String[] { Long.toString(pid), "--json" };
+        assertEquals(3, command(action).execute(args));
+        assertError(3, "requires Camel 4.23 or newer");
+        assertTrue(errors.toString().contains(version));
+        assertTrue(actionFiles().isEmpty());
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = { "4.23.0", "4.23.0-SNAPSHOT", 
"4.23.0.redhat-00001", "4.24.0", "5.0.0", "unknown" })
+    void acceptsSupportedOrUnknownRuntimeVersions(String version) throws 
Exception {
+        writeVersion(version);
+        assertEquals(0, respond(new CamelSemantic(main()), r -> 
jsonUnchecked("{\"evaluations\":[]}"), "--json"));
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = { "", "get", "eval" })
+    void mainDispatchesSemanticGroupIncludingDefaultGet(String subcommand) 
throws Exception {
+        AtomicInteger exitCode = new AtomicInteger(-1);
+        CamelJBangMain main = new CamelJBangMain() {
+            @Override
+            public void preExecute(CommandLine command, String[] args) {
+                command.setErr(new PrintWriter(errors, true));
+            }
+
+            @Override
+            public void quit(int code) {
+                exitCode.set(code);
+            }
+        };
+        main.withPrinter(printer);
+        List<String> args = new ArrayList<>(List.of("semantic"));
+        if (!subcommand.isEmpty()) {
+            args.add(subcommand);
+        }
+        args.add(Long.toString(pid));
+        args.add("--json");
+        if ("eval".equals(subcommand)) {
+            args.add("--evaluation=safe");
+        }
+        assertEquals(0, respond(() -> {
+            main.execute(args.toArray(String[]::new));
+            return exitCode.get();
+        }, request -> {
+            assertEquals("eval".equals(subcommand) ? "semantic-evaluate" : 
"semantic-metadata", request.get("action"));
+            return 
jsonUnchecked("{\"status\":\"success\",\"evaluations\":[]}");
+        }));
+        assertEquals("success", json(printer.getOutput()).get("status"));
+    }
+
+    private void writeVersion(String version) throws Exception {
+        Files.writeString(CommandLineHelper.getCamelDir().resolve(pid + 
"-status.json"),
+                
"{\"context\":{\"name\":\"semantic-test\",\"phase\":4,\"version\":\"" + version 
+ "\"}}");
+    }
+
     private CamelJBangMain main() {
         return new CamelJBangMain().withPrinter(printer);
     }
@@ -362,6 +483,12 @@ class SemanticCommandTest {
 
     private int respond(SemanticActionCommand command, Function<JsonObject, 
JsonObject> response, String... options)
             throws Exception {
+        List<String> args = new ArrayList<>(List.of(options));
+        args.add(Long.toString(pid));
+        return respond(() -> 
command(command).execute(args.toArray(String[]::new)), response);
+    }
+
+    private int respond(IntSupplier call, Function<JsonObject, JsonObject> 
response) throws Exception {
         var responder = CompletableFuture.runAsync(() -> {
             try {
                 await().atMost(5, TimeUnit.SECONDS).until(() -> 
!actionFiles().isEmpty());
@@ -372,9 +499,7 @@ class SemanticCommandTest {
                 throw new RuntimeException(e);
             }
         }, executor);
-        List<String> args = new ArrayList<>(List.of(options));
-        args.add(Long.toString(pid));
-        int result = command(command).execute(args.toArray(String[]::new));
+        int result = call.getAsInt();
         responder.get(5, TimeUnit.SECONDS);
         assertTrue(actionFiles().isEmpty());
         return result;
@@ -395,7 +520,7 @@ class SemanticCommandTest {
         JsonObject result = jsonUnchecked(printer.getOutput());
         assertEquals("error", result.get("status"));
         assertEquals(code, result.getInteger("code"));
-        assertTrue(result.getString("message").contains(message));
+        assertTrue(result.getString("message").contains(message), 
result.toJson());
         assertFalse(errors.toString().isBlank());
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelperTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelperTest.java
new file mode 100644
index 000000000000..967152e936cd
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/RuntimeHelperTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.common;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class RuntimeHelperTest {
+
+    @TempDir
+    Path home;
+    private Path previousHome;
+    private ExecutorService executor;
+
+    @BeforeEach
+    void setup() throws IOException {
+        previousHome = CommandLineHelper.getHomeDir();
+        CommandLineHelper.useHomeDir(home.toString());
+        Files.createDirectories(CommandLineHelper.getCamelDir());
+        executor = Executors.newSingleThreadExecutor();
+    }
+
+    @AfterEach
+    void cleanup() throws InterruptedException {
+        executor.shutdownNow();
+        assertTrue(executor.awaitTermination(5, TimeUnit.SECONDS));
+        CommandLineHelper.useHomeDir(previousHome.toString());
+    }
+
+    @Test
+    void textApiPreservesRawResponseAndRequestConfiguration() throws Exception 
{
+        var result = CompletableFuture.supplyAsync(() -> 
RuntimeHelper.executeAction(123, "route",
+                request -> request.put("id", "route1"), 5000), executor);
+        Path action = requestFile();
+        JsonObject request = (JsonObject) 
Jsoner.deserialize(Files.readString(action));
+        assertEquals("route", request.get("action"));
+        assertEquals("route1", request.get("id"));
+        String raw = "raw response\n  with whitespace\n";
+        Files.writeString(outputFile(action), raw);
+        assertEquals(raw, result.get(5, TimeUnit.SECONDS));
+        assertNoRequestFiles();
+    }
+
+    @Test
+    void textApiPreservesTimeoutDiagnostic() throws Exception {
+        assertEquals("Timeout waiting for response from PID 123 for action: 
route",
+                RuntimeHelper.executeAction(123, "route", null, 100));
+        assertNoRequestFiles();
+    }
+
+    @Test
+    void jsonApiWaitsForCompleteResponse() throws Exception {
+        JsonObject request = new JsonObject();
+        request.put("action", "semantic-metadata");
+        var result = executor.submit(() -> RuntimeHelper.executeAction(123, 
request, 5000));
+        Path action = requestFile();
+        assertEquals(request, Jsoner.deserialize(Files.readString(action)));
+        Path output = outputFile(action);
+        Files.writeString(output, "{\"operations\":[");
+        await().during(250, TimeUnit.MILLISECONDS).atMost(1, 
TimeUnit.SECONDS).until(() -> !result.isDone());
+        Files.writeString(output, "{\"operations\":[]}");
+        assertTrue(result.get(5, 
TimeUnit.SECONDS).getCollection("operations").isEmpty());
+        assertNoRequestFiles();
+    }
+
+    @Test
+    void interruptionCancelsRequestAndPreservesInterruptFlag() throws 
Exception {
+        AtomicReference<Thread> worker = new AtomicReference<>();
+        var result = executor.submit(() -> {
+            worker.set(Thread.currentThread());
+            JsonObject response = RuntimeHelper.executeAction(123, new 
JsonObject(), 5000);
+            assertTrue(Thread.currentThread().isInterrupted());
+            return response;
+        });
+        Path request = requestFile();
+        worker.get().interrupt();
+        assertNull(result.get(5, TimeUnit.SECONDS));
+        assertFalse(Files.exists(request));
+        assertNoRequestFiles();
+    }
+
+    @Test
+    void jsonApiPropagatesPublicationFailureWhileTextApiKeepsItsDiagnostic() {
+        CommandLineHelper.useHomeDir(home.resolve("missing").toString());
+        assertThrows(IOException.class, () -> RuntimeHelper.executeAction(123, 
new JsonObject(), 100));
+        assertEquals("Timeout waiting for response from PID 123 for action: 
route",
+                RuntimeHelper.executeAction(123, "route", null, 100));
+    }
+
+    private Path requestFile() throws IOException {
+        await().atMost(5, TimeUnit.SECONDS).until(() -> !requests().isEmpty());
+        return requests().get(0);
+    }
+
+    private List<Path> requests() throws IOException {
+        try (var files = Files.list(CommandLineHelper.getCamelDir())) {
+            return files.filter(p -> 
p.getFileName().toString().startsWith("123-action-")).toList();
+        }
+    }
+
+    private Path outputFile(Path action) {
+        return 
action.resolveSibling(action.getFileName().toString().replace("-action-", 
"-output-"));
+    }
+
+    private void assertNoRequestFiles() throws IOException {
+        try (var files = Files.list(CommandLineHelper.getCamelDir())) {
+            assertEquals(0, files.count());
+        }
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
index f97b5ee0df60..1a422ca3c3bb 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/MonitorContext.java
@@ -16,9 +16,9 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.tui;
 
+import java.io.IOException;
 import java.nio.file.Path;
 import java.util.List;
-import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutorService;
@@ -32,6 +32,7 @@ import dev.tamboui.tui.TuiRunner;
 import org.apache.camel.catalog.CamelCatalog;
 import org.apache.camel.dsl.jbang.core.common.CommandLineHelper;
 import org.apache.camel.dsl.jbang.core.common.PathUtils;
+import org.apache.camel.dsl.jbang.core.common.RuntimeHelper;
 import org.apache.camel.util.json.JsonObject;
 
 /**
@@ -232,17 +233,10 @@ class MonitorContext {
 
     /** Request-specific files keep a late response from being consumed by 
another action. */
     JsonObject executeIndependentAction(String pid, JsonObject request, long 
timeoutMs) {
-        String requestId = UUID.randomUUID().toString();
-        Path directory = CommandLineHelper.getCamelDir();
-        Path actionFile = directory.resolve(pid + "-action-" + requestId + 
".json");
-        Path outputFile = directory.resolve(pid + "-output-" + requestId + 
".json");
-        PathUtils.writeTextSafely(request.toJson(), actionFile);
         try {
-            // The runtime writes output in place; keep polling until the JSON 
is complete.
-            return TuiHelper.pollJsonResponse(outputFile, timeoutMs);
-        } finally {
-            PathUtils.deleteFile(outputFile);
-            PathUtils.deleteFile(actionFile);
+            return RuntimeHelper.executeAction(Long.parseLong(pid), request, 
timeoutMs);
+        } catch (IOException e) {
+            return null;
         }
     }
 

Reply via email to