File-Defined Commands¶
Add new Knosh subcommands by dropping Markdown files into your commands directory — no code required.
Overview¶
A file-defined command is a Markdown file with YAML frontmatter that lives in the commands directory (default: ~/.config/knosh/commands/). When Knosh starts, it loads every .md file in that directory and registers each one as a new subcommand. The file's body becomes the prompt sent to the agent.
Knosh ships with pre-packaged file-defined commands installed by knosh init --global. These bundled commands are installed into ~/.config/knosh/commands/ automatically, and you can create your own commands in the same directory to extend Knosh's functionality.
File Location¶
By default, command files live in:
You can change this by setting commandsDir in your knosh.json config file.
Run knosh init --global to create both the config file and the commands directory in one step.
File Format¶
Each command file is a Markdown file with a YAML frontmatter block:
---
agent: my-agent
description: A brief description of what this command does
---
The body of this file is the prompt sent to the agent.
You can write whatever instructions you need here.
Frontmatter Keys¶
agent(optional)- The agent ID to use for this command. Must match a
.mdfilename (without extension) in your agents directory. Uselist-agentsto see available agents. If not provided in the frontmatter, use--agentIdwhen running Knosh to specify the agent to use description(optional)- A short description shown in
--helpoutput and bylist-commands. temperature(optional)- Sampling temperature for this command's LLM calls. Must be in
[0.0, 2.0]. The--temperatureCLI option overrides this value; if neither is supplied, the agent configuration's temperature is used. parallel(optional)- Maximum number of concurrent iterations across the Cartesian product of
multiple-option values. Must be in[1, 32]. Defaults to1(sequential) when absent. The--parallel NCLI option overrides this value. Whenparallel > 1, the command must declare anoutputFile; runningparallel > 1against stdout is rejected at run start. version(optional)- A Semantic Versioning 2.0.0 string (e.g.
0.2.0). Used byknosh updateto decide when to replace an on-disk command file with a newer bundled copy. Knosh tolerates absent or malformed values at load time — the field never blocks command loading. To opt out of automatic replacement byknosh update, simply omit (or remove) theversion:line: Knosh treats a missingversion:as a signal that the user has modified the file and leaves it alone. options(optional)-
A sequence of option definitions. Each option is a YAML mapping with the following keys:
Key Required Description nameyes Option name, without the --prefix. Becomes--<name>on the CLI.typeyes Value type: string,int,float,boolean,choice, orfile.helpno Help text shown in --helpoutput.requiredno When true, the CLI fails if the option is not supplied. Default:false. Ignored forboolean.multipleno When true, the option may be supplied multiple times; values are collected as a list. Default:false. Ignored forboolean.choiceswhen type: choiceList of accepted values. Required and must be non-empty when typeischoice.trailingArgsNameno Name used as the Mustache context key for trailing positional arguments. Defaults to __extrawhen absent. Must not conflict with any declared option name.outputFileno Where to write each iteration's LLM output instead of stdout. Must either match the name of a multipleoption exactly, or be a Mustache template (contains{{ }}). The file is overwritten silently if it already exists.permissionno Per-tool access rules. Uses the same syntax as agent permissions: a map from tool name (or external_directory) toallow/denyor a pattern map. See Permissions below.Type reference:
Type CLI value Notes stringAny text intInteger Rejects non-integer input floatDecimal number boolean(flag — no value) Present = true; absent =false.requiredandmultipleare ignored.choiceOne of the listed values Requires a choiceslistfileA filesystem path The parent directory must exist; the file itself need not
Prompt Body as Mustache Template¶
The body of a command file is a Mustache template. When the command runs, Knosh injects each scalar (non-multiple) option value into the template context using the option name as the key.
- String, int, float, choice, file —
{{ optionName }}is replaced with the supplied value. If an optional option is absent, the tag renders as an empty string. - Boolean flags — use
{{#flag}}…{{/flag}}to include a section only when--flagis passed, and{{^flag}}…{{/flag}}for the inverse. The{{ flag }}interpolation tag always renders empty.
Example prompt body:
Summarise `{{ file }}` in {{ format }} format.
{{#verbose}}Include a section-by-section breakdown.{{/verbose}}
{{#max-words}}Keep the summary within {{ max-words }} words.{{/max-words}}
Iteration Across Multiple-Option Values¶
When a command declares one or more options with multiple: true, Knosh renders the prompt body once for each element of the Cartesian product of all multiple-option value lists. Each iteration's value is bound into the template context under the option name, so {{ optionName }} resolves to that iteration's value.
Trailing positional arguments (everything after the named options) participate in the product as a multiple dimension. By default the key is __extra; set trailingArgsName in the frontmatter to use a different key. The name __extra remains reserved and cannot be declared as a regular option name regardless of whether trailingArgsName is set.
An empty multiple-option value list (none supplied on the command line) is dropped from the product instead of zeroing it out, so a command with no multiple values supplied still renders exactly once and the corresponding {{ name }} tag renders as an empty string.
Each rendered prompt is sent to the agent sequentially. Responses are printed to stdout in iteration order, separated by a blank line.
For example, given:
---
agent: summariser
options:
- name: tag
type: string
multiple: true
- name: output
type: file
multiple: true
---
Tag `{{ tag }}` → `{{ output }}`
invoking knosh <cmd> --tag foo --tag bar --output a.txt --output b.txt renders four prompts in this order:
Tag foo → a.txtTag foo → b.txtTag bar → a.txtTag bar → b.txt
Output File¶
Set outputFile in the frontmatter to direct each iteration's LLM response to a file instead of stdout:
- Option-name form — set
outputFileto the exact name of amultipleoption (without--). Each iteration writes to the path that option supplied for that iteration. - Mustache template form — set
outputFileto a Mustache template (any value containing{{ }}). The template is rendered with the same context used for the prompt; the result is used as the file path.
If the file already exists it is overwritten silently. No output is printed to stdout for iterations that write to a file.
Example:
---
agent: kdoc-updater
outputFile: src
options:
- name: src
type: file
multiple: true
---
Update the KDoc comments in `{{ src }}`. Return the entire file with updated comments only.
Invoking knosh <cmd> --src Foo.kt --src Bar.kt sends two prompts and writes each response back to Foo.kt and Bar.kt respectively.
Dry Run¶
Pass --dry-run to preview each iteration's output without writing any files. This flag only affects iterations where outputFile is set; iterations that would print to stdout are unaffected.
Each iteration's output is printed to stdout as a Markdown level-1 header followed by the response in a fenced code block:
Multiple iterations each produce their own header + code block, separated by a blank line.
Example: given the kdoc-updater command from the Output File section, running with --dry-run prints each response to stdout instead of overwriting the source files:
# Output File: `Foo.kt`
```
// updated KDoc content for Foo.kt
```
# Output File: `Bar.kt`
```
// updated KDoc content for Bar.kt
```
Permissions¶
Set permission: in the frontmatter to restrict which tools the LLM may use for this command and which filesystem paths it may access. The syntax is identical to agent permissions.
Both the agent's permissions and the command's permissions must allow a tool invocation for it to proceed. Use command permissions to enforce stricter limits for a specific command without loosening the agent's global settings.
Tool-level allow/deny:
Path-pattern rules (last matching pattern wins):
External directory access (deny wins; relative paths are always allowed):
Tool names not listed in permission: are unrestricted at the command level (the agent's own permissions still apply). Command permissions are enforced automatically — no CLI flag is needed.
Command Name¶
The subcommand name is derived from the filename: the .md extension is stripped. For example, review-pr.md becomes the review-pr subcommand.
Command names must not conflict with Knosh's built-in commands (init, kdocs, list-agents, list-commands, list-tools, prompt, update). Files with conflicting names cause a hard error at startup.
Running a File-Defined Command¶
All of the common options apply: --agentId, --max-iterations, --temperature, --parallel N, retry flags, and logging flags.
Use --dry-run to preview what would be written to output files without touching the filesystem (see Dry Run below).
Command permissions declared in the permission: frontmatter key are enforced automatically alongside the agent's own permissions.
Overriding the Agent¶
The agent frontmatter value is the default, if supplied. Override it at run time with --agentId:
Example¶
Create ~/.config/knosh/commands/review-pr.md:
---
agent: code-reviewer
description: Review the staged diff and suggest improvements
---
Review the staged changes in this git repository.
1. Use the grep and glob tools to understand the codebase structure.
2. Read the staged diff using the shell tool.
3. Identify potential bugs, style issues, and improvements.
4. Summarise your findings, grouped by severity.
Then run:
Example with Options¶
Create ~/.config/knosh/commands/summarise.md:
---
agent: summariser
description: Summarise a file in the chosen format
options:
- name: file
help: Path to the file to summarise
type: file
required: true
- name: format
help: Output format
type: choice
choices: [markdown, plain, json]
- name: max-words
help: Maximum words in the summary
type: int
- name: verbose
help: Include section-by-section breakdown
type: boolean
---
Summarise `{{ file }}` in {{ format }} format.
{{#verbose}}Include a section-by-section breakdown.{{/verbose}}
{{#max-words}}Keep the summary within {{ max-words }} words.{{/max-words}}
Then run:
Failure Modes¶
Knosh validates all command files at startup, before any command runs. Any of the following will cause the CLI to exit with an error message and a non-zero exit code:
- Malformed frontmatter — the YAML block cannot be parsed or required keys are missing.
- Missing agent — the
agentvalue does not match any file in the agents directory. - Duplicate name — the filename conflicts with a built-in command or another command file.
- Unreadable file — the file cannot be read (permissions, I/O error).
Tip
Use list-commands to verify that your command files loaded correctly.
Pre-Packaged Commands¶
Knosh comes with some file-defined commands pre-packaged in the app. They will be installed in your environment when you use knosh init --global. Or, use knosh update.
knosh update will install missing pre-packaged commands and replace existing ones if Knosh has newer versions.
Version comparisons are based on the version frontmatter property. If you wish to revise one of Knosh's own commands, you can remove the version property, in which case knosh update will not replace your local copy of the command (and hence will not overwrite any of your changes).