Subcommand Hierarchy
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 directorycommand.tsdefines a directory-backed command nodegroup.tsdefines a group node*.subcommand.tsdefines 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.
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.
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:
- Shorthand files with
*.subcommand.ts - Directory-backed leaves with
[leaf]/command.ts
Both compile to executable subcommands.
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:
src/app/commands/
└── [admin]/
├── command.ts # Incorrect: exports chatInput while admin has children
└── [logs]/
└── command.ts
Correct:
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:
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.tsalways applies+middleware.tsapplies only from the current directory+<command>.middleware.tsapplies only from that same current directory
That means hierarchical leaves do not inherit +middleware.ts from
ancestor command or group directories.
Example:
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.
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.