- Go 79.6%
- Nix 20.4%
| nix | ||
| .gitignore | ||
| alejandra.toml | ||
| flake.lock | ||
| flake.nix | ||
| go.mod | ||
| go.sum | ||
| main.go | ||
| README.md | ||
osai
osai is "one shot ai": a small Go command-line utility for sending either a custom prompt or a named prompt script to an OpenAI-compatible chat completions endpoint.
It is designed around a simple XDG-based layout:
- config lives at
$XDG_CONFIG_HOME/osai/config.toml - prompt scripts live at
$XDG_CONFIG_HOME/osai/prompts/*.sh
If you run osai <name>, osai looks for a script called $XDG_CONFIG_HOME/osai/prompts/<name>.sh. If it exists, the script is executed and its stdout becomes the final prompt. If no matching script exists, the CLI treats the remaining arguments as a custom prompt and sends them directly.
Features
- simple CLI interface
- OpenAI-compatible chat completions support via
openai-go - XDG config discovery through
os.UserConfigDir() - prompt scripts for reusable prompts
- optional prompt script model hints
-mmodel override per invocation- Nix package output
- Home Manager module for declarative installation and configuration
Requirements
- an OpenAI-compatible API endpoint that supports
POST /chat/completions - a valid API key for that endpoint
- a model name accepted by that endpoint
For this project, the configured openai-endpoint should be the API base URL, for example:
https://api.openai.com/v1https://chat.example.com/apihttp://localhost:1234/v1
osai appends chat/completions through the client library.
Installation
Go
Build directly:
go build .
Run without installing:
go run . hello world
Nix
Build the package from this flake:
nix build .#default
The flake exports:
packages.x86_64-linux.defaultpackages.x86_64-linux.osai
Configuration
Create config.toml at:
$XDG_CONFIG_HOME/osai/config.toml
Example:
openai-endpoint = "https://api.openai.com/v1"
openai-key = "sk-..."
openai-default-model = "gpt-5.2"
Secret-file example:
openai-endpoint = "https://api.openai.com/v1"
openai-key-file = "/run/secrets/osai-openai-key"
openai-default-model = "gpt-5.2"
Fields:
openai-endpoint: base URL of the OpenAI-compatible APIopenai-key: bearer token used for the requestopenai-key-file: path to a file containing the bearer tokenopenai-default-model: default model used unless-mor a prompt script model hint is used
Exactly one of openai-key or openai-key-file must be set.
If openai-key-file is used, osai reads the file contents and trims surrounding whitespace. This makes it suitable for secret-management tools that write newline-terminated files, such as sops-nix.
Usage
Custom prompt
If no matching script exists, all remaining arguments are joined into a single prompt:
osai explain this shell pipeline
Equivalent prompt sent to the model:
explain this shell pipeline
Override the model
Use -m to override openai-default-model for a single call:
osai -m gpt-4.1 write a commit message for these changes
For named prompt scripts, -m also overrides any model hint in the script.
Named prompt script
If this file exists:
$XDG_CONFIG_HOME/osai/prompts/explain.sh
then:
osai explain
will execute that script and use its stdout as the prompt.
Additional CLI arguments are passed to the script:
osai explain main.go
Prompt scripts
Prompt scripts are plain executable files, typically shell scripts ending in .sh.
The script contract is intentionally small:
- the script is executed directly
- an optional
# osai-model: <model>comment in the leading comment block selects the model for that script - stdout is captured
- trimmed stdout becomes the final prompt
- stderr is preserved for error reporting
- a non-zero exit code causes
osaito fail
Model selection precedence is:
-m# osai-model: <model>in a named prompt scriptopenai-default-model
Example script:
#!/usr/bin/env sh
# osai-model: qwen2.5-coder:32b
file="${1:-main.go}"
cat <<EOF
Explain the following file briefly and point out the most important behavior:
$file
EOF
Make it executable:
chmod +x "$XDG_CONFIG_HOME/osai/prompts/explain.sh"
Error handling
osai exits with a non-zero status and prints an error to stderr when:
- the config file cannot be found or parsed
- a required config field is missing
- both
openai-keyandopenai-key-fileare set openai-key-filecannot be read or is empty- the resolved prompt is empty
- a prompt script fails or prints nothing
- the API request fails
- the API response contains no usable completion text
Endpoint compatibility note
osai uses github.com/openai/openai-go/v3. Some reverse proxies and Cloudflare setups block the default openai-go User-Agent and X-Stainless-* headers. This project strips those headers before sending requests because some OpenAI-compatible gateways reject them even when the same request succeeds with curl.
Home Manager module
This flake exports a Home Manager module under:
homeModules.defaulthomeModules.osai
The module provides:
programs.osai.enableprograms.osai.packageprograms.osai.settingsprograms.osai.scripts
When enabled, it:
- installs the
osaipackage - writes
~/.config/osai/config.toml - links the configured scripts into
~/.config/osai/prompts/
Home Manager example
{
imports = [ inputs.osai.homeModules.default ];
programs.osai = {
enable = true;
settings = {
"openai-endpoint" = "https://chat.cumsek.com/api";
"openai-key-file" = "/run/secrets/osai-openai-key";
"openai-default-model" = "gpt-oss:20b";
};
scripts = [
./prompts/explain.sh
./prompts/commit-message.sh
];
};
}
Home Manager option details
programs.osai.enable
Enables the package and XDG config generation.
programs.osai.package
Overrides the package to install. By default this uses the flake's own package:
self.packages.${pkgs.system}.default
programs.osai.settings
An attribute set rendered to TOML and written to osai/config.toml.
programs.osai.scripts
A list of Nix paths. Each path is linked into osai/prompts/ using its basename.
For example:
scripts = [
./prompts/explain.sh
./prompts/review.sh
];
produces:
~/.config/osai/prompts/explain.sh~/.config/osai/prompts/review.sh
That means the CLI command uses the basename without the .sh suffix:
osai explain
osai review
Development
Enter the dev shell:
nix develop
Build with Go:
go build ./...
Run locally:
go run . hello
Repository layout
.
├── flake.nix
├── go.mod
├── go.sum
├── main.go
└── nix
└── home-manager.nix
Current behavior summary
- Read config from the XDG config directory.
- Check whether the first CLI argument matches a prompt script.
- If it does, read any leading
# osai-model:hint, execute the script, and capture stdout as the prompt. - If it does not, join the CLI args into a custom prompt.
- Use
-m, the script model hint, or the configured default model, in that order. - Send the prompt to the configured OpenAI-compatible endpoint.
- Print the first completion message to stdout.