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

sjaranowski pushed a commit to branch maven-3.10.x
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/maven-3.10.x by this push:
     new 70d934d427 Migrate from JAnsi to JLine, introduce 
MessageBuilderFactory (#11874)
70d934d427 is described below

commit 70d934d427fd2b46be9824da97af31c68ebb6371
Author: Slawomir Jaranowski <[email protected]>
AuthorDate: Wed Apr 8 22:02:13 2026 +0200

    Migrate from JAnsi to JLine, introduce MessageBuilderFactory (#11874)
    
    * Migrate from JAnsi to JLine, introduce MessageBuilderFactory
    
    What changed:
    - Replaced JAnsi with JLine for terminal output handling.
    - Added new MessageBuilder and MessageBuilderFactory for JLine integration 
as replacement for maven-shared-utils
    
    Why:
    - JAnsi is going to EOL
    
    * Fix after review
    
    * Fix color mode detecting
---
 apache-maven/pom.xml                               |  10 +-
 apache-maven/src/bin/mvn                           |   2 +-
 apache-maven/src/bin/mvn.cmd                       |   2 +-
 apache-maven/src/lib/jansi-native/README.txt       |   8 -
 apache-maven/src/lib/jline-native/README.txt       |   8 +
 .../main/appended-resources/META-INF/LICENSE.vm    |   2 +
 .../appended-resources/licenses/BSD-3-Clause.txt   |  28 +
 apache-maven/src/main/assembly/component.xml       |   8 +-
 maven-core/pom.xml                                 |   4 -
 .../lifecycle/LifecycleExecutionException.java     |  29 +-
 .../internal/DefaultMojoExecutionConfigurator.java |  11 +-
 .../internal/LifecycleDependencyResolver.java      |   6 +-
 .../maven/lifecycle/internal/MojoExecutor.java     |  14 +-
 .../org/apache/maven/message/MessageBuilder.java   | 277 ++++++
 .../maven/message/MessageBuilderFactory.java       |  51 ++
 .../message/internal/DefaultMessageBuilder.java    |  78 ++
 .../internal/DefaultMessageBuilderFactory.java     |  52 ++
 .../plugin/internal/DeprecatedPluginValidator.java |  11 +-
 maven-embedder/pom.xml                             |   9 +-
 .../org/apache/maven/cli/CLIReportingUtils.java    |   4 +-
 .../main/java/org/apache/maven/cli/MavenCli.java   |  39 +-
 .../maven/cli/event/ExecutionEventLogger.java      |  23 +-
 .../transfer/AbstractMavenTransferListener.java    |   3 +-
 .../java/org/apache/maven/cli/MavenCliTest.java    | 123 ++-
 .../maven/cli/event/ExecutionEventLoggerTest.java  |   2 +-
 maven-jline/pom.xml                                |  60 ++
 .../java/org/apache/maven/jline/FastTerminal.java  | 291 +++++++
 .../maven/jline/JLineMessageBuilderFactory.java    | 156 ++++
 .../java/org/apache/maven/jline/MessageUtils.java  | 130 +++
 .../src/main/java/org/fusesource/jansi/Ansi.java   | 957 +++++++++++++++++++++
 maven-jline/src/site/site.xml                      |  35 +
 maven-slf4j-provider/pom.xml                       |   4 +-
 .../java/org/slf4j/impl/MavenSimpleLogger.java     |  26 +-
 pom.xml                                            |  52 +-
 34 files changed, 2365 insertions(+), 150 deletions(-)

diff --git a/apache-maven/pom.xml b/apache-maven/pom.xml
index 2fee6c9747..5d04cddad8 100644
--- a/apache-maven/pom.xml
+++ b/apache-maven/pom.xml
@@ -93,8 +93,8 @@ under the License.
       <artifactId>maven-slf4j-provider</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.fusesource.jansi</groupId>
-      <artifactId>jansi</artifactId>
+      <groupId>org.jline</groupId>
+      <artifactId>jline-terminal-ffm</artifactId>
     </dependency>
     <dependency>
       <groupId>org.junit.jupiter</groupId>
@@ -139,13 +139,13 @@ under the License.
         <artifactId>maven-dependency-plugin</artifactId>
         <executions>
           <execution>
-            <id>unpack-jansi-native</id>
+            <id>unpack-jline-native</id>
             <goals>
               <goal>unpack-dependencies</goal>
             </goals>
             <configuration>
-              <includeArtifactIds>jansi</includeArtifactIds>
-              
<includes>org/fusesource/jansi/internal/native/Windows/**</includes>
+              <includeArtifactIds>jline-native</includeArtifactIds>
+              <includes>org/jline/nativ/**</includes>
             </configuration>
           </execution>
         </executions>
diff --git a/apache-maven/src/bin/mvn b/apache-maven/src/bin/mvn
index 47dc7d7f4e..02c7ddd752 100755
--- a/apache-maven/src/bin/mvn
+++ b/apache-maven/src/bin/mvn
@@ -211,6 +211,6 @@ exec "$JAVACMD" \
   -classpath "${CLASSWORLDS_JAR}" \
   "-Dclassworlds.conf=${MAVEN_HOME}/bin/m2.conf" \
   "-Dmaven.home=${MAVEN_HOME}" \
-  "-Dlibrary.jansi.path=${MAVEN_HOME}/lib/jansi-native" \
+  "-Dlibrary.jline.path=${MAVEN_HOME}/lib/jline-native" \
   "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
   ${CLASSWORLDS_LAUNCHER} ${MAVEN_ARGS} "$@"
diff --git a/apache-maven/src/bin/mvn.cmd b/apache-maven/src/bin/mvn.cmd
index affbe43182..bf6b152f2f 100644
--- a/apache-maven/src/bin/mvn.cmd
+++ b/apache-maven/src/bin/mvn.cmd
@@ -190,7 +190,7 @@ set "INTERNAL_MAVEN_OPTS=--enable-native-access=ALL-UNNAMED 
%INTERNAL_MAVEN_OPTS
   -classpath %CLASSWORLDS_JAR% ^
   "-Dclassworlds.conf=%MAVEN_HOME%\bin\m2.conf" ^
   "-Dmaven.home=%MAVEN_HOME%" ^
-  "-Dlibrary.jansi.path=%MAVEN_HOME%\lib\jansi-native" ^
+  "-Dlibrary.jline.path=%MAVEN_HOME%\lib\jline-native" ^
   "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
   %CLASSWORLDS_LAUNCHER% %MAVEN_ARGS% %MAVEN_CMD_LINE_ARGS%
 if ERRORLEVEL 1 goto error
diff --git a/apache-maven/src/lib/jansi-native/README.txt 
b/apache-maven/src/lib/jansi-native/README.txt
deleted file mode 100644
index 26a957e1b5..0000000000
--- a/apache-maven/src/lib/jansi-native/README.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-This directory contains Jansi native libraries extracted from Jansi JAR.
-
-You can add your own build for platforms not natively supported by Jansi.
-See here [1] on how to compile for your platform and and here [2] how libraries
-follow Jansi's directory and filename conventions.
-
-[1] https://github.com/fusesource/jansi/tree/master/src/main/native
-[2] 
https://github.com/fusesource/jansi/blob/321a8ff71c731e10f4ea05c607860180276b2215/src/main/java/org/fusesource/jansi/internal/OSInfo.java
diff --git a/apache-maven/src/lib/jline-native/README.txt 
b/apache-maven/src/lib/jline-native/README.txt
new file mode 100644
index 0000000000..ed74b45f7e
--- /dev/null
+++ b/apache-maven/src/lib/jline-native/README.txt
@@ -0,0 +1,8 @@
+This directory contains JLine native libraries extracted from JLine JAR.
+
+You can add your own build for platforms not natively supported by JLine.
+See here [1] on how to compile for your platform and here [2] how libraries
+follow JLine's directory and filename conventions.
+
+[1] https://github.com/jline/jline3/tree/master/native
+[2] 
https://github.com/jline/jline3/blob/master/native/src/main/java/org/jline/nativ/OSInfo.java
diff --git a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm 
b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm
index b87dd6ab7b..e2b25312a6 100644
--- a/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm
+++ b/apache-maven/src/main/appended-resources/META-INF/LICENSE.vm
@@ -41,6 +41,8 @@ subject to the terms and conditions of the following licenses:
 #*      *##set ( $spdx = 'EPL-2.0' )
 #*    *##elseif ( $license.url.contains( "www.apache.org/licenses/LICENSE-2.0" 
) )
 #*      *##set ( $spdx = 'Apache-2.0' )
+#*    *##elseif ( $license.name == "BSD-3-Clause" || $license.url.contains( 
"opensource.org/licenses/BSD-3-Clause" ) )
+#*      *##set ( $spdx = 'BSD-3-Clause' )
 #*    *##else
 #*      *### unrecognized license will require analysis to know obligations
 #*      *##set ( $spdx = 'unrecognized' )
diff --git a/apache-maven/src/main/appended-resources/licenses/BSD-3-Clause.txt 
b/apache-maven/src/main/appended-resources/licenses/BSD-3-Clause.txt
new file mode 100644
index 0000000000..4b0469c95f
--- /dev/null
+++ b/apache-maven/src/main/appended-resources/licenses/BSD-3-Clause.txt
@@ -0,0 +1,28 @@
+Copyright <YEAR> <COPYRIGHT HOLDER>
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+“AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/apache-maven/src/main/assembly/component.xml 
b/apache-maven/src/main/assembly/component.xml
index 3413c4f040..f63ce88a97 100644
--- a/apache-maven/src/main/assembly/component.xml
+++ b/apache-maven/src/main/assembly/component.xml
@@ -63,10 +63,12 @@ under the License.
       </includes>
     </fileSet>
     <fileSet>
-      
<directory>target/dependency/org/fusesource/jansi/internal/native</directory>
-      <outputDirectory>lib/jansi-native</outputDirectory>
+      <directory>target/dependency/org/jline/nativ</directory>
+      <outputDirectory>lib/jline-native</outputDirectory>
       <includes>
-        <include>**</include>
+        <include>**/*.so</include>
+        <include>**/*.jnilib</include>
+        <include>**/*.dll</include>
       </includes>
     </fileSet>
     <fileSet>
diff --git a/maven-core/pom.xml b/maven-core/pom.xml
index 5f02119ecc..6867da4a03 100644
--- a/maven-core/pom.xml
+++ b/maven-core/pom.xml
@@ -86,10 +86,6 @@ under the License.
       <groupId>org.apache.maven.resolver</groupId>
       <artifactId>maven-resolver-util</artifactId>
     </dependency>
-    <dependency>
-      <groupId>org.apache.maven.shared</groupId>
-      <artifactId>maven-shared-utils</artifactId>
-    </dependency>
     <dependency>
       <groupId>org.eclipse.sisu</groupId>
       <artifactId>org.eclipse.sisu.plexus</artifactId>
diff --git 
a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionException.java
 
b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionException.java
index c7b8c085e6..8636b78f03 100644
--- 
a/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionException.java
+++ 
b/maven-core/src/main/java/org/apache/maven/lifecycle/LifecycleExecutionException.java
@@ -18,11 +18,11 @@
  */
 package org.apache.maven.lifecycle;
 
+import org.apache.maven.message.MessageBuilder;
+import org.apache.maven.message.MessageBuilderFactory;
+import org.apache.maven.message.internal.DefaultMessageBuilderFactory;
 import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.project.MavenProject;
-import org.apache.maven.shared.utils.logging.MessageBuilder;
-
-import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
 
 /**
  * @author <a href="mailto:[email protected]";>Jason van Zyl</a>
@@ -57,16 +57,33 @@ public LifecycleExecutionException(String message, 
MojoExecution execution, Mave
         this.project = project;
     }
 
+    /**
+     * @deprecated Use {@link 
#LifecycleExecutionException(MessageBuilderFactory, MojoExecution, 
MavenProject, Throwable)}
+     * instead to allow for better message formatting.
+     */
+    @Deprecated
     public LifecycleExecutionException(MojoExecution execution, MavenProject 
project, Throwable cause) {
-        this(createMessage(execution, project, cause), execution, project, 
cause);
+        this(createMessage(new DefaultMessageBuilderFactory(), execution, 
project, cause), execution, project, cause);
+    }
+
+    public LifecycleExecutionException(
+            MessageBuilderFactory messageBuilderFactory,
+            MojoExecution execution,
+            MavenProject project,
+            Throwable cause) {
+        this(createMessage(messageBuilderFactory, execution, project, cause), 
execution, project, cause);
     }
 
     public MavenProject getProject() {
         return project;
     }
 
-    private static String createMessage(MojoExecution execution, MavenProject 
project, Throwable cause) {
-        MessageBuilder buffer = buffer(256);
+    private static String createMessage(
+            MessageBuilderFactory messageBuilderFactory,
+            MojoExecution execution,
+            MavenProject project,
+            Throwable cause) {
+        MessageBuilder buffer = messageBuilderFactory.builder(256);
 
         buffer.a("Failed to execute goal");
 
diff --git 
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/DefaultMojoExecutionConfigurator.java
 
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/DefaultMojoExecutionConfigurator.java
index 87f4f88bc9..ca5937f706 100644
--- 
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/DefaultMojoExecutionConfigurator.java
+++ 
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/DefaultMojoExecutionConfigurator.java
@@ -26,6 +26,8 @@
 import java.util.stream.Stream;
 
 import org.apache.maven.lifecycle.MojoExecutionConfigurator;
+import org.apache.maven.message.MessageBuilder;
+import org.apache.maven.message.MessageBuilderFactory;
 import org.apache.maven.model.Plugin;
 import org.apache.maven.model.PluginExecution;
 import org.apache.maven.plugin.MojoExecution;
@@ -33,9 +35,8 @@
 import org.apache.maven.plugin.descriptor.Parameter;
 import org.apache.maven.plugin.descriptor.PluginDescriptor;
 import org.apache.maven.project.MavenProject;
-import org.apache.maven.shared.utils.logging.MessageBuilder;
-import org.apache.maven.shared.utils.logging.MessageUtils;
 import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
 import org.codehaus.plexus.util.StringUtils;
 import org.codehaus.plexus.util.xml.Xpp3Dom;
 import org.slf4j.Logger;
@@ -50,6 +51,9 @@
 public class DefaultMojoExecutionConfigurator implements 
MojoExecutionConfigurator {
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
+    @Requirement
+    private MessageBuilderFactory messageBuilderFactory;
+
     @Override
     public void configure(MavenProject project, MojoExecution mojoExecution, 
boolean allowPluginLevelConfig) {
         String g = mojoExecution.getPlugin().getGroupId();
@@ -142,7 +146,8 @@ private void 
checkUnknownMojoConfigurationParameters(MojoExecution mojoExecution
         unknownParameters.stream()
                 .filter(parameterName -> 
isNotReportPluginsForMavenSite(parameterName, mojoExecution))
                 .forEach(name -> {
-                    MessageBuilder messageBuilder = MessageUtils.buffer()
+                    MessageBuilder messageBuilder = messageBuilderFactory
+                            .builder()
                             .warning("Parameter '")
                             .warning(name)
                             .warning("' is unknown for plugin '")
diff --git 
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
 
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
index e1251c2d28..6abb43f979 100644
--- 
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
+++ 
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleDependencyResolver.java
@@ -38,6 +38,7 @@
 import org.apache.maven.eventspy.internal.EventSpyDispatcher;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.lifecycle.LifecycleExecutionException;
+import org.apache.maven.message.MessageBuilderFactory;
 import org.apache.maven.project.DefaultDependencyResolutionRequest;
 import org.apache.maven.project.DependencyResolutionException;
 import org.apache.maven.project.DependencyResolutionResult;
@@ -80,6 +81,9 @@ public class LifecycleDependencyResolver {
     @Inject
     private ProjectArtifactsCache projectArtifactsCache;
 
+    @Inject
+    private MessageBuilderFactory messageBuilderFactory;
+
     public LifecycleDependencyResolver() {}
 
     public LifecycleDependencyResolver(ProjectDependenciesResolver 
projectDependenciesResolver, Logger logger) {
@@ -243,7 +247,7 @@ private Set<Artifact> getDependencies(
 
                 logger.warn("Try running the build up to the lifecycle phase 
\"package\"");
             } else {
-                throw new LifecycleExecutionException(null, project, e);
+                throw new LifecycleExecutionException(messageBuilderFactory, 
null, project, e);
             }
         }
 
diff --git 
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java
 
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java
index cf40525c79..4061820ac6 100644
--- 
a/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java
+++ 
b/maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java
@@ -39,6 +39,7 @@
 import org.apache.maven.internal.MultilineMessageHelper;
 import org.apache.maven.lifecycle.LifecycleExecutionException;
 import org.apache.maven.lifecycle.MissingProjectException;
+import org.apache.maven.message.MessageBuilderFactory;
 import org.apache.maven.plugin.BuildPluginManager;
 import org.apache.maven.plugin.MavenPluginManager;
 import org.apache.maven.plugin.MojoExecution;
@@ -88,6 +89,9 @@ public class MojoExecutor {
     @Requirement
     private ExecutionEventCatapult eventCatapult;
 
+    @Requirement
+    private MessageBuilderFactory messageBuilderFactory;
+
     private final OwnerReentrantReadWriteLock aggregatorLock = new 
OwnerReentrantReadWriteLock();
 
     @Requirement
@@ -186,7 +190,7 @@ private void execute(
         try {
             
mavenPluginManager.checkPrerequisites(mojoDescriptor.getPluginDescriptor());
         } catch (PluginIncompatibleException e) {
-            throw new LifecycleExecutionException(mojoExecution, 
session.getCurrentProject(), e);
+            throw new LifecycleExecutionException(messageBuilderFactory, 
mojoExecution, session.getCurrentProject(), e);
         }
 
         if (mojoDescriptor.isProjectRequired() && 
!session.getRequest().isProjectPresent()) {
@@ -194,14 +198,15 @@ private void execute(
                     "Goal requires a project to execute" + " but there is no 
POM in this directory ("
                             + session.getExecutionRootDirectory() + ")."
                             + " Please verify you invoked Maven from the 
correct directory.");
-            throw new LifecycleExecutionException(mojoExecution, null, cause);
+            throw new LifecycleExecutionException(messageBuilderFactory, 
mojoExecution, null, cause);
         }
 
         if (mojoDescriptor.isOnlineRequired() && session.isOffline()) {
             if (MojoExecution.Source.CLI.equals(mojoExecution.getSource())) {
                 Throwable cause = new IllegalStateException(
                         "Goal requires online mode for execution" + " but 
Maven is currently offline.");
-                throw new LifecycleExecutionException(mojoExecution, 
session.getCurrentProject(), cause);
+                throw new LifecycleExecutionException(
+                        messageBuilderFactory, mojoExecution, 
session.getCurrentProject(), cause);
             } else {
                 eventCatapult.fire(ExecutionEvent.Type.MojoSkipped, session, 
mojoExecution);
 
@@ -330,7 +335,8 @@ private void doExecute2(MavenSession session, MojoExecution 
mojoExecution) throw
                     | PluginManagerException
                     | PluginConfigurationException
                     | MojoExecutionException e) {
-                throw new LifecycleExecutionException(mojoExecution, 
session.getCurrentProject(), e);
+                throw new LifecycleExecutionException(
+                        messageBuilderFactory, mojoExecution, 
session.getCurrentProject(), e);
             }
 
             eventCatapult.fire(ExecutionEvent.Type.MojoSucceeded, session, 
mojoExecution);
diff --git 
a/maven-core/src/main/java/org/apache/maven/message/MessageBuilder.java 
b/maven-core/src/main/java/org/apache/maven/message/MessageBuilder.java
new file mode 100644
index 0000000000..ee834aa374
--- /dev/null
+++ b/maven-core/src/main/java/org/apache/maven/message/MessageBuilder.java
@@ -0,0 +1,277 @@
+/*
+ * 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.maven.message;
+
+/**
+ * Message builder that supports configurable styling.
+ *
+ * @since 3.10.0
+ * @see MessageBuilderFactory
+ */
+public interface MessageBuilder extends Appendable {
+
+    /**
+     * Append message content in trace style.
+     * By default, bold magenta
+     *
+     * @param message the message to append
+     * @return the current builder
+     */
+    default MessageBuilder trace(Object message) {
+        return style("." + Constants.MAVEN_STYLE_TRACE_NAME + ":-" + 
Constants.MAVEN_STYLE_TRACE_DEFAULT, message);
+    }
+
+    /**
+     * Append message content in debug style.
+     * By default, bold cyan
+     *
+     * @param message the message to append
+     * @return the current builder
+     */
+    default MessageBuilder debug(Object message) {
+        return style("." + Constants.MAVEN_STYLE_DEBUG_NAME + ":-" + 
Constants.MAVEN_STYLE_DEBUG_DEFAULT, message);
+    }
+
+    /**
+     * Append message content in info style.
+     * By default, bold blue
+     *
+     * @param message the message to append
+     * @return the current builder
+     */
+    default MessageBuilder info(Object message) {
+        return style("." + Constants.MAVEN_STYLE_INFO_NAME + ":-" + 
Constants.MAVEN_STYLE_INFO_DEFAULT, message);
+    }
+
+    /**
+     * Append message content in warning style.
+     * By default, bold yellow
+     *
+     * @param message the message to append
+     * @return the current builder
+     */
+    default MessageBuilder warning(Object message) {
+        return style("." + Constants.MAVEN_STYLE_WARNING_NAME + ":-" + 
Constants.MAVEN_STYLE_WARNING_DEFAULT, message);
+    }
+
+    /**
+     * Append message content in error style.
+     * By default, bold red
+     *
+     * @param message the message to append
+     * @return the current builder
+     */
+    default MessageBuilder error(Object message) {
+        return style("." + Constants.MAVEN_STYLE_ERROR_NAME + ":-" + 
Constants.MAVEN_STYLE_ERROR_DEFAULT, message);
+    }
+
+    /**
+     * Append message content in success style.
+     * By default, bold green
+     *
+     * @param message the message to append
+     * @return the current builder
+     */
+    default MessageBuilder success(Object message) {
+        return style("." + Constants.MAVEN_STYLE_SUCCESS_NAME + ":-" + 
Constants.MAVEN_STYLE_SUCCESS_DEFAULT, message);
+    }
+
+    /**
+     * Append message content in failure style.
+     * By default, bold red
+     *
+     * @param message the message to append
+     * @return the current builder
+     */
+    default MessageBuilder failure(Object message) {
+        return style("." + Constants.MAVEN_STYLE_FAILURE_NAME + ":-" + 
Constants.MAVEN_STYLE_FAILURE_DEFAULT, message);
+    }
+
+    /**
+     * Append message content in strong style.
+     * By default, bold
+     *
+     * @param message the message to append
+     * @return the current builder
+     */
+    default MessageBuilder strong(Object message) {
+        return style("." + Constants.MAVEN_STYLE_STRONG_NAME + ":-" + 
Constants.MAVEN_STYLE_STRONG_DEFAULT, message);
+    }
+
+    /**
+     * Append message content in mojo style.
+     * By default, green
+     *
+     * @param message the message to append
+     * @return the current builder
+     */
+    default MessageBuilder mojo(Object message) {
+        return style("." + Constants.MAVEN_STYLE_MOJO_NAME + ":-" + 
Constants.MAVEN_STYLE_MOJO_DEFAULT, message);
+    }
+
+    /**
+     * Append message content in project style.
+     * By default, cyan
+     *
+     * @param message the message to append
+     * @return the current builder
+     */
+    default MessageBuilder project(Object message) {
+        return style("." + Constants.MAVEN_STYLE_PROJECT_NAME + ":-" + 
Constants.MAVEN_STYLE_PROJECT_DEFAULT, message);
+    }
+
+    default MessageBuilder style(String style, Object message) {
+        return style(style).a(message).resetStyle();
+    }
+
+    MessageBuilder style(String style);
+
+    MessageBuilder resetStyle();
+
+    //
+    // message building methods modelled after Ansi methods
+    //
+
+    @Override
+    MessageBuilder append(CharSequence cs);
+
+    @Override
+    MessageBuilder append(CharSequence cs, int start, int end);
+
+    @Override
+    MessageBuilder append(char c);
+
+    /**
+     * Append content to the message buffer.
+     *
+     * @param value the content to append
+     * @param offset the index of the first {@code char} to append
+     * @param len the number of {@code char}s to append
+     * @return the current builder
+     */
+    default MessageBuilder a(char[] value, int offset, int len) {
+        return append(String.valueOf(value, offset, len));
+    }
+
+    /**
+     * Append content to the message buffer.
+     *
+     * @param value the content to append
+     * @return the current builder
+     */
+    default MessageBuilder a(char[] value) {
+        return append(String.valueOf(value));
+    }
+
+    /**
+     * Append content to the message buffer.
+     *
+     * @param value the content to append
+     * @param start the starting index of the subsequence to be appended
+     * @param end the end index of the subsequence to be appended
+     * @return the current builder
+     */
+    default MessageBuilder a(CharSequence value, int start, int end) {
+        return append(value, start, end);
+    }
+
+    /**
+     * Append content to the message buffer.
+     *
+     * @param value the content to append
+     * @return the current builder
+     */
+    default MessageBuilder a(CharSequence value) {
+        return append(value);
+    }
+
+    /**
+     * Append content to the message buffer.
+     *
+     * @param value the content to append
+     * @return the current builder
+     */
+    default MessageBuilder a(Object value) {
+        return append(String.valueOf(value));
+    }
+
+    /**
+     * Append newline to the message buffer.
+     *
+     * @return the current builder
+     */
+    default MessageBuilder newline() {
+        return append(System.lineSeparator());
+    }
+
+    /**
+     * Append formatted content to the buffer.
+     * @see String#format(String, Object...)
+     *
+     * @param pattern a <a href="../util/Formatter.html#syntax">format 
string</a>
+     * @param args arguments referenced by the format specifiers in the format 
string
+     * @return the current builder
+     */
+    default MessageBuilder format(String pattern, Object... args) {
+        return append(String.format(pattern, args));
+    }
+
+    /**
+     * Set the buffer length.
+     *
+     * @param length the new length
+     * @return the current builder
+     */
+    MessageBuilder setLength(int length);
+
+    /**
+     * Return the built message.
+     *
+     * @return the message
+     */
+    String build();
+
+    class Constants {
+        // Style Names
+        public static final String MAVEN_STYLE_TRANSFER_NAME = "transfer";
+        public static final String MAVEN_STYLE_TRACE_NAME = "trace";
+        public static final String MAVEN_STYLE_DEBUG_NAME = "debug";
+        public static final String MAVEN_STYLE_INFO_NAME = "info";
+        public static final String MAVEN_STYLE_WARNING_NAME = "warning";
+        public static final String MAVEN_STYLE_ERROR_NAME = "error";
+        public static final String MAVEN_STYLE_SUCCESS_NAME = "success";
+        public static final String MAVEN_STYLE_FAILURE_NAME = "failure";
+        public static final String MAVEN_STYLE_STRONG_NAME = "strong";
+        public static final String MAVEN_STYLE_MOJO_NAME = "mojo";
+        public static final String MAVEN_STYLE_PROJECT_NAME = "project";
+
+        // Default Values
+        public static final String MAVEN_STYLE_TRANSFER_DEFAULT = 
"f:bright-black";
+        public static final String MAVEN_STYLE_TRACE_DEFAULT = 
"bold,f:magenta";
+        public static final String MAVEN_STYLE_DEBUG_DEFAULT = "bold,f:cyan";
+        public static final String MAVEN_STYLE_INFO_DEFAULT = "bold,f:blue";
+        public static final String MAVEN_STYLE_WARNING_DEFAULT = 
"bold,f:yellow";
+        public static final String MAVEN_STYLE_ERROR_DEFAULT = "bold,f:red";
+        public static final String MAVEN_STYLE_SUCCESS_DEFAULT = 
"bold,f:green";
+        public static final String MAVEN_STYLE_FAILURE_DEFAULT = "bold,f:red";
+        public static final String MAVEN_STYLE_STRONG_DEFAULT = "bold";
+        public static final String MAVEN_STYLE_MOJO_DEFAULT = "f:green";
+        public static final String MAVEN_STYLE_PROJECT_DEFAULT = "f:cyan";
+    }
+}
diff --git 
a/maven-core/src/main/java/org/apache/maven/message/MessageBuilderFactory.java 
b/maven-core/src/main/java/org/apache/maven/message/MessageBuilderFactory.java
new file mode 100644
index 0000000000..642ca7d773
--- /dev/null
+++ 
b/maven-core/src/main/java/org/apache/maven/message/MessageBuilderFactory.java
@@ -0,0 +1,51 @@
+/*
+ * 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.maven.message;
+
+/**
+ * A factory for {@link MessageBuilder}.
+ *
+ * @since 3.10.0
+ */
+public interface MessageBuilderFactory {
+    /**
+     * Checks if the underlying output does support styling or not.
+     * @return whether color styling is supported or not
+     */
+    boolean isColorEnabled();
+
+    /**
+     * Returns the terminal width or <code>-1</code> if not supported.
+     * @return the terminal width
+     */
+    int getTerminalWidth();
+
+    /**
+     * Creates a new message builder.
+     * @return a new message builder
+     */
+    MessageBuilder builder();
+
+    /**
+     * Creates a new message builder of the specified size.
+     * @param size the initial size of the message builder buffer
+     * @return a new message builder
+     */
+    MessageBuilder builder(int size);
+}
diff --git 
a/maven-core/src/main/java/org/apache/maven/message/internal/DefaultMessageBuilder.java
 
b/maven-core/src/main/java/org/apache/maven/message/internal/DefaultMessageBuilder.java
new file mode 100644
index 0000000000..474244f738
--- /dev/null
+++ 
b/maven-core/src/main/java/org/apache/maven/message/internal/DefaultMessageBuilder.java
@@ -0,0 +1,78 @@
+/*
+ * 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.maven.message.internal;
+
+import org.apache.maven.message.MessageBuilder;
+
+class DefaultMessageBuilder implements MessageBuilder {
+
+    private final StringBuilder buffer;
+
+    DefaultMessageBuilder() {
+        this(new StringBuilder());
+    }
+
+    DefaultMessageBuilder(StringBuilder buffer) {
+        this.buffer = buffer;
+    }
+
+    @Override
+    public MessageBuilder style(String style) {
+        return this;
+    }
+
+    @Override
+    public MessageBuilder resetStyle() {
+        return this;
+    }
+
+    @Override
+    public MessageBuilder append(CharSequence cs) {
+        buffer.append(cs);
+        return this;
+    }
+
+    @Override
+    public MessageBuilder append(CharSequence cs, int start, int end) {
+        buffer.append(cs, start, end);
+        return this;
+    }
+
+    @Override
+    public MessageBuilder append(char c) {
+        buffer.append(c);
+        return this;
+    }
+
+    @Override
+    public MessageBuilder setLength(int length) {
+        buffer.setLength(length);
+        return this;
+    }
+
+    @Override
+    public String build() {
+        return buffer.toString();
+    }
+
+    @Override
+    public String toString() {
+        return build();
+    }
+}
diff --git 
a/maven-core/src/main/java/org/apache/maven/message/internal/DefaultMessageBuilderFactory.java
 
b/maven-core/src/main/java/org/apache/maven/message/internal/DefaultMessageBuilderFactory.java
new file mode 100644
index 0000000000..a3e1daf924
--- /dev/null
+++ 
b/maven-core/src/main/java/org/apache/maven/message/internal/DefaultMessageBuilderFactory.java
@@ -0,0 +1,52 @@
+/*
+ * 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.maven.message.internal;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import org.apache.maven.message.MessageBuilder;
+import org.apache.maven.message.MessageBuilderFactory;
+import org.eclipse.sisu.Priority;
+
+@Named
+@Singleton
+@Priority(-1)
+public class DefaultMessageBuilderFactory implements MessageBuilderFactory {
+
+    @Override
+    public boolean isColorEnabled() {
+        return false;
+    }
+
+    @Override
+    public int getTerminalWidth() {
+        return -1;
+    }
+
+    @Override
+    public MessageBuilder builder() {
+        return new DefaultMessageBuilder();
+    }
+
+    @Override
+    public MessageBuilder builder(int size) {
+        return new DefaultMessageBuilder(new StringBuilder(size));
+    }
+}
diff --git 
a/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java
 
b/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java
index 5544a74f34..b876969d9a 100644
--- 
a/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java
+++ 
b/maven-core/src/main/java/org/apache/maven/plugin/internal/DeprecatedPluginValidator.java
@@ -23,10 +23,10 @@
 import javax.inject.Singleton;
 
 import org.apache.maven.execution.MavenSession;
+import org.apache.maven.message.MessageBuilderFactory;
 import org.apache.maven.plugin.PluginValidationManager;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.plugin.descriptor.Parameter;
-import org.apache.maven.shared.utils.logging.MessageUtils;
 import 
org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
 import org.codehaus.plexus.configuration.PlexusConfiguration;
 
@@ -39,9 +39,13 @@
 @Named
 class DeprecatedPluginValidator extends 
AbstractMavenPluginDescriptorSourcedParametersValidator {
 
+    private final MessageBuilderFactory messageBuilderFactory;
+
     @Inject
-    DeprecatedPluginValidator(PluginValidationManager pluginValidationManager) 
{
+    DeprecatedPluginValidator(
+            PluginValidationManager pluginValidationManager, 
MessageBuilderFactory messageBuilderFactory) {
         super(pluginValidationManager);
+        this.messageBuilderFactory = messageBuilderFactory;
     }
 
     @Override
@@ -94,7 +98,8 @@ private void checkParameter(
     }
 
     private String logDeprecatedMojo(MojoDescriptor mojoDescriptor) {
-        return MessageUtils.buffer()
+        return messageBuilderFactory
+                .builder()
                 .warning("Goal '")
                 .warning(mojoDescriptor.getGoal())
                 .warning("' is deprecated: ")
diff --git a/maven-embedder/pom.xml b/maven-embedder/pom.xml
index 9817dc1043..f8b548f9eb 100644
--- a/maven-embedder/pom.xml
+++ b/maven-embedder/pom.xml
@@ -69,8 +69,8 @@ under the License.
       <artifactId>maven-resolver-util</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.apache.maven.shared</groupId>
-      <artifactId>maven-shared-utils</artifactId>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-jline</artifactId>
     </dependency>
     <dependency>
       <groupId>com.google.inject</groupId>
@@ -160,11 +160,6 @@ under the License.
       <artifactId>mockito-core</artifactId>
       <scope>test</scope>
     </dependency>
-    <dependency>
-      <groupId>org.fusesource.jansi</groupId>
-      <artifactId>jansi</artifactId>
-      <scope>test</scope>
-    </dependency>
   </dependencies>
 
   <build>
diff --git 
a/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java 
b/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java
index e220ce783f..7bc661fa74 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/CLIReportingUtils.java
@@ -29,7 +29,7 @@
 import org.codehaus.plexus.util.StringUtils;
 import org.slf4j.Logger;
 
-import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
+import static org.apache.maven.jline.MessageUtils.builder;
 
 /**
  * Utility class used to report errors, statistics, application version info, 
etc.
@@ -53,7 +53,7 @@ public static String showVersion() {
         final String ls = System.lineSeparator();
         Properties properties = getBuildProperties();
         StringBuilder version = new StringBuilder(256);
-        
version.append(buffer().strong(createMavenVersionString(properties))).append(ls);
+        
version.append(builder().strong(createMavenVersionString(properties))).append(ls);
         version.append(reduce(properties.getProperty("distributionShortName") 
+ " home: "
                         + System.getProperty("maven.home", "<unknown Maven " + 
"home>")))
                 .append(ls);
diff --git a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java 
b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
index 5c62edfa17..985362b680 100644
--- a/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
+++ b/maven-embedder/src/main/java/org/apache/maven/cli/MavenCli.java
@@ -84,14 +84,14 @@
 import org.apache.maven.execution.scope.internal.MojoExecutionScopeModule;
 import org.apache.maven.extension.internal.CoreExports;
 import org.apache.maven.extension.internal.CoreExtensionEntry;
+import org.apache.maven.jline.MessageUtils;
 import org.apache.maven.lifecycle.LifecycleExecutionException;
+import org.apache.maven.message.MessageBuilder;
 import org.apache.maven.model.building.ModelProcessor;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.properties.internal.EnvironmentUtils;
 import org.apache.maven.properties.internal.SystemProperties;
 import org.apache.maven.session.scope.internal.SessionScopeModule;
-import org.apache.maven.shared.utils.logging.MessageBuilder;
-import org.apache.maven.shared.utils.logging.MessageUtils;
 import org.apache.maven.toolchain.building.DefaultToolchainsBuildingRequest;
 import org.apache.maven.toolchain.building.ToolchainsBuilder;
 import org.apache.maven.toolchain.building.ToolchainsBuildingResult;
@@ -123,7 +123,7 @@
 
 import static org.apache.maven.cli.CLIManager.COLOR;
 import static org.apache.maven.cli.ResolveFile.resolveFile;
-import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
+import static org.apache.maven.jline.MessageUtils.builder;
 
 // TODO push all common bits back to plexus cli and prepare for transition to 
Guice. We don't need 50 ways to make CLIs
 
@@ -525,6 +525,12 @@ void logging(CliRequest cliRequest) {
         } else if (cliRequest.commandLine.hasOption(CLIManager.BATCH_MODE)
                 || cliRequest.commandLine.hasOption(CLIManager.LOG_FILE)) {
             MessageUtils.setColorEnabled(false);
+        } else {
+            // if the terminal is detected as dumb terminal, disable colors
+            if (MessageUtils.getTerminal() != null
+                    && 
MessageUtils.getTerminal().getType().startsWith("dumb")) {
+                MessageUtils.setColorEnabled(false);
+            }
         }
 
         // LOG STREAMS
@@ -569,14 +575,15 @@ private void commands(CliRequest cliRequest) {
 
         if (slf4jLogger.isDebugEnabled()) {
             slf4jLogger.debug("Message scheme: {}", 
(MessageUtils.isColorEnabled() ? "color" : "plain"));
+            slf4jLogger.debug("Message terminal: {}", 
MessageUtils.getTerminal());
             if (MessageUtils.isColorEnabled()) {
-                MessageBuilder buff = MessageUtils.buffer();
+                MessageBuilder buff = builder();
                 buff.a("Message styles: ");
-                buff.a(MessageUtils.level().debug("debug")).a(' ');
-                buff.a(MessageUtils.level().info("info")).a(' ');
-                buff.a(MessageUtils.level().warning("warning")).a(' ');
-                buff.a(MessageUtils.level().error("error")).a(' ');
-
+                buff.a(builder().trace("trace")).a(' ');
+                buff.a(builder().debug("debug")).a(' ');
+                buff.a(builder().info("info")).a(' ');
+                buff.a(builder().warning("warning")).a(' ');
+                buff.a(builder().error("error")).a(' ');
                 buff.success("success").a(' ');
                 buff.failure("failure").a(' ');
                 buff.strong("strong").a(' ');
@@ -1014,11 +1021,12 @@ private int execute(CliRequest cliRequest) throws 
MavenExecutionRequestPopulatio
             if (!cliRequest.showErrors) {
                 slf4jLogger.error(
                         "To see the full stack trace of the errors, re-run 
Maven with the {} switch.",
-                        buffer().strong("-e"));
+                        builder().strong("-e"));
             }
             if (!slf4jLogger.isDebugEnabled()) {
                 slf4jLogger.error(
-                        "Re-run Maven using the {} switch to enable full debug 
logging.", buffer().strong("-X"));
+                        "Re-run Maven using the {} switch to enable full debug 
logging.",
+                        builder().strong("-X"));
             }
 
             if (!references.isEmpty()) {
@@ -1027,7 +1035,7 @@ private int execute(CliRequest cliRequest) throws 
MavenExecutionRequestPopulatio
                         + ", please read the following articles:");
 
                 for (Map.Entry<String, String> entry : references.entrySet()) {
-                    slf4jLogger.error("{} {}", 
buffer().strong(entry.getValue()), entry.getKey());
+                    slf4jLogger.error("{} {}", 
builder().strong(entry.getValue()), entry.getKey());
                 }
             }
 
@@ -1035,7 +1043,8 @@ private int execute(CliRequest cliRequest) throws 
MavenExecutionRequestPopulatio
                     && 
!project.equals(result.getTopologicallySortedProjects().get(0))) {
                 slf4jLogger.error("");
                 slf4jLogger.error("After correcting the problems, you can 
resume the build with the command");
-                slf4jLogger.error(buffer().a("  ")
+                slf4jLogger.error(builder()
+                        .a("  ")
                         .strong("mvn <args> -rf " + 
getResumeFrom(result.getTopologicallySortedProjects(), project))
                         .toString());
             }
@@ -1094,9 +1103,9 @@ private void logSummary(
 
         if (StringUtils.isNotEmpty(referenceKey)) {
             if (msg.indexOf('\n') < 0) {
-                msg += " -> " + buffer().strong(referenceKey);
+                msg += " -> " + builder().strong(referenceKey);
             } else {
-                msg += "\n-> " + buffer().strong(referenceKey);
+                msg += "\n-> " + builder().strong(referenceKey);
             }
         }
 
diff --git 
a/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java
 
b/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java
index f7e2b48633..b6f8af98a5 100644
--- 
a/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java
+++ 
b/maven-embedder/src/main/java/org/apache/maven/cli/event/ExecutionEventLogger.java
@@ -30,17 +30,18 @@
 import org.apache.maven.execution.ExecutionEvent;
 import org.apache.maven.execution.MavenExecutionResult;
 import org.apache.maven.execution.MavenSession;
+import org.apache.maven.jline.MessageUtils;
+import org.apache.maven.message.MessageBuilder;
 import org.apache.maven.plugin.MojoExecution;
 import org.apache.maven.plugin.descriptor.MojoDescriptor;
 import org.apache.maven.project.MavenProject;
-import org.apache.maven.shared.utils.logging.MessageBuilder;
 import org.codehaus.plexus.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import static org.apache.maven.cli.CLIReportingUtils.formatDuration;
 import static org.apache.maven.cli.CLIReportingUtils.formatTimestamp;
-import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
+import static org.apache.maven.jline.MessageUtils.builder;
 
 /**
  * Logs execution events to logger, eventually user-supplied.
@@ -81,7 +82,7 @@ private void infoLine(char c) {
     }
 
     private void infoMain(String msg) {
-        logger.info(buffer().strong(msg).toString());
+        logger.info(builder().strong(msg).toString());
     }
 
     @Override
@@ -185,9 +186,9 @@ private void logReactorSummary(MavenSession session) {
             BuildSummary buildSummary = result.getBuildSummary(project);
 
             if (buildSummary == null) {
-                buffer.append(buffer().warning("SKIPPED"));
+                buffer.append(builder().warning("SKIPPED"));
             } else if (buildSummary instanceof BuildSuccess) {
-                buffer.append(buffer().success("SUCCESS"));
+                buffer.append(builder().success("SUCCESS"));
                 buffer.append(" [");
                 String buildTimeDuration = 
formatDuration(buildSummary.getTime());
                 int padSize = MAX_PADDED_BUILD_TIME_DURATION_LENGTH - 
buildTimeDuration.length();
@@ -197,7 +198,7 @@ private void logReactorSummary(MavenSession session) {
                 buffer.append(buildTimeDuration);
                 buffer.append(']');
             } else if (buildSummary instanceof BuildFailure) {
-                buffer.append(buffer().failure("FAILURE"));
+                buffer.append(builder().failure("FAILURE"));
                 buffer.append(" [");
                 String buildTimeDuration = 
formatDuration(buildSummary.getTime());
                 int padSize = MAX_PADDED_BUILD_TIME_DURATION_LENGTH - 
buildTimeDuration.length();
@@ -214,7 +215,7 @@ private void logReactorSummary(MavenSession session) {
 
     private void logResult(MavenSession session) {
         infoLine('-');
-        MessageBuilder buffer = buffer();
+        MessageBuilder buffer = MessageUtils.builder();
 
         if (session.getResult().hasExceptions()) {
             buffer.failure("BUILD FAILURE");
@@ -272,7 +273,7 @@ public void projectStarted(ExecutionEvent event) {
                     + chars('-', Math.max(0, LINE_LENGTH - headerLen - 
prefix.length() + preHeader.length()));
 
             logger.info(
-                    
buffer().strong(prefix).project(projectKey).strong(suffix).toString());
+                    
builder().strong(prefix).project(projectKey).strong(suffix).toString());
 
             // Building Project Name Version    [i/n]
             String building = "Building " + event.getProject().getName() + " "
@@ -330,7 +331,7 @@ public void mojoStarted(ExecutionEvent event) {
         if (logger.isInfoEnabled()) {
             logger.info("");
 
-            MessageBuilder buffer = buffer().strong("--- ");
+            MessageBuilder buffer = builder().strong("--- ");
             append(buffer, event.getMojoExecution());
             append(buffer, event.getProject());
             buffer.strong(" ---");
@@ -350,7 +351,7 @@ public void forkStarted(ExecutionEvent event) {
         if (logger.isInfoEnabled()) {
             logger.info("");
 
-            MessageBuilder buffer = buffer().strong(">>> ");
+            MessageBuilder buffer = builder().strong(">>> ");
             append(buffer, event.getMojoExecution());
             buffer.strong(" > ");
             appendForkInfo(buffer, 
event.getMojoExecution().getMojoDescriptor());
@@ -372,7 +373,7 @@ public void forkSucceeded(ExecutionEvent event) {
         if (logger.isInfoEnabled()) {
             logger.info("");
 
-            MessageBuilder buffer = buffer().strong("<<< ");
+            MessageBuilder buffer = builder().strong("<<< ");
             append(buffer, event.getMojoExecution());
             buffer.strong(" < ");
             appendForkInfo(buffer, 
event.getMojoExecution().getMojoDescriptor());
diff --git 
a/maven-embedder/src/main/java/org/apache/maven/cli/transfer/AbstractMavenTransferListener.java
 
b/maven-embedder/src/main/java/org/apache/maven/cli/transfer/AbstractMavenTransferListener.java
index 3f5f2cb138..a283adff05 100644
--- 
a/maven-embedder/src/main/java/org/apache/maven/cli/transfer/AbstractMavenTransferListener.java
+++ 
b/maven-embedder/src/main/java/org/apache/maven/cli/transfer/AbstractMavenTransferListener.java
@@ -23,7 +23,7 @@
 import java.text.DecimalFormatSymbols;
 import java.util.Locale;
 
-import org.apache.maven.shared.utils.logging.MessageUtils;
+import org.apache.maven.jline.MessageUtils;
 import org.eclipse.aether.transfer.AbstractTransferListener;
 import org.eclipse.aether.transfer.TransferCancelledException;
 import org.eclipse.aether.transfer.TransferEvent;
@@ -192,6 +192,7 @@ protected AbstractMavenTransferListener(PrintStream out) {
 
     @Override
     public void transferInitiated(TransferEvent event) {
+        // TODO use MessageBuilder
         String darkOn = MessageUtils.isColorEnabled() ? ANSI_DARK_SET : "";
         String darkOff = MessageUtils.isColorEnabled() ? ANSI_DARK_RESET : "";
 
diff --git 
a/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java 
b/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java
index 5c5e7e72e2..1a7563917a 100644
--- a/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java
+++ b/maven-embedder/src/test/java/org/apache/maven/cli/MavenCliTest.java
@@ -27,12 +27,13 @@
 import org.apache.commons.cli.ParseException;
 import org.apache.maven.Maven;
 import org.apache.maven.eventspy.internal.EventSpyDispatcher;
-import org.apache.maven.shared.utils.logging.MessageUtils;
+import org.apache.maven.jline.MessageUtils;
 import org.apache.maven.toolchain.building.ToolchainsBuildingRequest;
 import org.apache.maven.toolchain.building.ToolchainsBuildingResult;
 import org.codehaus.plexus.PlexusContainer;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.InOrder;
 
@@ -42,7 +43,6 @@
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
-import static org.junit.jupiter.api.Assumptions.assumeTrue;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.mock;
@@ -258,58 +258,86 @@ public void testMVNConfigurationFunkyArguments() throws 
Exception {
         assertEquals("-Dpom.xml", 
request.getCommandLine().getOptionValue(CLIManager.ALTERNATE_POM_FILE));
     }
 
-    @Test
-    public void testStyleColors() throws Exception {
-        assumeTrue(MessageUtils.isColorEnabled(), "ANSI not supported");
-        CliRequest request;
+    @Nested
+    class StyleColors {
 
-        MessageUtils.setColorEnabled(true);
-        request = new CliRequest(new String[] {"-B"}, null);
-        cli.cli(request);
-        cli.properties(request);
-        cli.logging(request);
-        assertFalse(MessageUtils.isColorEnabled());
+        @BeforeEach
+        public void setUp() {
+            MessageUtils.systemInstall();
+            MessageUtils.getTerminalWidth(); // wait for fast terminal 
initialize
+        }
 
-        MessageUtils.setColorEnabled(true);
-        request = new CliRequest(new String[] {"-l", "target/temp/mvn.log"}, 
null);
-        request.workingDirectory = "target/temp";
-        cli.cli(request);
-        cli.properties(request);
-        cli.logging(request);
-        assertFalse(MessageUtils.isColorEnabled());
+        @AfterEach
+        public void tearDown() {
+            MessageUtils.systemUninstall();
+        }
 
-        MessageUtils.setColorEnabled(false);
-        request = new CliRequest(new String[] {"-Dstyle.color=always"}, null);
-        cli.cli(request);
-        cli.properties(request);
-        cli.logging(request);
-        assertTrue(MessageUtils.isColorEnabled());
+        @Test
+        public void testStyleColors() throws Exception {
+            CliRequest request;
 
-        MessageUtils.setColorEnabled(true);
-        request = new CliRequest(new String[] {"-Dstyle.color=never"}, null);
-        cli.cli(request);
-        cli.properties(request);
-        cli.logging(request);
-        assertFalse(MessageUtils.isColorEnabled());
+            MessageUtils.setColorEnabled(true);
+            request = new CliRequest(new String[] {"-B"}, null);
+            cli.cli(request);
+            cli.properties(request);
+            cli.logging(request);
+            assertFalse(MessageUtils.isColorEnabled());
 
-        MessageUtils.setColorEnabled(false);
-        request = new CliRequest(new String[] {"-Dstyle.color=always", "-B", 
"-l", "target/temp/mvn.log"}, null);
-        request.workingDirectory = "target/temp";
-        cli.cli(request);
-        cli.properties(request);
-        cli.logging(request);
-        assertTrue(MessageUtils.isColorEnabled());
+            MessageUtils.setColorEnabled(true);
+            request = new CliRequest(new String[] {"-l", 
"target/temp/mvn.log"}, null);
+            request.workingDirectory = "target/temp";
+            cli.cli(request);
+            cli.properties(request);
+            cli.logging(request);
+            assertFalse(MessageUtils.isColorEnabled());
+
+            MessageUtils.setColorEnabled(false);
+            request = new CliRequest(new String[] {"-Dstyle.color=always"}, 
null);
+            cli.cli(request);
+            cli.properties(request);
+            cli.logging(request);
+            assertTrue(MessageUtils.isColorEnabled());
+
+            MessageUtils.setColorEnabled(true);
+            request = new CliRequest(new String[] {"-Dstyle.color=never"}, 
null);
+            cli.cli(request);
+            cli.properties(request);
+            cli.logging(request);
+            assertFalse(MessageUtils.isColorEnabled());
 
-        try {
             MessageUtils.setColorEnabled(false);
-            request = new CliRequest(new String[] {"-Dstyle.color=maybe", 
"-B", "-l", "target/temp/mvn.log"}, null);
+            request = new CliRequest(new String[] {"-Dstyle.color=always", 
"-B", "-l", "target/temp/mvn.log"}, null);
             request.workingDirectory = "target/temp";
             cli.cli(request);
             cli.properties(request);
             cli.logging(request);
-            fail("maybe is not a valid option");
-        } catch (IllegalArgumentException e) {
-            // noop
+            assertTrue(MessageUtils.isColorEnabled());
+
+            try {
+                MessageUtils.setColorEnabled(false);
+                request = new CliRequest(new String[] {"-Dstyle.color=maybe", 
"-B", "-l", "target/temp/mvn.log"}, null);
+                request.workingDirectory = "target/temp";
+                cli.cli(request);
+                cli.properties(request);
+                cli.logging(request);
+                fail("maybe is not a valid option");
+            } catch (IllegalArgumentException e) {
+                // noop
+            }
+        }
+
+        @Test
+        void testDumpTerminal() throws Exception {
+            // test provide own sys out which is not a tty
+            // similar will be with redirected output
+            // check that colors are disabled
+
+            MessageUtils.setColorEnabled(true);
+            CliRequest request = new CliRequest(new String[] {}, null);
+            cli.cli(request);
+            cli.properties(request);
+            cli.logging(request);
+            assertFalse(MessageUtils.isColorEnabled());
         }
     }
 
@@ -357,17 +385,20 @@ public void testVersionStringWithoutAnsi() throws 
Exception {
         PrintStream oldOut = System.out;
         System.setOut(new PrintStream(systemOut));
 
+        System.setProperty(MavenCli.MULTIMODULE_PROJECT_DIRECTORY, ".");
+
         // when
         try {
-            cli.cli(cliRequest);
-        } catch (MavenCli.ExitException exitException) {
-            // expected
+            assertEquals(0, cli.doMain(cliRequest));
         } finally {
             // restore sysout
             System.setOut(oldOut);
+            System.clearProperty(MavenCli.MULTIMODULE_PROJECT_DIRECTORY);
         }
         String versionOut = new String(systemOut.toByteArray(), 
StandardCharsets.UTF_8);
 
+        assertTrue(versionOut.contains("Apache Maven"));
+
         // then
         assertEquals(MessageUtils.stripAnsiCodes(versionOut), versionOut);
     }
diff --git 
a/maven-embedder/src/test/java/org/apache/maven/cli/event/ExecutionEventLoggerTest.java
 
b/maven-embedder/src/test/java/org/apache/maven/cli/event/ExecutionEventLoggerTest.java
index b68d62b5e3..e48362a90d 100644
--- 
a/maven-embedder/src/test/java/org/apache/maven/cli/event/ExecutionEventLoggerTest.java
+++ 
b/maven-embedder/src/test/java/org/apache/maven/cli/event/ExecutionEventLoggerTest.java
@@ -23,8 +23,8 @@
 import org.apache.commons.io.FilenameUtils;
 import org.apache.maven.execution.ExecutionEvent;
 import org.apache.maven.execution.MavenSession;
+import org.apache.maven.jline.MessageUtils;
 import org.apache.maven.project.MavenProject;
-import org.apache.maven.shared.utils.logging.MessageUtils;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
diff --git a/maven-jline/pom.xml b/maven-jline/pom.xml
new file mode 100644
index 0000000000..e7db3548d6
--- /dev/null
+++ b/maven-jline/pom.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.maven</groupId>
+    <artifactId>maven</artifactId>
+    <version>3.10.0-SNAPSHOT</version>
+  </parent>
+
+  <artifactId>maven-jline</artifactId>
+  <name>Maven 3 JLine integration</name>
+  <description>Provides the JLine integration in Maven</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.jline</groupId>
+      <artifactId>jansi-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jline</groupId>
+      <artifactId>jline-terminal</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jline</groupId>
+      <artifactId>jline-terminal-jni</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.jline</groupId>
+      <artifactId>jline-terminal-ffm</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>javax.inject</groupId>
+      <artifactId>javax.inject</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-core</artifactId>
+    </dependency>
+  </dependencies>
+</project>
diff --git a/maven-jline/src/main/java/org/apache/maven/jline/FastTerminal.java 
b/maven-jline/src/main/java/org/apache/maven/jline/FastTerminal.java
new file mode 100644
index 0000000000..1c4d9b124c
--- /dev/null
+++ b/maven-jline/src/main/java/org/apache/maven/jline/FastTerminal.java
@@ -0,0 +1,291 @@
+/*
+ * 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.maven.jline;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Consumer;
+import java.util.function.IntConsumer;
+import java.util.function.IntSupplier;
+
+import org.jline.terminal.Attributes;
+import org.jline.terminal.Cursor;
+import org.jline.terminal.MouseEvent;
+import org.jline.terminal.Size;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.spi.SystemStream;
+import org.jline.terminal.spi.TerminalExt;
+import org.jline.terminal.spi.TerminalProvider;
+import org.jline.utils.ColorPalette;
+import org.jline.utils.InfoCmp;
+import org.jline.utils.NonBlockingReader;
+
+public class FastTerminal implements TerminalExt {
+
+    private final CompletableFuture<Terminal> terminal;
+
+    public FastTerminal(Callable<Terminal> builder, Consumer<Terminal> 
consumer) {
+        this.terminal = new CompletableFuture<>();
+        new Thread(
+                        () -> {
+                            try {
+                                Terminal term = builder.call();
+                                consumer.accept(term);
+                                terminal.complete(term);
+                            } catch (Exception e) {
+                                terminal.completeExceptionally(e);
+                            }
+                        },
+                        "fast-terminal-thread")
+                .start();
+    }
+
+    public TerminalExt getTerminal() {
+        try {
+            return (TerminalExt) terminal.get();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public String getName() {
+        return getTerminal().getName();
+    }
+
+    @Override
+    public SignalHandler handle(Signal signal, SignalHandler signalHandler) {
+        return getTerminal().handle(signal, signalHandler);
+    }
+
+    @Override
+    public void raise(Signal signal) {
+        getTerminal().raise(signal);
+    }
+
+    @Override
+    public NonBlockingReader reader() {
+        return getTerminal().reader();
+    }
+
+    @Override
+    public PrintWriter writer() {
+        return getTerminal().writer();
+    }
+
+    @Override
+    public Charset encoding() {
+        return getTerminal().encoding();
+    }
+
+    @Override
+    public InputStream input() {
+        return getTerminal().input();
+    }
+
+    @Override
+    public OutputStream output() {
+        return getTerminal().output();
+    }
+
+    @Override
+    public boolean canPauseResume() {
+        return getTerminal().canPauseResume();
+    }
+
+    @Override
+    public void pause() {
+        getTerminal().pause();
+    }
+
+    @Override
+    public void pause(boolean b) throws InterruptedException {
+        getTerminal().pause(b);
+    }
+
+    @Override
+    public void resume() {
+        getTerminal().resume();
+    }
+
+    @Override
+    public boolean paused() {
+        return getTerminal().paused();
+    }
+
+    @Override
+    public Attributes enterRawMode() {
+        return getTerminal().enterRawMode();
+    }
+
+    @Override
+    public boolean echo() {
+        return getTerminal().echo();
+    }
+
+    @Override
+    public boolean echo(boolean b) {
+        return getTerminal().echo(b);
+    }
+
+    @Override
+    public Attributes getAttributes() {
+        return getTerminal().getAttributes();
+    }
+
+    @Override
+    public void setAttributes(Attributes attributes) {
+        getTerminal().setAttributes(attributes);
+    }
+
+    @Override
+    public Size getSize() {
+        return getTerminal().getSize();
+    }
+
+    @Override
+    public void setSize(Size size) {
+        getTerminal().setSize(size);
+    }
+
+    @Override
+    public int getWidth() {
+        return getTerminal().getWidth();
+    }
+
+    @Override
+    public int getHeight() {
+        return getTerminal().getHeight();
+    }
+
+    @Override
+    public Size getBufferSize() {
+        return getTerminal().getBufferSize();
+    }
+
+    @Override
+    public void flush() {
+        getTerminal().flush();
+    }
+
+    @Override
+    public String getType() {
+        return getTerminal().getType();
+    }
+
+    @Override
+    public boolean puts(InfoCmp.Capability capability, Object... objects) {
+        return getTerminal().puts(capability, objects);
+    }
+
+    @Override
+    public boolean getBooleanCapability(InfoCmp.Capability capability) {
+        return getTerminal().getBooleanCapability(capability);
+    }
+
+    @Override
+    public Integer getNumericCapability(InfoCmp.Capability capability) {
+        return getTerminal().getNumericCapability(capability);
+    }
+
+    @Override
+    public String getStringCapability(InfoCmp.Capability capability) {
+        return getTerminal().getStringCapability(capability);
+    }
+
+    @Override
+    public Cursor getCursorPosition(IntConsumer intConsumer) {
+        return getTerminal().getCursorPosition(intConsumer);
+    }
+
+    @Override
+    public boolean hasMouseSupport() {
+        return getTerminal().hasMouseSupport();
+    }
+
+    @Override
+    public MouseTracking getCurrentMouseTracking() {
+        return getTerminal().getCurrentMouseTracking();
+    }
+
+    @Override
+    public boolean trackMouse(MouseTracking mouseTracking) {
+        return getTerminal().trackMouse(mouseTracking);
+    }
+
+    @Override
+    public MouseEvent readMouseEvent() {
+        return getTerminal().readMouseEvent();
+    }
+
+    @Override
+    public MouseEvent readMouseEvent(IntSupplier intSupplier) {
+        return getTerminal().readMouseEvent(intSupplier);
+    }
+
+    @Override
+    public MouseEvent readMouseEvent(String prefix) {
+        return getTerminal().readMouseEvent(prefix);
+    }
+
+    @Override
+    public MouseEvent readMouseEvent(IntSupplier reader, String prefix) {
+        return getTerminal().readMouseEvent(reader, prefix);
+    }
+
+    @Override
+    public boolean hasFocusSupport() {
+        return getTerminal().hasFocusSupport();
+    }
+
+    @Override
+    public boolean trackFocus(boolean b) {
+        return getTerminal().trackFocus(b);
+    }
+
+    @Override
+    public ColorPalette getPalette() {
+        return getTerminal().getPalette();
+    }
+
+    @Override
+    public void close() throws IOException {
+        getTerminal().close();
+    }
+
+    @Override
+    public TerminalProvider getProvider() {
+        return getTerminal().getProvider();
+    }
+
+    @Override
+    public SystemStream getSystemStream() {
+        return getTerminal().getSystemStream();
+    }
+
+    @Override
+    public String toString() {
+        return getTerminal().toString();
+    }
+}
diff --git 
a/maven-jline/src/main/java/org/apache/maven/jline/JLineMessageBuilderFactory.java
 
b/maven-jline/src/main/java/org/apache/maven/jline/JLineMessageBuilderFactory.java
new file mode 100644
index 0000000000..ff17bd59da
--- /dev/null
+++ 
b/maven-jline/src/main/java/org/apache/maven/jline/JLineMessageBuilderFactory.java
@@ -0,0 +1,156 @@
+/*
+ * 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.maven.jline;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.maven.message.MessageBuilder;
+import org.apache.maven.message.MessageBuilderFactory;
+import org.eclipse.sisu.Priority;
+import org.jline.utils.AttributedStringBuilder;
+import org.jline.utils.AttributedStyle;
+import org.jline.utils.StyleResolver;
+
+@Named
+@Singleton
+@Priority(10)
+public class JLineMessageBuilderFactory implements MessageBuilderFactory {
+    private final StyleResolver resolver;
+
+    public JLineMessageBuilderFactory() {
+        this.resolver = new MavenStyleResolver();
+    }
+
+    @Override
+    public boolean isColorEnabled() {
+        return MessageUtils.isColorEnabled();
+    }
+
+    @Override
+    public int getTerminalWidth() {
+        return MessageUtils.getTerminalWidth();
+    }
+
+    @Override
+    public MessageBuilder builder() {
+        return builder(64);
+    }
+
+    @Override
+    public MessageBuilder builder(int size) {
+        return new JlineMessageBuilder(resolver, size);
+    }
+
+    private static class JlineMessageBuilder implements MessageBuilder {
+        private final StyleResolver styleResolver;
+        private final AttributedStringBuilder builder;
+
+        private JlineMessageBuilder(StyleResolver styleResolver, int size) {
+            this.styleResolver = styleResolver;
+            this.builder = new AttributedStringBuilder(size);
+        }
+
+        @Override
+        public MessageBuilder style(String style) {
+            if (MessageUtils.isColorEnabled()) {
+                builder.style(styleResolver.resolve(style));
+            }
+            return this;
+        }
+
+        @Override
+        public MessageBuilder resetStyle() {
+            builder.style(AttributedStyle.DEFAULT);
+            return this;
+        }
+
+        @Override
+        public MessageBuilder append(CharSequence cs) {
+            builder.append(cs);
+            return this;
+        }
+
+        @Override
+        public MessageBuilder append(CharSequence cs, int start, int end) {
+            builder.append(cs, start, end);
+            return this;
+        }
+
+        @Override
+        public MessageBuilder append(char c) {
+            builder.append(c);
+            return this;
+        }
+
+        @Override
+        public MessageBuilder setLength(int length) {
+            builder.setLength(length);
+            return this;
+        }
+
+        @Override
+        public String build() {
+            return builder.toAnsi(MessageUtils.terminal);
+        }
+
+        @Override
+        public String toString() {
+            return build();
+        }
+    }
+
+    private static class MavenStyleResolver extends StyleResolver {
+        private final Map<String, AttributedStyle> styles = new 
ConcurrentHashMap<>();
+
+        private MavenStyleResolver() {
+            super(key -> {
+                String v = System.getProperty("maven.style." + key);
+                if (v == null) {
+                    v = System.getProperty("style." + key);
+                }
+                return v;
+            });
+        }
+
+        @Override
+        public AttributedStyle resolve(String spec) {
+            return styles.computeIfAbsent(spec, this::doResolve);
+        }
+
+        @Override
+        public AttributedStyle resolve(String spec, String defaultSpec) {
+            return resolve(defaultSpec != null ? spec + ":-" + defaultSpec : 
spec);
+        }
+
+        private AttributedStyle doResolve(String spec) {
+            String def = null;
+            int i = spec.indexOf(":-");
+            if (i != -1) {
+                String[] parts = spec.split(":-");
+                spec = parts[0].trim();
+                def = parts[1].trim();
+            }
+            return super.resolve(spec, def);
+        }
+    }
+}
diff --git a/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java 
b/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java
new file mode 100644
index 0000000000..94b3f10baa
--- /dev/null
+++ b/maven-jline/src/main/java/org/apache/maven/jline/MessageUtils.java
@@ -0,0 +1,130 @@
+/*
+ * 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.maven.jline;
+
+import java.util.function.Consumer;
+
+import org.apache.maven.message.MessageBuilder;
+import org.apache.maven.message.MessageBuilderFactory;
+import org.jline.jansi.AnsiConsole;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.TerminalBuilder;
+
+public class MessageUtils {
+
+    static Terminal terminal;
+    static MessageBuilderFactory messageBuilderFactory = new 
JLineMessageBuilderFactory();
+    static boolean colorEnabled = true;
+    static Thread shutdownHook;
+    static final Object STARTUP_SHUTDOWN_MONITOR = new Object();
+
+    public static void systemInstall(Terminal terminal) {
+        MessageUtils.terminal = terminal;
+    }
+
+    public static void systemInstall() {
+        systemInstall(null, null);
+    }
+
+    public static void systemInstall(Consumer<TerminalBuilder> 
builderConsumer, Consumer<Terminal> terminalConsumer) {
+        MessageUtils.terminal = new FastTerminal(
+                () -> {
+                    TerminalBuilder builder = TerminalBuilder.builder()
+                            .name("Maven")
+                            .dumb(true)
+                            .systemOutput(TerminalBuilder.SystemOutput.SysOut)
+                            .color(true);
+                    if (builderConsumer != null) {
+                        builderConsumer.accept(builder);
+                    }
+                    return builder.build();
+                },
+                terminal -> {
+                    AnsiConsole.setTerminal(terminal);
+                    AnsiConsole.systemInstall();
+                    if (terminalConsumer != null) {
+                        terminalConsumer.accept(terminal);
+                    }
+                });
+    }
+
+    public static void registerShutdownHook() {
+        if (shutdownHook == null) {
+            shutdownHook = new Thread(() -> {
+                synchronized (MessageUtils.STARTUP_SHUTDOWN_MONITOR) {
+                    MessageUtils.doSystemUninstall();
+                }
+            });
+            Runtime.getRuntime().addShutdownHook(shutdownHook);
+        }
+    }
+
+    public static void systemUninstall() {
+        doSystemUninstall();
+        if (shutdownHook != null) {
+            try {
+                Runtime.getRuntime().removeShutdownHook(shutdownHook);
+            } catch (IllegalStateException var3) {
+                // ignore
+            }
+        }
+    }
+
+    private static void doSystemUninstall() {
+        try {
+            if (terminal instanceof FastTerminal) {
+                // wait for the asynchronous systemInstall call before we 
uninstall to ensure a consistent state
+                ((FastTerminal) terminal).getTerminal();
+            }
+            AnsiConsole.systemUninstall();
+        } finally {
+            terminal = null;
+        }
+    }
+
+    public static void setColorEnabled(boolean enabled) {
+        colorEnabled = enabled;
+    }
+
+    public static boolean isColorEnabled() {
+        return colorEnabled && terminal != null;
+    }
+
+    public static int getTerminalWidth() {
+        return terminal != null ? terminal.getWidth() : -1;
+    }
+
+    public static MessageBuilder builder() {
+        return messageBuilderFactory.builder();
+    }
+
+    public static Terminal getTerminal() {
+        return terminal;
+    }
+
+    /**
+     * Remove any ANSI code from a message (colors or other escape sequences).
+     *
+     * @param msg message eventually containing ANSI codes
+     * @return the message with ANSI codes removed
+     */
+    public static String stripAnsiCodes(String msg) {
+        return msg.replaceAll("\u001B\\[[;\\d]*[ -/]*[@-~]", "");
+    }
+}
diff --git a/maven-jline/src/main/java/org/fusesource/jansi/Ansi.java 
b/maven-jline/src/main/java/org/fusesource/jansi/Ansi.java
new file mode 100644
index 0000000000..f861da0098
--- /dev/null
+++ b/maven-jline/src/main/java/org/fusesource/jansi/Ansi.java
@@ -0,0 +1,957 @@
+/*
+ * 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.fusesource.jansi;
+
+import java.util.ArrayList;
+
+/**
+ * Provides a fluent API for generating
+ * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences";>ANSI 
escape sequences</a>.
+ *
+ * This class comes from Jansi and is provided for backward compatibility
+ * with maven-shared-utils, while Maven has migrated to JLine (into which 
Jansi has been merged
+ * since JLine 3.25.0).
+ */
+@Deprecated
+@SuppressWarnings("unused")
+public class Ansi implements Appendable {
+
+    private static final char FIRST_ESC_CHAR = 27;
+    private static final char SECOND_ESC_CHAR = '[';
+
+    /**
+     * <a href="https://en.wikipedia.org/wiki/ANSI_escape_code#Colors";>ANSI 8 
colors</a> for fluent API
+     */
+    public enum Color {
+        BLACK(0, "BLACK"),
+        RED(1, "RED"),
+        GREEN(2, "GREEN"),
+        YELLOW(3, "YELLOW"),
+        BLUE(4, "BLUE"),
+        MAGENTA(5, "MAGENTA"),
+        CYAN(6, "CYAN"),
+        WHITE(7, "WHITE"),
+        DEFAULT(9, "DEFAULT");
+
+        private final int value;
+        private final String name;
+
+        Color(int index, String name) {
+            this.value = index;
+            this.name = name;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+        public int value() {
+            return value;
+        }
+
+        public int fg() {
+            return value + 30;
+        }
+
+        public int bg() {
+            return value + 40;
+        }
+
+        public int fgBright() {
+            return value + 90;
+        }
+
+        public int bgBright() {
+            return value + 100;
+        }
+    }
+
+    /**
+     * Display attributes, also known as
+     * <a 
href="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">SGR
+     * (Select Graphic Rendition) parameters</a>.
+     */
+    public enum Attribute {
+        RESET(0, "RESET"),
+        INTENSITY_BOLD(1, "INTENSITY_BOLD"),
+        INTENSITY_FAINT(2, "INTENSITY_FAINT"),
+        ITALIC(3, "ITALIC_ON"),
+        UNDERLINE(4, "UNDERLINE_ON"),
+        BLINK_SLOW(5, "BLINK_SLOW"),
+        BLINK_FAST(6, "BLINK_FAST"),
+        NEGATIVE_ON(7, "NEGATIVE_ON"),
+        CONCEAL_ON(8, "CONCEAL_ON"),
+        STRIKETHROUGH_ON(9, "STRIKETHROUGH_ON"),
+        UNDERLINE_DOUBLE(21, "UNDERLINE_DOUBLE"),
+        INTENSITY_BOLD_OFF(22, "INTENSITY_BOLD_OFF"),
+        ITALIC_OFF(23, "ITALIC_OFF"),
+        UNDERLINE_OFF(24, "UNDERLINE_OFF"),
+        BLINK_OFF(25, "BLINK_OFF"),
+        NEGATIVE_OFF(27, "NEGATIVE_OFF"),
+        CONCEAL_OFF(28, "CONCEAL_OFF"),
+        STRIKETHROUGH_OFF(29, "STRIKETHROUGH_OFF");
+
+        private final int value;
+        private final String name;
+
+        Attribute(int index, String name) {
+            this.value = index;
+            this.name = name;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+        public int value() {
+            return value;
+        }
+    }
+
+    /**
+     * ED (Erase in Display) / EL (Erase in Line) parameter (see
+     * <a 
href="https://en.wikipedia.org/wiki/ANSI_escape_code#CSI_sequences";>CSI 
sequence J and K</a>)
+     * @see Ansi#eraseScreen(Erase)
+     * @see Ansi#eraseLine(Erase)
+     */
+    public enum Erase {
+        FORWARD(0, "FORWARD"),
+        BACKWARD(1, "BACKWARD"),
+        ALL(2, "ALL");
+
+        private final int value;
+        private final String name;
+
+        Erase(int index, String name) {
+            this.value = index;
+            this.name = name;
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+        public int value() {
+            return value;
+        }
+    }
+
+    @FunctionalInterface
+    public interface Consumer {
+        void apply(Ansi ansi);
+    }
+
+    public static boolean isEnabled() {
+        return org.apache.maven.jline.MessageUtils.isColorEnabled() && 
org.jline.jansi.Ansi.isEnabled();
+    }
+
+    public static void setEnabled(final boolean flag) {
+        org.jline.jansi.Ansi.setEnabled(flag);
+    }
+
+    public static Ansi ansi() {
+        if (isEnabled()) {
+            return new Ansi();
+        } else {
+            return new NoAnsi();
+        }
+    }
+
+    public static Ansi ansi(StringBuilder builder) {
+        if (isEnabled()) {
+            return new Ansi(builder);
+        } else {
+            return new NoAnsi(builder);
+        }
+    }
+
+    public static Ansi ansi(int size) {
+        if (isEnabled()) {
+            return new Ansi(size);
+        } else {
+            return new NoAnsi(size);
+        }
+    }
+
+    private static class NoAnsi extends Ansi {
+        NoAnsi() {
+            super();
+        }
+
+        NoAnsi(int size) {
+            super(size);
+        }
+
+        NoAnsi(StringBuilder builder) {
+            super(builder);
+        }
+
+        @Override
+        public Ansi fg(Color color) {
+            return this;
+        }
+
+        @Override
+        public Ansi bg(Color color) {
+            return this;
+        }
+
+        @Override
+        public Ansi fgBright(Color color) {
+            return this;
+        }
+
+        @Override
+        public Ansi bgBright(Color color) {
+            return this;
+        }
+
+        @Override
+        public Ansi fg(int color) {
+            return this;
+        }
+
+        @Override
+        public Ansi fgRgb(int r, int g, int b) {
+            return this;
+        }
+
+        @Override
+        public Ansi bg(int color) {
+            return this;
+        }
+
+        @Override
+        public Ansi bgRgb(int r, int g, int b) {
+            return this;
+        }
+
+        @Override
+        public Ansi a(Attribute attribute) {
+            return this;
+        }
+
+        @Override
+        public Ansi cursor(int row, int column) {
+            return this;
+        }
+
+        @Override
+        public Ansi cursorToColumn(int x) {
+            return this;
+        }
+
+        @Override
+        public Ansi cursorUp(int y) {
+            return this;
+        }
+
+        @Override
+        public Ansi cursorRight(int x) {
+            return this;
+        }
+
+        @Override
+        public Ansi cursorDown(int y) {
+            return this;
+        }
+
+        @Override
+        public Ansi cursorLeft(int x) {
+            return this;
+        }
+
+        @Override
+        public Ansi cursorDownLine() {
+            return this;
+        }
+
+        @Override
+        public Ansi cursorDownLine(final int n) {
+            return this;
+        }
+
+        @Override
+        public Ansi cursorUpLine() {
+            return this;
+        }
+
+        @Override
+        public Ansi cursorUpLine(final int n) {
+            return this;
+        }
+
+        @Override
+        public Ansi eraseScreen() {
+            return this;
+        }
+
+        @Override
+        public Ansi eraseScreen(Erase kind) {
+            return this;
+        }
+
+        @Override
+        public Ansi eraseLine() {
+            return this;
+        }
+
+        @Override
+        public Ansi eraseLine(Erase kind) {
+            return this;
+        }
+
+        @Override
+        public Ansi scrollUp(int rows) {
+            return this;
+        }
+
+        @Override
+        public Ansi scrollDown(int rows) {
+            return this;
+        }
+
+        @Override
+        public Ansi saveCursorPosition() {
+            return this;
+        }
+
+        @Override
+        @Deprecated
+        public Ansi restorCursorPosition() {
+            return this;
+        }
+
+        @Override
+        public Ansi restoreCursorPosition() {
+            return this;
+        }
+
+        @Override
+        public Ansi reset() {
+            return this;
+        }
+    }
+
+    private final StringBuilder builder;
+    private final ArrayList<Integer> attributeOptions = new ArrayList<>(5);
+
+    public Ansi() {
+        this(new StringBuilder(80));
+    }
+
+    public Ansi(Ansi parent) {
+        this(new StringBuilder(parent.builder));
+        attributeOptions.addAll(parent.attributeOptions);
+    }
+
+    public Ansi(int size) {
+        this(new StringBuilder(size));
+    }
+
+    public Ansi(StringBuilder builder) {
+        this.builder = builder;
+    }
+
+    public Ansi fg(Color color) {
+        attributeOptions.add(color.fg());
+        return this;
+    }
+
+    public Ansi fg(int color) {
+        attributeOptions.add(38);
+        attributeOptions.add(5);
+        attributeOptions.add(color & 0xff);
+        return this;
+    }
+
+    public Ansi fgRgb(int color) {
+        return fgRgb(color >> 16, color >> 8, color);
+    }
+
+    public Ansi fgRgb(int r, int g, int b) {
+        attributeOptions.add(38);
+        attributeOptions.add(2);
+        attributeOptions.add(r & 0xff);
+        attributeOptions.add(g & 0xff);
+        attributeOptions.add(b & 0xff);
+        return this;
+    }
+
+    public Ansi fgBlack() {
+        return this.fg(Color.BLACK);
+    }
+
+    public Ansi fgBlue() {
+        return this.fg(Color.BLUE);
+    }
+
+    public Ansi fgCyan() {
+        return this.fg(Color.CYAN);
+    }
+
+    public Ansi fgDefault() {
+        return this.fg(Color.DEFAULT);
+    }
+
+    public Ansi fgGreen() {
+        return this.fg(Color.GREEN);
+    }
+
+    public Ansi fgMagenta() {
+        return this.fg(Color.MAGENTA);
+    }
+
+    public Ansi fgRed() {
+        return this.fg(Color.RED);
+    }
+
+    public Ansi fgYellow() {
+        return this.fg(Color.YELLOW);
+    }
+
+    public Ansi bg(Color color) {
+        attributeOptions.add(color.bg());
+        return this;
+    }
+
+    public Ansi bg(int color) {
+        attributeOptions.add(48);
+        attributeOptions.add(5);
+        attributeOptions.add(color & 0xff);
+        return this;
+    }
+
+    public Ansi bgRgb(int color) {
+        return bgRgb(color >> 16, color >> 8, color);
+    }
+
+    public Ansi bgRgb(int r, int g, int b) {
+        attributeOptions.add(48);
+        attributeOptions.add(2);
+        attributeOptions.add(r & 0xff);
+        attributeOptions.add(g & 0xff);
+        attributeOptions.add(b & 0xff);
+        return this;
+    }
+
+    public Ansi bgCyan() {
+        return this.bg(Color.CYAN);
+    }
+
+    public Ansi bgDefault() {
+        return this.bg(Color.DEFAULT);
+    }
+
+    public Ansi bgGreen() {
+        return this.bg(Color.GREEN);
+    }
+
+    public Ansi bgMagenta() {
+        return this.bg(Color.MAGENTA);
+    }
+
+    public Ansi bgRed() {
+        return this.bg(Color.RED);
+    }
+
+    public Ansi bgYellow() {
+        return this.bg(Color.YELLOW);
+    }
+
+    public Ansi fgBright(Color color) {
+        attributeOptions.add(color.fgBright());
+        return this;
+    }
+
+    public Ansi fgBrightBlack() {
+        return this.fgBright(Color.BLACK);
+    }
+
+    public Ansi fgBrightBlue() {
+        return this.fgBright(Color.BLUE);
+    }
+
+    public Ansi fgBrightCyan() {
+        return this.fgBright(Color.CYAN);
+    }
+
+    public Ansi fgBrightDefault() {
+        return this.fgBright(Color.DEFAULT);
+    }
+
+    public Ansi fgBrightGreen() {
+        return this.fgBright(Color.GREEN);
+    }
+
+    public Ansi fgBrightMagenta() {
+        return this.fgBright(Color.MAGENTA);
+    }
+
+    public Ansi fgBrightRed() {
+        return this.fgBright(Color.RED);
+    }
+
+    public Ansi fgBrightYellow() {
+        return this.fgBright(Color.YELLOW);
+    }
+
+    public Ansi bgBright(Color color) {
+        attributeOptions.add(color.bgBright());
+        return this;
+    }
+
+    public Ansi bgBrightCyan() {
+        return this.bgBright(Color.CYAN);
+    }
+
+    public Ansi bgBrightDefault() {
+        return this.bgBright(Color.DEFAULT);
+    }
+
+    public Ansi bgBrightGreen() {
+        return this.bgBright(Color.GREEN);
+    }
+
+    public Ansi bgBrightMagenta() {
+        return this.bgBright(Color.MAGENTA);
+    }
+
+    public Ansi bgBrightRed() {
+        return this.bgBright(Color.RED);
+    }
+
+    public Ansi bgBrightYellow() {
+        return this.bgBright(Color.YELLOW);
+    }
+
+    public Ansi a(Attribute attribute) {
+        attributeOptions.add(attribute.value());
+        return this;
+    }
+
+    /**
+     * Moves the cursor to row n, column m. The values are 1-based.
+     * Any values less than 1 are mapped to 1.
+     *
+     * @param row    row (1-based) from top
+     * @param column column (1 based) from left
+     * @return this Ansi instance
+     */
+    public Ansi cursor(final int row, final int column) {
+        return appendEscapeSequence('H', Math.max(1, row), Math.max(1, 
column));
+    }
+
+    /**
+     * Moves the cursor to column n. The parameter n is 1-based.
+     * If n is less than 1 it is moved to the first column.
+     *
+     * @param x the index (1-based) of the column to move to
+     * @return this Ansi instance
+     */
+    public Ansi cursorToColumn(final int x) {
+        return appendEscapeSequence('G', Math.max(1, x));
+    }
+
+    /**
+     * Moves the cursor up. If the parameter y is negative it moves the cursor 
down.
+     *
+     * @param y the number of lines to move up
+     * @return this Ansi instance
+     */
+    public Ansi cursorUp(final int y) {
+        return y > 0 ? appendEscapeSequence('A', y) : y < 0 ? cursorDown(-y) : 
this;
+    }
+
+    /**
+     * Moves the cursor down. If the parameter y is negative it moves the 
cursor up.
+     *
+     * @param y the number of lines to move down
+     * @return this Ansi instance
+     */
+    public Ansi cursorDown(final int y) {
+        return y > 0 ? appendEscapeSequence('B', y) : y < 0 ? cursorUp(-y) : 
this;
+    }
+
+    /**
+     * Moves the cursor right. If the parameter x is negative it moves the 
cursor left.
+     *
+     * @param x the number of characters to move right
+     * @return this Ansi instance
+     */
+    public Ansi cursorRight(final int x) {
+        return x > 0 ? appendEscapeSequence('C', x) : x < 0 ? cursorLeft(-x) : 
this;
+    }
+
+    /**
+     * Moves the cursor left. If the parameter x is negative it moves the 
cursor right.
+     *
+     * @param x the number of characters to move left
+     * @return this Ansi instance
+     */
+    public Ansi cursorLeft(final int x) {
+        return x > 0 ? appendEscapeSequence('D', x) : x < 0 ? cursorRight(-x) 
: this;
+    }
+
+    /**
+     * Moves the cursor relative to the current position. The cursor is moved 
right if x is
+     * positive, left if negative and down if y is positive and up if negative.
+     *
+     * @param x the number of characters to move horizontally
+     * @param y the number of lines to move vertically
+     * @return this Ansi instance
+     * @since 2.2
+     */
+    public Ansi cursorMove(final int x, final int y) {
+        return cursorRight(x).cursorDown(y);
+    }
+
+    /**
+     * Moves the cursor to the beginning of the line below.
+     *
+     * @return this Ansi instance
+     */
+    public Ansi cursorDownLine() {
+        return appendEscapeSequence('E');
+    }
+
+    /**
+     * Moves the cursor to the beginning of the n-th line below. If the 
parameter n is negative it
+     * moves the cursor to the beginning of the n-th line above.
+     *
+     * @param n the number of lines to move the cursor
+     * @return this Ansi instance
+     */
+    public Ansi cursorDownLine(final int n) {
+        return n < 0 ? cursorUpLine(-n) : appendEscapeSequence('E', n);
+    }
+
+    /**
+     * Moves the cursor to the beginning of the line above.
+     *
+     * @return this Ansi instance
+     */
+    public Ansi cursorUpLine() {
+        return appendEscapeSequence('F');
+    }
+
+    /**
+     * Moves the cursor to the beginning of the n-th line above. If the 
parameter n is negative it
+     * moves the cursor to the beginning of the n-th line below.
+     *
+     * @param n the number of lines to move the cursor
+     * @return this Ansi instance
+     */
+    public Ansi cursorUpLine(final int n) {
+        return n < 0 ? cursorDownLine(-n) : appendEscapeSequence('F', n);
+    }
+
+    public Ansi eraseScreen() {
+        return appendEscapeSequence('J', Erase.ALL.value());
+    }
+
+    public Ansi eraseScreen(final Erase kind) {
+        return appendEscapeSequence('J', kind.value());
+    }
+
+    public Ansi eraseLine() {
+        return appendEscapeSequence('K');
+    }
+
+    public Ansi eraseLine(final Erase kind) {
+        return appendEscapeSequence('K', kind.value());
+    }
+
+    public Ansi scrollUp(final int rows) {
+        if (rows == Integer.MIN_VALUE) {
+            return scrollDown(Integer.MAX_VALUE);
+        }
+        return rows > 0 ? appendEscapeSequence('S', rows) : rows < 0 ? 
scrollDown(-rows) : this;
+    }
+
+    public Ansi scrollDown(final int rows) {
+        if (rows == Integer.MIN_VALUE) {
+            return scrollUp(Integer.MAX_VALUE);
+        }
+        return rows > 0 ? appendEscapeSequence('T', rows) : rows < 0 ? 
scrollUp(-rows) : this;
+    }
+
+    @Deprecated
+    public Ansi restorCursorPosition() {
+        return restoreCursorPosition();
+    }
+
+    public Ansi saveCursorPosition() {
+        saveCursorPositionSCO();
+        return saveCursorPositionDEC();
+    }
+
+    // SCO command
+    public Ansi saveCursorPositionSCO() {
+        return appendEscapeSequence('s');
+    }
+
+    // DEC command
+    public Ansi saveCursorPositionDEC() {
+        builder.append(FIRST_ESC_CHAR);
+        builder.append('7');
+        return this;
+    }
+
+    public Ansi restoreCursorPosition() {
+        restoreCursorPositionSCO();
+        return restoreCursorPositionDEC();
+    }
+
+    // SCO command
+    public Ansi restoreCursorPositionSCO() {
+        return appendEscapeSequence('u');
+    }
+
+    // DEC command
+    public Ansi restoreCursorPositionDEC() {
+        builder.append(FIRST_ESC_CHAR);
+        builder.append('8');
+        return this;
+    }
+
+    public Ansi reset() {
+        return a(Attribute.RESET);
+    }
+
+    public Ansi bold() {
+        return a(Attribute.INTENSITY_BOLD);
+    }
+
+    public Ansi boldOff() {
+        return a(Attribute.INTENSITY_BOLD_OFF);
+    }
+
+    public Ansi a(String value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi a(boolean value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi a(char value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi a(char[] value, int offset, int len) {
+        flushAttributes();
+        builder.append(value, offset, len);
+        return this;
+    }
+
+    public Ansi a(char[] value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi a(CharSequence value, int start, int end) {
+        flushAttributes();
+        builder.append(value, start, end);
+        return this;
+    }
+
+    public Ansi a(CharSequence value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi a(double value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi a(float value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi a(int value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi a(long value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi a(Object value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi a(StringBuffer value) {
+        flushAttributes();
+        builder.append(value);
+        return this;
+    }
+
+    public Ansi newline() {
+        flushAttributes();
+        builder.append(System.lineSeparator());
+        return this;
+    }
+
+    public Ansi format(String pattern, Object... args) {
+        flushAttributes();
+        builder.append(String.format(pattern, args));
+        return this;
+    }
+
+    /**
+     * Applies another function to this Ansi instance.
+     *
+     * @param fun the function to apply
+     * @return this Ansi instance
+     * @since 2.2
+     */
+    public Ansi apply(Consumer fun) {
+        fun.apply(this);
+        return this;
+    }
+
+    /**
+     * Uses the {@link org.jline.jansi.AnsiRenderer}
+     * to generate the ANSI escape sequences for the supplied text.
+     *
+     * @param text text
+     * @return this
+     * @since 2.2
+     */
+    public Ansi render(final String text) {
+        a(new org.jline.jansi.Ansi().render(text).toString());
+        return this;
+    }
+
+    /**
+     * String formats and renders the supplied arguments.  Uses the {@link 
org.jline.jansi.AnsiRenderer}
+     * to generate the ANSI escape sequences.
+     *
+     * @param text format
+     * @param args arguments
+     * @return this
+     * @since 2.2
+     */
+    public Ansi render(final String text, Object... args) {
+        a(String.format(new org.jline.jansi.Ansi().render(text).toString(), 
args));
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        flushAttributes();
+        return builder.toString();
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    // Private Helper Methods
+    ///////////////////////////////////////////////////////////////////
+
+    private Ansi appendEscapeSequence(char command) {
+        flushAttributes();
+        builder.append(FIRST_ESC_CHAR);
+        builder.append(SECOND_ESC_CHAR);
+        builder.append(command);
+        return this;
+    }
+
+    private Ansi appendEscapeSequence(char command, int option) {
+        flushAttributes();
+        builder.append(FIRST_ESC_CHAR);
+        builder.append(SECOND_ESC_CHAR);
+        builder.append(option);
+        builder.append(command);
+        return this;
+    }
+
+    private Ansi appendEscapeSequence(char command, Object... options) {
+        flushAttributes();
+        return doAppendEscapeSequence(command, options);
+    }
+
+    private void flushAttributes() {
+        if (attributeOptions.isEmpty()) {
+            return;
+        }
+        if (attributeOptions.size() == 1 && attributeOptions.get(0) == 0) {
+            builder.append(FIRST_ESC_CHAR);
+            builder.append(SECOND_ESC_CHAR);
+            builder.append('m');
+        } else {
+            doAppendEscapeSequence('m', attributeOptions.toArray());
+        }
+        attributeOptions.clear();
+    }
+
+    private Ansi doAppendEscapeSequence(char command, Object... options) {
+        builder.append(FIRST_ESC_CHAR);
+        builder.append(SECOND_ESC_CHAR);
+        int size = options.length;
+        for (int i = 0; i < size; i++) {
+            if (i != 0) {
+                builder.append(';');
+            }
+            if (options[i] != null) {
+                builder.append(options[i]);
+            }
+        }
+        builder.append(command);
+        return this;
+    }
+
+    @Override
+    public Ansi append(CharSequence csq) {
+        builder.append(csq);
+        return this;
+    }
+
+    @Override
+    public Ansi append(CharSequence csq, int start, int end) {
+        builder.append(csq, start, end);
+        return this;
+    }
+
+    @Override
+    public Ansi append(char c) {
+        builder.append(c);
+        return this;
+    }
+}
diff --git a/maven-jline/src/site/site.xml b/maven-jline/src/site/site.xml
new file mode 100644
index 0000000000..4ee3b709cf
--- /dev/null
+++ b/maven-jline/src/site/site.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+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.
+-->
+
+<site xmlns="http://maven.apache.org/SITE/2.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+  xsi:schemaLocation="http://maven.apache.org/SITE/2.0.0 
https://maven.apache.org/xsd/site-2.0.0.xsd";>
+
+  <edit>${project.scm.url}</edit>
+
+  <body>
+    <menu name="Overview">
+      <item name="Introduction" href="index.html"/>
+      <item name="Javadocs" href="apidocs/index.html"/>
+      <item name="Source Xref" href="xref/index.html"/>
+      <!--item name="FAQ" href="faq.html"/-->
+    </menu>
+  </body>
+</site>
diff --git a/maven-slf4j-provider/pom.xml b/maven-slf4j-provider/pom.xml
index 12efb164c3..bee59b4c03 100644
--- a/maven-slf4j-provider/pom.xml
+++ b/maven-slf4j-provider/pom.xml
@@ -38,8 +38,8 @@ under the License.
       <artifactId>slf4j-api</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.apache.maven.shared</groupId>
-      <artifactId>maven-shared-utils</artifactId>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-jline</artifactId>
     </dependency>
     <dependency>
       <groupId>org.junit.jupiter</groupId>
diff --git 
a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java 
b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java
index 0d9b9c390e..0ef4d2f9af 100644
--- a/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java
+++ b/maven-slf4j-provider/src/main/java/org/slf4j/impl/MavenSimpleLogger.java
@@ -20,8 +20,7 @@
 
 import java.io.PrintStream;
 
-import static org.apache.maven.shared.utils.logging.MessageUtils.buffer;
-import static org.apache.maven.shared.utils.logging.MessageUtils.level;
+import static org.apache.maven.jline.MessageUtils.builder;
 
 /**
  * Logger for Maven, that support colorization of levels and stacktraces.
@@ -29,6 +28,7 @@
  * @since 3.5.0
  */
 public class MavenSimpleLogger extends SimpleLogger {
+
     MavenSimpleLogger(String name) {
         super(name);
     }
@@ -37,16 +37,16 @@ public class MavenSimpleLogger extends SimpleLogger {
     protected String renderLevel(int level) {
         switch (level) {
             case LOG_LEVEL_TRACE:
-                return level().debug("TRACE").toString();
+                return builder().trace("TRACE").toString();
             case LOG_LEVEL_DEBUG:
-                return level().debug("DEBUG").toString();
+                return builder().debug("DEBUG").toString();
             case LOG_LEVEL_INFO:
-                return level().info("INFO").toString();
+                return builder().info("INFO").toString();
             case LOG_LEVEL_WARN:
-                return level().warning("WARNING").toString();
+                return builder().warning("WARNING").toString();
             case LOG_LEVEL_ERROR:
             default:
-                return level().error("ERROR").toString();
+                return builder().error("ERROR").toString();
         }
     }
 
@@ -55,10 +55,10 @@ protected void writeThrowable(Throwable t, PrintStream 
stream) {
         if (t == null) {
             return;
         }
-        stream.print(buffer().failure(t.getClass().getName()));
+        stream.print(builder().failure(t.getClass().getName()));
         if (t.getMessage() != null) {
             stream.print(": ");
-            stream.print(buffer().failure(t.getMessage()));
+            stream.print(builder().failure(t.getMessage()));
         }
         stream.println();
 
@@ -69,9 +69,9 @@ private void printStackTrace(Throwable t, PrintStream stream, 
String prefix) {
         for (StackTraceElement e : t.getStackTrace()) {
             stream.print(prefix);
             stream.print("    ");
-            stream.print(buffer().strong("at"));
+            stream.print(builder().strong("at"));
             stream.print(" " + e.getClassName() + "." + e.getMethodName());
-            stream.print(buffer().a(" (").strong(getLocation(e)).a(")"));
+            stream.print(builder().a(" (").strong(getLocation(e)).a(")"));
             stream.println();
         }
         for (Throwable se : t.getSuppressed()) {
@@ -84,10 +84,10 @@ private void printStackTrace(Throwable t, PrintStream 
stream, String prefix) {
     }
 
     private void writeThrowable(Throwable t, PrintStream stream, String 
caption, String prefix) {
-        stream.print(buffer().a(prefix).strong(caption).a(": 
").a(t.getClass().getName()));
+        stream.print(builder().a(prefix).strong(caption).a(": 
").a(t.getClass().getName()));
         if (t.getMessage() != null) {
             stream.print(": ");
-            stream.print(buffer().failure(t.getMessage()));
+            stream.print(builder().failure(t.getMessage()));
         }
         stream.println();
 
diff --git a/pom.xml b/pom.xml
index 1b93d11684..e0b72c0dda 100644
--- a/pom.xml
+++ b/pom.xml
@@ -97,6 +97,7 @@ under the License.
     <module>maven-repository-metadata</module>
     <module>maven-slf4j-provider</module>
     <module>maven-embedder</module>
+    <module>maven-jline</module>
     <module>maven-compat</module>
     <module>apache-maven</module>
   </modules>
@@ -156,6 +157,7 @@ under the License.
     <checkstyle.violation.ignore>None</checkstyle.violation.ignore>
     <checkstyle.excludes>**/package-info.java</checkstyle.excludes>
     
<project.build.outputTimestamp>2026-03-24T21:15:50Z</project.build.outputTimestamp>
+    <jlineVersion>3.30.9</jlineVersion>
   </properties>
 
   <!--bootstrap-start-comment-->
@@ -229,6 +231,11 @@ under the License.
         <artifactId>maven-slf4j-provider</artifactId>
         <version>${project.version}</version>
       </dependency>
+      <dependency>
+        <groupId>org.apache.maven</groupId>
+        <artifactId>maven-jline</artifactId>
+        <version>${project.version}</version>
+      </dependency>
       <!--bootstrap-end-comment-->
       <!--  Plexus -->
       <dependency>
@@ -339,21 +346,24 @@ under the License.
         <version>${plexusInterpolationVersion}</version>
       </dependency>
       <dependency>
-        <groupId>org.apache.maven.shared</groupId>
-        <artifactId>maven-shared-utils</artifactId>
-        <version>3.4.2</version>
-        <exclusions>
-          <!-- We use org.apache.maven.shared.utils.logging only in Maven Core 
-->
-          <exclusion>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-          </exclusion>
-        </exclusions>
+        <groupId>org.jline</groupId>
+        <artifactId>jansi-core</artifactId>
+        <version>${jlineVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.jline</groupId>
+        <artifactId>jline-terminal</artifactId>
+        <version>${jlineVersion}</version>
+      </dependency>
+      <dependency>
+        <groupId>org.jline</groupId>
+        <artifactId>jline-terminal-jni</artifactId>
+        <version>${jlineVersion}</version>
       </dependency>
       <dependency>
-        <groupId>org.fusesource.jansi</groupId>
-        <artifactId>jansi</artifactId>
-        <version>2.4.3</version>
+        <groupId>org.jline</groupId>
+        <artifactId>jline-terminal-ffm</artifactId>
+        <version>${jlineVersion}</version>
       </dependency>
       <dependency>
         <groupId>org.slf4j</groupId>
@@ -686,6 +696,22 @@ under the License.
               <fail>true</fail>
             </configuration>
           </execution>
+          <execution>
+            <id>enforce-bytecode-version</id>
+            <goals>
+              <goal>enforce</goal>
+            </goals>
+            <configuration>
+              <rules>
+                <enforceBytecodeVersion>
+                  <excludes>
+                    <exclude>org.jline:jline-terminal-ffm</exclude>
+                  </excludes>
+                </enforceBytecodeVersion>
+              </rules>
+              <fail>true</fail>
+            </configuration>
+          </execution>
         </executions>
       </plugin>
     </plugins>

Reply via email to