fix: expand codex custom prompt

This commit is contained in:
weishu
2026-01-02 21:45:29 +08:00
parent c112f3f93e
commit 2de3a3d635
7 changed files with 81 additions and 21 deletions
+60 -17
View File
@@ -1,11 +1,13 @@
import { readdir } from 'fs/promises';
import { readdir, readFile } from 'fs/promises';
import { join } from 'path';
import { homedir } from 'os';
import { parse as parseYaml } from 'yaml';
export interface SlashCommand {
name: string;
description?: string;
source: 'builtin' | 'user';
content?: string; // Expanded content for Codex user prompts
}
export interface ListSlashCommandsRequest {
@@ -42,6 +44,29 @@ const BUILTIN_COMMANDS: Record<string, SlashCommand[]> = {
],
};
/**
* Parse frontmatter from a markdown file content.
* Returns the description (from frontmatter) and the body content.
*/
function parseFrontmatter(fileContent: string): { description?: string; content: string } {
// Match frontmatter: starts with ---, ends with ---
const match = fileContent.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
if (match) {
const yamlContent = match[1];
const body = match[2].trim();
try {
const parsed = parseYaml(yamlContent) as Record<string, unknown> | null;
const description = typeof parsed?.description === 'string' ? parsed.description : undefined;
return { description, content: body };
} catch {
// Invalid YAML - the --- block is not valid frontmatter, return entire file
return { content: fileContent.trim() };
}
}
// No frontmatter, entire file is content
return { content: fileContent.trim() };
}
/**
* Get the user commands directory for an agent type.
* Returns null if the agent doesn't support user commands.
@@ -64,6 +89,7 @@ function getUserCommandsDir(agent: string): string | null {
/**
* Scan a directory for user-defined commands (*.md files).
* For Codex, reads file content and parses frontmatter.
* Returns the command names (filename without extension).
*/
async function scanUserCommands(agent: string): Promise<SlashCommand[]> {
@@ -72,29 +98,46 @@ async function scanUserCommands(agent: string): Promise<SlashCommand[]> {
return [];
}
const shouldReadContent = agent === 'codex';
try {
const entries = await readdir(dir, { withFileTypes: true });
const commands: SlashCommand[] = [];
const mdFiles = entries.filter(e => e.isFile() && e.name.endsWith('.md'));
for (const entry of entries) {
if (!entry.isFile()) continue;
if (!entry.name.endsWith('.md')) continue;
// Read all files in parallel
const commands = await Promise.all(
mdFiles.map(async (entry): Promise<SlashCommand | null> => {
const name = entry.name.slice(0, -3);
if (!name) return null;
// Remove .md extension to get command name
const name = entry.name.slice(0, -3);
if (!name) continue;
const command: SlashCommand = {
name,
description: 'Custom command',
source: 'user',
};
commands.push({
name,
description: 'Custom command',
source: 'user',
});
}
if (shouldReadContent) {
try {
const filePath = join(dir, entry.name);
const fileContent = await readFile(filePath, 'utf-8');
const parsed = parseFrontmatter(fileContent);
if (parsed.description) {
command.description = parsed.description;
}
command.content = parsed.content;
} catch {
// Failed to read file, keep default description
}
}
// Sort alphabetically
commands.sort((a, b) => a.name.localeCompare(b.name));
return command;
})
);
return commands;
// Filter nulls and sort alphabetically
return commands
.filter((cmd): cmd is SlashCommand => cmd !== null)
.sort((a, b) => a.name.localeCompare(b.name));
} catch {
// Directory doesn't exist or not accessible - return empty array
return [];