Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Codex CLI supports a rich set of configuration options, with preferences stored
- [CLI usage](./docs/getting-started.md#cli-usage)
- [Running with a prompt as input](./docs/getting-started.md#running-with-a-prompt-as-input)
- [Example prompts](./docs/getting-started.md#example-prompts)
- [Custom prompts](./docs/prompts.md)
- [Memory with AGENTS.md](./docs/getting-started.md#memory-with-agentsmd)
- [Configuration](./docs/config.md)
- [**Sandbox & approvals**](./docs/sandbox.md)
Expand Down
47 changes: 43 additions & 4 deletions codex-rs/tui/src/bottom_pane/command_popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,17 @@ impl CommandPopup {
CommandItem::Builtin(cmd) => {
(format!("/{}", cmd.command()), cmd.description().to_string())
}
CommandItem::UserPrompt(i) => (
format!("/{PROMPTS_CMD_PREFIX}:{}", self.prompts[i].name),
"send saved prompt".to_string(),
),
CommandItem::UserPrompt(i) => {
let prompt = &self.prompts[i];
let description = prompt
.description
.clone()
.unwrap_or_else(|| "send saved prompt".to_string());
(
format!("/{PROMPTS_CMD_PREFIX}:{}", prompt.name),
description,
)
}
};
GenericDisplayRow {
name,
Expand Down Expand Up @@ -221,6 +228,7 @@ impl WidgetRef for CommandPopup {
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;

#[test]
fn filter_includes_init_when_typing_prefix() {
Expand Down Expand Up @@ -322,4 +330,35 @@ mod tests {
"prompt with builtin name should be ignored"
);
}

#[test]
fn prompt_description_uses_frontmatter_metadata() {
let popup = CommandPopup::new(vec![CustomPrompt {
name: "draftpr".to_string(),
path: "/tmp/draftpr.md".to_string().into(),
content: "body".to_string(),
description: Some("Create feature branch, commit and open draft PR.".to_string()),
argument_hint: None,
}]);
let rows = popup.rows_from_matches(vec![(CommandItem::UserPrompt(0), None, 0)]);
let description = rows.first().and_then(|row| row.description.as_deref());
assert_eq!(
description,
Some("Create feature branch, commit and open draft PR.")
);
}

#[test]
fn prompt_description_falls_back_when_missing() {
let popup = CommandPopup::new(vec![CustomPrompt {
name: "foo".to_string(),
path: "/tmp/foo.md".to_string().into(),
content: "body".to_string(),
description: None,
argument_hint: None,
}]);
let rows = popup.rows_from_matches(vec![(CommandItem::UserPrompt(0), None, 0)]);
let description = rows.first().and_then(|row| row.description.as_deref());
assert_eq!(description, Some("send saved prompt"));
}
}
2 changes: 2 additions & 0 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ Below are a few bite-size examples you can copy-paste. Replace the text in quote
| 6 | `codex "Carefully review this repo, and propose 3 high impact well-scoped PRs"` | Suggests impactful PRs in the current codebase. |
| 7 | `codex "Look for vulnerabilities and create a security review report"` | Finds and explains security bugs. |

Looking to reuse your own instructions? Create slash commands with [custom prompts](./prompts.md).

### Memory with AGENTS.md

You can give Codex extra instructions and guidance using `AGENTS.md` files. Codex looks for `AGENTS.md` files in the following places, and merges them top-down:
Expand Down
73 changes: 55 additions & 18 deletions docs/prompts.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,57 @@
## Custom Prompts

Save frequently used prompts as Markdown files and reuse them quickly from the slash menu.

- Location: Put files in `$CODEX_HOME/prompts/` (defaults to `~/.codex/prompts/`).
- File type: Only Markdown files with the `.md` extension are recognized.
- Name: The filename without the `.md` extension becomes the slash entry. For a file named `my-prompt.md`, type `/my-prompt`.
- Content: The file contents are sent as your message when you select the item in the slash popup and press Enter.
- Arguments: Local prompts support placeholders in their content:
- `$1..$9` expand to the first nine positional arguments typed after the slash name
- `$ARGUMENTS` expands to all arguments joined by a single space
- `$$` is preserved literally
- Quoted args: Wrap a single argument in double quotes to include spaces, e.g. `/review "docs/My File.md"`.
- How to use:
- Start a new session (Codex loads custom prompts on session start).
- In the composer, type `/` to open the slash popup and begin typing your prompt name.
- Use Up/Down to select it. Press Enter to submit its contents, or Tab to autocomplete the name.
- Notes:
- Files with names that collide with built‑in commands (e.g. `/init`) are ignored and won’t appear.
- New or changed files are discovered on session start. If you add a new prompt while Codex is running, start a new session to pick it up.
Custom prompts turn your repeatable instructions into reusable slash commands, so you can trigger them without retyping or copy/pasting. Each prompt is a Markdown file that Codex expands into the conversation the moment you run it.

### Where prompts live

- Location: store prompts in `$CODEX_HOME/prompts/` (defaults to `~/.codex/prompts/`). Set `CODEX_HOME` if you want to use a different folder.
- File type: Codex only loads `.md` files. Non-Markdown files are ignored.
- Naming: The filename (without `.md`) becomes the prompt name. A file called `review.md` registers the prompt `review`.
- Refresh: Prompts are loaded when a session starts. Restart Codex (or start a new session) after adding or editing files.
- Conflicts: Files whose names collide with built-in commands (like `init`) are skipped.

### File format

- Body: The file contents are sent verbatim when you run the prompt (after placeholder expansion).
- Frontmatter (optional): Add YAML-style metadata at the top of the file to improve the slash popup.

```markdown
---
description: Request a concise git diff review
argument-hint: FILE=<path> [FOCUS=<section>]
---
```

- `description` shows under the entry in the popup.
- `argument-hint` (or `argument_hint`) displays a short hint about expected inputs.

### Placeholders and arguments

- Numeric placeholders: `$1`–`$9` insert the first nine positional arguments you type after the command. `$ARGUMENTS` inserts all positional arguments joined by a single space. Use `$$` to emit a literal dollar sign (Codex leaves `$$` untouched).
- Named placeholders: Tokens such as `$FILE` or `$TICKET_ID` expand from `KEY=value` pairs you supply. Keys are case-sensitive—use the same uppercase name in the command (for example, `FILE=...`).
- Quoted arguments: Double-quote any value that contains spaces, e.g. `TICKET_TITLE="Fix logging"`.
- Invocation syntax: Run prompts via `/prompts:<name> ...`. When the slash popup is open, typing either `prompts:` or the bare prompt name will surface `/prompts:<name>` suggestions.
- Error handling: If a prompt contains named placeholders, Codex requires them all. You will see a validation message if any are missing or malformed.

### Running a prompt

1. Start a new Codex session (ensures the prompt list is fresh).
2. In the composer, type `/` to open the slash popup.
3. Type `prompts:` (or start typing the prompt name) and select it with ↑/↓.
4. Provide any required arguments, press Enter, and Codex sends the expanded content.

### Examples

**Draft PR helper**

`~/.codex/prompts/draftpr.md`

```markdown
---
description: Create feature branch, commit and open draft PR.
---

Create a branch named `tibo/<feature_name>`, commit the changes, and open a draft PR.
```

Usage: type `/prompts:draftpr` to have codex perform the work.
Loading