Contributing
Thank you for contributing to KOPI O Agent! This guide covers setting up your dev environment, understanding the codebase, and getting your PR merged.
Contribution Priorities
We value contributions in this order:
- Bug fixes — crashes, incorrect behavior, data loss
- Cross-platform compatibility — macOS, different Linux distros, WSL2
- Security hardening — shell injection, prompt injection, path traversal
- Performance and robustness — retry logic, error handling, graceful degradation
- New skills — broadly useful ones (see Creating Skills)
- New tools — rarely needed; most capabilities should be skills
- Documentation — fixes, clarifications, new examples
Common contribution paths
- Building a custom/local tool without modifying Kopi core? Start with Build a Kopi Plugin
- Building a new built-in core tool for Kopi itself? Start with Adding Tools
- Building a new skill? Start with Creating Skills
- Building a new inference provider? Start with Adding Providers
Development Setup
Prerequisites
| Requirement | Notes |
|---|---|
| Git | With --recurse-submodules support, and the git-lfs extension installed |
| Python 3.11+ | uv will install it if missing |
| uv | Fast Python package manager (install) |
| Node.js 20+ | Optional — needed for browser tools and WhatsApp bridge (matches root package.json engines) |
Clone and Install
git clone --recurse-submodules https://github.com/XingBaoKu/kopi-agent.git
cd kopi-agent
# Create venv with Python 3.11
uv venv venv --python 3.11
export VIRTUAL_ENV="$(pwd)/venv"
# Install with all extras (messaging, cron, CLI menus, dev tools)
uv pip install -e ".[all,dev]"
# tinker-atropos is a git submodule — needs `git submodule update --init` first
# if you didn't clone with `--recurse-submodules`
uv pip install -e "./tinker-atropos"
# Optional: browser tools
npm install
Configure for Development
mkdir -p ~/.kopi/{cron,sessions,logs,memories,skills}
cp cli-config.yaml.example ~/.kopi/config.yaml
touch ~/.kopi/.env
# Add at minimum an LLM provider key:
echo 'OPENROUTER_API_KEY=sk-or-v1-your-key' >> ~/.kopi/.env
Run
# Symlink for global access
mkdir -p ~/.local/bin
ln -sf "$(pwd)/venv/bin/kopi" ~/.local/bin/kopi
# Verify
kopi doctor
kopi chat -q "Hello"
Run Tests
pytest tests/ -v
Code Style
- PEP 8 with practical exceptions (no strict line length enforcement)
- Comments: Only when explaining non-obvious intent, trade-offs, or API quirks
- Error handling: Catch specific exceptions. Use
logger.warning()/logger.error()withexc_info=Truefor unexpected errors - Cross-platform: Never assume Unix (see below)
- Profile-safe paths: Never hardcode
~/.kopi— useget_kopi_home()fromkopi_constantsfor code paths anddisplay_kopi_home()for user-facing messages. See AGENTS.md for full rules.
Cross-Platform Compatibility
Kopi officially supports Linux, macOS, WSL2, and native Windows (early beta — via PowerShell install). Native Windows uses Git Bash (from Git for Windows) for shell commands. A few features require POSIX kernel primitives and are gated: the dashboard's embedded PTY terminal pane (/chat tab) is WSL2-only. The native-Windows path is new and moves fast — if you're doing Windows-heavy dev, expect to hit and fix rough edges.
When contributing code, keep these rules in mind:
- Don't add unguarded
signal.SIGKILLreferences. It's not defined on Windows. Either route throughgateway.status.terminate_pid(pid, force=True)(the centralized primitive that doestaskkill /T /Fon Windows and SIGKILL on POSIX), or fall back withgetattr(signal, "SIGKILL", signal.SIGTERM). - Catch
OSErroralongsideProcessLookupErroronos.kill(pid, 0)probes. Windows raisesOSError(WinError 87, "parameter is incorrect") for an already-gone PID instead ofProcessLookupError. - Don't force the terminal to POSIX semantics.
os.setsid,os.killpg,os.getpgid,os.forkall raise on Windows — gate them withif sys.platform != "win32":orif os.name != "nt":. - Open files with an explicit
encoding="utf-8". The Python default on Windows is the system locale (often cp1252), which mojibakes or crashes on non-Latin text. - Use
pathlib.Path/os.path.join— never manually concat with/. This matters less for strings the OS gives us back and more for strings we construct to hand to subprocesses.
Key patterns:
1. termios and fcntl are Unix-only
Always catch both ImportError and NotImplementedError:
try:
from simple_term_menu import TerminalMenu
menu = TerminalMenu(options)
idx = menu.show()
except (ImportError, NotImplementedError):
# Fallback: numbered menu
for i, opt in enumerate(options):
print(f" {i+1}. {opt}")
idx = int(input("Choice: ")) - 1
2. File encoding
Some environments may save .env files in non-UTF-8 encodings:
try:
load_dotenv(env_path)
except UnicodeDecodeError:
load_dotenv(env_path, encoding="latin-1")
3. Process management
os.setsid(), os.killpg(), and signal handling differ across platforms:
import platform
if platform.system() != "Windows":
kwargs["preexec_fn"] = os.setsid
4. Path separators
Use pathlib.Path instead of string concatenation with /.
Security Considerations
Kopi has terminal access. Security matters.
Existing Protections
| Layer | Implementation |
|---|---|
| Sudo password piping | Uses shlex.quote() to prevent shell injection |
| Dangerous command detection | Regex patterns in tools/approval.py with user approval flow |
| Cron prompt injection | Scanner blocks instruction-override patterns |
| Write deny list | Protected paths resolved via os.path.realpath() to prevent symlink bypass |
| Skills guard | Security scanner for hub-installed skills |
| Code execution sandbox | Child process runs with API keys stripped |
| Container hardening | Docker: all capabilities dropped, no privilege escalation, PID limits |
Contributing Security-Sensitive Code
- Always use
shlex.quote()when interpolating user input into shell commands - Resolve symlinks with
os.path.realpath()before access control checks - Don't log secrets
- Catch broad exceptions around tool execution
- Test on all platforms if your change touches file paths or processes
Pull Request Process
Branch Naming
fix/description # Bug fixes
feat/description # New features
docs/description # Documentation
test/description # Tests
refactor/description # Code restructuring
Before Submitting
- Run tests:
pytest tests/ -v - Test manually: Run
kopiand exercise the code path you changed - Check cross-platform impact: Consider macOS and different Linux distros
- Keep PRs focused: One logical change per PR
PR Description
Include:
- What changed and why
- How to test it
- What platforms you tested on
- Reference any related issues
Commit Messages
We use Conventional Commits:
<type>(<scope>): <description>
| Type | Use for |
|---|---|
fix | Bug fixes |
feat | New features |
docs | Documentation |
test | Tests |
refactor | Code restructuring |
chore | Build, CI, dependency updates |
Scopes: cli, gateway, tools, skills, agent, install, whatsapp, security
Examples:
fix(cli): prevent crash in save_config_value when model is a string
feat(gateway): add WhatsApp multi-user session isolation
fix(security): prevent shell injection in sudo password piping
Reporting Issues
- Use GitHub Issues
- Include: OS, Python version, Kopi version (
kopi version), full error traceback - Include steps to reproduce
- Check existing issues before creating duplicates
- For security vulnerabilities, please report privately
Community
- Discord: discord.gg/XingBaoKu
- GitHub Discussions: For design proposals and architecture discussions
- Skills Hub: Upload specialized skills and share with the community
License
By contributing, you agree that your contributions will be licensed under the MIT License.