Skip to main content
Version: 1.x

Subcommand Hierarchy

info

This feature is currently available under commandkit@dev release only.

CommandKit supports fully filesystem-based slash command trees. The filesystem tokens are explicit:

  • [command] defines a root command directory
  • {group} defines a subcommand group directory
  • command.ts defines a directory-backed command node
  • group.ts defines a group node
  • *.subcommand.ts defines a shorthand subcommand leaf

Root Command Directories [command]

Wrap a directory name in square brackets to declare a root slash command. The root directory must contain a command.ts file.

Directory Structure
src/app/commands/
└── (general)/
└── [settings]/
├── command.ts
├── profile.subcommand.ts
└── security.subcommand.ts

This produces:

  • /settings profile
  • /settings security

Use command.ts to define the root command's description and shared options or metadata for the /settings root.

Group Directories {group}

Use curly braces inside a root command directory to define a subcommand group. Group directories must contain a group.ts file.

Directory Structure
src/app/commands/
└── [settings]/
├── command.ts
└── {notifications}/
├── group.ts
├── enable.subcommand.ts
└── disable.subcommand.ts

This produces:

  • /settings notifications enable
  • /settings notifications disable

Folder Leaves And Shorthand Leaves

You can define leaves in two ways:

  1. Shorthand files with *.subcommand.ts
  2. Directory-backed leaves with [leaf]/command.ts

Both compile to executable subcommands.

Directory Structure
src/app/commands/
└── [ops]/
├── command.ts
├── status.subcommand.ts
└── [deploy]/
└── command.ts

This produces:

  • /ops status
  • /ops deploy

Non-Leaf Command Files

A hierarchical node with children is a non-leaf node. When a directory such as [admin] contains child subcommands or groups, its command.ts file can define command metadata and configuration, but it must not export executable handlers such as chatInput, message, or autocomplete.

Executable handlers belong in leaf nodes, such as [logs]/command.ts or logs.subcommand.ts.

Incorrect:

Directory Structure
src/app/commands/
└── [admin]/
├── command.ts # Incorrect: exports chatInput while admin has children
└── [logs]/
└── command.ts

Correct:

Directory Structure
src/app/commands/
└── [admin]/
├── command.ts # Correct: metadata/config only
└── [logs]/
└── command.ts # Correct: executable handlers live here

You can also use a shorthand leaf:

Directory Structure
src/app/commands/
└── [admin]/
├── command.ts # metadata/config only
└── logs.subcommand.ts

Middleware Scope In Hierarchical Trees

Hierarchical commands use the same middleware model as flat commands:

  • +global-middleware.ts always applies
  • +middleware.ts applies only from the current directory
  • +<command>.middleware.ts applies only from that same current directory

That means hierarchical leaves do not inherit +middleware.ts from ancestor command or group directories.

Example:

Directory Structure
src/app/commands/
└── [workspace]/
├── command.ts
├── +middleware.ts
└── {notes}/
├── group.ts
├── +middleware.ts
├── +add.middleware.ts
├── add.subcommand.ts
└── [archive]/
├── +middleware.ts
└── command.ts

add.subcommand.ts uses:

  • global middleware
  • {notes}/+middleware.ts
  • {notes}/+add.middleware.ts

[archive]/command.ts uses:

  • global middleware
  • [archive]/+middleware.ts

Categories Still Work

Parenthesis directories such as (general) remain organizational only. They do not change the slash route shape and they do not create command or group nodes by themselves.

info

Use (category) for organization, [command] for root commands, and {group} for subcommand groups. That separation keeps the filesystem shape and the generated Discord route aligned.