원클릭으로
slash-commands
Developer guide for adding and implementing local slash commands in the Phosphor TUI.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Developer guide for adding and implementing local slash commands in the Phosphor TUI.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use when the user needs to query, filter, reshape, extract, create, or construct JSON data — including API responses, config files, log output, or any structured data — or when helping the user write or debug JSON transformations.
Use when the user needs help configuring Phosphor — working with phosphor.json, setting up providers, configuring LSPs, adding MCP servers, managing skills or permissions, or changing Phosphor behavior.
Use when the user wants to add, write, debug, or configure a Phosphor hook — gating or blocking tool calls, approving or rewriting tool input before execution, injecting context into tool results, or troubleshooting hook behavior in phosphor.json.
Use when creating a new builtin skill for Phosphor, editing an existing builtin skill (internal/skills/builtin/), or when the user needs to understand how the embedded skill system works.
Use when creating a new shell builtin command for Phosphor (internal/shell/), editing an existing one, or when the user needs to understand how commands are intercepted in Phosphor's embedded shell.
| name | slash-commands |
| description | Developer guide for adding and implementing local slash commands in the Phosphor TUI. |
Slash commands are typed in the prompt starting with a / (e.g., /menu, /stats, /goal). They are executed locally in the TUI layer and are strictly prevented from leaking into the LLM conversation history.
Phosphor uses a split architecture for slash commands:
internal/commands/slash_commands.go): A data-only package containing the list of available slash commands and their descriptions. This package is imported by the completions engine.internal/ui/model/ui.go): The TUI model maintains a dynamic map (m.slashHandlers) linking command names to handler methods that return a tea.Cmd./learn)While most slash commands are purely local (UI-only) and never reach the LLM, some commands are hybrid.
For instance, /learn <source> is typed in the TUI, but instead of being blocked, the handler translates it into a highly structured system-guided instruction prompt and calls m.sendMessage(prompt) to submit it to the LLM agent.
Because the generated prompt is in plain English and does not start with a /, it naturally bypasses the backend slash-prevention failsafes and leverages the agent's existing file/web tools to execute the command.
To add a new slash command /foo:
Open internal/commands/slash_commands.go and add a new entry to the SlashCommands slice:
{
Name: "foo",
Description: "Open the foo menu or trigger action",
}
Open internal/ui/model/ui.go and implement the handler method on the UI struct. The method must take args []string and return tea.Cmd:
// handleFooSlashCommand handles the "/foo" slash command.
func (m *UI) handleFooSlashCommand(args []string) tea.Cmd {
// Implement command logic here.
// For example, to open a dialog:
// m.dialog.OpenDialog(dialog.NewFoo(m.com))
return func() tea.Msg { return nil }
}
In internal/ui/model/ui.go, locate the registerSlashCommands method and map your command name to the handler:
func (m *UI) registerSlashCommands() {
m.slashHandlers = map[string]slashCommandHandler{
"goal": m.handleGoalSlashCommand,
"menu": m.handleMenuSlashCommand,
"stats": m.handleStatsSlashCommand,
"quit": m.handleQuitSlashCommand,
"foo": m.handleFooSlashCommand, // <-- Register your new handler here
}
}
Open internal/ui/model/ui_test.go and add a unit test to verify that the command executes properly, sanitizes the TUI input states, and triggers the expected side effect (e.g., opening a dialog):
func TestUI_HandleSlashCommand_FooOpensDialog(t *testing.T) {
t.Parallel()
tw := &testWorkspace{}
st := uistyles.CharmtonePantera()
ui := &UI{
com: &common.Common{
Workspace: tw,
Styles: &st,
},
}
ui.registerSlashCommands()
ui.dialog = dialog.NewOverlay()
ui.slashMode = true
ui.completionsOpen = true
ui.completions = completions.New(lipgloss.NewStyle(), lipgloss.NewStyle(), lipgloss.NewStyle())
// Call handleSlashCommand with /foo.
cmd := ui.handleSlashCommand("/foo")
require.NotNil(t, cmd)
// Verify that the dialog is open and states are cleaned up.
require.True(t, ui.dialog.ContainsDialog(dialog.FooID))
require.False(t, ui.slashMode)
require.False(t, ui.completionsOpen)
}
m.slashMode = false and calls m.closeCompletions() before executing any handler. You do not need to do this manually in your handler.m.hasSession() first and return util.ReportWarn if one is not active.