Skip to content

Commit 7d63fa5

Browse files
feat(wrangler): Add command categories to help menu (#11728)
* Add initial help menu command categories * Improved category ordering * Updated `wrangler` help menu test snapshots * Fixed Wrangler invalid command help menu categories * Fixed uncategorized commands empty spacing * Minor refactoring * Minor `showHelpWithCategories` refactoring * Added changeset * Updated changeset to `minor` + improved description * Updated D1 test snapshots * Fixed command registry category tracking * Fixed vectorize command category * Fixed help menu inline snapshots * Fixed storage & databases catgeory inline snapshots * Minor code cleanup from suggestions Co-authored-by: Dario Piotrowicz <dario@cloudflare.com> * Minor code cleanup based on review suggestions * Minor command registry cleanup * Added explicit return type to `#trackCategory` method * Restore nullish `definition.metadata` category property * Updated `#trackCategory` JSDoc description * Added explicit types to line parameters Co-authored-by: Dario Piotrowicz <dario@cloudflare.com> * Minor code cleanup / refactoring * Convert ordered category entries to array to use `.find()` --------- Co-authored-by: Dario Piotrowicz <dario@cloudflare.com>
1 parent fada563 commit 7d63fa5

File tree

36 files changed

+373
-58
lines changed

36 files changed

+373
-58
lines changed

.changeset/large-hotels-open.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Add command categories to `wrangler` help menu
6+
7+
The help output now groups commands by product category (Account, Compute & AI, Storage & Databases, Networking & Security) to match the Cloudflare dashboard organization:
8+
9+
` ` `
10+
$ wrangler --help
11+
12+
COMMANDS
13+
wrangler docs [search..] 📚 Open Wrangler's command documentation in your browser
14+
15+
ACCOUNT
16+
wrangler auth 🔓 Manage authentication
17+
wrangler login 🔑 Login to Cloudflare
18+
...
19+
20+
COMPUTE & AI
21+
wrangler ai 🤖 Manage AI models
22+
wrangler containers 📦 Manage Containers [open beta]
23+
...
24+
` ` `
25+
26+
This improves discoverability by organizing the 20+ wrangler commands into logical groups.

packages/wrangler/src/__tests__/d1/d1.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ describe("d1", () => {
1515
expect(std.out).toMatchInlineSnapshot(`
1616
"wrangler d1
1717
18-
🗄 Manage Workers D1 databases
18+
🗄 Manage Workers D1 databases
1919
2020
COMMANDS
2121
wrangler d1 create <name> Creates a new D1 database, and provides the binding and UUID that you will put in your config file
@@ -52,7 +52,7 @@ describe("d1", () => {
5252
"
5353
wrangler d1
5454
55-
🗄 Manage Workers D1 databases
55+
🗄 Manage Workers D1 databases
5656
5757
COMMANDS
5858
wrangler d1 create <name> Creates a new D1 database, and provides the binding and UUID that you will put in your config file

packages/wrangler/src/__tests__/index.test.ts

Lines changed: 58 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -38,40 +38,46 @@ describe("wrangler", () => {
3838
COMMANDS
3939
wrangler docs [search..] 📚 Open Wrangler's command documentation in your browser
4040
41-
wrangler init [name] 📥 Initialize a basic Worker
42-
wrangler dev [script] 👂 Start a local server for developing your Worker
41+
ACCOUNT
42+
wrangler auth 🔐 Manage authentication
43+
wrangler login 🔓 Login to Cloudflare
44+
wrangler logout 🚪 Logout from Cloudflare
45+
wrangler whoami 🕵️ Retrieve your user information
46+
47+
COMPUTE & AI
48+
wrangler ai 🤖 Manage AI models
49+
wrangler containers 📦 Manage Containers [open beta]
50+
wrangler delete [script] 🗑️ Delete a Worker from Cloudflare
4351
wrangler deploy [script] 🆙 Deploy a Worker to Cloudflare
44-
wrangler setup 🪄 Setup a project to work on Cloudflare [experimental]
4552
wrangler deployments 🚢 List and view the current and past deployments for your Worker
53+
wrangler dev [script] 👂 Start a local server for developing your Worker
54+
wrangler dispatch-namespace 🏗️ Manage dispatch namespaces
55+
wrangler init [name] 📥 Initialize a basic Worker
56+
wrangler pages ⚡️ Configure Cloudflare Pages
57+
wrangler pubsub 📮 Manage Pub/Sub brokers [private beta]
58+
wrangler queues 📬 Manage Workers Queues
4659
wrangler rollback [version-id] 🔙 Rollback a deployment for a Worker
47-
wrangler versions 🫧 List, view, upload and deploy Versions of your Worker to Cloudflare
48-
wrangler triggers 🎯 Updates the triggers of your current deployment [experimental]
49-
wrangler delete [script] 🗑 Delete a Worker from Cloudflare
50-
wrangler tail [worker] 🦚 Start a log tailing session for a Worker
5160
wrangler secret 🤫 Generate a secret that can be referenced in a Worker
61+
wrangler setup 🪄 Setup a project to work on Cloudflare [experimental]
62+
wrangler tail [worker] 🦚 Start a log tailing session for a Worker
63+
wrangler triggers 🎯 Updates the triggers of your current deployment [experimental]
5264
wrangler types [path] 📝 Generate types from your Worker configuration
65+
wrangler versions 🫧 List, view, upload and deploy Versions of your Worker to Cloudflare
66+
wrangler vpc 🌐 Manage VPC [open beta]
67+
wrangler workflows 🔁 Manage Workflows
5368
69+
STORAGE & DATABASES
70+
wrangler d1 🗄️ Manage Workers D1 databases
71+
wrangler hyperdrive 🚀 Manage Hyperdrive databases
5472
wrangler kv 🗂️ Manage Workers KV Namespaces
55-
wrangler queues 📬 Manage Workers Queues
73+
wrangler pipelines 🚰 Manage Cloudflare Pipelines [open beta]
5674
wrangler r2 📦 Manage R2 buckets & objects
57-
wrangler d1 🗄 Manage Workers D1 databases
75+
wrangler secrets-store 🔐 Manage the Secrets Store [open beta]
5876
wrangler vectorize 🧮 Manage Vectorize indexes
59-
wrangler hyperdrive 🚀 Manage Hyperdrive databases
77+
78+
NETWORKING & SECURITY
6079
wrangler cert 🪪 Manage client mTLS certificates and CA certificate chains used for secured connections [open beta]
61-
wrangler pages ⚡️ Configure Cloudflare Pages
6280
wrangler mtls-certificate 🪪 Manage certificates used for mTLS connections
63-
wrangler containers 📦 Manage Containers [open beta]
64-
wrangler pubsub 📮 Manage Pub/Sub brokers [private beta]
65-
wrangler dispatch-namespace 🏗️ Manage dispatch namespaces
66-
wrangler ai 🤖 Manage AI models
67-
wrangler secrets-store 🔐 Manage the Secrets Store [open beta]
68-
wrangler workflows 🔁 Manage Workflows
69-
wrangler pipelines 🚰 Manage Cloudflare Pipelines [open beta]
70-
wrangler vpc 🌐 Manage VPC [open beta]
71-
wrangler login 🔓 Login to Cloudflare
72-
wrangler logout 🚪 Logout from Cloudflare
73-
wrangler whoami 🕵️ Retrieve your user information
74-
wrangler auth 🔐 Manage authentication
7581
7682
GLOBAL FLAGS
7783
-c, --config Path to Wrangler configuration file [string]
@@ -103,40 +109,46 @@ describe("wrangler", () => {
103109
COMMANDS
104110
wrangler docs [search..] 📚 Open Wrangler's command documentation in your browser
105111
106-
wrangler init [name] 📥 Initialize a basic Worker
107-
wrangler dev [script] 👂 Start a local server for developing your Worker
112+
ACCOUNT
113+
wrangler auth 🔐 Manage authentication
114+
wrangler login 🔓 Login to Cloudflare
115+
wrangler logout 🚪 Logout from Cloudflare
116+
wrangler whoami 🕵️ Retrieve your user information
117+
118+
COMPUTE & AI
119+
wrangler ai 🤖 Manage AI models
120+
wrangler containers 📦 Manage Containers [open beta]
121+
wrangler delete [script] 🗑️ Delete a Worker from Cloudflare
108122
wrangler deploy [script] 🆙 Deploy a Worker to Cloudflare
109-
wrangler setup 🪄 Setup a project to work on Cloudflare [experimental]
110123
wrangler deployments 🚢 List and view the current and past deployments for your Worker
124+
wrangler dev [script] 👂 Start a local server for developing your Worker
125+
wrangler dispatch-namespace 🏗️ Manage dispatch namespaces
126+
wrangler init [name] 📥 Initialize a basic Worker
127+
wrangler pages ⚡️ Configure Cloudflare Pages
128+
wrangler pubsub 📮 Manage Pub/Sub brokers [private beta]
129+
wrangler queues 📬 Manage Workers Queues
111130
wrangler rollback [version-id] 🔙 Rollback a deployment for a Worker
112-
wrangler versions 🫧 List, view, upload and deploy Versions of your Worker to Cloudflare
113-
wrangler triggers 🎯 Updates the triggers of your current deployment [experimental]
114-
wrangler delete [script] 🗑 Delete a Worker from Cloudflare
115-
wrangler tail [worker] 🦚 Start a log tailing session for a Worker
116131
wrangler secret 🤫 Generate a secret that can be referenced in a Worker
132+
wrangler setup 🪄 Setup a project to work on Cloudflare [experimental]
133+
wrangler tail [worker] 🦚 Start a log tailing session for a Worker
134+
wrangler triggers 🎯 Updates the triggers of your current deployment [experimental]
117135
wrangler types [path] 📝 Generate types from your Worker configuration
136+
wrangler versions 🫧 List, view, upload and deploy Versions of your Worker to Cloudflare
137+
wrangler vpc 🌐 Manage VPC [open beta]
138+
wrangler workflows 🔁 Manage Workflows
118139
140+
STORAGE & DATABASES
141+
wrangler d1 🗄️ Manage Workers D1 databases
142+
wrangler hyperdrive 🚀 Manage Hyperdrive databases
119143
wrangler kv 🗂️ Manage Workers KV Namespaces
120-
wrangler queues 📬 Manage Workers Queues
144+
wrangler pipelines 🚰 Manage Cloudflare Pipelines [open beta]
121145
wrangler r2 📦 Manage R2 buckets & objects
122-
wrangler d1 🗄 Manage Workers D1 databases
146+
wrangler secrets-store 🔐 Manage the Secrets Store [open beta]
123147
wrangler vectorize 🧮 Manage Vectorize indexes
124-
wrangler hyperdrive 🚀 Manage Hyperdrive databases
148+
149+
NETWORKING & SECURITY
125150
wrangler cert 🪪 Manage client mTLS certificates and CA certificate chains used for secured connections [open beta]
126-
wrangler pages ⚡️ Configure Cloudflare Pages
127151
wrangler mtls-certificate 🪪 Manage certificates used for mTLS connections
128-
wrangler containers 📦 Manage Containers [open beta]
129-
wrangler pubsub 📮 Manage Pub/Sub brokers [private beta]
130-
wrangler dispatch-namespace 🏗️ Manage dispatch namespaces
131-
wrangler ai 🤖 Manage AI models
132-
wrangler secrets-store 🔐 Manage the Secrets Store [open beta]
133-
wrangler workflows 🔁 Manage Workflows
134-
wrangler pipelines 🚰 Manage Cloudflare Pipelines [open beta]
135-
wrangler vpc 🌐 Manage VPC [open beta]
136-
wrangler login 🔓 Login to Cloudflare
137-
wrangler logout 🚪 Logout from Cloudflare
138-
wrangler whoami 🕵️ Retrieve your user information
139-
wrangler auth 🔐 Manage authentication
140152
141153
GLOBAL FLAGS
142154
-c, --config Path to Wrangler configuration file [string]

packages/wrangler/src/ai/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const aiNamespace = createNamespace({
55
description: "🤖 Manage AI models",
66
status: "stable",
77
owner: "Product: AI",
8+
category: "Compute & AI",
89
},
910
});
1011

packages/wrangler/src/cert/cert.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const certNamespace = createNamespace({
1919
"🪪 Manage client mTLS certificates and CA certificate chains used for secured connections",
2020
status: "open beta",
2121
owner: "Product: SSL",
22+
category: "Networking & security",
2223
},
2324
});
2425

packages/wrangler/src/core/CommandRegistry.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,30 @@ import type {
1313
DefinitionTreeNode,
1414
InternalDefinition,
1515
Metadata,
16+
MetadataCategory,
1617
NamedArgDefinitions,
1718
NamespaceDefinition,
1819
} from "./types";
1920

2021
const BETA_CMD_COLOR = "#BD5B08";
2122

23+
/**
24+
* Map of category names to the top-level command segments that belong to them.
25+
* Used for grouping commands in the help output.
26+
*/
27+
type CategoryMap = Map<MetadataCategory, Array<string>>;
28+
29+
/**
30+
* The default order for categories in the help output.
31+
* Categories not in this list will appear after these in alphabetical order.
32+
*/
33+
const COMMAND_CATEGORY_ORDER = [
34+
"Account",
35+
"Compute & AI",
36+
"Storage & databases",
37+
"Networking & security",
38+
] satisfies Array<MetadataCategory>;
39+
2240
/**
2341
* Class responsible for registering and managing commands within a command registry.
2442
*/
@@ -43,6 +61,12 @@ export class CommandRegistry {
4361
*/
4462
#tree: DefinitionTree;
4563

64+
/**
65+
* Map of category names to command segments.
66+
* Used for grouping commands in the help output.
67+
*/
68+
#categories: CategoryMap;
69+
4670
/**
4771
* Initializes the command registry with the given command registration function.
4872
*/
@@ -51,6 +75,7 @@ export class CommandRegistry {
5175
this.#registeredNamespaces = new Set<string>();
5276
this.#registerCommand = registerCommand;
5377
this.#tree = this.#DefinitionTreeRoot.subtree;
78+
this.#categories = new Map();
5479
}
5580

5681
/**
@@ -109,6 +134,52 @@ export class CommandRegistry {
109134
this.#walkTreeAndRegister(namespace, node, `wrangler ${namespace}`);
110135
}
111136

137+
/**
138+
* Returns the map of categories to command segments, ordered according to
139+
* the category order. Commands within each category are sorted alphabetically.
140+
* Used for grouping commands in the help output.
141+
*/
142+
get orderedCategories(): CategoryMap {
143+
const orderedCategories: CategoryMap = new Map();
144+
for (const category of COMMAND_CATEGORY_ORDER) {
145+
if (!this.#categories.has(category)) {
146+
continue;
147+
}
148+
149+
const commands = this.#categories.get(category) ?? [];
150+
orderedCategories.set(category, [...commands].sort());
151+
}
152+
153+
const remainingCategories = Array.from(this.#categories.keys())
154+
.filter(
155+
(cat) => !COMMAND_CATEGORY_ORDER.includes(cat as MetadataCategory)
156+
)
157+
.sort();
158+
for (const category of remainingCategories) {
159+
const commands = this.#categories.get(category) ?? [];
160+
orderedCategories.set(category, [...commands].sort());
161+
}
162+
163+
return orderedCategories;
164+
}
165+
166+
/**
167+
* Registers a category for a legacy command that doesn't use the CommandRegistry.
168+
* This is used for commands like `containers`, `pubsub`, etc, that use the old yargs pattern.
169+
*/
170+
registerLegacyCommandCategory(
171+
command: string,
172+
category: MetadataCategory
173+
): void {
174+
const existing = this.#categories.get(category) ?? [];
175+
if (existing.includes(command)) {
176+
return;
177+
}
178+
179+
existing.push(command);
180+
this.#categories.set(category, existing);
181+
}
182+
112183
/**
113184
* Defines a single command and its corresponding definition.
114185
*/
@@ -128,8 +199,37 @@ export class CommandRegistry {
128199

129200
if (isCommandDefinition(definition)) {
130201
this.#upsertDefinition({ type: "command", command, ...definition });
202+
this.#trackCategory(command, definition.metadata?.category);
131203
} else if (isNamespaceDefinition(definition)) {
132204
this.#upsertDefinition({ type: "namespace", command, ...definition });
205+
this.#trackCategory(command, definition.metadata?.category);
206+
}
207+
}
208+
209+
/**
210+
* Adds a top-level command (e.g., "wrangler r2") to the `#categories` map for help output grouping.
211+
* Subcommands (e.g., "wrangler r2 bucket") are ignored since the parent already represents them.
212+
*/
213+
#trackCategory(
214+
command: Command,
215+
category: MetadataCategory | undefined
216+
): void {
217+
const segments = command.split(" ").slice(1);
218+
219+
// Only track categories for top-level commands (e.g., "wrangler r2", not "wrangler r2 bucket")
220+
if (segments.length !== 1) {
221+
return;
222+
}
223+
224+
if (!category) {
225+
return;
226+
}
227+
228+
const segment = segments[0];
229+
const existing = this.#categories.get(category) ?? [];
230+
if (!existing.includes(segment)) {
231+
existing.push(segment);
232+
this.#categories.set(category, existing);
133233
}
134234
}
135235

packages/wrangler/src/core/handle-errors.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,28 @@ export async function handleError(
8787
// We are not able to ask the `wrangler` CLI parser to show help for a subcommand programmatically.
8888
// The workaround is to re-run the parsing with an additional `--help` flag, which will result in the correct help message being displayed.
8989
// The `wrangler` object is "frozen"; we cannot reuse that with different args, so we must create a new CLI parser to generate the help message.
90-
const { wrangler } = createCLIParser([...subCommandParts, "--help"]);
90+
91+
// Check if this is a root-level error (unknown argument at root level)
92+
// by looking at the error message - if it says "Unknown argument" or "Unknown command",
93+
// and there's only one non-flag argument, show the categorized root help
94+
const nonFlagArgs = subCommandParts.filter(
95+
(arg) => !arg.startsWith("-") && arg !== ""
96+
);
97+
const isRootLevelError =
98+
nonFlagArgs.length <= 1 &&
99+
(e.message.includes("Unknown argument") ||
100+
e.message.includes("Unknown command"));
101+
102+
const { wrangler, showHelpWithCategories } = createCLIParser([
103+
...(isRootLevelError ? [] : subCommandParts),
104+
"--help",
105+
]);
106+
107+
if (isRootLevelError) {
108+
await showHelpWithCategories();
109+
return;
110+
}
111+
91112
await wrangler.parse();
92113
} else if (
93114
isAuthenticationError(e) ||

packages/wrangler/src/core/types.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ export type DeepFlatten<T> = T extends object
4848
? { [K in keyof T]: DeepFlatten<T[K]> }
4949
: T;
5050

51+
export type MetadataCategory =
52+
| "Account"
53+
| "Compute & AI"
54+
| "Storage & databases"
55+
| "Networking & security";
56+
5157
export type Command = `wrangler${string}`;
5258
export type Metadata = {
5359
description: string;
@@ -64,6 +70,12 @@ export type Metadata = {
6470
description: string;
6571
}[];
6672
hideGlobalFlags?: string[];
73+
/**
74+
* Optional category for grouping commands in the help output.
75+
* Commands with the same category will be grouped together under a shared heading.
76+
* Commands without a category will appear under the default "COMMANDS" group.
77+
*/
78+
category?: MetadataCategory;
6779
};
6880

6981
export type ArgDefinition = Omit<PositionalOptions, "type"> &

0 commit comments

Comments
 (0)