Skip to content

Commit 4c74127

Browse files
committed
refactor: use 'usage:' instead of 'mux:' for command descriptions
- Frontmatter field changed from 'description' to 'usage' - Executable comment changed from '# mux: ...' to '# usage: ...' - Updated all docs examples to show usage comments - Pattern is case-insensitive for flexibility The 'usage:' pattern is more conventional and self-documenting.
1 parent ff28da8 commit 4c74127

File tree

4 files changed

+71
-41
lines changed

4 files changed

+71
-41
lines changed

docs/hooks/slash-commands.mdx

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,70 @@ Add files to `.mux/commands/` to define custom slash commands. The file's conten
77

88
## Quick Start
99

10-
The simplest command is a text file:
10+
The simplest command is a markdown file:
1111

1212
```bash
1313
mkdir -p .mux/commands
14-
echo "Refactor the code to follow DRY (Don't Repeat Yourself) principles." > .mux/commands/dry.txt
14+
cat > .mux/commands/dry.md << 'EOF'
15+
---
16+
usage: /dry - refactor code to follow DRY principles
17+
---
18+
Refactor the code to follow DRY (Don't Repeat Yourself) principles.
19+
EOF
1520
```
1621

17-
Now type `/dry` in chat - the file contents become your message.
22+
Now type `/dry` in chat - the file contents (after frontmatter) become your message.
1823

1924
## File Types
2025

21-
Commands can be either **static files** or **executable scripts**:
26+
Commands can be either **static markdown files** or **executable scripts**:
2227

23-
| Type | File | Behavior |
24-
| ---------- | --------------------------- | ----------------------------------- |
25-
| Static | `<name>.txt` or `<name>.md` | Contents read verbatim |
26-
| Executable | `<name>` (no extension) | Script runs, stdout becomes message |
28+
| Type | File | Behavior |
29+
| ---------- | ----------------------- | --------------------------------------------- |
30+
| Static | `<name>.md` | Contents read verbatim (frontmatter stripped) |
31+
| Executable | `<name>` (no extension) | Script runs, stdout becomes message |
2732

28-
Static files are checked first (`.txt` before `.md`), so you can override an executable with a text file.
33+
Static `.md` files take precedence over executables with the same name.
2934

3035
## File Convention
3136

32-
- **Location**: `<workspacePath>/.mux/commands/<name>[.txt|.md]`
37+
- **Location**: `<workspacePath>/.mux/commands/<name>.md` or `<name>` (executable)
3338
- **Name format**: lowercase letters, numbers, and hyphens (e.g., `my-command`)
3439
- **Executables**: must have shebang (`#!/bin/bash`, `#!/usr/bin/env node`, etc.)
3540

36-
## Progressive Examples
41+
## Usage Comments
42+
43+
Add a `usage:` line to show a description in autocomplete:
3744

38-
### Level 1: Static File
45+
**Markdown files** - use YAML frontmatter:
3946

40-
The simplest command is a text file (`.txt` or `.md`):
47+
```markdown
48+
---
49+
usage: /dry - refactor code to follow DRY principles
50+
---
4151

52+
Your prompt content here...
4253
```
43-
# .mux/commands/dry.txt
54+
55+
**Executable scripts** - use a comment after the shebang:
56+
57+
```bash
58+
#!/bin/bash
59+
# usage: /context - show current git context
60+
```
61+
62+
## Progressive Examples
63+
64+
### Level 1: Static Markdown File
65+
66+
The simplest command is a markdown file with optional frontmatter:
67+
68+
```markdown
69+
# .mux/commands/dry.md
70+
71+
---
72+
73+
## usage: /dry - refactor code to follow DRY principles
4474

4575
Refactor the code to follow DRY (Don't Repeat Yourself) principles.
4676
Identify repeated patterns and extract them into reusable functions or modules.
@@ -55,7 +85,7 @@ For dynamic content, use an executable:
5585
```bash
5686
#!/usr/bin/env bash
5787
# .mux/commands/context
58-
# Usage: /context
88+
# usage: /context - show current git context
5989

6090
echo "Current directory: $(pwd)"
6191
echo "Git branch: $(git branch --show-current)"
@@ -72,7 +102,7 @@ Executable commands receive arguments from the same line:
72102
```bash
73103
#!/usr/bin/env bash
74104
# .mux/commands/pr-review
75-
# Usage: /pr-review <PR-NUM>
105+
# usage: /pr-review <PR-NUM> - review a pull request
76106

77107
pr_num="$1"
78108
if [ -z "$pr_num" ]; then
@@ -109,7 +139,7 @@ Use exit code 2 when a command wants to show a preview without sending:
109139
```bash
110140
#!/usr/bin/env bash
111141
# .mux/commands/preview-diff
112-
# Shows staged changes - exit 2 to let user decide
142+
# usage: /preview-diff - preview staged changes without sending
113143

114144
diff=$(git diff --cached)
115145
if [ -z "$diff" ]; then
@@ -146,7 +176,7 @@ Custom commands receive the same [environment variables](/hooks/environment-vari
146176
````bash
147177
#!/usr/bin/env bash
148178
# .mux/commands/review
149-
# Usage: /review <file>
179+
# usage: /review <file> - review a specific file
150180

151181
file="${1:-}"
152182
if [ -z "$file" ]; then
@@ -166,7 +196,7 @@ echo '```'
166196
```javascript
167197
#!/usr/bin/env node
168198
// .mux/commands/deps
169-
// Usage: /deps - shows outdated npm dependencies
199+
// usage: /deps - show outdated npm dependencies
170200

171201
const { execSync } = require("child_process");
172202

src/browser/stories/App.slashCommands.stories.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ export const WithCustomCommands: AppStory = {
7373
[
7474
DEFAULT_WORKSPACE_ID,
7575
[
76-
{ name: "dry", description: "Do a dry run without making changes" },
77-
{ name: "review", description: "Review changes before committing" },
78-
{ name: "context", description: "Summarize project context" },
79-
{ name: "pr-summary", description: "Generate a PR description" },
76+
{ name: "dry", description: "/dry - do a dry run without making changes" },
77+
{ name: "review", description: "/review - review changes before committing" },
78+
{ name: "context", description: "/context - summarize project context" },
79+
{ name: "pr-summary", description: "/pr-summary - generate a PR description" },
8080
],
8181
],
8282
]),
@@ -126,9 +126,9 @@ export const FilteredCommands: AppStory = {
126126
[
127127
DEFAULT_WORKSPACE_ID,
128128
[
129-
{ name: "dry", description: "Do a dry run" },
129+
{ name: "dry", description: "/dry - do a dry run" },
130130
{ name: "review" },
131-
{ name: "context", description: "Summarize project context" },
131+
{ name: "context", description: "/context - summarize project context" },
132132
],
133133
],
134134
]),

src/node/services/slashCommandService.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ const COMMAND_NAME_REGEX = /^[a-z0-9][a-z0-9-]{0,63}$/;
2020
/** Static file extension (markdown only, supports frontmatter) */
2121
const STATIC_FILE_EXTENSION = ".md";
2222

23-
/** Regex to match magic comment for description in executables: # mux: <description> */
24-
const MAGIC_COMMENT_REGEX = /^#\s*mux:\s*(.+)$/;
23+
/** Regex to match usage comment in executables: # usage: <usage> */
24+
const USAGE_COMMENT_REGEX = /^#\s*usage:\s*(.+)$/i;
2525

2626
export interface SlashCommand {
2727
name: string;
@@ -162,26 +162,26 @@ export class SlashCommandService extends EventEmitter {
162162
}
163163

164164
/**
165-
* Parse description from markdown frontmatter.
165+
* Parse usage from markdown frontmatter.
166166
*/
167167
private parseMarkdownDescription(content: string): string | undefined {
168168
const { frontmatter } = parseSimpleFrontmatter(content);
169-
return typeof frontmatter.description === "string" ? frontmatter.description : undefined;
169+
return typeof frontmatter.usage === "string" ? frontmatter.usage : undefined;
170170
}
171171

172172
/**
173-
* Parse description from executable magic comment.
174-
* Looks for `# mux: <description>` in first few lines after shebang.
173+
* Parse usage from executable comment.
174+
* Looks for `# usage: <usage>` in first few lines after shebang.
175175
*/
176176
private parseExecutableDescription(content: string): string | undefined {
177177
const lines = content.split("\n");
178-
// Skip shebang, check next few lines for magic comment
178+
// Skip shebang, check next few lines for usage comment
179179
for (let i = 0; i < Math.min(lines.length, 5); i++) {
180180
const line = lines[i].trim();
181181
// Skip shebang
182182
if (line.startsWith("#!")) continue;
183-
// Check for magic comment
184-
const match = MAGIC_COMMENT_REGEX.exec(line);
183+
// Check for usage comment
184+
const match = USAGE_COMMENT_REGEX.exec(line);
185185
if (match) {
186186
return match[1].trim();
187187
}

tests/ipc/slashCommands.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -288,30 +288,30 @@ describeIntegration("Custom Slash Commands (Backend)", () => {
288288
await createStaticCommand(
289289
metadata.namedWorkspacePath,
290290
"with-desc",
291-
"---\ndescription: My cool command\n---\nBody content"
291+
"---\nusage: /with-desc - my cool command\n---\nBody content"
292292
);
293293

294294
const result = await env.orpc.workspace.slashCommands.list({ workspaceId });
295295

296296
const cmd = result.commands.find((c) => c.name === "with-desc");
297297
expect(cmd).toBeDefined();
298-
expect(cmd?.description).toBe("My cool command");
298+
expect(cmd?.description).toBe("/with-desc - my cool command");
299299
});
300300
}, 30_000);
301301

302-
test("listSlashCommands extracts description from executable magic comment", async () => {
302+
test("listSlashCommands extracts description from executable usage comment", async () => {
303303
await withSharedWorkspace("anthropic", async ({ env, workspaceId, metadata }) => {
304304
await createExecutableCommand(
305305
metadata.namedWorkspacePath,
306-
"magic-desc",
307-
'#!/bin/bash\n# mux: Run tests with coverage\necho "test"'
306+
"usage-desc",
307+
'#!/bin/bash\n# usage: /usage-desc - run tests with coverage\necho "test"'
308308
);
309309

310310
const result = await env.orpc.workspace.slashCommands.list({ workspaceId });
311311

312-
const cmd = result.commands.find((c) => c.name === "magic-desc");
312+
const cmd = result.commands.find((c) => c.name === "usage-desc");
313313
expect(cmd).toBeDefined();
314-
expect(cmd?.description).toBe("Run tests with coverage");
314+
expect(cmd?.description).toBe("/usage-desc - run tests with coverage");
315315
});
316316
}, 30_000);
317317
});

0 commit comments

Comments
 (0)