mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 17:30:31 +00:00
Classify slash commands by surface
This commit is contained in:
parent
ebb16fad30
commit
d8937f3191
5 changed files with 131 additions and 19 deletions
|
|
@ -1,42 +1,115 @@
|
|||
export type SlashCommandArgsKind = "none" | "optionalThread" | "requiredThread" | "optionalMessage" | "threadAndMessage" | "showOrSet";
|
||||
|
||||
export type SlashCommandSurface = "panelAction" | "threadSetting" | "diagnostic" | "composition";
|
||||
|
||||
export const SLASH_COMMAND_SURFACE_LABELS: Record<SlashCommandSurface, string> = {
|
||||
panelAction: "Panel actions",
|
||||
threadSetting: "Thread settings",
|
||||
diagnostic: "Diagnostics",
|
||||
composition: "Composition",
|
||||
};
|
||||
|
||||
export const SLASH_COMMANDS = [
|
||||
{
|
||||
command: "/clear",
|
||||
usage: "/clear",
|
||||
argsKind: "none",
|
||||
surface: "panelAction",
|
||||
detail: "Clear the current panel and start a fresh Codex thread.",
|
||||
},
|
||||
{ command: "/resume", usage: "/resume [thread]", argsKind: "optionalThread", detail: "Resume a recent Codex thread." },
|
||||
{
|
||||
command: "/resume",
|
||||
usage: "/resume [thread]",
|
||||
argsKind: "optionalThread",
|
||||
surface: "panelAction",
|
||||
detail: "Resume a recent Codex thread.",
|
||||
},
|
||||
{
|
||||
command: "/refer",
|
||||
usage: "/refer <thread> <message>",
|
||||
argsKind: "threadAndMessage",
|
||||
surface: "composition",
|
||||
detail: "Send a message with recent turns from another non-archived thread.",
|
||||
},
|
||||
{ command: "/fork", usage: "/fork", argsKind: "none", detail: "Fork the active Codex thread." },
|
||||
{ command: "/rollback", usage: "/rollback", argsKind: "none", detail: "Roll back the latest turn and restore its prompt." },
|
||||
{ command: "/compact", usage: "/compact", argsKind: "none", detail: "Compact the current thread context." },
|
||||
{ command: "/archive", usage: "/archive <thread>", argsKind: "requiredThread", detail: "Archive the selected Codex thread." },
|
||||
{ command: "/fork", usage: "/fork", argsKind: "none", surface: "panelAction", detail: "Fork the active Codex thread." },
|
||||
{
|
||||
command: "/rollback",
|
||||
usage: "/rollback",
|
||||
argsKind: "none",
|
||||
surface: "panelAction",
|
||||
detail: "Roll back the latest turn and restore its prompt.",
|
||||
},
|
||||
{ command: "/compact", usage: "/compact", argsKind: "none", surface: "panelAction", detail: "Compact the current thread context." },
|
||||
{
|
||||
command: "/archive",
|
||||
usage: "/archive <thread>",
|
||||
argsKind: "requiredThread",
|
||||
surface: "panelAction",
|
||||
detail: "Archive the selected Codex thread.",
|
||||
},
|
||||
{
|
||||
command: "/auto-review",
|
||||
usage: "/auto-review",
|
||||
argsKind: "none",
|
||||
surface: "threadSetting",
|
||||
detail: "Toggle approval auto-review.",
|
||||
},
|
||||
{ command: "/fast", usage: "/fast", argsKind: "none", detail: "Toggle fast service tier for subsequent turns." },
|
||||
{ command: "/plan", usage: "/plan [message]", argsKind: "optionalMessage", detail: "Toggle Plan mode, optionally with a message." },
|
||||
{ command: "/status", usage: "/status", argsKind: "none", detail: "Show current thread, context, and usage limits." },
|
||||
{ command: "/doctor", usage: "/doctor", argsKind: "none", detail: "Show Codex CLI and Codex App Server diagnostics." },
|
||||
{ command: "/mcp", usage: "/mcp", argsKind: "none", detail: "Show MCP servers reported by Codex App Server." },
|
||||
{ command: "/model", usage: "/model [model|default]", argsKind: "showOrSet", detail: "Show or set the model for subsequent turns." },
|
||||
{
|
||||
command: "/fast",
|
||||
usage: "/fast",
|
||||
argsKind: "none",
|
||||
surface: "threadSetting",
|
||||
detail: "Toggle fast service tier for subsequent turns.",
|
||||
},
|
||||
{
|
||||
command: "/plan",
|
||||
usage: "/plan [message]",
|
||||
argsKind: "optionalMessage",
|
||||
surface: "threadSetting",
|
||||
detail: "Toggle Plan mode, optionally with a message.",
|
||||
},
|
||||
{
|
||||
command: "/status",
|
||||
usage: "/status",
|
||||
argsKind: "none",
|
||||
surface: "diagnostic",
|
||||
detail: "Show current thread, context, and usage limits.",
|
||||
},
|
||||
{
|
||||
command: "/doctor",
|
||||
usage: "/doctor",
|
||||
argsKind: "none",
|
||||
surface: "diagnostic",
|
||||
detail: "Show Codex CLI and Codex App Server diagnostics.",
|
||||
},
|
||||
{
|
||||
command: "/mcp",
|
||||
usage: "/mcp",
|
||||
argsKind: "none",
|
||||
surface: "diagnostic",
|
||||
detail: "Show MCP servers reported by Codex App Server.",
|
||||
},
|
||||
{
|
||||
command: "/model",
|
||||
usage: "/model [model|default]",
|
||||
argsKind: "showOrSet",
|
||||
surface: "threadSetting",
|
||||
detail: "Show or set the model for subsequent turns.",
|
||||
},
|
||||
{
|
||||
command: "/effort",
|
||||
usage: "/effort [effort|default]",
|
||||
argsKind: "showOrSet",
|
||||
surface: "threadSetting",
|
||||
detail: "Show or set reasoning effort for subsequent turns.",
|
||||
},
|
||||
{ command: "/help", usage: "/help", argsKind: "none", detail: "Show available Codex slash commands." },
|
||||
{
|
||||
command: "/help",
|
||||
usage: "/help",
|
||||
argsKind: "none",
|
||||
surface: "diagnostic",
|
||||
detail: "Show available Codex slash commands.",
|
||||
},
|
||||
] as const;
|
||||
|
||||
type SlashCommand = (typeof SLASH_COMMANDS)[number]["command"];
|
||||
|
|
@ -58,3 +131,12 @@ export function slashCommandHelpLines(): string[] {
|
|||
export function slashCommandHelpRows(): { key: string; value: string }[] {
|
||||
return SLASH_COMMANDS.map((item) => ({ key: item.usage, value: item.detail }));
|
||||
}
|
||||
|
||||
export function slashCommandHelpSections(): { title: string; rows: { key: string; value: string }[] }[] {
|
||||
return (Object.keys(SLASH_COMMAND_SURFACE_LABELS) as SlashCommandSurface[])
|
||||
.map((surface) => ({
|
||||
title: SLASH_COMMAND_SURFACE_LABELS[surface],
|
||||
rows: SLASH_COMMANDS.filter((item) => item.surface === surface).map((item) => ({ key: item.usage, value: item.detail })),
|
||||
}))
|
||||
.filter((section) => section.rows.length > 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
sortedAvailableModels,
|
||||
supportedEffortsForModel,
|
||||
} from "../../../runtime/model";
|
||||
import { SLASH_COMMANDS, type SlashCommandName } from "./slash-commands";
|
||||
import { SLASH_COMMANDS, SLASH_COMMAND_SURFACE_LABELS, type SlashCommandName } from "./slash-commands";
|
||||
import { getThreadTitle } from "../../../domain/threads/model";
|
||||
import { shortThreadId } from "../../../utils";
|
||||
|
||||
|
|
@ -170,7 +170,7 @@ export function activeSlashCommandSuggestions(beforeCursor: string): ComposerSug
|
|||
.slice(0, 8)
|
||||
.map((item) => ({
|
||||
display: item.command,
|
||||
detail: `${item.usage} - ${item.detail}`,
|
||||
detail: `${SLASH_COMMAND_SURFACE_LABELS[item.surface]}: ${item.usage} - ${item.detail}`,
|
||||
replacement: item.command,
|
||||
start,
|
||||
appendSpaceOnInsert: true,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { Thread } from "../../generated/app-server/v2/Thread";
|
|||
import type { UserInput } from "../../generated/app-server/v2/UserInput";
|
||||
import { getThreadTitle } from "../../domain/threads/model";
|
||||
import type { ReferencedThreadDisplay } from "../../domain/threads/reference";
|
||||
import { slashCommandDefinition, slashCommandHelpRows, type SlashCommandName } from "./composer/slash-commands";
|
||||
import { slashCommandDefinition, slashCommandHelpSections, type SlashCommandName } from "./composer/slash-commands";
|
||||
import type { DisplayDetailSection, DisplayDetailMetaRow } from "./display/types";
|
||||
import {
|
||||
modelOverrideMessage,
|
||||
|
|
@ -206,7 +206,7 @@ export async function executeSlashCommand(
|
|||
return;
|
||||
}
|
||||
|
||||
context.addStructuredSystemMessage("Available slash commands", [{ rows: slashCommandHelpRows() }]);
|
||||
context.addStructuredSystemMessage("Available slash commands", slashCommandHelpSections());
|
||||
}
|
||||
|
||||
function validateSlashCommandArguments(command: SlashCommandName, args: string): string | null {
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ describe("composer suggestions", () => {
|
|||
const wikilink = expectPresent(activeComposerSuggestions("[[bet", notes, [])[0]);
|
||||
|
||||
expect(slash).toMatchObject({
|
||||
detail: "/status - Show current thread, context, and usage limits.",
|
||||
detail: "Diagnostics: /status - Show current thread, context, and usage limits.",
|
||||
replacement: "/status",
|
||||
appendSpaceOnInsert: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { slashCommandHelpLines, slashCommandHelpRows } from "../../../../src/features/chat/composer/slash-commands";
|
||||
import { slashCommandHelpLines, slashCommandHelpSections } from "../../../../src/features/chat/composer/slash-commands";
|
||||
import type { Thread } from "../../../../src/generated/app-server/v2/Thread";
|
||||
import { executeSlashCommand, type SlashCommandExecutionContext } from "../../../../src/features/chat/slash-commands";
|
||||
|
||||
|
|
@ -303,7 +303,37 @@ describe("slash commands", () => {
|
|||
await executeSlashCommand("help", "", ctx);
|
||||
|
||||
expect(ctx.addSystemMessage).not.toHaveBeenCalled();
|
||||
expect(ctx.addStructuredSystemMessage).toHaveBeenCalledWith("Available slash commands", [{ rows: slashCommandHelpRows() }]);
|
||||
expect(ctx.addStructuredSystemMessage).toHaveBeenCalledWith("Available slash commands", slashCommandHelpSections());
|
||||
});
|
||||
|
||||
it("groups slash command help by command surface", () => {
|
||||
expect(slashCommandHelpSections()).toEqual([
|
||||
{
|
||||
title: "Panel actions",
|
||||
rows: expect.arrayContaining([
|
||||
{ key: "/clear", value: "Clear the current panel and start a fresh Codex thread." },
|
||||
{ key: "/archive <thread>", value: "Archive the selected Codex thread." },
|
||||
]),
|
||||
},
|
||||
{
|
||||
title: "Thread settings",
|
||||
rows: expect.arrayContaining([
|
||||
{ key: "/plan [message]", value: "Toggle Plan mode, optionally with a message." },
|
||||
{ key: "/model [model|default]", value: "Show or set the model for subsequent turns." },
|
||||
]),
|
||||
},
|
||||
{
|
||||
title: "Diagnostics",
|
||||
rows: expect.arrayContaining([
|
||||
{ key: "/status", value: "Show current thread, context, and usage limits." },
|
||||
{ key: "/help", value: "Show available Codex slash commands." },
|
||||
]),
|
||||
},
|
||||
{
|
||||
title: "Composition",
|
||||
rows: [{ key: "/refer <thread> <message>", value: "Send a message with recent turns from another non-archived thread." }],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("rejects /help arguments", async () => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue