๐Ÿค” Design Decisions

This document explains why OpsTerm is designed the way it is โ€” the reasoning behind each technical decision, including the trade-offs that were made.


๐Ÿ“œ Decision Summary

# Decision Choice Rejected Alternatives
1 Language Python 3 Go, Rust, Node.js, Bash
2 Architecture Single-file CLI Modular package, Client-server
3 Dependencies Zero (stdlib only) PyYAML, requests, click
4 Config format YAML (custom parser) JSON, TOML, INI
5 AI Protocol OpenAI-compatible API LangChain, custom gRPC
6 SSH Method Subprocess + system SSH paramiko, asyncssh
7 State File-based (no daemon) SQLite daemon, Redis
8 Vault Optional cryptography Hardcoded key, no encryption
9 Completion Generated script Manual completion
10 Shell Integration Separate Zsh plugin Hook shell, LD_PRELOAD

1๏ธโƒฃ Why Python, not Go/Rust?

Context

OpsTerm needed a language that is portable, zero-dependency, and easy to maintain.

Decision: โœ… Python 3

Reasons:

  1. Ubiquitous โ€” Every Linux and macOS system has Python 3. Users just download the script and run it. With Go/Rust, you'd need to compile first or download a binary.

  2. Rich stdlib โ€” argparse, json, sqlite3, urllib, hashlib, subprocess โ€” all built-in. In Go/Rust, these libraries require external imports or have less complete standard libraries.

  3. Maintainability โ€” Python is easier to read and modify. Potential contributors can understand the code more easily.

  4. File size โ€” ~50KB Python script vs ~10MB Go binary. Small and easy to copy to any server.

  5. Rapid iteration โ€” New features can be used immediately without compilation. Edit โ†’ save โ†’ run.

Trade-offs:
- Lower performance (but for a CLI tool, this is imperceptible)
- Requires Python interpreter (but every modern system already has one)


2๏ธโƒฃ Why single file, not a package?

Context

OpsTerm could have been built as a Python package installable via pip install.

Decision: โœ… Single file (bin/ai)

Reasons:

  1. Portable โ€” Copy one file to any server and it runs. scp bin/ai server:~/.local/bin/

  2. Zero setup โ€” No pip install, no python -m opsterm. Just ./ai.

  3. Transparent โ€” The entire code can be read and understood. No __pycache__, egg-info, etc.

  4. Easy debugging โ€” If there's an error, just edit a specific line. No need to hunt through directories.

Trade-offs:
- Large file (~1500 lines). Still small compared to other applications.
- All functions in one file โ€” harder to unit test.
- Mitigation: code is organized with section header comments.


3๏ธโƒฃ Why zero dependencies?

Context

Many Python CLI tools use PyYAML, requests, click, etc.

Decision: โœ… Zero external dependencies

Reasons:

  1. No install step โ€” Clone repo โ†’ run directly. No pip install -r requirements.txt needed.

  2. No version conflicts โ€” Will never clash with system libraries or other projects.

  3. Works in any Python environment โ€” Virtual env, system Python, container โ€” all work.

  4. Easy to audit โ€” All running code can be read. No hidden dependencies.

What was sacrificed:


4๏ธโƒฃ Why YAML, not JSON/TOML?

Context

Config format for servers, workflows, and AI settings.

Decision: โœ… YAML (with custom parser)

Reasons:

  1. Readability โ€” YAML is more human-readable than JSON. Comments and clean indentation.

  2. User-friendly โ€” OpsTerm's target users are DevOps engineers who already use YAML (Docker Compose, Kubernetes, Ansible, GitHub Actions).

  3. No brackets โ€” JSON is full of {} and [] that hurt readability for long configs.

Trade-offs:
- Custom parser โ†’ limited. But the YAML subset we use (mapping, list, scalar) is stable and sufficient.
- Inconsistency with "zero dep" โ€” technically YAML parsing is custom code, not an external dependency.


5๏ธโƒฃ Why OpenAI-compatible API, not an AI library?

Context

Could have used LangChain, LiteLLM, or another AI abstraction library.

Decision: โœ… Direct HTTP call (OpenAI-compatible)

Reasons:

  1. Zero dependency โ€” Only urllib (stdlib). LangChain requires 20+ dependencies.

  2. Provider agnostic โ€” The OpenAI request/response format has become the de-facto standard. DeepSeek, Ollama, vLLM, OpenRouter โ€” all support it.

  3. Simple โ€” The Chat Completion API is just one endpoint with a simple format.

  4. Debug friendly โ€” Requests/responses can be logged, inspected, and tested with curl.

Trade-offs:
- No built-in retry logic, streaming, or tool calling.
- But for a blocking CLI tool, this doesn't matter.


6๏ธโƒฃ Why system SSH, not a Python library?

Context

Could have used paramiko (a Python SSH client library).

Decision: โœ… System SSH (subprocess + /usr/bin/ssh)

Reasons:

  1. Feature parity โ€” System SSH has every feature: key management, ProxyJump, agent forwarding, compression, etc.

  2. Zero dependency โ€” paramiko requires bcrypt, cryptography, pynacl, etc.

  3. Familiar โ€” All SSH configuration (known_hosts, config, agent) works automatically.

  4. Interactive support โ€” os.execvp() can provide a full interactive SSH session. paramiko can't do this seamlessly.

  5. Already configured โ€” Users already have SSH keys set up on their system.

Trade-offs:
- Cannot parse SSH output in real-time.
- Limited error handling (exit code only).


7๏ธโƒฃ Why file-based state, not a daemon?

Context

Some tools use a background daemon that stores state in memory.

Decision: โœ… File-based (no daemon)

Reasons:

  1. Zero resource โ€” No background process. No RAM/CPU consumed when not in use.

  2. Crash-proof โ€” If the terminal is killed, data isn't lost. Everything is in files.

  3. Transparent โ€” Users can open the config directly and edit with a text editor.

  4. Simple โ€” No need to manage process lifecycle, signal handling, or lock files.

Trade-offs:
- State is read from disk on every command โ†’ slight overhead (<10ms).
- No "real-time" notification capability.


8๏ธโƒฃ Why optional vault?

Context

Vault requires cryptography which is not in stdlib.

Reasons:

  1. Zero dep still applies โ€” OpsTerm's core features work without the vault.

  2. Security remains a priority โ€” Vault uses AES via cryptography. Fallback uses XOR + HMAC when cryptography is unavailable.

  3. Progressive enhancement โ€” Users can start without the vault and install cryptography later if needed.

Trade-offs:
- Two code paths to maintain (with and without cryptography).
- Fallback encryption is weaker than AES.


9๏ธโƒฃ Why generated completion?

Context

Completion could be written manually or generated.

Decision: โœ… Generated (embedded in script)

Reasons:

  1. Always up-to-date โ€” When new subcommands are added, completion automatically updates because it's generated from the code.

  2. Zero additional files โ€” ai completion bash prints directly to stdout. No separate file needed.

  3. Works out of the box โ€” source <(ai completion bash) โ€” one command, done.

Trade-offs:
- Requires the ai script to be installed before completion works.
- Generator logic adds ~50 lines to the script.


๐Ÿ”Ÿ Why a separate Zsh plugin?

Context

Shell integration could be part of the main script or a separate file.

Decision: โœ… Separate file (zsh/opsterm.plugin.zsh)

Reasons:

  1. Different language โ€” Zsh plugins use Zsh script, not Python. Combining them in the Python file would be messy.

  2. Zsh-only โ€” Shell hook features (preexec, precmd) only exist in Zsh.

  3. Lazy loading โ€” The plugin is only loaded if the user sources it in .zshrc. Doesn't add weight otherwise.

  4. Familiar pattern โ€” Zsh plugin format is already standard (oh-my-zsh, antigen, zplug).

Trade-offs:
- Only supports Zsh out of the box (no Bash/Fish support yet).
- Users must manually source the plugin in their shell config.


๐Ÿ’ก Suggestions for Improvement

Based on the design decisions above, some future considerations:

  1. Python โ†’ Rust (future) โ€” If performance and distribution become issues, rewriting in Rust would provide a single binary.

  2. YAML โ†’ TOML โ€” If stricter configs with type safety are needed, TOML would be a better fit. However, this would require an external library.

  3. Add plugin system โ€” Allow the community to add features without editing the core script.

  4. Support Fish shell โ€” Popular among modern developers.


๐Ÿ”— References