Skip to content

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:

~/.config/knosh/commands/

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 .md filename (without extension) in your agents directory. Use list-agents to see available agents. If not provided in the frontmatter, use --agentId when running Knosh to specify the agent to use
description (optional)
A short description shown in --help output and by list-commands.
temperature (optional)
Sampling temperature for this command's LLM calls. Must be in [0.0, 2.0]. The --temperature CLI 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 to 1 (sequential) when absent. The --parallel N CLI option overrides this value. When parallel > 1, the command must declare an outputFile; running parallel > 1 against stdout is rejected at run start.
version (optional)
A Semantic Versioning 2.0.0 string (e.g. 0.2.0). Used by knosh update to 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 by knosh update, simply omit (or remove) the version: line: Knosh treats a missing version: 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
name yes Option name, without the -- prefix. Becomes --<name> on the CLI.
type yes Value type: string, int, float, boolean, choice, or file.
help no Help text shown in --help output.
required no When true, the CLI fails if the option is not supplied. Default: false. Ignored for boolean.
multiple no When true, the option may be supplied multiple times; values are collected as a list. Default: false. Ignored for boolean.
choices when type: choice List of accepted values. Required and must be non-empty when type is choice.
trailingArgsName no Name used as the Mustache context key for trailing positional arguments. Defaults to __extra when absent. Must not conflict with any declared option name.
outputFile no Where to write each iteration's LLM output instead of stdout. Must either match the name of a multiple option exactly, or be a Mustache template (contains {{ }}). The file is overwritten silently if it already exists.
permission no Per-tool access rules. Uses the same syntax as agent permissions: a map from tool name (or external_directory) to allow/deny or a pattern map. See Permissions below.

Type reference:

Type CLI value Notes
string Any text
int Integer Rejects non-integer input
float Decimal number
boolean (flag — no value) Present = true; absent = false. required and multiple are ignored.
choice One of the listed values Requires a choices list
file A 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 --flag is 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.txt
  • Tag foo → b.txt
  • Tag bar → a.txt
  • Tag 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 outputFile to the exact name of a multiple option (without --). Each iteration writes to the path that option supplied for that iteration.
  • Mustache template form — set outputFile to 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:

# Output File: `<resolved path>`

```
<LLM response>
```

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:

knosh kdoc-updater --dry-run --src Foo.kt --src Bar.kt
# 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:

permission:
  text-write: deny
  glob: allow

Path-pattern rules (last matching pattern wins):

permission:
  text-write:
    "src/**": allow
    "*": deny

External directory access (deny wins; relative paths are always allowed):

permission:
  external_directory:
    "~/projects/**": allow
    "*": deny

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

knosh <command-name>

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:

knosh review-pr --agentId faster-agent

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:

cd my-project
knosh review-pr

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:

knosh summarise --file ./README.md --format markdown --verbose

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 agent value 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).