Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/reference/commands-nemohermes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,24 @@ nemohermes resources [--json]

If the gateway is not running, Kubernetes allocatable fields are omitted and host CPU/RAM totals are still shown.

### `nemohermes agents list`

List the installed agent runtimes that can be selected with `nemohermes onboard --agent <name>`.
Use this global command when you need valid runtime names before creating or recreating a sandbox.
It lists runtime names with the descriptions from their installed manifests.

```bash
nemohermes agents list
```

Expected output:

```text
openclaw Gateway-based AI agent with plugin ecosystem (openclaw.ai)
hermes Self-improving AI agent with learning loop (Nous Research)
langchain-deepagents-code Terminal coding agent built on the Deep Agents SDK
```

### `nemohermes onboard`

Run the interactive setup wizard (recommended for new installs).
Expand Down Expand Up @@ -192,6 +210,7 @@ The CLI rejects names that do not match these rules.
It also prints a `Try: <suggested-slug>` recovery line whenever it can derive a valid lowercase, hyphen-separated form from the input, so passing `--name MyAssistant` reports `Try: myassistant`.
Names that match global CLI commands (`status`, `list`, `debug`, etc.) are rejected to avoid routing conflicts.
Use `--agent <name>` to target a specific installed agent profile during onboarding.
The `nemohermes onboard --help` output lists installed runtime names inline, and `nemohermes agents list` shows the same runtimes with manifest descriptions.

Use `--control-ui-port <N>` to choose the host dashboard port for a sandbox.
The value must be an integer from `1024` through `65535`.
Expand Down
19 changes: 19 additions & 0 deletions docs/reference/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,24 @@ $$nemoclaw resources [--json]

If the gateway is not running, Kubernetes allocatable fields are omitted and host CPU/RAM totals are still shown.

### `$$nemoclaw agents list`

List the installed agent runtimes that can be selected with `$$nemoclaw onboard --agent <name>`.
Use this global command when you need valid runtime names before creating or recreating a sandbox.
It lists runtime names with the descriptions from their installed manifests.

```bash
$$nemoclaw agents list
```

Expected output:

```text
openclaw Gateway-based AI agent with plugin ecosystem (openclaw.ai)
hermes Self-improving AI agent with learning loop (Nous Research)
langchain-deepagents-code Terminal coding agent built on the Deep Agents SDK
```

### `$$nemoclaw onboard`

Run the interactive setup wizard (recommended for new installs).
Expand Down Expand Up @@ -263,6 +281,7 @@ The CLI rejects names that do not match these rules.
It also prints a `Try: <suggested-slug>` recovery line whenever it can derive a valid lowercase, hyphen-separated form from the input, so passing `--name MyAssistant` reports `Try: myassistant`.
Names that match global CLI commands (`status`, `list`, `debug`, etc.) are rejected to avoid routing conflicts.
Use `--agent <name>` to target a specific installed agent profile during onboarding.
The `$$nemoclaw onboard --help` output lists installed runtime names inline, and `$$nemoclaw agents list` shows the same runtimes with manifest descriptions.

<AgentOnly variant="openclaw">

Expand Down
19 changes: 19 additions & 0 deletions src/commands/agents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { NemoClawCommand } from "../lib/cli/nemoclaw-oclif-command";

export default class AgentsCommand extends NemoClawCommand {
static id = "agents";
static strict = true;
static summary = "Discover available agent runtimes";
static description = "Discover installed agent runtimes that can be selected during onboarding.";
static usage = ["agents list"];
static examples = ["<%= config.bin %> agents list"];
static flags = {};

public async run(): Promise<void> {
await this.parse(AgentsCommand);
this.log(`Usage: ${this.config.bin} agents list`);
}
}
28 changes: 28 additions & 0 deletions src/commands/agents/list.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { beforeEach, describe, expect, it, vi } from "vitest";

const mocks = vi.hoisted(() => ({
printAgentRuntimeList: vi.fn(),
}));

vi.mock("../../lib/agent/list-command", () => ({
printAgentRuntimeList: mocks.printAgentRuntimeList,
}));

import AgentsListCommand from "./list";

const rootDir = process.cwd();

describe("agents list oclif command", () => {
beforeEach(() => {
vi.clearAllMocks();
});

it("prints available global agent runtimes", async () => {
await AgentsListCommand.run([], rootDir);

expect(mocks.printAgentRuntimeList).toHaveBeenCalledWith(expect.any(Function));
});
});
20 changes: 20 additions & 0 deletions src/commands/agents/list.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { printAgentRuntimeList } from "../../lib/agent/list-command";
import { NemoClawCommand } from "../../lib/cli/nemoclaw-oclif-command";

export default class AgentsListCommand extends NemoClawCommand {
static id = "agents:list";
static strict = true;
static summary = "List available agent runtimes for onboard --agent";
static description = "List installed agent runtimes that can be selected with onboard --agent.";
static usage = ["agents list"];
static examples = ["<%= config.bin %> agents list"];
static flags = {};

public async run(): Promise<void> {
await this.parse(AgentsListCommand);
printAgentRuntimeList(this.log.bind(this));
}
}
7 changes: 3 additions & 4 deletions src/commands/onboard.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { NemoClawCommand } from "../lib/cli/nemoclaw-oclif-command";

import { runOnboardAction } from "../lib/actions/global";
import { NemoClawCommand } from "../lib/cli/nemoclaw-oclif-command";
import {
buildOnboardFlags,
onboardExamples,
type OnboardFlags,
onboardExamples,
onboardUsage,
toLegacyOnboardArgs,
} from "../lib/onboard/command-support";

export default class OnboardCliCommand extends NemoClawCommand {
static id = "onboard";
static strict = true;
static summary = "Configure inference endpoint and credentials";
static summary = "Configure inference endpoint and credentials (--agent to choose runtime)";
static description = "Configure inference, credentials, and sandbox settings.";
static usage = onboardUsage;
static examples = onboardExamples;
Expand Down
35 changes: 35 additions & 0 deletions src/lib/agent/list-command.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, it, vi } from "vitest";

vi.mock("./defs", () => ({
getAgentChoices: vi.fn(() => []),
}));

import { renderAgentRuntimeList } from "./list-command";

describe("agent runtime list command support", () => {
it("renders available agent runtimes and descriptions in aligned columns", () => {
expect(
renderAgentRuntimeList([
{ name: "openclaw", description: "Gateway-based AI agent with plugin ecosystem" },
{ name: "hermes", description: "Self-improving AI agent with learning loop" },
{
name: "langchain-deepagents-code",
description: "Terminal coding agent built on the Deep Agents SDK",
},
]),
).toBe(
[
"openclaw Gateway-based AI agent with plugin ecosystem",
"hermes Self-improving AI agent with learning loop",
"langchain-deepagents-code Terminal coding agent built on the Deep Agents SDK",
].join("\n"),
);
});

it("prints a fallback message when no runtimes are installed", () => {
expect(renderAgentRuntimeList([])).toBe("No agent runtimes are installed.");
});
});
28 changes: 28 additions & 0 deletions src/lib/agent/list-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { type AgentChoice, getAgentChoices } from "./defs";

export type AgentRuntimeListEntry = Pick<AgentChoice, "name" | "description">;

export function listAgentRuntimeEntries(): AgentRuntimeListEntry[] {
return getAgentChoices().map(({ name, description }) => ({ name, description }));
}

export function renderAgentRuntimeList(
entries: readonly AgentRuntimeListEntry[] = listAgentRuntimeEntries(),
): string {
if (entries.length === 0) return "No agent runtimes are installed.";

const nameWidth = Math.max(...entries.map((entry) => entry.name.length));
return entries
.map((entry) => {
if (!entry.description) return entry.name;
return `${entry.name.padEnd(nameWidth + 2)}${entry.description}`;
})
.join("\n");
}

export function printAgentRuntimeList(log: (message: string) => void = console.log): void {
log(renderAgentRuntimeList());
}
1 change: 1 addition & 0 deletions src/lib/cli/command-display-metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe("public command display metadata", () => {
).toEqual(
expect.arrayContaining([
{ commandId: "onboard", usage: "nemoclaw onboard" },
{ commandId: "agents:list", usage: "nemoclaw agents list" },
{ commandId: "sandbox:status", usage: "nemoclaw <name> status" },
{ commandId: "inference:get", usage: "nemoclaw inference get" },
]),
Expand Down
16 changes: 9 additions & 7 deletions src/lib/cli/command-registry.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, it, expect } from "vitest";
import { describe, expect, it } from "vitest";
import {
COMMANDS,
globalCommands,
sandboxCommands,
visibleCommands,
commandsByGroup,
canonicalUsageList,
commandsByGroup,
GROUP_ORDER,
globalCommands,
globalCommandTokens,
sandboxActionTokens,
GROUP_ORDER,
sandboxCommands,
visibleCommands,
} from "./command-registry";
import { getRegisteredOclifCommandsMetadata } from "./oclif-metadata";

Expand Down Expand Up @@ -41,6 +41,7 @@ describe("command-registry", () => {
describe("globalCommands()", () => {
it("includes public global service commands", () => {
const usages = globalCommands().map((cmd) => cmd.usage);
expect(usages).toContain("nemoclaw agents list");
expect(usages).toContain("nemoclaw tunnel start");
expect(usages).toContain("nemoclaw tunnel stop");
expect(usages).toContain("nemoclaw tunnel status");
Expand Down Expand Up @@ -182,9 +183,10 @@ describe("command-registry", () => {
});

describe("globalCommandTokens()", () => {
it("returns the exact set of 24 tokens matching the global dispatch commands", () => {
it("returns the exact set of 25 tokens matching the global dispatch commands", () => {
const tokens = globalCommandTokens();
const expected = new Set([
"agents",
"onboard",
"update",
"list",
Expand Down
2 changes: 1 addition & 1 deletion src/lib/cli/public-argv-translation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export function translatePublicGlobalArgv(cmd: string, args: string[]): PublicTr
return nativeArgv(route.commandId, inputTokens.slice(route.tokens.length));
}

if (cmd === "tunnel" || cmd === "inference" || cmd === "credentials") {
if (cmd === "agents" || cmd === "tunnel" || cmd === "inference" || cmd === "credentials") {
return nativeGlobalParentArgv(cmd, args);
}

Expand Down
8 changes: 8 additions & 0 deletions src/lib/cli/public-display-defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import { globalRouteTokenVariants, sandboxRouteTokens } from "./public-route-met

const PUBLIC_DISPLAY_LAYOUT: Record<string, readonly PublicDisplayLayout[]> = {
...SANDBOX_AGENTS_DISPLAY_LAYOUT,
"agents:list": [
{
group: "Getting Started",
order: 1.5,
description: "List available agent runtimes for onboard --agent",
},
],
...SANDBOX_SESSIONS_DISPLAY_LAYOUT,
"backup-all": [
{
Expand Down Expand Up @@ -80,6 +87,7 @@ const PUBLIC_DISPLAY_LAYOUT: Record<string, readonly PublicDisplayLayout[]> = {
{
group: "Getting Started",
order: 0,
description: "Configure inference endpoint and credentials (--agent to choose runtime)",
},
{
group: "Getting Started",
Expand Down
19 changes: 19 additions & 0 deletions src/lib/onboard/agent-flag-help.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, it } from "vitest";

import { describeAgentFlag } from "./agent-flag-help";

describe("describeAgentFlag (#5779)", () => {
it("lists the installed agent runtimes inline", () => {
expect(describeAgentFlag(["openclaw", "hermes", "langchain-deepagents-code"])).toBe(
"Agent runtime to onboard (openclaw, hermes, langchain-deepagents-code)",
);
});

it("ignores empty entries and falls back to the generic text when none are known", () => {
expect(describeAgentFlag([])).toBe("Agent runtime to onboard");
expect(describeAgentFlag(["", "openclaw"])).toBe("Agent runtime to onboard (openclaw)");
});
});
13 changes: 13 additions & 0 deletions src/lib/onboard/agent-flag-help.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

// Build the `--agent` flag help. Listing the installed agent runtimes inline
// means users don't have to discover valid names by triggering an error (#5779).
// Kept dependency-free so it stays trivially testable without the agent
// registry / runner import chain.
export function describeAgentFlag(agents: readonly string[]): string {
const names = agents.filter((name) => typeof name === "string" && name.length > 0);
return names.length > 0
? `Agent runtime to onboard (${names.join(", ")})`
: "Agent runtime to onboard";
}
32 changes: 32 additions & 0 deletions src/lib/onboard/command-support.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { afterEach, describe, expect, it } from "vitest";

import { buildOnboardFlags, setAgentRegistryReaderForTest } from "./command-support";

afterEach(() => {
setAgentRegistryReaderForTest(null);
});

describe("buildOnboardFlags --agent help (#5779)", () => {
it("includes installed agent runtime names in the --agent description when listAgents succeeds", () => {
setAgentRegistryReaderForTest(() => ["hermes", "langchain-deepagents-code", "openclaw"]);

const flags = buildOnboardFlags();

expect(flags.agent.description).toBe(
"Agent runtime to onboard (openclaw, hermes, langchain-deepagents-code)",
);
});

it("falls back to the generic --agent description when listAgents throws", () => {
setAgentRegistryReaderForTest(() => {
throw new Error("registry unavailable");
});

const flags = buildOnboardFlags();

expect(flags.agent.description).toBe("Agent runtime to onboard");
});
});
Loading
Loading