Automation: Custom Subcommands
Lectic’s CLI is extensible through “git-style” custom subcommands. If you create an executable named lectic-<command>, or lectic-<command>.<file-extension> and place it in your configuration directory, data directory, or PATH, you can invoke it as lectic <command>.
This allows you to wrap common workflows, build project-specific tools, and create shortcuts for complex Lectic invocations.
How It Works
When you run lectic foo args..., Lectic searches for an executable named lectic-foo or lectic-foo.*.
By default, search order is:
- Configuration Directory:
$LECTIC_CONFIG(defaults to~/.config/lecticon Linux). Searched recursively. - Data Directory:
$LECTIC_DATA(defaults to~/.local/share/lecticon Linux). Searched recursively. - System PATH: Any directory in your
$PATH. Top-level per PATH entry.
If LECTIC_RUNTIME is set, Lectic adds those directories to recursive subcommand discovery (before $LECTIC_CONFIG and $LECTIC_DATA). LECTIC_RUNTIME is a PATH-style directory list (for example, /opt/lectic/plugins:$HOME/work/lectic-extra on Linux/macOS).
The first match found is executed. The subprocess receives the remaining arguments, inherits the standard input, output, and error streams, and has access to Lectic’s environment variables.
Examples
Bash Script
Create a file named lectic-hello in ~/.config/lectic/:
#!/bin/bash
echo "Hello from a custom subcommand!"
echo "My config dir is: $LECTIC_CONFIG"Make it executable: chmod +x ~/.config/lectic/lectic-hello
Run it:
lectic helloJavaScript/TypeScript via lectic script
Lectic bundles a Bun runtime, so you can write subcommands in JavaScript, TypeScript, JSX, or TSX without installing anything extra.
lectic script works well as a shebang interpreter, and it bundles your script before executing it. This lets you use explicit remote https://... imports without a local node_modules (and http:// is allowed only for localhost).
For TSX/JSX scripts using React’s automatic runtime, include an explicit import React from "https://..." so Lectic can resolve the implicit react/jsx-runtime / react/jsx-dev-runtime imports.
Bundled output is cached on disk under $LECTIC_CACHE/scripts/. The cache key includes the script contents, the Bun version, and an internal plugin version. Delete that directory to force a re-bundle.
Note: remote imports are treated as pinned by URL. Changes on the remote server will not invalidate the cache automatically.
Prefer versioned URLs so builds are reproducible. For example, when using esm.sh, import react@18.3.1 instead of react:
import React from "https://esm.sh/react@18.3.1"
Create ~/.config/lectic/lectic-calc:
#!/usr/bin/env -S lectic script
const args = process.argv.slice(2);
if (args.length === 0) {
console.error("Usage: lectic calc <expression>");
process.exit(1);
}
// Access standard Lectic environment variables
const configDir = process.env.LECTIC_CONFIG;
try {
console.log(eval(args.join(" ")));
} catch (e) {
console.error("Error:", e.message);
}Make it executable and run:
lectic calc 1 + 2lectic script?
You get Bun’s full capabilities without installing Bun separately. This includes built-in YAML parsing, HTTP servers, SQLite, fetch, and much more. See Bun’s documentation for what’s available.
Because Lectic bundles your script first, you can also write self-contained TSX/JSX scripts that pull dependencies from explicit https://... imports.
This is especially useful for writing more complex subcommands that would be awkward in Bash.
Environment Variables
Subcommands receive the standard set of Lectic environment variables:
LECTIC_CONFIG: Path to the configuration directory.LECTIC_DATA: Path to the data directory.LECTIC_CACHE: Path to the cache directory.LECTIC_STATE: Path to the state directory.LECTIC_TEMP: Path to the temporary directory.LECTIC_RUNTIME: Optional PATH-style directory list used for recursive subcommand discovery. If set, entries are searched before the default recursive roots (LECTIC_CONFIGandLECTIC_DATA).
These ensure your subcommands respect the user’s directory configuration.
Tab Completion
You can add tab completion for your custom subcommands. The completion system supports plugging in custom completion functions.
Installation
First, ensure you have enabled tab completion by sourcing the completion script in your shell configuration (e.g., ~/.bashrc):
source /path/to/lectic/extra/tab_complete/lectic_completion.bash(The path depends on how you installed Lectic. If you installed via Nix or an AppImage, you may need to locate this file in the repository or extract it.)
Adding Completions
To provide completions for a subcommand lectic-foo, create a bash script that defines a completion function and registers it.
The script can be placed in: 1. <recursive-root>/completions/ where recursive roots are, in order: - $LECTIC_RUNTIME entries (if set), then - $LECTIC_CONFIG and $LECTIC_DATA 2. Or alongside the executable itself, named lectic-foo.completion.bash.
Example:
Create $LECTIC_CONFIG/completions/foo.bash:
_lectic_complete_foo() {
local cur
cur="${COMP_WORDS[COMP_CWORD]}"
# Suggest 'bar' and 'baz'
COMPREPLY=( $(compgen -W "bar baz" -- "${cur}") )
}
# Register the function for the 'foo' subcommand
lectic_register_completion foo _lectic_complete_fooNow, typing lectic foo <TAB> will suggest bar and baz.
For performance, define completions in a separate .completion.bash file rather than inside the subcommand script itself. This allows the shell to load completions without executing the subcommand.
The completion loader mirrors runtime discovery: it scans recursive roots ($LECTIC_RUNTIME entries first when set, then $LECTIC_CONFIG and $LECTIC_DATA) and loads adjacent completion files from discovered commands.