1 Nuts Application Framework

Nuts is much more than a package manager — it is a powerful and extensible runtime framework for Java applications. While it provides robust support for dependency resolution and artifact management compatible with Maven repositories, Nuts also delivers a comprehensive suite of libraries and services that turn it into a foundation for building modular, interactive, and automated tools.Whether you're developing a CLI utility, a scripting engine, a DevOps automation tool, or a distributed plugin-based system, Nuts provides the scaffolding to manage dependencies, sessions, input/output, configuration, extensions, and workspace isolation — all in a portable and embeddable way.

nuts is a package manager that can be embedded in your application and hence present a solid Application Framework and tooling to make the applicable more robust and more portable.

nuts as a Framework (or Nuts Application Framework, or even simpler : NAF):

  • Adds support for Application Lifecycle (Hooks for install, update, uninstall)
  • Adds support for auto update
  • Adds support for isolated input/output (via session in/out)
  • Adds support for Desktop Integration
    • Adds Shortcuts, Menus
    • Adds Aliases
  • Adds support for Base Directory API
    • API to manage per application directories (log, cache, config,...)
  • Adds support for Base Commandline API
    • standardized commandline options
    • inherit common options (--table, --json, ...)

Dual Role by Design

At its core, Nuts plays two essential roles:
  • As a package manager, Nuts handles runtime dependency resolution, dynamic execution of artifacts, and integration with Maven-compatible repositories.
  • As a framework, Nuts offers a rich set of APIs to handle I/O, configuration, text formatting, command execution, file systems, compressed archives, and more — helping developers write cleaner, more powerful, and portable applications.

Build, Embed, Automate

Nuts is designed to work in multiple modes of operation:
  • Run Java programs and tools directly from repositories without downloading or installing them manually.
  • Integrate it as a library to give your own application full access to the Nuts runtime capabilities.
  • Use it as a DevOps engine to build repeatable scripts, installers, or deployment workflows.
  • Use Nuts as a command-line tool to install, run, or deploy applications.
  • Build your own ecosystem of versioned components, launchers, and tools powered by Nuts' modular runtime.
In short, Nuts is a full-stack developer toolbox — part package manager, part framework, part scripting engine — designed to make modern Java development more dynamic, composable, and automation-friendly.

Whether you are:

  • Building CLI tools that need versioned plugins and extensible command handling,
  • Developing DevOps utilities with integrated workspace/session management,
  • Creating modular applications that dynamically load artifacts at runtime,
  • Writing cross-environment installers, launchers, or monitoring tools,
  • Or designing educational platforms, scripting DSLs, or JVM-based OS abstractions,
Nuts delivers the foundational infrastructure so you can focus on features, not plumbing. Key CapabilitiesThe NAF framework offers a wide set of features: ✅ Package & Dependency Management
  • Compatible with Maven repositories and standards
  • Supports local, remote, and custom repositories
  • Enables runtime dependency resolution and dynamic artifact loading
✅ Configurable Workspaces & Sessions
  • Isolated and shared workspace models with full lifecycle control
  • Sessions encapsulate runtime configuration, user preferences, I/O handling, logging, and output styles
  • Built-in support for dry-run, trace, confirmation, GUI/headless modes
✅ Structured I/O & Logging
  • Supports semantic console output (NTF format) with colors, styles, and structured messages
  • Unified API for stdout/stderr, input, logging, formatting, and piping
  • Handles both human-friendly and machine-readable outputs (e.g., JSON, XML, Props, Tree)
✅ Filesystem & Networking Utilities
  • Provides advanced file abstraction via NPath
  • Built-in support for streaming, compressing, uncompressing, digesting, and manipulating file trees
  • Unified access to HTTP, classpath, resources, and virtual filesystems
✅ Extensibility & Integration
  • Supports modular extension points: listeners, commands, install hooks, and repositories
  • Embedded scripting with support for Java source snippets and other languages
  • Can be integrated into Spring, JavaFX, Swing, or any plain Java project
✅ Developer & DevOps Friendly
  • Features like NRun, NExec, NShell, and NOps make it ideal for automation
  • Cross-platform support: Linux, macOS, Windows
  • Zero external runtime dependencies — deploy as a bare JAR, with automatic resolution on first use

Why Use NAF?

Unlike traditional libraries or shell utilities, NAF brings together the flexibility of a modern scripting environment, the structure of a dependency-aware runtime, and the developer convenience of a polished CLI framework — all in one embeddable package. You can think of it as your:

  • Maven-like package manager, but runtime aware
  • Command-line framework, but fully pluggable
  • Launcher platform, but without installation or configuration hurdles
  • Shell scripting toolkit, but type-safe and Java-native

With NAF, the boundary between development and runtime fades away, letting you write, deploy, and evolve tools without sacrificing portability, maintainability, or developer experience.

Lightweight, Modular, and Composable

  • Modular architecture built around workspaces, sessions, and execution contexts.
  • Supports multi-repository, multi-version, and multi-runtime setups.
  • Helps maintain separation between application logic and environment/runtime logic — a key feature for scripting, testing, and dynamic execution.

Not Just Tools — Ecosystems

NAF encourages composability: it enables you to define reusable, versioned components that can evolve independently and work across environments. It brings a new level of reusability to the Java ecosystem, bridging the gap between packaged applications, scripts, and shared libraries. Whether you're building a command-line tool, a plugin system, a runtime launcher, or a portable enterprise toolkit — Nuts gives you a structured, scalable, and developer-friendly platform to do it all.

1.1 Hello World

To make use of NAF you need add the dependency .thevpc.nuts#nuts:0.8.6 and provide a hint to maven to point to the right repository https://maven.thevpc.net

Configure your pom.xml


    <dependencies>
        <dependency><groupId>net.thevpc.nuts</groupId><artifactId>nuts</artifactId><version>0.8.6</version></dependency>
    </dependencies>
    <repositories>
        <repository><id>thevpc</id><url>https://maven.thevpc.net</url></repository>
    </repositories>
    

Bootstrap your Workspace


    import net.thevpc.nuts.*;
    public class HelloWorld {
        public static void main(String[] args){
            Nuts.require();
        }
    }

Use NAF components, anywhere in your app


    import net.thevpc.nuts.*;
    public class HelloWorld {
        public static void main(String[] args){
            Nuts.require(); // <-- this command should be called only once per app
            NOut.println(NMsg.ofC("Hello %s","World"));
            runMethod();
        }
        public static void runMethod(){
            NOut.println(NMsg.ofV("Hello $v",NMaps.of("v","World")));
        }
    }


2 Command Line Processing

NCmdLine is a flexible and OS-portable command line parser that supports short and long options, boolean flags, valued options, non-options, comments, and argument files. It is the recommended way to handle command line input in NAF applications.

NCmdLine supports command lines in the following form :


my-app -o=y --name='some name' -ex --extra value arg1 arg2

where the command here supports short and long options (short ones are -o, -e and -x, where -e and -x are combined as -ex), and of course non options or regular arguments (here arg1 and arg2). Note also that value could be interpreted as a value for --extra (or not; depending on how you configure your parser, for this option).

2.1 Creating NCmdLine instance

Command line can either be created manually or parsed.

Manual creation

You can create a command by providing the arguments:

    NCmdLine c1= NCmdLine.ofArgs("ls","-l");

Parsing from string

You can also create a commandline by parsing a string.

NAF supports multiple commandline dialects (bash/linux, bat/Windows,...)


    NCmdLine c1= NCmdLine.of("ls -l", NShellFamily.BASH);
When you do not specify the NShellFamily, runtime OS default is considered.

    NCmdLine c1= NCmdLine.parse("ls -l");

Portable Parsing

You would want to be portable across all operating systems, you can use ofDefault method.


    NCmdLine c1= NCmdLine.ofDefault("ls -l");

2.2 Command Line Elements

Options vs Non-options

In NAF, command line arguments are categorized into three main types:

  • Options : Arguments that start with - or + and may carry a value.
  • Non-Options : Arguments that do not start with - or +. Typically filenames, commands, or positional arguments.
  • Comments : Arguments that are ignored, starting with -// or +//.

Short vs Long Options

  • Short options: single prefix - or + followed by a single character, e.g., -o or +x.
  • Long options: double prefix -- or ++ followed by a word, e.g., --output or ++enable-feature.
Short options can be combined:

-ex   # equivalent to -e -x
Exception: certain options like -version or +version are treated as single options and not expanded.

Options With Values

  • Options can carry a value (string, boolean, number).
  • Values can be attached with = or provided as the next argument:

--name=Alice   # attached value
+name Alice    # next-argument value

Boolean Options

Boolean options do not always require a value:

--install      # equivalent to true
+install=true
--install=false  # or --!install-companions / --~install-companions

! and ~ are treated as negation. This is useful in shells where ! has special meaning.

Boolean options in NAF can be true or false without explicitly writing true or false. The parser recognizes multiple synonyms for convenience:

True Values


true, enable, enabled, yes, always, y, on, ok, t, o

False Values


false, disable, disabled, no, none, never, n, off, ko, f

Non-Options

Non-options are all other arguments — files, commands, or positional values:

my-app -o --name Alice file1.txt file2.txt
Here, file1.txt and file2.txt are non-options.

Ignored Arguments

Arguments starting with -// or +// are ignored and can be used for comments or metadata in the command line.

2.3 Customizing CmdLine parsing

Configuring NCmdLine

setCommandName(String)

This method help defining the name of the command supporting this command line. This is helpful when generating errors/exception so that the message is relevant for instance, you would call ("ls"), so that all errors are in the form of unexpected argument --how

setExpandSimpleOptions(true|false)

This method can change the default behavior of NCmdLine (defaults to true). When true, options in the form -ex are expanded to -e -x.

registerSpecialSimpleOption(argName)

This method limits setExpandSimpleOptions application so that for some options that start with - (simple options), they are not expanded. A useful example is '-version'. You wouldn't want it to be interpreted as '-v -e -r -s -i -o -n', would you?

setExpandArgumentsFile(true|false)

This method can change the default behavior of NCmdLine (defaults to true). When false, options in the form @path/to/arg/file are interpreted as non options. When true (which is the default), the parser will load arguments from the given file/location.


2.4 Processing the commandline

Using CommandLine, The recommended way...

NCmdLine has a versatile parsing API. One way to use it is as follows :

    NCmdLine cmdLine = NApp.of().getCmdLine(); // or from somewhere else
    NRef<Boolean> boolOption = NRef.of(false);
    NRef<String> stringOption = NRef.ofNull();
    List<String> others = new ArrayList<>();
    while (cmdLine.hasNext()) {
        cmdLine.matcher()
                .with("-o", "--option").matchFlag((v) -> boolOption.set(v.booleanValue()))
                .with("-n", "--name").matchEntry((v) -> stringOption.set(v.stringValue()))
                .withNonOption().matchAny((v) -> stringOption.set(v.image()))
                .requireDefaults()
        ;
    }
    // test if application is running in exec mode
    // (and not in autoComplete mode)
    if (cmdLine.isExecMode()) {
        //do the good staff here
        NOut.println(NMsg.ofC("boolOption=%s stringOption=%s others=%s", boolOption, stringOption, others));
    }

Using CommandLine, The simple and legacy way...


NCmdLine cmdLine = NApp.of().getCmdLine();
boolean boolOption = false;
String stringOption = null;
List<String> others = new ArrayList<>();
NArg a;
while (cmdLine.hasNext()) {
    a = cmdLine.peek().get();
    if (a.isOption()) {
        switch (a.key()) {
            case "-o":
            case "--option": {
                a = cmdLine.nextFlag().get();
                if (a.isUncommented()) {
                    boolOption = a.getValue().asBoolean().get();
                }
                break;
            }
            case "-n":
            case "--name": {
                a = cmdLine.nextEntry().get();
                if (a.isUncommented()) {
                    stringOption = a.getValue().asString().get();
                }
                break;
            }
            default: {
                NSession.of().configureLast(cmdLine);
            }
        }
    } else {
        others.add(cmdLine.next().get().image());
    }
}
// test if application is running in exec mode
// (and not in autoComplete mode)
if (cmdLine.isExecMode()) {
    //do the good staff here
    NOut.println(NMsg.ofC("boolOption=%s stringOption=%s others=%s", boolOption, stringOption, others));
}


3 Structured Messaging

NMsg is the Nuts Application Framework (NAF) message and text formatting system. It allows you to create dynamic, flexible, and visually rich messages in your applications. You can use it for console output, logs, and any scenario requiring formatted text.

NMsg is fully integrated with NOut and NErr for displaying rich and meaningful CLI output.

3.1 Placeholder Formats

NMsg supports multiple placeholder formats for dynamic message generation:
  • C-style (ofC) – like printf in C (%s, %d, etc.)

  • Java / SLF4J style (ofJ) – {} or {0}, {1}

  • Variable substitution (ofV) – named placeholders using $name or ${name}

Examples

// C-style formatting
NMsg.ofC("Hello %s, you have %d new notifications", "Alice", 5);

// Java formatting
NMsg.ofJ("Downloading {0} from {1}", "report.pdf", "server-01");

// SLF4J-style formatting
NMsg.ofJ("Downloading {} from {}", "report.pdf", "server-01");

// Variable substitution from map
NMsg.ofV("User $user on ${app}", Map.of("user", "Alice", "app", "NAF"));

// Variable substitution from function
NMsg.ofV("Threshold=$th, Date=$date", name -> switch (name) {
    case "th"   -> 0.85;
    case "date" -> LocalDate.now();
    default     -> null;
});
Notes:
  • Avoid mixing styles in a single message.
  • ${} syntax is safer for complex strings (e.g., $val123text vs ${val}123text).

C-style Formatting (ofC)

Use ofC to create messages using standard String.format()-style syntax:


NOut.println(NMsg.ofC("Hello %s", "world"));

Placeholders like %s, %d, etc., behave as expected. Useful for simple messages with positional arguments.

Java MessageFormat (ofJ)

Use ofJ for Java-style formatting with {0}, {1} placeholders:


NOut.println(NMsg.ofJ("Hello {0}", "world"));
NOut.println(NMsg.ofJ("Hello {}", "world"));      // SLF4J-style
Both formats are supported, and will be filled using the provided arguments in order (but should not be mixed).
  • {} placeholders are matched sequentially, like in SLF4J.

  • {0}, {1}, etc. allow for specific argument reordering or reuse.

Variable-based Formatting (ofV)

Use ofV to format messages using named variables:


NOut.println(NMsg.ofV("Hello $v", NMaps.of("v", "world")));
NOut.println(NMsg.ofV("Hello ${v}", NMaps.of("v", "world")));

Both $v and ${v} syntaxes are supported.

Variables are replaced by name using the $ prefix. This is useful for dynamically named arguments or template-based rendering, particularly when formatting messages from dynamic key-value maps (e.g., for templates or localization).
  • $v is simple and concise.
  • ${v} is safer when followed by alphanumeric characters (e.g., $val123text vs ${val}123text).

Missing variables are left as-is or replaced with a placeholder, depending on context or configuration.

3.2 Styling Messages

In NAF, messages are not just plain text — they can be styled and formatted to convey meaning, emphasize content, or improve readability. The NMsg API allows you to combine colors, text modes, and semantic tokens to create rich, dynamic messages that adapt to different contexts (CLI, GUI terminals, logs, etc.).Why style messages?
  • Highlight important information: e.g., warnings, errors, success messages.
  • Improve readability: visually distinguish values, keys, or code snippets.
  • Semantic clarity: convey the role of a message part (like a keyword, boolean, or comment) rather than just its content.
  • Consistency: pre-defined color schemes and semantic tokens help maintain a unified look across your application.

Styling Categories

Default Styling

NAF automatically applies default styles to many common data types, so messages are expressive without requiring explicit styling:
  • Boolean values (true / false) are styled using NTextStyle.bool().
  • Numbers
  • Dates / Times / Temporals
  • Enums
  • etc.

// Boolean value without explicit styling
NOut.println(NMsg.of("Value=%s", true));

// Equivalent to explicitly styling the boolean
NOut.println(NMsg.of("Value=%s", NMsg.ofStyledBool("true")));

Color Index / Theme

NAF provides predefined colors, e.g., primary1, secondary5, error, warn, which map to your application’s theme. These ensure consistent appearance without manually specifying RGB values.

// Primary / Secondary themed colors
NMsg.ofStyledPrimary1("text");
NMsg.ofStyledSecondary5("text");

// Arbitrary foreground color
NMsg.ofStyledForegroundColor("text", Color.RED);

// Modes: bold, blink, striked
NMsg.ofStyledBold("text");
NMsg.ofStyledBlink("text", Color.RED);
NMsg.ofStyledStriked("text");

Foreground / Background Colors

You can specify arbitrary colors using Java Color objects. Foreground colors affect text color; background colors can be combined to create highlighted blocks or banners.

// Arbitrary foreground color
NMsg.ofStyledForegroundColor("text", Color.RED);

Text Modes

Modes like bold, italic, blink, strikethrough add emphasis and can be combined with colors for richer visual cues.

// Modes: bold, blink, striked
NMsg.ofStyledBold("text");
NMsg.ofStyledBlink("text", Color.RED);
NMsg.ofStyledStriked("text");

Semantic Tokens

These are high-level categories representing the meaning of text:
  • comments → for secondary or muted content
  • warn → for warnings
  • error → for errors or alerts
  • keyword, string, boolean → for syntax-like highlighting

NMsg.ofStyledComments("comment");
NMsg.ofStyledWarn("warning");
NMsg.ofStyledString("string");
NMsg.ofStyledKeyword("keyword");
NMsg.ofStyledBoolean("boolean");
NMsg.ofStyledError("error");

3.3 Nested Messages

You can nest messages to create complex, styled outputs:

// Custom styling example
NMsg.ofC("Task %s completed with status %s",
    "Upload",
    NText.ofStyled("OK", NTextStyle.primary1())
);
Nested messages combine formatting, styling, and placeholders dynamically.

// Nested messages example
NMsg.ofV("User $user completed ${task}",
    NMaps.of(
        "user", "Alice",
        "task", NMsg.ofV("task %s in %s", "Upload",
        NText.ofStyled("123ms", NTextStyle.secondary1()))
    )
);

3.4 Text Rendering Formats

NMsgnull NMsg also provides multiple text rendering formats to make messages visually expressive. You can use plain messages that print as-is, styled messages to highlight errors, warnings, or other emphasis, code blocks for monospaced text with optional language hints, and NTF (Nuts Text Format) for lightweight markup supporting bold, italic, colors, and other rich formatting. These formats, combined with placeholders, allow NMsg to produce rich, dynamic, and visually informative output in any context.NTF allows quick and readable text formatting directly in message strings.

NMsg.ofNtf("##bold## ##:/:italic## 
java public class{}
");
you can use Code blocks to render source code with syntax coloring

NMsg.ofPlain("This is a plain message");
NMsg.ofCode("java", "System.out.println(\"Hello NAF\");");
you can also force plain text rendering

NMsg.ofPlain("This is a plain message");


4 Structured Elements

With null, null lets you build, parse, and format structured data effortlessly. From plain objects tonull, null, ornull, you can read and write files, parse into Java objects, or print with optional null color formatting — all in a runtime-friendly way.

4.1 Create Elements

Create a structured element


// Build an element
NElement document = NElement.ofObjectBuilder()
    .set("app-id", NApp.of().getId().get())
    .set("error", messageString)
    .build();


4.2 Parse Elements

use ofJson, ofTson, ofYaml, and ofXml to parse into NElement



// Parse JSON into Java object
NElement personJson = NElementParser.ofJson().parse(NPath.of("person.json"));
NElement personXml = NElementParser.ofXml().parse(NPath.of("person.xml"));

You can parse into a Java class as well :


// Parse JSON into Java object
Person person = NElementParser.ofJson().parse(NPath.of("person.json"), Person.class);

// Format and write XML to file
NElementFormat.ofPlainXml(document).println(NPath.of("person.xml"));

// Print TSON to terminal with colors
        NElementFormat.ofNtfTson(document).println();

4.3 Format Elements

Format element



// Parse JSON into Java object
Person person = ...;

// Format and write XML to file
NElementFormat.ofPlainXml(document).println(NPath.of("person.xml"));

// Print TSON to terminal with colors
NElementFormat.ofNtfTson(document).println();


// Parse JSON into Java object
Person person = ...;

// Print TSON to terminal with colors
        NElementFormat.ofNtfTson(person).println(NPath.of("/some/path/file.tson"));

4.4 Convert Elements

NElement is an excellent way to convert between text formats (json to tson etc.)



// Parse JSON into Element
NElement personJson = NElementParser.ofJson().parse(NPath.of("person.json"));

// Format and write XML to file
NElementFormat.ofPlainXml(personJson).println(NPath.of("person.xml"));


5 Expressions & Templates

5.1 NExpr

NExpr is a powerful expression parser and evaluator designed to handle dynamic, runtime expressions within the Nuts ecosystem. It allows declaration of constants, variables, and functions, supports custom operators, and can evaluate expressions in a context-aware way. This makes it ideal for dynamic queries, runtime computation, scripting, and configuration-driven logic in Java applications — all while remaining type-safe, extensible, and embeddable.

NExprMutableDeclarations expr = NExprs.of().newMutableDeclarations();
NExprNode n1 = expr.parse("1+2*3").get();
NExprNode n = expr.parse("a.b>1").get();

NExprs nExprs = NExprs.of();
NDocNExprVar v = new NDocNExprVar();
decl = nExprs.newMutableDeclarations(true, new NExprEvaluator() {
    @Override
    public NOptional<NExprVar> getVar(String varName, NExprDeclarations context2) {
        return NOptional.of(new MyVar());
    }
});

decl.declareConstant("cwd", System.getProperty("user.dir"));
decl.declareFunction("myfct", new MyFct());

NExprMutableDeclarations d = NExprs.of().newMutableDeclarations();
d.declareConstant("pi", Math.PI);
d.declareFunction("sin", (name, args, ctx) -> {
    NExprNodeValue a = args.get(0);
    return Math.sin(asDouble(a.eval(ctx), rendererContext));
});

NOptional&lt;NExprNode> ne = d.parse("sin(x*pi)");
if (ne.isPresent()) {
    NExprNode node = ne.get();
    NDoubleFunction fct = x -> {
        NOptional&lt;Object> r = node.eval(new NExprEvaluator() {
            @Override
            public NOptional&lt;NExprVar> getVar(String varName, NExprDeclarations ctx) {
                return "x".equals(varName)
                        ? Optional.of(x)
                        : NOptional.ofNamedEmpty("var " + varName);
            }
        });
        return NTxExprHelper.asDouble(r, rendererContext);
    };

    double result = fct.apply(0.5); // evaluates sin(0.5*pi)
}


6 Text Rendering & Models

At the heart of Nuts' text rendering capabilities lies NTF (Nuts Text Format) — a structured, styled, and composable text model. NTF defines how text content, styles, and layout are represented, enabling you to build complex output (with colors, emphasis, alignment, structure, and more) in a way that is device-agnostic and output-agnostic.The NText API represents these styled text blocks, and NTextStyle describes how they appear (e.g., bold, italic, success, error). Because every renderer produces NText, different outputs — from simple styled strings to complex ASCII tables or banners — can be nested, combined, and transformed consistently.Once generated, NTF text can be rendered into multiple output formats:
  • ✅ ANSI sequences – On POSIX-compatible terminals, NText is rendered as styled ANSI text. On Windows, Nuts uses Jansi to ensure the same result.
  • 🌐 HTML – NText can also be rendered as HTML, enabling use in web pages or documentation. Tools like NSite (a static site generator built on Nuts/NAF) rely on this feature to transform NText blocks into HTML markup.
  • 📦 Other targets – Because NTF is an abstract representation, it can be serialized to other formats or even stored as structured data for later rendering.
Building on this foundation, NTextArt offers a versatile rendering framework that converts structured data, text, and even images into expressive visual representations. It provides specialized renderers for different visualization needs:
  • Text Renderers – Convert text into stylized ASCII art (e.g., figlets, banners).
  • Structured Renderers – Render structured data such as tables and trees.
  • Image Renderers – Transform raster images into ASCII-based art.
Because all renderers output NText (based on NTF), their results can be further styled, composed with other messages, serialized, or rendered to different backends — all while remaining portable across platforms.

6.1 Nuts Text Format

Nuts Text Format (NTF)

The Nuts Text Format (NTF) is a lightweight, expressive markup language designed specifically to enhance command-line interface (CLI) output with rich, portable, and visually appealing formatting. It provides a powerful yet simple syntax that lets developers create colorful, structured, and semantically meaningful text that works seamlessly across different terminal environments and beyond.

The Need for NTF

Traditional terminal output often relies on plain text or embedded ANSI escape codes to achieve colors and styles. This approach has several drawbacks:
  • Lack of readability: Raw escape sequences are hard to read and maintain within source code.

  • Poor portability: Different terminals support different levels of ANSI or control codes, leading to inconsistent rendering.

  • Limited structure: Plain text and raw ANSI codes do not express document structure well (like lists, tables, sections).

On the other hand, existing markup languages each have their own limitations for CLI contexts:
  • HTML is rich and flexible but too verbose and requires an HTML viewer, unsuitable for terminals.

  • Markdown is easy to write and readable but lacks support for dynamic styling and rich terminal features.

  • Man pages (troff/groff) provide basic terminal help formatting but are complex to author and limited in styling options.


What Makes NTF Unique?

NTF is crafted to fill this gap by being a terminal-first markup format that is both human-readable and machine-processable, with several key advantages:

  • Readable markup: NTF syntax uses simple inline markers for colors, font styles, lists, tables, and sections, which are easy to write and understand.

  • Rich formatting: Supports foreground and background colors, bold/italic/underline, code blocks, bullet and numbered lists, tables, links, and other structured elements.

  • Portability: NTF content is independent of the terminal's escape code specifics. Instead, it is parsed and translated to the appropriate ANSI sequences or other target formats at runtime.

  • Multi-target rendering: Beyond ANSI terminals, NTF can be converted to Markdown (for documentation or developer notes) and HTML (for web-based manuals), ensuring a unified authoring experience.

  • Context-awareness: NTF rendering adapts automatically to the capabilities of the target output device or session configuration, allowing graceful degradation when color or style is not supported.

  • Easy toggling: Users can enable or disable colored output through standard Nuts options or programmatically, without affecting the underlying markup.


NTF in Nuts Ecosystem

Within the Nuts toolbox, NTF plays a central role in delivering a consistent, high-quality user experience:

  • Command-line help system: All command help, options, examples, and warnings in Nuts are authored in NTF. This allows help to be:

    • Colorful and well-structured on capable terminals.

    • Plain-text friendly when color is disabled.

    • Automatically exportable to Markdown or HTML for documentation portals.

  • Output formatting: When printing messages, lists, objects, or errors, Nuts can utilize NTF to add semantic structure and emphasis, improving clarity and user comprehension.

  • Unified authoring: Developers write output messages once in NTF and can be confident it will render correctly across all supported environments without manual adjustments.


How NTF compares to other formats:

NTF is specifically designed for developer-friendly, portable, terminal-first output formatting. It bridges the gap between simple text styling (like ANSI escape codes) and more advanced document-oriented formats (like Markdown or AsciiDoctor), making it uniquely suited for CLI applications.

The table below highlights how NTF compares to other common formats across key capabilities:
Feature

NTF

ANSI Escape Codes

Markdown

AsciiDoctor

HTML

Colored output

✅ Named colors, indexed and hex support✅ Manual, code-based❌ (Extensions needed)✅ With roles/styles✅ CSS/inline styles

Styled text

✅ Bold, italic, underline, strikethrough✅ Limited (manual control)✅ Bold, italic✅ Bold, italic, underline✅ Full style control

Semantic color tags (e.g. error, warning)

✅ Built-in mappings (##:red:word##, ##:info:word##, etc.)

❌ None❌ None⚠️ Manual via roles✅ Possible via class

Nested/Combined styles

✅ Fully supported (e.g. ##{:red:##{:bold:word}##}##

❌ Complex / fragile❌ Not supported✅ Supported✅ Fully supported

Structured sections (titles, subtitles)

✅ NTF supports semantic headers (# Title, ## Subtitle)

❌ None✅ Basic headings✅ Full document structure✅ Rich document structure

Lists (bullet, numbered)

❌ Not yet❌ None✅ Yes✅ Yes✅ Yes

Tables

❌ Not yet❌ None✅ Basic tables✅ Rich tables✅ Rich tables

Syntax highlighting (code snippets)

✅ With language tag❌ None✅ (Limited, via extensions)✅ With language tag✅ Full, with JS/CSS

Terminal rendering support

✅ Auto-adapts (ANSI, plain, HTML, Markdown)✅ Terminal only❌ Not terminal-targeted❌ Not terminal-targeted❌ Not terminal-targeted

Portability across environments

✅ Designed for CLI and convertible to HTML/Markdown❌ Terminal only✅ Editor/docs only✅ Editor/docs only✅ Web/browser only

Ease of authoring for CLI output

✅ Very high (compact, readable, intuitive)❌ Low (escape-heavy)⚠️ Limited styling⚠️ Verbose❌ Too verbose for CLI

Summary

The Nuts Text Format is a modern, terminal-optimized markup language that:

  • Improves readability and maintainability of CLI output markup.
  • Enables richly formatted, colorful, and structured terminal output.
  • Supports conversion to Markdown and HTML for seamless documentation.
  • Enhances the Nuts ecosystem by unifying CLI and documentation presentation.
NTF represents a thoughtful balance between simplicity, expressiveness, and portability, empowering Nuts users and developers to build sophisticated, professional command-line applications with minimal effort.

nuts comes up with a simple coloring syntax that helps writing better looking portable command line programs. standard output is automatically configured to accept the "Nuts Text Format" (NTF) syntax. Though it remains possible to disable this ability using the --!color standard option (or programmatically, see nuts API documentation). NTF will be translated to the underlying terminal implementation using ANSI escape code on linux/windows terminals if available.

Here after a showcase of available NTF syntax.

text-coloring-format

text-coloring-format

text-coloring-format

text-coloring-format

Nuts Text Format Specification


<TOKEN> S10: '##########'
<TOKEN> S9 : '#########'
<TOKEN> S8 : '########'
<TOKEN> S7 : '#######'
<TOKEN> S6 : '######'
<TOKEN> S5 : '#####'
<TOKEN> S4 : '####'
<TOKEN> S3 : '###'
<TOKEN> S2 : '##'
<TOKEN> S1 : '##'
<TOKEN> A3 : '\```'

<RULE>  S2 ':' KEY ':' ANYTHING S2
<RULE>  S2 '{:' WORD ANYTHING S2
<RULE>  13 ANYTHING A3


6.2 Rendering Text

Bring your CLI, logs, or console output to life with NTextArt. Render text as classic ASCII banners, pixel-style visuals, or even image-like representations, with multiple renderers at your fingertips — all workspace-aware and fully embeddable.

Basic Usage


    NTextArt art = NTextArt.of();
NText text = NText.of("hello world");
    NOut.println(art.getTextRenderer("figlet:standard").get().render(text));
Output:

  _                _    _                                             _        _
 | |              | |  | |                                           | |      | |
 | |__      ___   | |  | |    ___       __      __    ___     _ __   | |    __| |
 | '_ \    / _ \  | |  | |   / _ \      \ \ /\ / /   / _ \   | '__|  | |   / _` |
 | | | |  |  __/  | |  | |  | (_) |      \ V  V /   | (_) |  | |     | |  | (_| |
 |_| |_|   \___|  |_|  |_|   \___/        \_/\_/     \___/   |_|     |_|   \__,_|

You can choose from multiple built-in figlet renderers or even use your own.

    NTextArt art = NTextArt.of();
    
    NOut.println(art.getImageRenderer("pixel:standard").get()
            .setFontSize(20) .setOutputColumns(60) .render(text));
Output:


 █         ░██   ███                             ███      █
 █           █    ▓█                              ░█      █
 █ █▒  ░██   █    ▓█    █▒       █   █  █▓  ██░█░ ░█   ▒█ █
 █▓▓█░░█ █░  █    ▓█  ░█ ▓▒      █ █ █░█ ▓▓ ██▓▓  ░█   █░▒█
 █  █░█████  █    ▓█  ▓█ ▒█      █▓█ ▓▒█ ░█ ██    ░█  ░█  █
 █  █░ █     ██   ▓█   █ █░      ██░█▓ █ █▒ ██    ░█░ ░█░██
 ▓  ▓░ ░▓▓    ▓▓   ▓▓░  ▓░       ░▓ ▓   ▓▒  ▒▒     ░▓▓ ░▓ ▓

6.3 Rendering Tables

One of the most powerful text rendering features in NAF is its ability to render structured tables directly in the terminal. This is made possible through the NTextArt API, which can render tabular data with automatic alignment, wrapping, spanning, and per-cell styling — all while remaining fully compatible with Nuts’ messaging and text system (NText, NMsg, NOut, etc.).

Unlike ad-hoc printf-based formatting, NTextArt tables are aware of structure, style, and layout. They handle complex cases such as multiline cells, column and row spanning, per-cell styling, and even semantic rendering (e.g., italic, success, error) without losing readability.

Basic Usage

To render a table, create a NMutableTableModel, populate it with rows and optional headers, and pass it to a NTextArtTableRenderer.

NMutableTableModel table = NTableModel.of()
.addHeaderRow(NText.of("Name"), NText.of("Status"))
.addRow(NText.of("adam"), NText.ofStyled("active", NTextStyle.italic()))
.addRow(NText.of("eve"),  NText.ofStyled("inactive", NTextStyle.success()));

NOut.println(NTextArt.of().getTableRenderer().get().render(table));
Output:

+------+----------+
| Name | Status   |
+------+----------+
| adam | active   |
| eve  | inactive |
+------+----------+

You can choose from multiple built-in renderers (e.g. "table:ascii", "table:spaces") or register your own custom renderer.

Advanced Features

Cells can contain multiple lines of text, and the renderer will automatically adjust row heights:

NMutableTableModel table = NTableModel.of()
    .addRow(NText.of("adam\nwas\nhere"), NText.of("active"))
    .addRow(NText.of("eve"), NText.of("inactive"));
Output:

+------------------------+
| adam                  |
| was                   |
| here                  |
+------------+----------+
| adam       | adam     |
| here       | is here  |
+------------+----------+

Column Spanning (colspan)

Cells can span across multiple columns, just like in HTML tables:

NMutableTableModel table = NTableModel.of()
    .addRow(NText.of("adam\nwas\nhere"))
    .addRow(NText.of("adam\nhere"), NText.of("adam\nis\nhere"))
    .setCellColSpan(0, 0, 2); // first cell spans 2 columns

Result:

+------------------------+
| adam                  |
| was                   |
| here                  |
+------------+----------+
| adam       | adam     |
| here       | is here  |
+------------+----------+

Row Spanning (rowspan)

Cells can also span vertically across rows:

NMutableTableModel table = NTableModel.of()
    .addRow(NText.of("tall\ncell\nvery\ntall"), NText.of("short"))
    .addRow(NText.of("another"))
    .setCellRowSpan(0, 0, 2); // span vertically over 2 rows

Mixed Column Counts

Rows can have different numbers of columns. The renderer handles layout automatically:

NMutableTableModel table = NTableModel.of()
    .addRow(NText.of("adam\nwas\nhere"))
    .addRow(NText.of("adam\nhere"), NText.of("adam\nis\nhere"), NText.of(3))
    .setCellColSpan(0, 0, 3);

Per-Cell Styling

Cells can carry formatting information using NTextStyle. This enables bold, italic, color, semantic meaning (e.g., success, error), and more.

.addRow(NText.of("adam"), NText.ofStyled("active", NTextStyle.italic()))
.addRow(NText.of("eve"),  NText.ofStyled("inactive", NTextStyle.success()));

Multiple Renderers

Different renderers can be used for different table aesthetics or output contexts. For example:

NOut.println(art.getTableRenderer("table:spaces").get().render(table));
This renderer uses space padding instead of ASCII borders — useful for compact, plain-text output.You can also iterate over all registered renderers:

for (NTextArtTableRenderer renderer : art.getTableRenderers()) {
    NOut.println(renderer.getName() + "::");
    NOut.println(renderer.render(table));
}

Performance Considerations

Rendering tables is efficient, but when dealing with thousands of rows, consider paginating or streaming rows instead of rendering all at once.Cell layout calculations (especially with spanning) are cached internally to minimize overhead.

Why NTextArt Tables Matter

You might ask: why not just use System.out.printf() or format strings manually?Because NTextArt tables are semantic and structure-aware:
  • They understand cell spanning, multiline content, and style.
  • They integrate seamlessly with NText, NMsg, and NOut.
  • They’re renderer-agnostic — the same model can be rendered as ASCII, space-aligned text, or even graphical pixel art in the future.
  • They form a foundation for higher-level features like search results, dependency trees, and diagnostics in Nuts CLI.

6.4 Rendering Trees

For hierarchical data like dependency graphs or process hierarchies:

NTreeNode root = new MyNode("Root", List.of(
    new MyNode("Child 1"),
    new MyNode("Child 2", List.of(
        new MyNode("Grandchild A"),
        new MyNode("Grandchild B")
    ))
));

NOut.println(NTextArt.of().getTreeRenderer().get().render(root));
Result :

Root
├─ Child 1
└─ Child 2
   ├─ Grandchild A
   └─ Grandchild B
NTextArt integrates seamlessly with tree rendering as well. You can render a tree whose nodes themselves contain rendered tables:


class MyNode implements NTreeNode {
    int value;
    public MyNode(int value) { this.value = value; }
    @Override
    public NText value() {
        return art.getTableRenderer().get().render(
            NTableModel.of().addRow(NText.of(value))
        );
    }
    @Override
    public List<NTreeNode> children() {
        return value < 3 ? Arrays.asList(value+1, value+2).stream().map(MyNode::new).collect(Collectors.toList()) : List.of();
    }
}
NTreeNode tree = new MyNode(1);
NOut.println(art.getTreeRenderer().get().render(tree));

Result:

   ╭─╮
   │1│
   ╰─╯
   ├── ╭─╮
   │   │2│
   │   ╰─╯
   │   ├── ╭─╮
   │   │   │3│
   │   │   ╰─╯
   │   └── ╭─╮
   │       │4│
   │       ╰─╯
   └── ╭─╮
       │3│
       ╰─╯



7 IO & Filesystem Abstractions

The Nuts library offers a comprehensive and flexible input/output system designed for modern CLI and application needs, including:
  • NIn: Simplifies and unifies input handling, supporting both interactive and programmatic input sources.

  • NOut: The standard output stream abstraction supporting colorful, structured, and formatted output that respects the current session context and output format (plain text, JSON, XML, tables, etc).

  • NErr: Dedicated error output stream, separate from standard output, ensuring proper logging and error visibility.

  • NTrace: Specialized output stream for trace/debug information that is only displayed when trace mode is enabled, helping users debug without cluttering normal output.

  • NLog: A logging interface integrating with Nuts' session and workspace model to produce contextual logs with flexible verbosity and output controls.

  • NMsg: A message abstraction enabling rich, multi-language, and parameterized messages with support for localization and structured formatting.

  • NTF: Nuts Text Format — a powerful formatting system that extends standard text output with features like color, styles, and structured data rendering, allowing consistent and attractive CLI outputs.

Together, these components create a rich and developer-friendly IO system, enabling:
  • Colorful and readable console output.
  • Structured output formats automatically adapting to context.
  • Clear separation of standard, error, and debug outputs.
  • Localizable and parameterized messages.
  • Simplified user input handling.
This system is integrated tightly with the Nuts session concept, meaning output behavior automatically adapts to user preferences, environment, and command-line options, making CLI tools more usable and professional.The Nuts ecosystem provides a comprehensive set of tools designed to simplify and unify file and resource management across different protocols and formats.What Nuts Supports
  • NPath: A versatile path abstraction that extends beyond traditional file system paths. It supports local files, URLs (HTTP/HTTPS), classpath resources, and Maven-style artifact resources. This unified API enables seamless access and manipulation of diverse resource types.

  • NCp: An advanced copying utility capable of copying files and directories with support for validation, progress tracking, overwrite policies, and more. It provides robust features to reliably transfer resources locally or remotely.

  • NCompress / NUncompress: Utilities to compress and decompress files and folders using popular archive formats such as ZIP and TAR. These tools handle format detection, filtering, and extraction/compression workflows transparently.

  • NDigest: A digest computation tool for calculating checksums and hashes (e.g., SHA-256) of files, streams, and folders. It supports integrity verification and recursive digesting to ensure file content authenticity.

Together, these components offer a flexible and powerful framework to interact with files and resources efficiently, whether for local development or distributed environments.

7.1 NOut Standard output

The NOut class is a simple and powerful utility for writing to the standard output in Nuts. It provides a consistent and extensible way to print text, formatted messages, and structured data.

By default, NOut delegates to the session's configured output stream, defined as an NPrintStream in the current NSession. This stream is customizable, structured, and NTF-aware, making it suitable for both human-readable and machine-readable outputs (JSON, XML, etc.).

Unlike System.out, NOut provides enhanced capabilities:

  • Intelligent rendering of objects (beyond basic toString()),

  • Colorized and formatted output via NTF (Nuts Text Format),

  • Support for various structured formats (e.g., JSON, YAML, XML, TSON),
  • Support for formatted messages with placeholders,
  • Table and tree rendering.

Basic Usage

The simplest way to print a message to the console:

    Nuts.require();
    NOut.println("Hello");

Using NTF

NTF enables you to add rich formatting and colorization:

    NOut.println("##Hello colored## ##:_:Hello underlined## ");
    NOut.println("##:yellow:Hello in yellow##");
    NOut.println("##:warn:this is a warning##");
    NOut.println("##:fxFF0000:this is a red message##");

Rendering structured output

NOut can render structured output based on the active format in the NSession.


    class Customer{String id;String name;}
    Customer customer1,customer2,customer3; ...
    // configure le current output to render objects as json
    // to display the curstomer list as a json array
    NSession.of().json();
    NOut.println(Arrays.asList(customer1,customer2,customer3));

    // you can do the same for yaml,tson,xml,table and tree (as formats)
    NSession.of().tree();
    NOut.println(Arrays.asList(customer1,customer2,customer3));

Formatted Messages

You can build formatted messages using NMsg, with placeholder support and type-aware formatting:

    NOut.println(NMsg.of("this is a %s message that is %s %% beautiful",true,100));
Values such as booleans and numbers are rendered with distinct styles (e.g., colors) for better readability.Or you can build your own styled arguments :

    NOut.println(NMsg.of("this is a %s ",NMsg.ofStyledPrimary1("message")));

Working with Tables

To have full control over tabular output, use NMutableTableModel:


    NSession session=...;
    Object a,b,c,d; ...
    NMutableTableModel m = NTableModel.of();
    m.newRow().addCells(a,b,c,d);
    NOut.println(m);

Working with Trees

To render hierarchical structures, you can implement a custom NTreeModel:


    NOut.println(
        new NTreeModel() {
            @Override
            public Object getRoot () {
                return "/";
            }
        
            @Override
            public List<NDependencyTreeNodeAndFormat> getChildren (Object node){
                if ("/".equals(node)) {
                    return Arrays.asList(1,2,3);
                }
                return Arrays.asList();
            }
        }
        );

Summary

The NOut class provides a robust and extensible mechanism for console output in the Nuts ecosystem. Whether you're logging simple messages, displaying structured data, or building CLI tools, NOut ensures consistent and powerful rendering—fully aligned with Nuts' NTF and output formatting infrastructure.


7.2 NErr Standard Error

NErr is the error-stream counterpart to NOut, providing structured, colored, and format-aware error output in the Nuts ecosystem.

It writes to the standard error stream configured in the current NSession, represented by a customizable NPrintStream. Like NOut, this stream is fully NTF-aware and supports a wide range of formats such as JSON, YAML, TSON, XML, tree, and table.

Key Features

  • Delegates to NSession.err() (an NPrintStream)

  • Fully supports NTF (Nuts Text Format) for colored and styled messages

  • Supports structured output in multiple formats
  • Works seamlessly with NMsg, NMutableTableModel, and NTreeModel

  • Ideal for logging warnings, errors, diagnostics, and debugging information

Basic Example


Nuts.require();
NErr.println("An error occurred");

Styled Error Messages

You can leverage NTF for expressive and styled output:

NErr.println("##:error:Something went wrong!##");
NErr.println("##:warn:Warning:## Potential issue detected");
NErr.println("##:fxFF0000:Critical failure##");

Structured Error Reporting

Just like with NOut, you can render structured error data using the current session format:

NSession.of().json();
NErr.println(errorList); // errorList = List<ErrorDetail>
You can switch to any other supported format (yaml, xml, table, tree, tson, etc.) using:

NSession.of().table();
NErr.println(errorList);

Formatted Diagnostic Messages

Use NMsg to build dynamic, strongly typed error messages:


NErr.println(NMsg.of("Task %s failed after %d attempts", "SyncJob", 3));

Use Cases

  • Displaying runtime errors or exceptions in a user-friendly way
  • Emitting machine-readable diagnostics for automation tools
  • Rendering hierarchical error trees or tabular summaries
  • Debugging output during CLI tool development

Summary

NErr brings all the expressive power of NOut to the standard error stream. Whether you're showing simple warnings or structured diagnostic trees, NErr ensures your error messages are readable, styled, and format-compliant with the Nuts session configuration.


7.3 NTrace, the output companion

NTrace — Conditional Trace Output Utility

NTrace is a structured output utility in Nuts used to emit optional diagnostic or trace information to the standard output stream. It behaves like NOut, but only prints output when tracing is explicitly enabled in the current session.


🔍 When to Use NTrace

Use NTrace to provide optional messages that :

  • Help during development or debugging,
  • Provide insights into internal steps,
  • Are not critical and should not mix with standard output (NOut) or error messages (NErr).

Unlike NOut, NTrace output is optional and controlled by the trace flag, so it won’t clutter output if tracing is turned off.

Output Destination

> Note: > NTrace writes to NSession.out() — the same output stream used by NOut. > In contrast, NErr writes to NSession.err().

This means trace messages can be redirected, styled, and formatted consistently with standard output, but only appear when tracing is active.

Trace Mode Behavior

Trace is enabled by default.To disable trace output, users can:
  • Pass --trace=false or --!trace on the command line:

nuts --trace=false my-app 
nuts --!trace my-app 
  • Programmatically disable it in the session:

NSession.of().setTrace(false);
When trace is disabled, all calls to NTrace.println(...) are ignored silently.

Example Usage


Nuts.require();

// This will print only if trace is enabled (default is true)
NTrace.println("Loading configuration from default path...");

Features (Same as NOut)

NTrace supports the complete feature set of NOut, including:

  • NTF formatting for colors and styling,
  • Structured rendering (e.g., JSON, YAML, XML, TSON, table, tree),
  • Formatted messages using NMsg,
  • Integration with the current session’s output configuration.

Best Practices

Use NTrace to display less relevant or verbose messages that:
  • Are helpful for end users who want to better understand what the tool is doing,
  • Should not appear during normal usage but may provide useful context when verbosity is desired (e.g., progress steps, skipped actions, fallback behavior),
  • Can be safely ignored without impacting the understanding of the main output.

> Note: > NTrace is not a developer logging mechanism. > For internal developer-oriented logging, use NLog.


7.4 NIn for simplified Input

NIn — Structured Input Utility

The NIn class is the interactive input utility of the Nuts platform. It provides a simple and consistent interface to read from the standard input stream (NSession::in()), with built-in support for prompts, password masking, and type-safe values.

Basic Input Reading

Reading a Line


String line = NIn.readLine();
Reads a full line from the user input.You can also provide a prompt using an NMsg:

String name = NIn.readLine(NMsg.ofC("Enter your ##name##: "));

Reading a Password


char[] pwd = NIn.readPassword();
Reads a password without echoing characters (as far as the required extensions are loaded) to the terminal.With prompt:

char[] pwd = NIn.readPassword(NMsg.ofPlain("Password: "));

Reading a Literal


NLiteral lit = NIn.readLiteral();

Reads a string input and wraps it in an NLiteral, allowing you to safely extract typed values.


NLiteral lit = NIn.readLiteral(NMsg.ofPlain("Enter a number: "));
int value = lit.asInt().get();

use the NLiteral::asXYZ series of methods to convert the string input to common types like double, boolean, etc...


NLiteral lit = NIn.readLiteral(NMsg.ofPlain("Enter a number: "));
double value = lit.asDouble().get();

Interactive and Typed Input with NAsk

For complex or type-safe input, NIn.ask() (or NAsk.of()) provides a fluent API to build interactive prompts with support for:
  • Custom messages,
  • Typed inputs (String, int, boolean, enum, etc.),
  • Default values,
  • Validators,
  • Accepted values,
  • Password input,
  • "Remember me" options,
  • Custom parsing and formatting.
It will re-prompt indefinitely until a valid input is provided, based on type and validation constraints, or until the user cancels the prompt (e.g., by sending an interrupt like Ctrl+C or entering an empty value when allowed).NAsk will re-prompt indefinitely in an interactive loop until:
  • A valid value is provided (based on expected type and validation),
  • Or the user explicitly cancels the prompt (e.g., by interrupting input or when input is blank and optional). This ensures reliable and robust user interaction with clear guidance and fallback behavior.

Password Input Example


char[] password = NAsk.of()
.forPassword(NMsg.ofPlain("Password for user " + user))
.getValue();
Prompts for a password securely (input not echoed), and returns a char[].

Boolean Confirmation with Context


boolean usePcp = NAsk.of()
.forBoolean(
NMsg.ofPlain(
remote
? "Use PCP users the same as the instances hosts users?"
: "Use PCP user as the same as the current user?"
)
)
.getValue();
Prompts the user with a yes/no question.

"Remember Me" with Default


boolean override = NAsk.of()
    .setDefaultValue(true)
    .setRememberMeKey(
        rememberMeKey == null ? null : ("Override." + rememberMeKey)
    )
    .forBoolean(
        NMsg.ofC("Override %s?",
            NText.ofStyled(
                betterPath(out.toString()),
                NTextStyle.path()
            )
        )
    )
    .getBooleanValue();
This example:
  • Proposes a default answer (true),
  • Persists the answer under the given key (rememberMeKey), so the question may be skipped next time,
  • Uses styled output in the question message.

Custom Validation Example


String mainClass = NAsk.of()
    .forString(NMsg.ofNtf("Enter the name or index:"))
    .setValidator((value, question) -> {
        Integer index = NLiteral.of(value).asInt().orNull();
        if (index != null && index >= 1 && index <= possibleClasses.size()) {
            return possibleClasses.get(index - 1);
        }
        if (possibleClasses.contains(value)) {
            return value;
        }
        throw new NValidationException(); // Triggers re-prompt
    })
    .getValue();

NAsk Supported NAsk Features

  • forString(...) Prompt for a String

  • forInt(...) Prompt for an int

  • forDouble(...) Prompt for a double

  • forBoolean(...) Prompt for a boolean (yes/no, true/false)

  • forEnum(Class<E> enumType, ...) Prompt for an enum value

  • forPassword(...) Prompt for a password (char[])

  • setDefaultValue(T) Sets a default value used when input is blank

  • setHintMessage(NMsg) Displays hint under the question

  • setAcceptedValues(List<Object>) Restricts accepted values and may display suggestions

  • setRememberMeKey(String) Automatically stores and reuses the answer based on a key

  • setValidator(NAskValidator<T>) Adds input validation logic

  • setParser(NAskParser<T>) Custom parsing from String to T

  • setFormat(NAskFormat<T>) Custom formatting of expected values for user display

NAsk Re-prompting Behavior

NAsk will loop until a valid answer is provided, according to:
  • Type expectations (e.g., integer, enum),
  • Custom validators (if any),
  • Accepted values (if defined).
This ensures robust, user-friendly interaction without premature failure.

7.5 NPath

NPath is a powerful abstraction introduced by nuts to handle paths in a uniform way, similar to Java's URL, but with extended capabilities, built-in protocol support, and a fluent, intuitive API.

Key Features

  • Unified path abstraction for local files, URLs, classpath resources, and artifacts.
  • Protocol-aware: supports file, http(s), classpath, and resource URLs.
  • Stream access, input/output helpers, and file tree navigation.
  • Support for creating temporary files/folders and content manipulation.

Supported Protocols

  • File paths: "/path/to/resource", "C:\path\to\resource"
  • File URLs: "file:/path/to/resource", "file:C:/path/to/resource"
  • HTTP/HTTPS URLs: "http://...", "https://..."
  • Ssh URLs: "ssh://user@server/path/to/resource"
  • Classpath: "classpath:/path/to/resource" (requires classloader)
  • Resource paths: "resource://group:artifact#version/path/to/resource"

Creating an NPath


NPath localFile = NPath.of("C:/path/to/resource");

Other creation methods include:


NPath.of(URL url);
NPath.of(File file);
NPath.of(Path path);
NPath.of(String path, ClassLoader cl);
NPath.of(NConnexionString connection);

Special Locations


NPath.ofUserHome();         // User home directory
NPath.ofUserDirectory();    // Current working directory
NPath.ofUserStore(type);    // User store path
NPath.ofSystemStore(type);  // System store path

NPath.ofUserStore(NStoreType storeType)

Returns the path to the user-specific store folder of the given storeType.This method is used to access various predefined storage locations that conform to standard OS conventions (such as XDG Base Directory Specification on Linux). These locations are used to store user-specific data in structured, OS-compliant folders.Example:

NPath configFolder = NPath.ofUserStore(NStoreType.CONF);
System.out.println("User config path: " + configFolder);

Supported NStoreTypes

Each NStoreType corresponds to a logical category of user data:

StoreTypePurposeTypical UsageLinux Equivalent

BIN

Stores user-specific executable binaries (non-modifiable).Custom installed commands/tools.

$HOME/.local/bin

CONF

Stores user-specific configuration files.App or tool settings, preferences.

$XDG_CONFIG_HOME or $HOME/.config

VAR

Stores user-specific modifiable data.Data files, downloaded content.

$XDG_DATA_HOME or $HOME/.local/share

LOG

Stores log files.Runtime logs, audit trails.

$XDG_LOG_HOME or $HOME/.local/log

TEMP

Stores temporary files.Temp input/output files.

$TMPDIR, /tmp, or equivalent

CACHE

Stores non-essential cache files.Cached packages, resources.

$XDG_CACHE_HOME or $HOME/.cache

LIB

Stores user-specific non-executable binaries.Local libraries and dependencies.

$HOME/.local/lib

RUN

Stores runtime file system entries.Sockets, pipes, PID files.

$XDG_RUNTIME_DIR or /run/user/<uid>

Why Use ofUserStore()?

This method ensures:
  • Cross-platform compatibility: Automatically maps to platform-appropriate folders.
  • Correct file placement: Keeps your workspace clean and organized.
  • Portable behavior: Works reliably across Linux, Windows, and macOS.

Advanced Example: Write to User Log Folder


NPath logFile = NPath.ofUserStore(NStoreType.LOG).resolve("myapp.log");
logFile.writeString("This is a log entry.\n", StandardCharsets.UTF_8);
Notes : You can get the resolved directory path as a string via getLocation() or toString().
Notes : If you're working in a system context (e.g. root user or shared tools), consider NPath.ofSystemStore(NStoreType).

Browsing HTML Folders

Supports Apache Tomcat and Apache Httpd directory listings.

NPath httpFolder = NPath.of("htmlfs:https://archive.apache.org/dist/tomcat/");
try (NStream s = httpFolder.stream()) {
    List<NPath> matches = s.filter(x -> x.isDirectory() && x.getName().matches("tomcat-[0-9.]+"))
                            .toList();
}

Working with Temp Files and Folders


NPath tempFile = NPath.ofTempFile("example.txt");
NPath tempFolder = NPath.ofTempFolder("project-workspace");
Also supports temp locations for repositories and artifact ids:

NPath.ofTempRepositoryFile("temp.txt", repository);
NPath.ofTempIdFolder(id);

Content I/O

Reading:

byte[] data = path.readBytes();
String content = path.readString();
Writing:

path.writeBytes(data);
path.writeString("Hello World");
Writing Structured Objects:

path.writeObject(myObject);     // Any serializable
path.writeMsg(NMsg.ofText("Hi"));

Path Operations

  • resolve, resolveSibling, normalize, toAbsolute, toRelative
  • exists(), isDirectory(), isFile(), delete(), mkdir()
  • getName(), getNameCount(), getNames(), getParent()

File Tree


path.walk();                    // DFS walk
path.walkGlob();               // Glob walk

Streaming and Navigation


try (NStream<NPath> stream = path.stream()) {
    stream.forEach(x -> ...);
}

Permissions & Metadata


Set<NPathPermission> perms = path.getPermissions();
path.setPermissions(...);

Type & Protocol


String protocol = path.getProtocol();
boolean isFile = path.isFile();
boolean isHttp = path.isHttp();
NPathType type = path.type();

Conversion


URL url = path.toURL().orNull();
File file = path.toFile().orNull();
Path nioPath = path.toPath().orNull();

Example: Directory Listing


NPath dir = NPath.of("/my/folder");
List<NPath> files = dir.stream()
    .filter(p -> p.getName().endsWith(".txt"))
    .toList();

Summary

NPath is a versatile and protocol-aware abstraction that unifies file, URL, and resource path handling. Its rich API and Nuts integration make it ideal for building tools that require flexible resource access, remote artifact inspection, or file system utilities.

7.6 IO: Working with files

nuts Library allows multiple variants of string interpolation

NCp


    NCp.of()
        .from("http://my-server.com/file.pdf")
        .to("/home/my-file")
        .setProgressMonitor(true)
        .setValidator((in)->checkSHA1Hash(in))
        .run();

    NPs ps=NPs.of()
        if(ps.isSupportedKillProcess()){
            ps.killProcess("1234");
        }

NCompress/NUncompress


    NCompress aa = NCompress.of()
        .setTarget(options.outZip);
        for (NPath file : options.files) {
        aa.addSource(file);
        }
        aa.run();
        

    NUncompress.of()
                    .from(is)
                    .visit(new NUncompressVisitor() {
    @Override
    public boolean visitFolder(String path) {
        return true;
    }

    @Override
    public boolean visitFile(String path, InputStream inputStream) {
        if ("META-INF/MANIFEST.MF".equals(path)) {
            ...
        } else) {
            ...
        }
        return true;
    }
}).run();

NDigest


   String digest=NDigest.of().setSource(x.getPath().getBytes()).computeString();
}).run();

7.7 File system

nuts manages multiple workspaces. It has a default one located at ~/.config/nuts (~ is the user home directory). Each workspace handles a database and files related to the installed applications. The workspace has a specific layout to store different types of files relatives to your applications. nuts

is largely inspired by XDG Base Directory Specification and hence defines several store locations for each file type. Such organization of folders is called Layout and is dependent on the current operating system, the layout strategy and any custom configuration.

Store Locations

Supported Store Locations are :

nuts File System defines the following folders :

  • config : defines the base directory relative to which application specific configuration files should be stored.

  • apps : defines the base directory relative to which application executable binaries should be stored

  • lib : defines the base directory relative to which application non executable binaries should be stored

  • var : defines the base directory relative to which application specific data files (other than config) should be stored

  • log : defines the base directory relative to which application specific log and trace files should be stored

  • temp : defines the base directory relative to which application specific temporary files should be stored

  • cache : defines the base directory relative to which application non-essential data and binary files should be stored to optimize bandwidth or performance

  • run : defines the base directory relative to which application-specific non-essential runtime files and other file objects (such as sockets, named pipes, ...) should be stored

nuts defines such distinct folders (named Store Locations) for storing different types of application data according to your operating system.

On Windows Systems the default locations are :
  • apps : "$HOME/AppData/Roaming/nuts/apps"
  • lib : "$HOME/AppData/Roaming/nuts/lib"
  • config : "$HOME/AppData/Roaming/nuts/config"
  • var : "$HOME/AppData/Roaming/nuts/var"
  • log : "$HOME/AppData/Roaming/nuts/log"
  • temp : "$HOME/AppData/Local/nuts/temp"
  • cache : "$HOME/AppData/Local/nuts/cache"
  • run : "$HOME/AppData/Local/nuts/run"
On Linux, Unix, MacOS and any POSIX System the default locations are :
  • config : "$HOME/.config/nuts"
  • apps : "$HOME/.local/share/nuts/apps"
  • lib : "$HOME/.local/share/nuts/lib"
  • var : "$HOME/.local/share/nuts/var"
  • log : "$HOME/.local/log/nuts"
  • cache : "$HOME/.cache/nuts"
  • temp : "$java.io.tmpdir/$username/nuts"
  • run : "/run/user/$USER_ID/nuts"
As an example, the configuration folder for the artifact net.thevpc.app:netbeans-launcher#1.2.4 in the default workspace in a Linux environment is

home/me/.config/nuts/default-workspace/config/id/net/vpc/app/netbeans-launcher/1.2.4/
And the log file "app.log" for the same artifact in the workspace named "personal" in a Windows environment is located at

C:/Users/me/AppData/Roaming/nuts/log/nuts/personal/config/id/net/vpc/app/netbeans-launcher/1.2.4/app.log

Store Location Strategies

When you install any application using the nuts command a set of specific folders for the presented Store Locations are created. For that, two strategies exist : Exploded strategy (the default) and Standalone strategy.

In Exploded strategy nuts defines top level folders (in linux ~/.config for config Store Location etc), and then creates withing each top level Store Location a sub folder for the given application (or application version to be more specific). This helps putting all your config files in a SSD partition for instance and make nuts run faster. However if you are interested in the backup or roaming of your workspace, this may be not the best approach.

The Standalone strategy is indeed provided mainly for Roaming workspaces that can be shared, copied, moved to other locations. A single root folder will contain all of the Store Locations.

As an example, in "Standalone Strategy", the configuration folder for the artifact net.thevpc.app:netbeans-launcher#1.2.4 in the default workspace in a Linux environment is

home/me/.config/nuts/default-workspace/config/id/net/vpc/app/netbeans-launcher/1.2.4/
And the log file "app.log" for the same artifact in the workspace named "personal" in the same Linux environment is located at

/home/me/.config/nuts/default-workspace/log/id/net/vpc/app/netbeans-launcher/1.2.4/
You can see here that the following folder will contain ALL the data files of the workspace.

/home/me/.config/nuts/default-workspace

whereas in the Exploded strategy the Store Location are "exploded" into multiple root folders.

Custom Store Locations

Of course, you are able to configure separately each Store Location to meet your needs.

Selecting strategies

The following command will create an exploded workspace

nuts -w my-workspace --exploded
The following command will create a standalone workspace

nuts -w my-workspace --standalone

Finer Customization

The following command will create an exploded workspace and moves all config files to the SSD partition folder /myssd/myconfig

nuts -w my-workspace --system-conf-home=/myssd/myconfig
You can type help for more details.

nuts help


8 Concurrency & Stability

In Nuts, building reliable and high-performance applications requires tools that manage shared state, caching, rate-limiting, and progress tracking safely across threads and execution contexts. The Nuts framework provides a suite of concurrency primitives and stability helpers to simplify these common challenges:
  • NScopedValuenull

8.1 Stable Value

null stores a value that is computed lazily — the supplier is not invoked until the first call to null. Once evaluated, the value remains stable and is reused for all subsequent accesses. This is perfect for expensive computations, constants, or resources that should only be initialized once. contentType: java

// Example 1: Lazy initialization
NStableValue&lt;Double> stableRandom = NStableValue.of(Math::random);

// Value is computed on first access
NOut.println("First value = " + stableRandom.get());

// Subsequent accesses return the same value
NOut.println("Same value = " + stableRandom.get());

// Check evaluation status
NOut.println("Evaluated? " + stableRandom.isEvaluated());
NOut.println("Valid? " + stableRandom.isValid());

8.2 Cached Value

null helps you cache expensive computations or resources. It evaluates a null once, stores the result, and reuses it until the cache expires or is invalidated. You can configure expiry policies to automatically refresh values.

// Example 1: Cache with expiry
NCachedValue&lt;Double> cachedRandom = NCachedValue.of(Math::random)
        .setExpiry(Duration.ofSeconds(5));

// First call computes and caches the value
        NOut.println("First value = " + cachedRandom.get());

// Subsequent calls reuse the cached value (within 5 seconds)
        NOut.println("Cached value = " + cachedRandom.get());

// Invalidate to force recomputation
        cachedRandom.invalidate();
NOut.println("New value after invalidate = " + cachedRandom.get());

Sometimes a computation may fail (for example, a remote call).null can automatically retry, retain the last good value on failure, and recover gracefully. This makes it ideal for unstable resources or intermittent network services.


// Example 2: Cache with retries and fallback
AtomicInteger counter = new AtomicInteger();

NCachedValue&lt;Integer> cached = NCachedValue.of(() -> {
    int attempt = counter.incrementAndGet();
    if (attempt % 2 == 0) {
        throw new RuntimeException("Simulated failure");
    }
    return attempt;
})
.setRetry(3, Duration.ofMillis(100))   // retry up to 3 times
.retainLastOnFailure(true);            // keep last value if failure occurs

// First call computes and caches
NOut.println("Value = " + cached.get());

// Next call may fail internally but still returns last good value
NOut.println("Resilient value = " + cached.get());


8.3 Rate Limit Value

null lets you control how often actions can be executed. For example, you can allow only null, using a strategy like null. This makes it easy to protect APIs, services, or expensive operations from overuse.

// Example 1: Basic sliding window rate limit
NRateLimitedValue limiter = NRateLimitedValue.ofBuilder("example")
                .withLimit("calls", 10).per(Duration.ofMinutes(2))
                .withStrategy(NRateLimitDefaultStrategy.SLIDING_WINDOW)
                .build();

for (int i = 0; i < 15; i++) {
NRateLimitValueResult res = limiter.take();
    if (res.success()) {
        NOut.println("Action " + i + " allowed at " + Instant.now());
        } else {
        NOut.println("Action " + i + " rejected. Retry after "
        + res.getRetryAfter().orElse(Duration.ZERO));
        }
        }

Instead of consuming tokens immediately, null will wait until the limiter can provide a slot before executing your code. This is useful when you want the action to eventually run rather than fail. You can also define multiple independent limits (per minute, per day, etc.) in a single limiter, and null will respect all limits before executing.

/// Example 2: Using claimAndRun
limiter.claimAndRun(() -> {
    NOut.println("Doing a limited action...");
});

// Adding multiple limits at once
NRateLimitedValue limiter2 = NRateLimitedValue.ofBuilder("api-calls")
        .withLimit("per-minute", 60).per(Duration.ofMinutes(1))
        .withLimit("per-day", 1000).per(Duration.ofDays(1))
        .build();

limiter2.claimAndRun(() -> {
    NOut.println("API call allowed");
});


8.4 Stable Value

null provides a flexible, high-level way to lock resources across threads and even processes. You can create locks from objects, paths, or resource IDs, and execute code safely while the lock is held. This ensures that critical sections are executed exclusively and consistently.

// Example 1: Create a lock from an object
NLock lock = NLock.ofPath(NPath.of("/path/to/resource.txt");

// Check lock status
Nout.println("Is locked? " + lock.isLocked());

// Run code while holding the lock
        lock.runWith(() -> {
        NOut.println("Executing critical section...");
});

// Check if current thread holds the lock
        Nout.println("Held by current thread? " + lock.isHeldByCurrentThread());

null also supports locks tied to workspace resources or IDs, allowing inter-process synchronization. You can execute tasks immediately, with a timeout, or safely retrieve results using null.

NId resourceId = NId.of("net.thevpc.nuts:nuts#0.8.6");
// Lock tied to workspace resource
NLock idLock = NLock.ofIdPath(resourceId);

// Execute a task and get result
// The lock is process-safe and prevents other processes from entering the critical section
String result = idLock.callWith(() -> {
    NOut.println("Working with locked resource...");
    return "done";
}, 5, TimeUnit.SECONDS).orNull();

NOut.println("Result = " + result);

// Run immediately if lock is free
boolean executed = idLock.runWithImmediately(() -> {
    NOut.println("Quick task executed under lock");
});
NOut.println("Was executed? " + executed);

8.5 Progress Monitoring

Long-running or multi-step operations benefit from structured progress tracking. NProgressMonitor provides a flexible, hierarchical, and thread-safe way to track, report, and manage task progress. It supports splitting, weighting, cancellation, suspension, and undoing progress, making it suitable for both simple and complex workflows.

Key features:

  • Basic Progress Tracking – Update progress using setProgress(double progress) or as a fraction of total work setProgress(long current, long max). Events are emitted for start, progress, completion, undo, cancellation, and suspension.
  • Hierarchical Progress – Split a monitor into subtasks using split(int count) or split(double... weights) to assign relative weights. This allows aggregation of multiple subtask progress into a single overall progress.
  • Listeners & Reporting – Attach listeners via addListener(NProgressListener) to handle progress events programmatically. Output can be printed to streams or loggers using NProgressMonitors.of().ofPrintStream(...) or ofLogger(...).
  • Structured Execution – runWith(Runnable) and runWithAll(Runnable...) integrate progress tracking into actual task execution. Each runnable can be associated with a subtask and progress weight.
  • Indeterminate Progress – Supports tasks with unknown total duration using setIndeterminate().
  • Estimated Duration – Automatically calculates elapsed and remaining time via getEstimatedRemainingDuration() and getEstimatedTotalDuration().
  • Convenient Defaults – NProgressMonitors.of().of() returns the current monitor if one exists, or a silent fallback otherwise.
  • ANSI-friendly Output – Works seamlessly with NText/NTextStyle for styled terminal output.

NProgressMonitor monitor = NProgressMonitors.of().of(event -> {
    NOut.println(event);
});
monitor.setProgress(0);
monitor.setProgress(0.2);
monitor.setProgress(1.0);
monitor.complete();

Example: Structured Subtasks


NProgressMonitor monitor = NProgressMonitor.of(); // scoped monitor or silenced one
NProgressMonitor[] subtasks = monitor.split(3); // 3 weighted subtasks

subtasks[0].runWith(() -> doWork("Task A"));
subtasks[1].runWith(() -> doWork("Task B"));
subtasks[2].runWith(() -> doWork("Task C"));

Example: Integration with Runnable Execution


NProgressMonitor.of().runWithAll(
    tasks.stream()
         .map(task -> (Runnable) () -> processTask(task))
         .toArray(Runnable[]::new)
);
These capabilities make NProgressMonitor a robust tool to structure, visualize, and control progress in multi-step or parallel operations, including support for cancellation, suspension, and progress weighting.

Terminal & ASCII Progress Rendering

In addition to structured progress monitoring, NAF allows rendering progress directly in the terminal, including an ASCII progress bar with messages:


for (int i = 0; i < 100; i++) {
    Thread.sleep(100);
    NSession.of().getTerminal()
            .printProgress((i / 100f), NMsg.ofC("Processing item %s", i));
}
This will render a live progress bar with the associated message, updating in-place in the terminal. It works on both POSIX terminals and Windows terminals via Jansi, leveraging the same styling framework (NText/NTextStyle) used elsewhere.

Integration with IO Streams

For monitoring IO progress, Nuts provides NInputStreamMonitor, which wraps an input stream to log or trace progress dynamically:


NInputStreamMonitor monitor = NInputStreamMonitor.of()
        .setSource(new FileInputStream("/some/path"))
        .setLogProgress(true)
        .setTraceProgress(false);

NProgressListener listener= event->NOut.println(NMsg.of("progress : %s",event.getProgress()));

NInputSource monitoredSource = NInputSource.of(
        monitor.setProgressFactory(()->listener)
                .setLength(NPath.of("/some/path").length()) //estimated length
                .create()
);
// Reading the bytes will show progress in the console
byte[] bytes=monitoredSource.readBytes();

  • NInputStreamMonitor wraps the source stream.
  • NProgressListener receives events with progress information.
  • setLength() allows estimating progress if the total size is known.
  • This integrates seamlessly with the terminal output, leveraging NOut and NMsg for styled messages. This demonstrates that progress monitoring in Nuts is not limited to computations—it can be applied to IO operations in a clean, observable way.


9 External Commands & Processes

Nuts provides a robust and unified API to manage external processes, execute commands, and interact with the underlying OS in a platform-independent way. This includes:
  • Executing shell commands or external binaries.
  • Capturing output, error streams, and exit codes.
  • Filtering and manipulating running processes.
  • Gracefully handling interactivity and embedded execution.

9.1 Process Discovery and Management

NPs allows listing and filtering running processes across platforms and optionally killing them:

To create a new process


// List all visible processes
for (NPsInfo nPsInfo : NPs.of().getResultList()) {
        System.out.println(nPsInfo);
}

// Kill all Tomcat Java processes
if (NPs.of().isSupportedKillProcess()) {
    NPsInfo[] tomcats = NPs.of()
            .setPlatformFamily(NPlatformFamily.JAVA)
            .getResultList()
            .stream()
            .filter(p -> p.getName().equals("org.apache.catalina.startup.Bootstrap"))
            .toArray(NPsInfo[]::new);

    for (NPsInfo ps : tomcats) {
        if (NPs.of().killProcess(ps.getPid())) {
            NOut.print(NMsg.ofC("Tomcat process killed (%s).\n", ps.getPid()));
        } else {
            NOut.print(NMsg.ofC("Tomcat process could not be killed (%s).\n", ps.getPid()));
        }
    }
}
  • NPs.of() provides a snapshot of running processes.
  • Processes can be filtered by name, platform family, or other criteria.
  • killProcess(pid) supports inter-process termination when the platform allows it.

9.2 Executing External Commands

NAF provides a high-level API to execute external commands, whether locally or remotely, in a concise, structured, and cross-platform manner. Using NExecCmd, you can run processes, capture their output, handle errors, and even execute them on remote hosts via SSH—all without the verbosity of the standard Java Process API.Key Features
  • Embedded & Local Execution: Run commands in the current JVM or OS environment.
  • Remote Execution: Execute commands on SSH-enabled hosts transparently.
  • Automatic Output Capture: Grab stdout and stderr without manually reading streams.
  • Run Java Artifacts Directly: Download a JAR and its dependencies from Maven or remote repositories and execute it automatically.
  • Fail-Fast Control: Stop execution immediately on errors if desired.
  • Minimal Boilerplate: Avoid complex Runtime.exec() and thread-based stream consumption.

To create a new process


// Simple embedded command execution
String result = NExecCmd.of("info")
                .getGrabbedAllString();
NOut.println(result);
  • .of("info") creates the command to execute.

  • .getGrabbedAllString() captures the full output (stdout + stderr) as a string.

  • No exceptions are thrown automatically; the caller can inspect result or getResultCode() to decide if the execution succeeded.

Example: Executing a shell command with NSH


String result = NExecCmd.of()
        .addCommand(NConstants.Ids.NSH, "-c", "ls")
        .grabAll()
        .failFast()
        .getGrabbedOutString();

NOut.println("Result:");
NOut.println(result);
  • Nuts provides embedded shells (NSH) for running commands in a sandboxed environment.
  • Output is automatically sanitized to remove terminal formatting unless explicitly requested.

Special Executor IDs

Nuts can handle commands with special identifiers or remote resources:

String result = NExecCmd.of()
        .addExecutorOptions("--bot")
        .addCommand("com.mycompany:my-remote-artifact")
        .addCommand("list", "-i")
        .getGrabbedAllString();

NOut.println(result);
  • Useful for running workspace-bound tools or remote executables.
  • Works with Maven coordinates or URLs pointing to executable jars.

Remote & Structured Command Execution

NExecCmd is not limited to local commands: you can execute processes on remote systems via SSH (or any supported executor) and easily capture stdout/stderr with minimal boilerplate:

NExecCmd u = NExecCmd.of()
        .at("ssh://me@myserver")              // Execute on remote server
        .setIn(NExecInput.ofNull())           // No stdin input
        .addCommand("ps", "-eo",             // Command + arguments
            "user,pid,%cpu,%mem,vsz,rss,tty,stat,lstart,time,command")
        .grabErr()                            // Capture stderr
        .setFailFast(true)                    // Optional: fail immediately on error
        .grabOut();                           // Capture stdout

// Access results
String stdout = u.getGrabbedOutString();
String stderr = u.getGrabbedErrString();
int exitCode = u.getResultCode();

NOut.println("Exit code: " + exitCode);
NOut.println("Output:\n" + stdout);
Key points:
  • at(...) allows specifying a remote target (SSH, etc.).
  • grabOut() / grabErr() capture output streams automatically.
  • setFailFast(true) ensures that any failure will stop execution immediately.
  • No need for Runtime.exec() boilerplate or manual stream handling.
This makes NExecCmd a powerful, concise, and cross-platform alternative to the traditional Java Process API.

Why NExecCmd is Better than Runtime.exec()

Using Runtime.exec() requires verbose boilerplate to:
  • manage stdin/stdout/stderr streams,
  • handle threading to avoid blocking,
  • wait for process completion,
  • and manually check exit codes.
With NExecCmd, all of this is simplified and made cross-platform, while also supporting structured output handling, remote execution, and direct execution of downloaded Java artifacts transparently.


10 Utilities and Support

10.1 NLog for elegant Logging

NLog — Structured Developer Logging in Nuts

null combines structured messages (null), styled output with colors and null formatting, and seamless integration with null ornull. Messages carry an null, similar to logging markers, which allows you to classify, filter, or style logs at runtime. Whether you’re building CLI tools, scripts, or full applications, null makes logs expressive, rich, and easy to understand.

🔧 Basic Usage

You can obtain a logger using:

NLog log = NLog.of(MyClass.class);
Then log messages with a semantic level:

log.error(NMsg.ofC("[%s] not yet supported", featureName));
log.warn(NMsg.ofC("Slow lock file: waited %s on %s", duration, path));
Log methods include:
  • log.info(...)
  • log.warn(...)
  • log.error(...)
  • log.debug(...)
  • log.trace(...)

All accept an NMsg, or lazily supplied via Supplier<NMsg>.

Structured Logging with Verbs and Levels

Each log message can include:
  • A standard log level: SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST, etc.

  • A semantic verb (e.g., READ, START, FAIL, CACHE, etc.) to describe the nature of the event.

Generic Log Example


NLog.of("myapp").info(
        NMsg.ofV("Hello %s, task %s completed with status %s",
"user","Alice","status",
        NText.ofStyled("OK", NTextStyle.primary1()), exception
)
        .withIntent(NMsgIntent.CONFIG)   // Classifies this log, can be used for filtering or styling
.withThrowable(exception)        // Attaches the exception
.withDurationMs(123)             // Optional: report task duration
);

Common NLogIntent Values

NLogIntent adds semantic meaning to logs beyond plain text:

  • INFO Informational messages

  • DEBUG Debug-level messages

  • START Start of an operation

  • SUCCESS Successful end of an operation

  • FAIL Operation failure

  • WARNING Warnings

  • READ Read/access events

  • UPDATE State or file updates

  • ADD Resource addition

  • REMOVE Resource removal

  • CACHE Cache-related operations

  • PROGRESS Ongoing task progress

You can define custom verbs using NLogIntent.of("CUSTOM_VERB").

Fluent Logging with NMsg

For advanced logging, use the fluent builder API with .withXYZ() :


log
    .log(
            NMsg.ofC("[%s] %s", action, NCmdLine.of(context.getCommand()))
            .withLlevel(Level.FINER)
            .withIntent(NMsgIntent.START)
        );
Supports:
  • Setting level (withLevel(...))
  • Adding a verb (withIntent(...))
  • Attaching exceptions (withThrowable(Throwable))
  • Attaching per uration (withDurationMs(Throwable))
  • Final logging (log(...))

Example with Exception


NLog.of(Nsh.class)
    .log(NMsg.ofC("Error resolving history file: %s", history.getHistoryFile())
        .withError(ex)
    );
// equivalent
NLog.of(Nsh.class)
    .log(NMsg.ofC("Error resolving history file: %s", history.getHistoryFile())
        .withLevel(Level.SEVERE)
        .withIntent(NMsgIntent.FAIL)
        .withThrowable(ex)
    );

Scoped Logging

Traditional MDC allows you to store key-value pairs for the current thread, but managing nested or dynamic contexts often becomes cumbersome. NLog builds on and extends this concept by allowing nested logging scopes, dynamic placeholders, message prefixes, and custom log handlers — all without touching global configuration or rewriting classes. Logs automatically inherit the correct context, making them structured, expressive, and runtime-aware. With NLog, you get the convenience of MDC plus the power of non-invasive, modular, and scoped logging.

class MyApp {
    public void test() {
        NLogs.of().runWith(
            NLogContext.of()
                    .withMessagePrefix(NMsg.ofC("[My Application]")) // Prefix added to every log message in this scope
                    .withPlaceholder("user", "Adam") // Placeholder available to all logs in this scope
                    .withLog(message -> NOut.println(NMsg.ofC("[SCOPED] %s", message))), // Custom log handler: redirect all messages to stdout with a [SCOPED] prefix
            () -> {
                OtherClass.doThis(); // Perform actions that generate logs
            }
        );
    }
}
class OtherClass {
    private static void doThis() {
        // Standard logger: picks up class context but not the scoped stdout handler
        NLog.of(LogTest.class).log(NMsg.ofC("hello %s", NMsg.placeholder("user")));
        // Scoped logger: inherits context from outer scope (user=Adam) and uses the custom stdout handler
        NLog.ofScoped(OtherClass.class).log(NMsg.ofV("hello $user"));
    }
}


Output Destinations

By default, NLog messages are rendered to:
  • Standard error (NSession.err()), so they remain separate from user output.
  • And/or a workspace log file, depending on the session configuration.
This ensures that logs:
  • Do not pollute standard output (NOut, NTrace),
  • Can be persisted and analyzed later if needed,
  • Can be redirected or filtered using Nuts session capabilities.
You can customize log destinations by configuring the NSession, such as redirecting logs to files, suppressing error output, or adjusting verbosity.

Why NLog + NMsg Is Unique

In most Java logging frameworks:
  • Message formatting is tied to string interpolation styles (e.g., SLF4J uses {} placeholders, Java Logging uses {0}, Log4j2 can use %s, etc.).
  • You must format messages manually for each logging backend, or risk inconsistent logs.
  • Structured output (e.g., JSON logs, table views) requires extra work or third-party wrappers.
With NLog, this complexity disappears: You write once with NMsg:

log.warn(NMsg.ofC("File %s could not be read", path));
Or:

log.info(NMsg.ofJ("User {0} logged in at {1}", username, timestamp));
Or with variables:

log.debug(NMsg.ofV("Downloading $file to $path", NMaps.of("file", id, "path", target)));
Then the rendering adapts to the output:
  • CLI (NTF) → colored and styled
  • File → plain text
  • JSON → structured representation
  • GUI → rich visual log viewers
  • Remote API → formatted strings or JSON

Summary

  • Use NLog for developer-focused internal logs.
  • Use NOut/NTrace for user-facing messages.
  • Attach semantic verbs (NLogIntent) to add context.
  • Use NMsg for structured, styled messages.
  • Use .with() for flexible, readable, builder-style logging.
This makes NLog an ideal logging facility for modular, structured, and contextual diagnostics in the Nuts platform.

10.2 NOptional

NOptional is a powerful alternative to Java’s Optional that goes beyond being just a nullable container. It’s designed to make null handling more expressive, composable, and safer — closer to the expressive capabilities of TypeScript or Kotlin.In addition to the standard of(), get(), and orElse() methods, it introduces:
  • Named values for better error messages (ofNamed()).
  • Optional chaining (then(...)) to safely traverse object graphs.

  • Nullish coalescing (orElse()), similar to ??.

  • Assertion-style accessors (get() vs orNull()).

These features make it ideal for writing expressive, null-safe code in complex object hierarchies — without endless if (x != null) checks.

Non Null Assertion and Coalescing

Java has a builtin null Check mechanism but it does not enable customized messages or exceptions. Optional are described as per Java's (c) Documentation "A container object which may or may not contain a non-null value". NOptional is more of an Object Wrapper than addes several useful null related operators like '??' '?.' and '!' in typescript.
ConceptJavaNAF (NOptional)Equivalent TS
Null check with exception

(user == null) throw new ...

.ofNamed(user,"user").get()

!

Null-safe call

(user != null) user.toUpperCase()

.of(user).map(String::toUpperCase).orNull()

?.toUpperCase()

user?.toUpperCase()

!= null ? user : "default"

.of(user).orElse("default")

?? "default"

Optional Chaining

One of the most useful features of NOptional is the ability to traverse deep object graphs safely without repetitive null checks.

With then(...), you can chain field access or method calls, and NOptional automatically short-circuits the chain if any part is null.


    Number roadNumber=(app!=null && app.person!=null && app.person.road!=null)? app.person.address.road.number:null;
 
    // expected var roadNumber=app?.person?.address?.road?.number;
    Number roadNumber=NOptional.of(app).then(v->v.person).then(v->v.road).then(v->v.number).orNull();

Combining Optional Chaining


    Address address=(app!=null && app.person!=null)?app.person.address:null;
    if(address==null){
       throw new IllegalArgumentException("missing address"); 
    }
    Number roadNumber=(address!=null 
        && address.road!=null)
        ? address.road.number:0;
 
    // expected : var roadNumber=app?.person?.address!.road?.number??0;
    Number roadNumber=NOptional.of(app).then(v->v.person).then(v->v.address).get().then(v->v.road).then(v->v.number).orElse(0);

When to use get(), orNull(), and orElse()

  • get() – returns the value or throws an exception if absent (assertive).

  • orNull() – returns null if absent (passive).

  • orElse(value) – returns a fallback if absent (coalescing).

Why not just use Optional

While Java’s built-in Optional is useful, it lacks several features needed for expressive, null-safe code in complex applications:
  • ❌ No support for named values or custom exception messages.
  • ❌ No built-in chaining (Optional chaining is verbose with flatMap).
  • ❌ No nullish coalescing equivalent.
  • ❌ No describe() or integration with structured diagnostics.

NOptional solves all of these while remaining compatible with functional idioms.


10.3 NStream

Java’s Stream API is powerful, but NAF introduces NStream to provide additional features useful in application development and debugging, particularly when you want describable, inspectable, and framework-integrated streams.NStream is not meant to replace Stream, but rather to wrap and extend it, providing:
  • Describable operations for logging, debugging, and reporting
  • Integration with NAF features like NElement, NMsg, and structured outputs
  • Helper methods that simplify working with iterables, iterators, or streams

Creating NStream


// From values
NStream<Integer> s1 = NStream.of(1, 2, 3, 4, 5);

// From a Java Stream
Stream<String> javaStream = Stream.of("a","b","c");
NStream<String> s2 = NStream.ofStream(javaStream);


// From Iterable or Iterator
List<Double> numbers = List.of(0.1, 0.2, 0.3);
NStream<Double> s3 = NStream.of(numbers);

// From any object
NStream<Double> s3 = NStream.ofSingleton(1.0);

// From Optional
NOptional<Double> number = NOptional.of(1);
NStream<Double> s3 = NStream.ofOptional(number);
NStream works transparently over all these, giving you a single, uniform API.

Stream Operations

NStream supports familiar operations:

NStream<Integer> s = NStream.of(1,2,3,4,5)
                             .filter(x -> x % 2 == 0)
                             .map(x -> x * 10);
You can use any combination of map, filter, flatMap, sorted, etc., just like a standard Java Stream.

Describable Pipelines

One of NStream’s main benefits is that you can describe the operations for inspection or reporting. This is done using describable functional interfaces like NFunction and NPredicate:

NStream<Integer> s = NStream.ofArray(1,2,3,4,5)
    .filter(NPredicate.of(x -> x % 2 == 0)
                      .withDesc(() -> NElement.ofString("even numbers")))
    .map(NFunction.of(x -> x * 10)
                  .withDesc(NElement.ofObject("mul", NElement.ofNumber(10))));

NElement description = s.describe();
NOut.println(description);
Example output (structured NElement):

{
  "source": [1,2,3,4,5],
  "operations": [
    {"filter": "even numbers"},
    {"map": {"mul": 10}}
  ]
}
NOTE : Descriptions are optional. You can still use plain Stream functions if you don’t need introspection.

Why Use NStream?

  • Visibility – describe() gives a structured view of the pipeline, useful for debugging and reporting.
  • Consistency – Unified API over Streams, Iterables, and Iterators.
  • NAF Integration – Works seamlessly with NElement, NMsg, and other NAF utilities.
  • Optional – You can ignore the descriptive features and use it just like a standard Stream.
  • NStream is particularly helpful in NAF search commands, logging pipelines, or anywhere you want structured insight into the data processing steps.


11 Application Lifecycle

11.1 Nuts Application Framework

Using Nuts Application Framework (NAF)

Using nuts is transparent as we have seen so far. It's transparent both at build time and runtime. However, nuts can provide our application a set of unique helpful features, such as install and uninstall hooks, comprehensive command line support and so on.

To create your first NAF application, you will need to add nuts as a dependency and change your pom.xml as follows:


<?xml version="1.0" encoding="UTF-8"?>
<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>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>net.thevpc.nuts</groupId>
            <artifactId>nuts-lib</artifactId>
            <version></version>
        </dependency>
        <dependency>
            <groupId>jexcelapi</groupId>
            <artifactId>jxl</artifactId>
            <version>2.4.2</version>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <nuts.application>true</nuts.application>
    </properties>
</project> 

Please take note that we have added a property nuts.application=true. Actually this is not mandatory, but this will help nuts package manager detect that this application uses NAF before downloading its jar (the information will be available in the .xml descriptor on the remote repository).

Then we will add some cool features to our application. We write a dummy message whenever the application is installed, uninstalled or updated. We will also add support to "--file=[path]" argument to specify the workbook path.

package com.mycompany.app;

import java.io.File;

import jxl.Workbook;
import jxl.write.WritableWorkbook;

public class App implements NApplication {

    public static void main(String[] args) {
        // just create an instance and call runAndExit in the main method
        // this method ensures that exist code is well propagated
        // from exceptions to caller processes
        new App().run(NAppRunOptions.ofExit(args));
    }

    @Override
    public void run() {
        NCmdLine cmd = NApp.of().getCmdLine();
        File file = new File("file.xls");
        while (cmd.hasNext()) {
            switch (cmd.getKey().getString()) {
                case "--file": {
                    NArg a = cmd.nextEntry().get();
                    file = new File(a.getStringValue());
                    break;
                }
                case "--fill": {
                    // process other options here ...
                    break;
                }
                default: {
                    s.configureLast(cmd);
                }
            }
        }
        try {
            WritableWorkbook w = Workbook.createWorkbook(file);
            s.out().printf("Workbook just created at %s%n", file);
        } catch (Exception ex) {
            ex.printStackTrace(s.err());
        }
    }

    @Override // this method is not required, implement when needed
    public void onInstallApplication() {
        NOut.println(NMsg.ofC("we are installing My Application : %s%n", NApp.of().getId()));
    }

    @Override // this method is not required, implement when needed
    public void onUninstallApplication() {
        NOut.println(NMsg.ofC("we are uninstalling My Application : %s%n", NApp.of().getId()));
    }

    @Override // this method is not required, implement when needed
    public void onUpdateApplication() {
        NOut.println(NMsg.ofC("we are updating My Application : %s%n", NApp.of().getId()));
    }
}

Now we can install or uninstall the application and see the expected messages.

nuts -y install com.mycompany.app:my-app
nuts -y uninstall com.mycompany.app:my-app

11.2 Your first Application using nuts

Running your application with Nuts

Lets take, step by step, an example of an application that you will run using nuts package manager

First we can create the project using your favourite IDE or using simply mvn command


mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-simple -DarchetypeVersion=1.4 -DinteractiveMode=false
We will have a fully generated java project

~/> tree
.
└── my-app
    ├── pom.xml
    └── src
        ├── main
            └── java
                └── com
                    └── mycompany
                        └── app
                            └── App.java

Now we will add some dependencies to the project. Let's add jexcelapi:jxl#2.4.2 and update pom.xml consequently.


<?xml version="1.0" encoding="UTF-8"?>
<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>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>jexcelapi</groupId>
            <artifactId>jxl</artifactId>
            <version>2.4.2</version>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
</project> 
Now we update the App.java file

package com.mycompany.app;

import java.io.File;

import jxl.Workbook;
import jxl.write.WritableWorkbook;

public class App {

    public static void main(String[] args) {
        try {
            WritableWorkbook w = Workbook.createWorkbook(new File("any-file.xls"));
            System.out.println("Workbook just created");
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

finally we compile the app:

mvn clean install

Of course, we won't be able to run the application yet. Would we? For this app to work there are several ways, all of them are complicated and require modifying the pom.xml and even modifying the output jar. we can for instance generate an output lib directory and update the META-INF file using maven-dependency-plugin. (see https://maven.apache.org/plugins/maven-shade-plugin ; https://www.baeldung.com/executable-jar-with-maven). We could also use maven-assembly-plugin to include the dependencies into the jar itself ('what the fat' jar!). Another alternative is to use an uglier solution with maven-shade-plugin and blend libraries into the main jar. In all cases we need as well to configure maven-jar-plugin to specify the main class file.

I am not exposing all solutions here. You can read this article for more details (https://www.baeldung.com/executable-jar-with-maven) but trust me, they all stink.Instead of that we will use nuts. In that case, actually we are already done, the app is already OK! We do not need to specify the main class neither are we required to bundle jxl and its dependencies. We only need to run the app. That's it.

Basically, you can install the application using its identifier com.mycompany.app:my-app. The latest version will be resolved.


nuts install com.mycompany.app:my-app
nuts my-app
This will install the application and run it on the fly. Dependencies will be detected, resolved and downloaded. The application is installed from local maven repository. It needs to be deployed to a public repository for it to be publicly accessible, however.We can also choose not to install the app and bundle it as a jar. No need for a public repository in that case:

nuts -y com my-app-1.0.0-SNAPSHOT.jar

As we can see, nuts provides the simplest and the most elegant way to deploy your application.

One question though. what happens if we define multiple main methods (in multiple public classes). It's handled as well by nuts seamlessly. It just asks, at runtime, for the appropriate class to run.

Using Nuts Application Framework

Using nuts is transparent as we have seen so far. It's transparent both at build time and runtime. However, nuts can provide our application a set of unique helpful features, such as install and uninstall hooks, comprehensive command line support and so on.

To create your first NAF application, you will need to add nuts as a dependency and change your pom.xml as follows:


<?xml version="1.0" encoding="UTF-8"?>
<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>
    <groupId>com.mycompany.app</groupId>
    <artifactId>my-app</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>net.thevpc.nuts</groupId>
            <artifactId>nuts</artifactId>
            <version>0.8.7.0</version>
        </dependency>
        <dependency>
            <groupId>jexcelapi</groupId>
            <artifactId>jxl</artifactId>
            <version>2.4.2</version>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <nuts.application>true</nuts.application>
    </properties>
</project> 

Please take note that we have added a property nuts.application=true. Actually this is not mandatory, but this will help nuts package manager detect that this application uses NAF before downloading its jar (the information will be available in the .xml descriptor on the remote repository).

Then we will add some cool features to our application. We write a dummy message whenever the application is installed, uninstalled or updated. We will also add support to "--file=[path]" argument to specify the workbook path.

package com.mycompany.app;

import java.io.File;

import jxl.Workbook;
import jxl.write.WritableWorkbook;

@NAppDefinition
public class App {

    public static void main(String[] args) {
        // just create an instance and call runAndExit in the main method
        // this method ensures that exist code is well propagated
        // from exceptions to caller processes
        new App().run(NAppRunOptions.ofExit(args));
    }

    @NAppRunner
    public void run() {
        NCmdLine cmd = NApp.of().getCmdLine();
        File file = new File("file.xls");
        while (cmd.hasNext()) {
            switch (cmd.getKey().getString()) {
                case "--file": {
                    NArg a = cmd.nextEntry().get();
                    file = new File(a.getStringValue());
                    break;
                }
                case "--fill": {
                    // process other options here ...
                    break;
                }
                default: {
                    s.configureLast(cmd);
                }
            }
        }
        try {
            WritableWorkbook w = Workbook.createWorkbook(file);
            s.out().printf("Workbook just created at %s%n", file);
        } catch (Exception ex) {
            ex.printStackTrace(s.err());
        }
    }

    @NAppInstall // this method is not required, implement when needed
    public void onInstallApplication() {
        NOut.println(NMsg.ofC("we are installing My Application : %s%n", NApp.of().getId()));
    }

    @NAppUninstaller // this method is not required, implement when needed
    public void onUninstallApplication() {
        NOut.println(NMsg.ofC("we are uninstalling My Application : %s%n", NApp.of().getId()));
    }

    @NAppUpdater // this method is not required, implement when needed
    public void onUpdateApplication() {
        NOut.println(NMsg.ofC("we are updating My Application : %s%n", NApp.of().getId()));
    }
}

Now we can install or uninstall the application and see the expected messages.

nuts -y install com.mycompany.app:my-app
nuts -y uninstall com.mycompany.app:my-app

11.3 Command Line Arguments

Nuts Application Framework CommandLine

Application Command line can be retrieved via NApp instance:


    NCmdLine c1= NApp.of().getCmdine();

Exec / Autocomplete modes


    NCmdLine c= NApp.of().getCmdine();
    if(c.isExecMode()){
        ///    
    }

11.4 Nuts Descriptor Integration

Nuts Descriptor Integration

  • Seamless integration
  • Maven Solver

Nuts and Maven

  • nuts.executable=<true|false> : when true the artifact is an executable (contains main class)

  • nuts.application=<true|false> : when true the artifact is an executable application (implements NutsApplication)

  • nuts.gui=<true|false> : when true the requires a gui environment to execute

  • nuts.term=<true|false> : when true the artifact is a command line executable

  • nuts.icons=<icon-path-string-array> : an array (separated with ',' or new lines) of icon paths (url in the NPath format)

  • nuts.genericName=<genericNameString> : a generic name for the application like 'Text Editor'

  • nuts.<os>-os-dependencies : list (':',';' or line separated) of short ids of dependencies that shall be appended to classpath only if running on the given os (see NutsOsFamily). This is a ways more simple than using the builtin ' profile' concept of Maven (which is of course supported as well)

  • nuts.<arch>-arch-dependencies : list (':',';' or line separated) of short ids of dependencies that shall be appended to classpath only if running on the given hardware architecture (see NutsArchFamily). This is a ways more simple than using the builtin 'profile' concept of Maven (which is of course supported as well)

  • nuts.<os>-os-<arch>-arch-dependencies : list (':',';' or line separated) of short ids of dependencies that shall be appended to classpath only if running on the given hardware architecture and os family


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         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>
    <groupId>your-group</groupId>
    <artifactId>your-project</artifactId>
    <version>1.2.3</version>
    <packaging>jar</packaging>
    <properties>
        <!--properties having special meanings in Nuts-->
        <maven.compiler.target>1.8</maven.compiler.target>

        <!--properties specific to nuts for developers extending nuts-->
        <nuts.runtime>true</nuts.runtime> <!--if you implement a whole new runtime-->
        <nuts.extension>true</nuts.extension> <!--if you implement an extension-->

        <!--other properties specific to nuts-->
        <nuts.genericName>A Generic Name</nuts.genericName>
        <nuts.executable>true</nuts.executable>
        <nuts.application>true</nuts.application>
        <nuts.gui>true</nuts.gui>
        <nuts.term>true</nuts.term>

        <nuts.categories>
            /Settings/YourCategory
        </nuts.categories>
        <nuts.icons>
            classpath://net/yourpackage/yourapp/icon.svg
            classpath://net/yourpackage/yourapp/icon.png
            classpath://net/yourpackage/yourapp/icon.ico
        </nuts.icons>
        <nuts.windows-os-dependencies>
            org.fusesource.jansi:jansi
            com.github.vatbub:mslinks
        </nuts.windows-os-dependencies>
        <nuts.windows-os-x86_32-arch-dependencies>
            org.fusesource.jansi:jansi
            com.github.vatbub:mslinks
        </nuts.windows-os-x86_32-arch-dependencies>
    </properties>

    <dependencies>
    </dependencies>
</project>

Nuts and Java MANIFEST.MF



Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: vpc
Created-By: Apache Maven 3.8.1
Build-Jdk: 1.8.0_302

Nuts-Id: groupid:artifactid#version
Nuts-Dependencies: org.fusesource.jansi:jansi#1.2?os=windows;com.github.vatbub:mslinks#1.3?os=windows
Nuts-Name: Your App Name
Nuts-Generic-Name: Your App Generic Name
Nuts-Description: Your App Description
Nuts-Categories: /Settings/YourCategory;/Settings/YourCategory2
Nuts-Icons: classpath://net/yourpackage/yourapp/icon.svg;classpath://net/yourpackage/yourapp/icon.png
Nuts-Property-YourProp: YourValue

Comment: if the Nuts-Id could not be found, best effort will be used from the following
Automatic-Module-Name: yourgroupid.yourartifactid.YourClass
Main-Class: groupid.artifactid.YourClass
Implementation-Version: 1.2.3

Nuts and Java 9 (jdeps)

Nuts supports Automatic-Module-Name.


Automatic-Module-Name: yourgroupid.yourartifactid.YourClass

Nuts and Gradle (TODO)



12 Integration & Interoperability

NAF (Nuts Application Framework) is designed not only to provide powerful core utilities but also to integrate seamlessly with other Java ecosystems and frameworks. This section explores how NAF can interoperate with common tools and frameworks, allowing you to leverage its features without sacrificing compatibility or flexibility.

Key Points

Spring Boot Integration

NAF can be used alongside Spring Boot, providing its services (like NWorkspace, NExecCmd, NLog, and NTextArt) as beans or components in a Spring-managed application. This enables combining Spring’s dependency injection and lifecycle management with NAF’s runtime utilities.

SLF4J Backend for NLog

While NAF provides its own logging API (NLog) with advanced features like structured logging, scopes, and message formatting, it can delegate to widely-used logging frameworks like SLF4J. This allows applications to retain NLog capabilities while integrating with existing logging infrastructure, including Logback or Log4j2.

Flexible Output & Messaging

NAF’s components—such as NTextArt, NProgressMonitor, and NExecCmd—can output to different destinations, including standard streams, files, or logging backends. This makes it easy to integrate visual and progress feedback into GUI applications, web services, or enterprise pipelines.

Cross-Framework Reusability

Because NAF is modular and decoupled, its APIs can coexist with other libraries without requiring significant refactoring. You can use Nuts for dependency resolution, task execution, logging, and progress monitoring in the context of Spring Boot, Micronaut, or even plain Java SE projects.

12.1 NAF Spring Boot Integration

nuts can work flawlessly with spring boot applications. You just need one annotation @NAppDefinition to mark your SpringBootApplication.Add the following dependency to you spring boot project

    <dependency>
        <groupId>net.thevpc.nuts</groupId>
        <artifactId>nuts-spring-boot</artifactId>
        <version>0.8.6.0</version>
    </dependency>

Add @NAppDefinition in your SpringBootApplication top class.


@NAppDefinition
@SpringBootApplication
@Import(NutsSpringBootConfig.class)
public class AppExample {
    public static void main(String[] args) {
        SpringApplication.run(AppExample.class, args);
    }

    @NAppRunner // not mandatory
    public void run() {
        NOut.println("Hello ##World##");
    }
}
Now you can inject Nuts objects in your beans

@Component
public class MyBean {
    @Autowired NSession session;
    @Autowired NWorkspace workspace;
    @Autowired NScheduler scheduler;
    @Autowired NTerminal term;
    @Autowired NPrintStream out;
}


12.2 NAF SLF4J Integration


    <dependency>
        <groupId>net.thevpc.nuts</groupId>
        <artifactId>nuts-nuts-slf4j</artifactId>
        <version>0.8.6.0</version>
    </dependency>


13 Workspace & Package Context

NWorkspace and NSession are the foundation of Nuts runtime context. Understanding how they work is essential for correctly using all Nuts components.

What is a NWorkspace?

A NWorkspace represents the application context and component container in Nuts. It is:

  • Comparable to a Spring ApplicationContext,

  • A container for all globally or locally scoped Nuts services (like NOut, NLog, NWorkspaceService, etc.),

  • Required for all operations using Nuts APIs — at any moment, a Nuts component is always used in the context of a workspace.


What is a NSession?

A NSession is a lightweight, thread-scoped execution context associated with a NWorkspace. It:

  • Controls options like verbosity, trace mode, output streams, and more,
  • Is thread-local by default and can be inherited by child threads,

  • Provides runtime configuration for rendering, formatting, user input, log levels, etc.

Any Nuts operation (e.g., NOut.println(...)) implicitly uses the current NSession, and therefore accesses the NWorkspace bound to it.


13.1 NWorkspace

Opening and Sharing Workspaces

Default (global) workspace


NWorkspace.require();
  • Returns the currently shared (global) workspace if one exists.
  • If no global workspace is present, creates and shares one by delegating to

Nuts.openWorkspace("--reset-options", "--in-memory").share();
This ensures that a workspace is always available, without requiring manual setup.
This workspace is in-memory and ignores any inherited CLI options (--reset-options). It’s ideal for quick use cases, testing, or tools that need a minimal setup.

Scoped (local) workspace

If you need isolation or temporary workspace setup:

Nuts.openWorkspace().runWith(() -> {
        // This code runs inside a thread-local scoped workspace
        // Nuts components here use the scoped context
        });
  • Temporarily hides the global workspace inside the block,
  • Scoped workspace is available in current and inherited threads,
  • Ideal for frameworks, sandboxing, plugins and testing.
  • Nuts.openWorkspace() uses persistent location (across processes) unless --in-memory is passed.

Sharing workspace globally

To explicitly promote a workspace to global:

Nuts.openWorkspace().share();
You may also customize it before sharing:

Nuts.openWorkspace("--in-memory", "--color").share();

Environment & System Info

Workspaces provide access to environment metadata:NWorkspace ws = NWorkspace.current();

ws.getHostName();               // Host name
ws.getPid();                    // Process ID
ws.getOsFamily();               // Linux, Windows, Mac, etc.
ws.getShellFamily();            // bash, cmd, powershell, etc.
ws.getPlatform();               // Java, Android, etc.
ws.getOs();                     // Full OS ID
ws.getOsDist();                 // OS distribution (e.g. Ubuntu)
ws.getArch();                   // CPU architecture (e.g. amd64)
ws.getArchFamily();            // Arch family (e.g. x86_64)
ws.getDesktopEnvironment();     // Gnome, KDE, etc.
ws.getDesktopEnvironmentFamily(); // Gnome-like, etc.
ws.isGraphicalDesktopEnvironment(); // true if graphical session
You can also list all available shell families or desktop environments:

ws.getShellFamilies(); // e.g. [BASH, ZSH, CMD]
ws.getDesktopEnvironments(); // List of detected environments

Accessing the Current Workspace

To retrieve the current NWorkspace (i.e., the one bound to the current thread context), you have two options:

Elegant, fail-fast access


NWorkspace ws = NWorkspace.of();
  • Returns the current workspace if one is available,
  • Throws an exception if no workspace is present,
  • Recommended when a workspace is expected to exist.
  • Equivalent to NWorkspace.get().get() but more expressive and fails clearly.

Safe, optional access


Optional<NWorkspace> wsOpt = NWorkspace.get();
  • Returns an NOptionalnull

13.2 NSession

NSession defines the current execution context within a Nuts NWorkspace. It encapsulates:

  • Command-line options
  • User preferences
  • Output formatting
  • Trace/log verbosity
  • Interactive modes
  • Runtime state

Every operation within Nuts is executed in the scope of a NSession.

Getting the Current Session

Elegant (fail-fast)


    NSession session = NSession.of();

Safe (optional)


Optional<NSession> opt = NSession.get();
Returns an Optional session, or empty if not available.

What Does a Session Do?

  • Holds contextual flags like --trace, --yes, --bot, --dry, --confirm
  • Controls output formatting: plain, json, xml, tree, table, etc.
  • Configures confirmation/interaction modes
  • Tracks fetch/cache strategies and expiration
  • Controls repository settings
  • Defines runtime identity (root(), sudo(), etc.)

Thread-Scoped Context

NSession is thread-local and inherited by spawned threads unless explicitly changed. To run with a different session:

session.runWith(() -> {
    // Executes within the session context
});
Or return a result:

String result = session.callWith(() -> computeSomething());

Common Flags and States

  • --trace isTrace() Enables trace-mode output

  • --yes isYes() Assume “yes” for confirmations

  • --no isNo() Assume “no” for confirmations

  • --ask isAsk() Always ask for confirmation

  • --bot isBot() Enable non-interactive/script mode

  • --dry isDry() Dry-run only, no actual execution

Output Format


Control the rendering format of structured output:
session.json();     // JSON
session.table();    // Tabular
session.tree();     // Tree
session.xml();      // XML
session.props();    // Properties
session.plain();    // Default/plain text

Output formats affect rendering of NOut.println(...), logging, tables, etc.

Streams Access

Each session controls its I/O streams (also accessible via NOut, NErr and NIn):

session.out().println("Standard Output");
session.err().println("Error Output");
session.in().readLine();
This enables custom I/O redirection (e.g., GUI, files, remote shells).

Trace Modes

Trace mode activates auxiliary output useful for end users (not developers):
  • isPlainTrace(): plain-text trace

  • isIterableTrace(): structured trace with iterable format

  • isStructuredTrace(): structured trace without iterable mode


if (session.isTrace()) {
    NOut.println("Tracing enabled...");
}
// same as
NTrace.println("Tracing enabled...");

Dependency Resolution Options And Fetch Strategy

Used when resolving artifacts and loading jars from repositories (dynamic classloading)
  • isTransitive() Use transitive repositories

  • isCached() Use cached data when possible

  • isIndexed() Use indexed metadata

  • getExpireTime() Expire cache before this date

  • setFetchStrategy() Customize fetch strategy


session.setFetchStrategy(NFetchStrategy.ONLINE);
The fetch strategy determines how and where Nuts searches for artifacts (e.g., dependencies, packages) across local and remote repositories.This affects resolution against repositories such as Maven Central, Nuts-based repositories, and custom remotes.

🔎 Available Strategies

StrategyDescription

ONLINE

Default mode. Searches locally first; if not found, falls back to remotes.

OFFLINE

Searches only local caches. No remote access is allowed.

ANYWHERE

Searches both local and remote repositories concurrently.

REMOTE

Searches only remote repositories, ignoring local cache.

Confirmation and Interaction

Control how user prompts are handled:

session.yes();              // Force auto-yes
session.no();               // Force auto-no
session.ask();              // Always prompt

session.setConfirm(NConfirmationMode.YES);

Interactive Session Features

Enable/disable progress output:

session.setProgressOptions("auto");

Control GUI/headless:


session.setGui(true);

The gui flag in a session determines whether user interactions should be performed using graphical UI dialogs or standard console input.

  • When gui is enabled, interactive methods like NIn.readLine() or NIn.ask() may display graphical dialogs for input instead of using the terminal.

  • When gui is disabled (default in headless or CLI environments), all interactions fall back to console-based prompts.

In GUI-enabled environments, this may pop up a dialog window rather than prompting in the console.

Customize output line prefixes:


session.setOutLinePrefix("[out] ");
session.setErrLinePrefix("[err] ");

Sample Use Case


NSession session = NSession.of().json().setTrace(true);
List<MyObject> data = ...;
NOut.out(session).println(data);  // will output JSON trace if enabled

Advanced Configuration

You can clone and configure sessions:

NSession childSession = session.copy()
        .setOutputFormat(NContentType.XML)
        .setBot(true)
        .setTrace(false);
        

Redirecting Session Streams (Advanced I/O Control)

Nuts allows you to redirect the I/O streams of a session to memory, files, or custom terminals. This is especially useful for scripting, capturing outputs programmatically, or testing. You can run a block of code using a customized session that redirects output to memory. This is useful for capturing the result of structured rendering (e.g., JSON, XML, table) without printing it to the console. Example:

String result = NSession.of().copy()
    .setTerminal(NTerminal.ofMem())     // redirect all I/O to memory
    .callWith(() -> {
        NSession.of().json();           // structured output (e.g., JSON)
        NOut.println(Arrays.asList("a", "b", "c"));
        return NOut.out().toString();   // retrieve the rendered output as string
    });
Explanation:
  • setTerminal(NTerminal.ofMem()): uses an in-memory terminal for all I/O

  • NSession.of().json(): sets the output format to JSON

  • NOut.println(...): renders the list to the output stream

  • NOut.out().toString(): fetches the printed result from memory

This technique is useful when you want to render structured output for internal use (e.g., passing to another API or storing in a log file), rather than displaying it directly.

Log Configuration

Control logging levels and filters:

session.setLogTermLevel(Level.INFO);
session.setLogFileLevel(Level.FINE);
session.setLogFilter(log -> log.getVerb().equals(NLogIntent.FAIL));

Listeners

Register and listen to session/workspace events:

session.addListener(new NWorkspaceListener() {
...
});
Supported listener types:
  • NWorkspaceListener
  • NRepositoryListener
  • NInstallListener
  • NObservableMapListener

Best Practices

  • Use NSession.of() only when you're sure a session context exists
  • Always configure session flags (--yes, --bot, etc.) when parsing application commandlines