diff --git a/README.md b/README.md index c9cc4e1fd4..3a78f96573 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/codex-rs/tui/src/bottom_pane/command_popup.rs b/codex-rs/tui/src/bottom_pane/command_popup.rs index 1842a999e3..d7501cebbc 100644 --- a/codex-rs/tui/src/bottom_pane/command_popup.rs +++ b/codex-rs/tui/src/bottom_pane/command_popup.rs @@ -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, @@ -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() { @@ -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")); + } } diff --git a/docs/getting-started.md b/docs/getting-started.md index e97de6a048..6df93b5817 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -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: diff --git a/docs/prompts.md b/docs/prompts.md index 5157c4ecea..2e6c9fc1e6 100644 --- a/docs/prompts.md +++ b/docs/prompts.md @@ -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= [FOCUS=
] + --- + ``` + + - `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: ...`. When the slash popup is open, typing either `prompts:` or the bare prompt name will surface `/prompts:` 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/`, commit the changes, and open a draft PR. +``` + +Usage: type `/prompts:draftpr` to have codex perform the work.