Skip to content
Back to skills

Nocobase App Plugin Cli Example

ASecurity

Use when adding commands to an application's nocobase CLI, or when working out how a plugin contributes CLI commands. Covers the ./cli entry, defineCliPlugin, and how topics are assembled.

  • 24,837 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added November 5, 2026
ai-agentsbash

Works with

  • cli

Security analysis

A100/100

Scanned November 5, 2026

npx -y skills add nocobase/nocobase --skill nocobase-app-plugin-cli-example --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Nocobase App Plugin Cli Example?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Nocobase App Plugin Cli Example
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/nocobase-nocobase-app-plugin-cli-example/badge)](https://www.skillsdirectory.com/skills/nocobase-nocobase-app-plugin-cli-example)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
name: nocobase-app-plugin-cli-example
description: Use when adding commands to an application's nocobase CLI, or when working out how a plugin contributes CLI commands. Covers the ./cli entry, defineCliPlugin, and how topics are assembled.
---

# CLI Example Plugin

This plugin contributes commands to the application's `nocobase` CLI. Read it as the reference implementation when
writing commands for another plugin.

## What a CLI plugin is

A plugin contributes commands by exporting a `./cli` entry. The application lists that entry in `cli/plugins.ts`, and `pnpm nocobase` assembles it into one command tree alongside the built-in commands of `@nocobase/app-cli`, such as `config *`, `db *`, `plugin *` and `build`, and the application's own `app *` commands.

Every command extends `AppCommand` from `@nocobase/app-cli`: it returns its result, throws `CommandError` on failure, and gets `--json` from the base class. By default a command is static tooling that operates on files and packages; one that needs the application creates it with `this.withApp(async ({ app }) => …)`. The `nocobase-app-plugin-development` Skill's CLI reference has the whole contract.

## The three pieces

**A command** is an `AppCommand` subclass — the same base the built-in commands use:

```ts
// cli/greet.ts
import { AppCommand } from '@nocobase/app-cli';
import { Args, Flags } from '@oclif/core';

export default class Greet extends AppCommand {
  static override summary = 'Print a greeting.';
  static override examples = ['<%= config.bin %> <%= command.id %> world'];
  static override args = {
    target: Args.string({ description: 'Who to greet.', required: true }),
  };
  static override flags = {
    loud: Flags.boolean({ default: false, description: 'Upper-case it.' }),
  };

  public async run(): Promise<{ message: string }> {
    const { args, flags } = await this.parse(Greet);
    const message = flags.loud
      ? `HELLO, ${args.target.toUpperCase()}!`
      : `Hello, ${args.target}.`;
    this.log(message); // for people; silent under --json
    return { message }; // the --json result
  }
}
```

**The entry** names the package and declares the command maps: `commands` run wherever the application runs, a built `dist/` included, and `devCommands` only in a source checkout. The topic is derived from the package name, without its scope and `app-plugin-` prefix, so `@nocobase/app-plugin-cli-example` mounts under `cli-example`:

```ts
// cli/index.ts
import { defineCliPlugin, type AppCliPlugin } from '@nocobase/app-cli';
import ArtifactBuild from './artifact-build.ts';
import Greet from './greet.ts';

const cliPlugin: AppCliPlugin = defineCliPlugin({
  packageName: '@nocobase/app-plugin-cli-example',
  description: 'Example commands contributed by a plugin.',
  commands: { greet: Greet },
  // Reads source directories a built dist/ does not carry, so it is registered only in a source checkout.
  devCommands: { 'artifact:build': ArtifactBuild },
});

export default cliPlugin;
```

Keys are sub-command names. `greet` becomes `nocobase cli-example greet`; the colon in `artifact:build` nests one level
further, into `nocobase cli-example artifact build`.

**The manifest** exposes the entry and declares oclif:

```json
{
  "exports": {
    "./cli": { "types": "./cli/index.ts", "import": "./cli/index.ts" }
  },
  "publishConfig": {
    "exports": {
      "./cli": {
        "types": "./dist/cli/index.d.ts",
        "import": "./dist/cli/index.js"
      }
    }
  },
  "peerDependencies": {
    "@nocobase/app-cli": "workspace:^",
    "@oclif/core": "^4.14.0"
  }
}
```

Both are peers, so the application supplies them. `@nocobase/app-cli` is the runtime the entry plugs into, and one shared `@oclif/core` keeps help rendering and flag parsing consistent between the plugin and the application. A peer range is published metadata resolved in an installation that is not this workspace, so it cannot be `catalog:`.

`cli/` must reach consumers — through `files`, or through `dist` as here.

## Rules that matter

**Topics are one flat namespace.** Every plugin claims the one its package name gives it. The built-in topics and top-level commands, such as `plugin`, `db` and `build`, and `app` for the application are reserved even where their commands are not registered, so a plugin whose name matches one fails CLI assembly with an error naming both claimants, rather than resolving silently. Two plugins collide whenever their names match once the scope and an `app-plugin-` prefix are removed: `@acme/reports` and `@acme/app-plugin-reports` both claim `reports`, as do `@nocobase/app-plugin-x` and `@other/x`.

**Keep command modules cheap to import.** The application imports every registered plugin's `./cli` entry to build
`--help`. A class declaration costs nothing; load anything expensive inside `run()` with `await import()`, as
`cli/artifact-build.ts` does.

**Follow the output conventions.** Return the result from `run()` and throw `CommandError` on failure; `AppCommand` turns either into the one `--json` document on stdout, and a failure also exits non-zero. Never call `this.exit()`, `this.logJson()` or `console.log`, and do not report a failure with `this.error()`. Exit codes are `0` success, `1` runtime error, `2` argument error.

## Verify

```bash
pnpm nocobase cli-example --help              # the topic and its commands
pnpm nocobase cli-example greet --help        # one command's flags and args
pnpm nocobase cli-example greet world --json
```

A command missing from `--help` usually means the plugin is absent from the application's `cli/plugins.ts`. Run
`pnpm nocobase plugin inspect <name> --json` and read `composition.cli`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.