Use hooks and slash commands
Slash commands turn /… input into session controls or extension macros. Hooks let enabled plugins
and mini apps, or an invoked skill or agent, run handlers at defined runtime boundaries.
What you can do
| Outcome | Mechanism | Boundary |
|---|---|---|
| Run session controls | Select a core command for models, reasoning, context, loops, or extensions. | Client-scoped commands depend on the active client. |
| Invoke an extension macro | Run a command from an enabled plugin or mini app, or a user-invocable skill. | Macros start model work; during a run they wait in the queue. |
| Gate prompts or tools | Use native onPreToolCall or declarative UserPromptSubmit and PreToolUse hooks. | The extension or run-scoped declaration must be active. |
| Transform model-facing data | Rewrite prompts, tool inputs, tool outputs, assistant text, or added context. | Each hook contract controls which persisted data can change. |
| Observe lifecycle events | Receive run, worker, context-checkpoint, and human-input events. | Observer hooks cannot answer a human-in-the-loop request. |
How it works
Slash-command dispatch
Web/desktop derives its built-in menus from the shared slash catalog, then merges enabled plugin and mini app commands and user-invocable skills for the active scope.
| Order | Owner | Dispatch behavior |
|---|---|---|
| 1 | Compatible client | Handles client commands such as /fork and /open before an API turn. |
| 2 | Core API catalog | Resolves canonical names and aliases to typed control handlers. |
| 3 | Enabled extension | Resolves a plugin's /<plugin>:<command>, a workflow, a mini app or mod command. |
| 4 | User-invocable skill | Resolves /<skill-name> when no earlier owner claimed it. |
| 5 | Unresolved gate | Returns an error and suggestions instead of sending command-shaped input to the model. |
A core name or alias wins a collision. A plugin's command (commands/*.md) and a folder's
(.claude/commands/*.md) are skills, as in Claude Code: they run through the same plugin kit
session as a skill, with its arguments, variables, ! commands and @ files (see
Skill authoring). A plugin's command wins a
skill of its plugin with the same name. There is no bare alias: a command named after its plugin is
/<plugin>:<plugin>. A workflow (.claude/workflows, ~/.claude/workflows or a plugin's) typed as
/<name> asks the agent to run it with the Workflow tool; a skill of the same name runs instead.
Mini app commands are agent prompt templates: the backend validates positional arguments and static choices, expands their variables, then starts a macro run. Dynamic option scripts stay server-side; clients receive only their returned choices.
A mini app command's arguments are positional. Non-final text and choice fields consume
whitespace-delimited tokens; only a final text field captures the remainder. Static choices must
match a declared value, while dynamic choices accept any non-empty token without checking the
returned menu. A conditional argument whose earlier value does not match is skipped. Extra tokens
are not rejected by the declared-argument parser; $ARGUMENTS can still include the complete raw
input.
An argument containing ! command syntax or @path does not start another preprocessing pass. A
skill's or command's ! commands meet the agent's permission rules before they run; an argument
reaches them as data, never as shell code.
| Category | Core commands |
|---|---|
| Autonomous | /loop |
| Configuration | /agents, /model (/models), /effort, /output-style, /theme, /plan, /permissions, /config, /add-dir |
| Context | /clear, /resume, /rename, /rewind, /copy, /fork, /open (client-scoped), /compact, /export |
| Information | /help, /usage (/cost), /context, /recap, /status, /diff |
| Memory and skills | /memory, /skills (/skill), /init |
| Extensions | /mcp, /plugin, /mini-app, /hooks |
A command that takes an argument offers it as crumbs: pick from the list, or type the value.
/plugin manages Claude Code plugins and their marketplaces; /mini-app lists, creates, switches
on and off, removes and reloads mini apps (see Mini apps).
Everyday Claude Code commands
Claude Code commands in Localix lists the built-in Claude Code commands Localix runs, what each does here, and what the others answer.
Native mini app hooks
Native hooks are callbacks returned by an enabled mini app's register(ctx) function.
| Hook | Primary effect | Boundary |
|---|---|---|
onSessionStart | Observe the first run of a session through the app's context. | Fires once per session per API process and is not awaited. |
onUserMessage | Rewrite the model-facing prompt. | The persisted user message does not change. |
onPreToolCall | Observe, rewrite, or block built-in, extension, and MCP calls. | A rewrite is schema-checked when the tool exposes a compatible schema. |
onPostToolCall | Replace result content or append a note. | Appended text is capped at 1,024 UTF-16 code units per hook result. |
onAssistantMessage | Replace persisted assistant text or add message bindings. | Content must be non-empty; bindings are validated and capped. |
onSessionEnd | Observe the end of each run. | It is not a logical session-deletion event. |
onSubagentStop | Observe a child session reaching a terminal status. | Observer-only. |
onHitlRequest / onHitlResolved | Observe human-input request lifecycle. | These hooks cannot answer or veto the request. |
onHumanAssigned | Observe a board card assigned to the person. | Receives sessionId, messageId, from and a text excerpt; observer-only. |
Native rewrites compose in mini app load order. Tool pre-hooks stop at the first block; post-hooks all run and later hooks see earlier replacements.
Post-hooks also see execution errors converted into tool results; a pre-hook block skips execution and post-hooks. Assistant hooks can add at most eight bindings in total, with an 8 MiB payload cap per binding. These caps do not impose an aggregate limit on replacement message or tool text.
Declarative hooks
Declarative hooks use the supported hooks JSON schema from enabled plugins or hooks frontmatter on
the invoked skill or delegated agent. Localix dispatches these events:
Session events are SessionStart, SessionEnd, InstructionsLoaded, ConfigChange, CwdChanged,
FileChanged, PreCompact, PostCompact, PreModelSwitch, PostModelSwitch and Notification.
Turn events are UserPromptSubmit, UserPromptExpansion, Stop, StopFailure and
MessageDisplay. Tool events are PreToolUse, PermissionRequest, PermissionDenied,
PostToolUse, PostToolUseFailure and PostToolBatch. Agent events are SubagentStart,
SubagentStop, TaskCreated and TaskCompleted. Elicitation, ElicitationResult and
WorktreeCreate complete the twenty-nine.
Setup, DirectoryAdded and WorktreeRemove are accepted but never fire, as Localix has no
matching moment; a hook on any other event is reported in validation and never fires.
PostToolUseFailure runs in place of PostToolUse when a tool throws; its handlers receive the
error, and their context or decision: "block" reason is added to the failed result.
PreCompact fires with trigger auto before a context checkpoint is
committed, and with trigger manual and the typed text as custom_instructions when /compact
asked for it; PostCompact follows with the same trigger. A block skips a proactive checkpoint, and
the turn continues with the uncompacted view. A block on a checkpoint that recovers from a
context-limit error fails the turn. A checkpoint taken only because the history does not fit the
request is announced to PreCompact after it is taken, and that call cannot block. After a
checkpoint, the next run fires SessionStart with source compact.
SessionStart fires on a session's first run in the API process. Its source is startup for a new
session, fork for a forked one, resume for a session that ran before the API last started, and
clear or compact after /clear or a checkpoint. In a run nobody is typing into, such as a
scheduled one, its initialUserMessage is the user turn before the run's prompt; with
reloadSkills, skills the hook installed are named to the agent and listed from the next run on.
SessionEnd fires when a session ends, not a run: /clear fires it with reason clear, deleting a
session with other, and quitting the app with prompt_input_exit for each session that started
and is still open. At quit all the hooks share Claude Code's 1.5 second budget; one that runs longer
is left behind so the app can close.
A handler's statusMessage is the chat's activity line while it runs. A non-blocking error (another
exit code, a timeout, a hook that cannot start, output that is not valid JSON) shows a
<event> hook error notice in the transcript, as does the exit-2 stderr of SessionStart,
SubagentStart and the other events that only observe. Claude Code's allowedHttpHookUrls and
httpHookAllowedEnvVars settings hold every http hook: one whose URL matches no pattern does not
run, and its headers read only the variables both lists name.
Matching handlers run in parallel, as in Claude Code, at most twelve prompt or agent handlers at
once. Each sees the same input, and their answers fold in declaration and plugin load order: a deny
from any PreToolUse handler stops the call without stopping the others from running, and the last
updatedInput or PostToolUse replacement declared is the one applied.
| Event | Effect of a blocking verdict |
|---|---|
UserPromptSubmit | Rejects the prompt on any block; the message ends with the prompt, unless suppressed (below). |
PreToolUse | Stops the tool on any deny; ask and allow shape the permission prompt (below). |
PostToolUse | Cannot undo execution; a block reason becomes added result context. |
PostToolUseFailure | Cannot block; added context is appended to the failed result. |
Stop | Requests another run with the reason; eight in a row end the turn, and a tool call resets that. |
Stop additionalContext | Requests the same bounded run as feedback, without a block. |
Scoped child SubagentStop | Can request the same bounded continuation in the child; parent observation does not restart it. |
PreCompact | Skips a proactive checkpoint; fails a turn whose checkpoint recovers from a context-limit error. |
| Other lifecycle events | Do not stop the run; context or observer effects depend on the event. |
A command or http handler with "onFailure": "block" turns its own failure into a block: when
it cannot start, exits with a code other than 0 or 2, times out, or answers with invalid output, the
event acts as on exit 2, and a PermissionRequest is denied. The error names the hook and says
failed; blocking because onFailure is "block". Stop, SubagentStop, TaskCompleted and
background (async) handlers ignore the field.
Tool calls reach hooks in Claude Code's names and shapes: Hook tool calls.
A PreToolUse ask asks even where your rules allow the call, with its reason on the card. An
allow skips only a mode's questions about how a call is made; deny rules, named ask rules and
irreversible calls stand. defer needs a headless caller: it is warned about and ignored.
Matcher, if, and once
A tool-event matcher meets the call's Claude Code tool name (see
Hook tool calls), mcp__<server>__<tool> for an MCP tool, and no other
spelling. Empty, omitted, or * matchers match all. Simple names separated by | or commas compare
exactly, case included (Bash, not bash, is Claude Code's name); other patterns are unanchored
JavaScript regular expressions. Invalid regexes do not match. UserPromptSubmit and Stop ignore
group matchers; other lifecycle matchers inspect the event's source, reason, agent type,
notification type, or checkpoint trigger.
A matcher meets the Claude Code name of the call itself: a file listing is Glob and a content
search Grep, so a Glob matcher does not fire for a content search. MultiEdit matches an edit
with several replacements and NotebookEdit an edit of an .ipynb file.
A handler's if is one permission-style rule evaluated against the original tool input before
rewrites, read in Claude Code's shape. A Bash rule matches any command of a batch. A file rule
anchors as in Claude Code: src/** is the working directory's src, // the filesystem root and
~/ your home folder. Agent(Explore), Skill(deploy), WebFetch(domain:example.com) and
Tool(param:value) read the agent type, the skill, the host and the named field. It applies only to
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest and PermissionDenied;
carrying it on another event prevents that handler from running. Unparseable rules or unavailable
comparison fields let the handler run. This filter does not grant tool permission.
Configuration
Contribution locations
| Contribution | Accepted source |
|---|---|
| Plugin slash command | The default commands/ tree, or commands paths or inline entries in .claude-plugin/plugin.json |
| Declarative plugin hooks | Default hooks/hooks.json, then hooks paths or inline objects in .claude-plugin/plugin.json |
| Run-scoped hooks | hooks frontmatter on the invoked skill or delegated agent |
| Mini app slash command | An entry under commands in mini-app.json, which carries its own prompt |
| Native mini app hooks | hooks returned from the mini app's register(ctx) function |
Inline commands win same-name collisions with command Markdown files. Explicit file/path entries win
over the default directory, and nested paths become colon-separated names. Paths listed in
.claude-plugin/plugin.json replace the default tree, as in Claude Code; a root plugin.json is
not read. Markdown commands need a non-empty body in a regular UTF-8 file up to 1 MiB. Discovery
considers at most 64 declared roots, eight directory levels, and 2,000 entries per scanned root, and
checks canonical paths against the plugin directory.
Plugin command example and options
A command is a Markdown file in the plugin, commands/review.md:
---
description: Review a change and report concrete findings.
arguments: [request]
---
Review the requested change and report concrete findings: $requestFor a plugin named team-tools, it runs as /team-tools:review <request>, here and in Claude Code.
A mini app declares its commands in mini-app.json, each with its prompt and the chips and
pickers of its args; ${name} takes each argument's value. In a mini app named team-board this
runs as /team-board:review <request>:
{
"commands": {
"review": {
"description": "Review a change and report concrete findings.",
"prompt": "Review the requested change and report concrete findings: ${request}",
"args": [{ "name": "request", "type": "text", "required": true }]
}
}
}An options script runs directly through Bun when the composer requests choices for an enabled mini
app. It receives { query, cwd, sessionId, argName, command, config } JSON on stdin and returns a
JSON array of strings or { value, label?, description? } objects on stdout. Valid rows are
normalized, with at most 200 returned. The run has an eight-second deadline and a 256 KiB stdout
capture ceiling. A parse failure or timeout yields an empty menu; a non-zero exit alone does not
discard otherwise usable stdout.
The working directory is the resolved session directory, otherwise the project directory, otherwise the shared Localix workspace. The script path must be lexically within the mini app's directory, but this path check does not resolve symlink targets. This subprocess has normal local access and runs outside Bash tool approval. Treat it as executable mini app code, rather than a sandboxed lookup.
Declarative handler support
| Handler | Primary mechanism | Boundary |
|---|---|---|
command | Runs the agent's Bash (bash -c; Git Bash on Windows), sends event JSON on stdin, and folds exit code plus output. | Supported on every dispatched event. |
http | POSTs event JSON and folds a successful response like command output. | Unsupported on SessionStart; network and non-2xx failures do not block. |
mcp_tool | Calls a gathered MCP tool with the handler's input as its arguments. | Missing, disconnected, or failed tools do not block; skipped on SessionStart at launch and resume. |
prompt | Uses one auxiliary model call to return an allow/block verdict. | The events listed below; requires a live provider connection. |
agent | Runs a bounded read-only verifier with Read and Search. | The same events except PermissionRequest; needs the agent service and a live provider connection. |
For mcp_tool arguments and prompt and agent verdicts, see Hook handlers.
| Handler | Default timeout | Additional cap |
|---|---|---|
command, http, mcp_tool | 600 seconds | Declared timeouts are capped at 1,800 seconds. |
prompt | 30 seconds | Declared timeouts are capped at 1,800 seconds. |
agent | 60 seconds | Default 50 turns; declared timeouts are capped at 1,800 seconds. |
A prompt or agent hook's $ARGUMENTS is replaced by the event JSON; without it, the JSON is
appended to the prompt. A backslash keeps a dollar sign literal (\$1.00 reads $1.00).
A command handler's ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_DATA}
are expanded by the shell from the child environment, not pasted into the command text, and a
non-secret ${user_config.KEY} becomes ${CLAUDE_PLUGIN_OPTION_<KEY>} with its value in the
environment. A project path or config value is therefore data even when it contains $(…), quotes
or backticks. Quote the placeholder ("${CLAUDE_PROJECT_DIR}") when the value may contain spaces.
Plugin monitor commands follow the same rule. An http handler's URL is not shell text, so its
placeholders are substituted directly.
Stdout counts as JSON output only when it is one JSON object with nothing around it. Text that does
not start with { or end with } is plain text; anything else that fails to parse is a
non-blocking error, applies nothing and never becomes context. On exit 2 a JSON object is still
read, and a deny or block reason in it replaces stderr as the message.
On Stop, SubagentStop, TaskCompleted and a plugin's UserPromptSubmit, exit 2 with empty
stdout and a stderr that says No such file or directory is a non-blocking error, so a hook whose
script is missing does not trap the agent. An http handler's 2xx body must be empty or a JSON
object; plain text is an error.
Command-hook stdout and stderr capture is capped at 64 KiB each. A hook's additionalContext,
systemMessage or plain stdout longer than 10,000 characters is saved to a file in the session's
folder and replaced by that file's path and its first 2,000 characters, as in Claude Code.
A command handler with async: true cannot affect the current event. When it exits 0, the
additionalContext and systemMessage of its JSON response reach the model with the session's next
turn; print the JSON alone on stdout or on a line of its own. asyncRewake can wake an idle session
when that command exits with code 2. That wake is armed when the hook starts and delivered at most
once; a hook that exits any other way, or is lost to an API restart, wakes nobody.
What a blocked prompt shows
A blocked prompt never reaches the model, and the run fails with the hook's reason followed by
Original prompt: and the text you sent. A hook that prints "suppressOriginalPrompt": true inside
hookSpecificOutput leaves the text out, whether it blocks with decision: "block" or by exiting 2
(the JSON on stdout is still read). The flag changes only that message: the prompt stays in your
conversation's transcript. A UserPromptExpansion hook may send it too, as Claude Code's types
allow, but an expansion block never echoed the prompt, so there is nothing to leave out.
Limits
strictPluginOnlyCustomizationwithhooksin managed settings stops thehooksof user, project and local settings and of skills from your folders; the organization's hooks and plugins' keep running. See Use your Claude Code settings.- Declarative hooks enter through an enabled plugin, the invoked skill or agent's run-scoped
frontmatter, and the
hooksof your Claude Code settings files for the conversation's folder:~/.claude/settings.jsonand managed settings always, a project's.claude/settings.jsonand.claude/settings.local.jsononce you trusted the folder in Claude Code.disableAllHooksandallowManagedHooksOnlylimit them, plugins' included, as in Claude Code. - Enabling a hook-bearing plugin or mini app is a code-execution trust decision. Native and command hooks can run local code; HTTP hooks make network requests; MCP, prompt, and agent hooks invoke their named runtimes.
- Unsupported event and handler combinations are skipped during parsing. They do not execute.
- A declarative
PreToolUseallowcannot bypass the Localix permission gate: it lifts only the mode-level questions about how a call is made, and a deny rule, a named ask rule or an irreversible call still stands. Anaskalways asks, and a deny always stops the tool. - Prompt, agent, MCP, HTTP, and command handler failures are non-blocking unless the handler returns a supported blocking verdict. Missing provider or MCP connections therefore fail open.
- A malformed auxiliary-model verdict also allows the event to continue, and says so in a hook error line. Prompt hooks make billed model calls; agent hooks create verifier jobs, with a default of 50 turns rather than a fixed maximum for every declaration.
- MCP hook calls still obey agent capability and permission rules. Calls requiring denial or confirmation do not execute through the hook path; their hook verdict fails open.
- Command hooks spawn directly through the resolved shell, outside Bash tool approval. Timeout or cancellation kills the shell child; it does not undo prior effects or guarantee descendant cleanup.
- Async command handlers are observer-only for the current event. Their decision fields cannot block or rewrite in-flight work.
- Native callbacks other than
onSessionStartisolate failures and use a five-second wait per callback. The timeout stops waiting without cancelling callback work. It cannot interrupt synchronous JavaScript that blocks the API event loop. onSessionStartis fire-and-forget with rejected promises logged. A synchronous throw from the callback occurs before that promise handler and can propagate into run startup./fork,/openand the client commands above require web/desktop. A client without those surfaces omits them from its forwarded server command list.- A plugin, mini app or skill macro sent while a run is active waits in the queue as a whole command — its badge, model override, tool grants and hooks — and runs as its own turn.
/modelwalks the model picker's steps in the composer — provider, model, then optional reasoning, turns and context limit settings — and never prints a model list. See Switch models with/model. Typed as/model <name>, it switches the conversation's model by name.
Related guides
- Hook tool calls: the names and fields a tool call reaches a hook with
- Install Claude Code plugins: what a Claude Code plugin runs in Localix
- Your first session
- Build a mini app
- Skills
- Skill authoring
- Human-in-the-loop controls
Source of truth
- Core slash-command catalog
- Slash parser
- Turn dispatch order
- Plugin and skill command handlers
- Plugin command schema
- Plugin command dispatch
- Workflow commands
- Command Markdown discovery
- Manifest command precedence
- Dynamic option execution
- Native hook contracts
- Native hook dispatch
- Native tool-hook composition and limits
- Native message hooks and bindings
- Native hook wait and isolation
- Session-start dispatch
- Declarative hook contracts
- Declarative hook runtime
- Scoped skill and agent hooks
- One call per file
- Tool rewrite and output folding
- Bounded continuation
- Model-backed evaluators
- MCP hook permission gate
- Command transport and cancellation