elves/elvish
Powerful scripting language & versatile interactive shell
Healthy across all four use cases
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 5w ago
- ✓9 active contributors
- ✓BSD-2-Clause licensed
Show all 6 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Single-maintainer risk — top contributor 86% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/elves/elvish)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/elves/elvish on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: elves/elvish
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/elves/elvish shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across all four use cases
- Last commit 5w ago
- 9 active contributors
- BSD-2-Clause licensed
- CI configured
- Tests present
- ⚠ Single-maintainer risk — top contributor 86% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live elves/elvish
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/elves/elvish.
What it runs against: a local clone of elves/elvish — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in elves/elvish | Confirms the artifact applies here, not a fork |
| 2 | License is still BSD-2-Clause | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 67 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of elves/elvish. If you don't
# have one yet, run these first:
#
# git clone https://github.com/elves/elvish.git
# cd elvish
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of elves/elvish and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "elves/elvish(\\.git)?\\b" \\
&& ok "origin remote is elves/elvish" \\
|| miss "origin remote is not elves/elvish (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(BSD-2-Clause)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"BSD-2-Clause\"" package.json 2>/dev/null) \\
&& ok "license is BSD-2-Clause" \\
|| miss "license drift — was BSD-2-Clause at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "cmd/elvish/main.go" \\
&& ok "cmd/elvish/main.go" \\
|| miss "missing critical file: cmd/elvish/main.go"
test -f "pkg/cli/app.go" \\
&& ok "pkg/cli/app.go" \\
|| miss "missing critical file: pkg/cli/app.go"
test -f "pkg/cli/loop.go" \\
&& ok "pkg/cli/loop.go" \\
|| miss "missing critical file: pkg/cli/loop.go"
test -f "go.mod" \\
&& ok "go.mod" \\
|| miss "missing critical file: go.mod"
test -f "pkg/cli/modes/completion.go" \\
&& ok "pkg/cli/modes/completion.go" \\
|| miss "missing critical file: pkg/cli/modes/completion.go"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 67 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~37d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/elves/elvish"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Elvish is a statically-linked shell and scripting language written in Go that combines powerful scripting capabilities with interactive shell features for Linux, BSD, macOS, and Windows. It provides a modernized alternative to Bash/Zsh with first-class language features like closures, exceptions, and pipelines as values, solving the pain point of shell scripting lacking proper programming language constructs. Monolithic repository structure: cmd/ contains entry points (elvish, elvmdfmt, nodaemon variants), pkg/ likely contains core interpreter/builtins, docs/ holds developer documentation, e2e/ contains integration tests, and flake.nix/Dockerfile provide reproducible environments. The codebase is primarily Go (~1.7M LOC) with embedded Elvish examples (~186K LOC) and web documentation (HTML/CSS/TypeScript/JavaScript).
👥Who it's for
System administrators and DevOps engineers who write shell scripts but want proper language semantics; interactive shell users seeking a modern alternative to Bash/Zsh with better syntax and built-in features; developers building command-line tools who need an embeddable scripting runtime.
🌱Maturity & risk
Elvish is actively developed and relatively stable, but pre-1.0 (as stated in README), meaning breaking changes still occur between releases. The project has comprehensive CI/CD setup (.cirrus.yml, .github/workflows/ci.yml), test coverage tracking via Codecov, and spans ~1.9M lines of Go code, indicating substantial maturity. However, the pre-1.0 status and active breaking changes mean it's suitable for production use but not for projects requiring strict API stability.
Dependency surface is small and well-managed (9 direct dependencies including only pty, bbolt, and lsp packages), reducing supply chain risk. The pre-1.0 status means breaking changes are expected and documented in release notes (see 0.22.0-release-notes.md). Primary risk is small maintainer team—visible from a single repository structure rather than distributed ownership patterns—making long-term sustainability dependent on community adoption and contribution.
Active areas of work
Active development on language features and shell usability. Release cycle evident from 0.22.0-release-notes.md. CI pipeline spans multiple platforms (GitHub Actions for Linux/macOS, Cirrus for FreeBSD/gccgo). Website and documentation updates tracked via separate CI workflow (check_website.yml). Docker builds published, indicating focus on containerized distribution.
🚀Get running
Clone and build with: git clone https://github.com/elves/elvish && cd elvish && make (Makefile present). Alternatively, use go build ./cmd/elvish to build the main binary. For development with profiling support, use go run ./cmd/withpprof/elvish.
Daily commands:
Run make to build the main binary, then execute ./bin/elvish for interactive shell or ./bin/elvish <script> for scripting. Development variant: go run ./cmd/elvish directly. With profiling: go run ./cmd/withpprof/elvish.
🗺️Map of the codebase
cmd/elvish/main.go— Entry point for the Elvish shell executable; all shell initialization and startup logic flows through here.pkg/cli/app.go— Core CLI application framework; defines the App struct and event loop that powers interactive shell behavior.pkg/cli/loop.go— Main event loop implementation; handles user input, rendering, and coordination between CLI modes.go.mod— Module declaration and dependency manifest; critical for understanding build configuration and external dependencies.pkg/cli/modes/completion.go— Completion mode implementation; handles tab completion and suggestion rendering, a primary user-facing feature.pkg/buildinfo/buildinfo.go— Build metadata and version information; used across the codebase for version reporting and feature flags.docs/architecture/doc.go— High-level architecture documentation; provides overview of design decisions and component responsibilities.
🛠️How to make changes
Add a new input mode (like completion or filter)
- Define mode struct and implement the Mode interface in a new file under pkg/cli/modes/ (
pkg/cli/modes/new_mode.go) - Register the mode in the app's mode dispatcher (
pkg/cli/app.go) - Add event handlers for input keys and rendering in the mode's Handle and Render methods (
pkg/cli/modes/new_mode.go) - Create tests using the app testing framework (
pkg/cli/clitest/apptest.go)
Add platform-specific TTY behavior
- Create platform-specific file with _unix, _windows, or _solaris suffix (
pkg/cli/lscolors/stat_unix.go) - Implement the required interface (e.g., Stat) with platform-specific code (
pkg/cli/lscolors/stat_unix.go) - Ensure the generic fallback is defined in the base file (
pkg/cli/lscolors/lscolors.go) - Add platform-specific tests with _unix_test suffix (
pkg/cli/lscolors/feature_unix_test.go)
Add new history storage backend
- Implement the Store interface defined in pkg/cli/histutil/store.go (
pkg/cli/histutil/store.go) - Create the new store implementation file (
pkg/cli/histutil/new_store.go) - Register it in the HybridStore if applicable (
pkg/cli/histutil/hybrid_store.go) - Add comprehensive tests following existing patterns (
pkg/cli/histutil/new_store_test.go)
Add a new CLI example or demo
- Create a new subdirectory under pkg/cli/examples/ with a main.go (
pkg/cli/examples/myexample/main.go) - Import the cli app framework and use the App struct (
pkg/cli/app.go) - Implement custom input modes if needed for your use case (
pkg/cli/examples/myexample/main.go) - Reference the example in docs or add to Makefile targets (
Makefile)
🔧Why these technologies
- Go 1.22+ — Cross-platform compilation, fast startup, built-in concurrency for event loop and async operations, excellent tooling for shell embedding
- bbolt (go.etcd.io/bbolt) — Embedded, ACID-compliant key-value store for command history without external dependencies
- creack/pty — Pseudo-terminal handling for subprocess spawning and interactive shell sessions
- LSP (go-lsp) + jsonrpc2 — Language Server Protocol integration for editor integration and code analysis features
- Platform-specific stat/TTY modules — Accurate file metadata and color support across Unix, Linux, Solaris, and Windows without vendor lock-in
⚖️Trade-offs already made
-
Embedded TTY handling vs. relying on readline/libedit
- Why: Full control over keybindings, completion rendering, and rich UI features
- Consequence: Higher maintenance burden for terminal quirks and platform-specific behavior; tightly coupled to terminal implementation
-
In-process history DB (bbolt) vs. daemon-based history
- Why: Simpler deployment, no external services, instant feedback
- Consequence: History not shared across simultaneous shell instances; deduplication logic must be careful about concurrency
-
Mode-based architecture for input handling vs. monolithic input processor
- Why: Clean separation between completion, filtering, normal input; easier to add new interactive modes
- Consequence: More boilerplate per mode; mode switching logic must be explicit
-
Building a scripting language runtime alongside the shell
- Why: Tight integration of user scripts with interactive shell behavior; unified language for both
- Consequence: Very large codebase; script parsing and evaluation must be fast for interactive use
🚫Non-goals (don't propose these)
- Does not provide bash/zsh/POSIX shell compatibility; uses a distinct scripting syntax
- Does not include built-in debugger for scripts (external tooling only)
- Does not run on Windows natively; requires WSL or virtualization
- Does not provide authentication or privilege escalation (relies on OS mechanisms)
🪤Traps & gotchas
Go 1.22+ is required (specified in go.mod); older versions will fail. The pre-1.0 API means internal pkg/ exports may change without warning between minor versions—pin dependencies carefully. Building for multiple platforms requires appropriate CGO setup due to pty dependency (creack/pty requires C headers). Tests assume Unix-like environment; Windows support is for runtime only, development primarily on Linux/BSD/macOS. Documentation is split: user docs at elv.sh (external), development docs in ./docs/ (in-repo); easy to miss updates if only checking GitHub.
🏗️Architecture
💡Concepts to learn
- Value pipelines (pipe as first-class value) — Core language feature distinguishing Elvish from Bash; pipes can be passed as arguments, stored in variables, and composed programmatically rather than only as shell syntax
- Pseudo-terminal (PTY) handling — Shell must manage interactive I/O, job control, and signal handling via creack/pty; essential for REPL responsiveness and subprocess terminal emulation
- LSP (Language Server Protocol) — Elvish implements LSP via sourcegraph/jsonrpc2 for IDE/editor integration; understanding RPC messaging is key to contributing to editor features
- Closure semantics in shell context — Elvish supports first-class functions and closures, requiring careful scoping and variable capture—non-trivial to implement correctly in a shell context
- Persistent state with BBolt (embedded key-value store) — go.etcd.io/bbolt is used for history, environment, and state persistence; understanding its transaction model is crucial for data integrity in long-running shell sessions
- Static binary linking and cross-platform compilation — Design goal is single distributable binary; requires careful dependency management and platform-specific code paths (see cmd/ variants for profiling, daemonless modes)
- Exception handling in scripting languages — Elvish implements try/catch-like exception semantics in a shell; understanding how errors propagate through pipelines and control flow differs from traditional shell error codes
🔗Related repos
zsh/zsh— Direct competitor shell with similar interactive features but traditional shell syntax; understanding its design helps contextualize Elvish's language improvementsnushell/nushell— Parallel modern shell project in Rust with similar goals (modernized syntax, structured data) but different implementation; useful for cross-pollination of ideaselves/awesome-elvish— Community-curated collection of Elvish packages and tools; essential reference for ecosystem and integration pointsgolang/go— Upstream dependency; Elvish's pkg/diff and pkg/rpc are derived from Go stdlib (see LICENSE in those directories) and understanding Go design influences Elvish's implementationelves/elvish-docs— User-facing documentation site source (if separate); likely contains tutorials and reference material not in this repo's ./docs/
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add end-to-end tests for LSP functionality in pkg/cli
The repo has LSP support (evidenced by go-lsp dependency and pkg.nimblebun.works/go-lsp import), but e2e/script_test.elvts appears minimal. The CLI package has app_test.go and clitest helpers for fake TTY testing, but no comprehensive E2E tests validating LSP server initialization, message handling, and editor integration workflows. This would catch regressions in language server features across platforms.
- [ ] Review pkg/cli/app.go and pkg/cli/clitest/apptest.go to understand LSP initialization patterns
- [ ] Examine e2e/script_test.elvts structure and create new LSP-specific test scenarios
- [ ] Add test cases for: LSP server startup, JSON-RPC message parsing (via sourcegraph/jsonrpc2), hover/completion requests, and graceful shutdown
- [ ] Integrate into CI pipeline via .github/workflows/ci.yml if not already run
Add cross-platform PTY handling tests for creack/pty integration
The repo depends on creack/pty v1.1.21 and has pkg/cli/clitest/fake_tty.go with fake_tty_test.go, but lacks comprehensive tests for real PTY allocation across FreeBSD (tested via Cirrus CI) and Linux. The fake TTY tests don't validate actual PTY file descriptor behavior on real systems. Adding transcripts-style tests (following the pattern in pkg/buildinfo/buildinfo_test.elvts) would catch PTY-related breakage early.
- [ ] Study pkg/cli/clitest/fake_tty.go implementation and existing fake_tty_test.go coverage gaps
- [ ] Create pkg/cli/clitest/pty_integration_test.elvts with real PTY allocation scenarios (resize, signal handling, EOF)
- [ ] Add Go test helpers in pkg/cli/clitest/pty_test.go to validate PTY state on Linux and FreeBSD
- [ ] Reference buildinfo_test.go pattern for transcript-based testing approach
Add transcript tests for elvmdfmt command-line tool
The repo has cmd/elvmdfmt/main.go but no visible test coverage in the file structure, and no .elvts transcript tests. The docs/documenting.md likely describes markdown formatting requirements for Elvish docs, but the tool itself lacks integration tests. Following the pattern used in pkg/buildinfo/buildinfo_test.elvts, transcript tests would validate markdown formatting behavior on real files.
- [ ] Review docs/documenting.md to understand Elvish markdown conventions for elvmdfmt
- [ ] Create cmd/elvmdfmt/elvmdfmt_test.elvts with test cases: basic formatting, edge cases (unclosed blocks, nested code), and error handling
- [ ] Add Go test scaffolding in cmd/elvmdfmt/main_test.go following pkg/buildinfo/transcripts_test.go pattern
- [ ] Ensure tests run in CI via .github/workflows/ci.yml
🌿Good first issues
- Add test coverage for error handling paths in cmd/elvish/main.go argument parsing; currently only happy paths appear tested
- Document the pkg/ directory structure with a README.md in each major package (parse, eval, builtin, etc.) to help new contributors understand module responsibilities
- Implement missing integration tests for LSP server features (jsonrpc2 integration) in e2e/ to catch regressions in language server protocol implementations
⭐Top contributors
Click to expand
Top contributors
- @xiaq — 86 commits
- @MrOverlord — 4 commits
- @eunos-1128 — 3 commits
- @alexandear — 2 commits
- @ejrichards — 1 commits
📝Recent commits
Click to expand
Recent commits
26a8bd5— pkg/mods/epm: Add sourcehut to default domain list (ejrichards)49937be— vscode: Bump dependency versions. (xiaq)54ab2ee— .cirrus.yml: Bump FreeBSD image version. (xiaq)64268e6— Bump staticcheck version in CI config and doc (xiaq)1054a1d— website/slides: Tweak template, and add FOSDEM slides (xiaq)dad2f5c— pkg/glob: Simplify test with filepath.ToSlash (alexandear)3e41d29— website: Fix typos (alexandear)1ecd68d— pkg/eval: Refactor the argument parsing logic of randint. (xiaq)5aafdd5— Add test mixing large/small args in randint (MrOverlord)d078bc1— Fix type casting with mixed arguments in randint (MrOverlord)
🔒Security observations
The Elvish codebase demonstrates reasonable security practices with comprehensive CI/CD pipelines, test coverage monitoring, and documented security considerations. However, there are several areas for improvement: (1) Docker base images are slightly outdated and should be updated to receive latest security patches, (2) the Docker image runs as root by default, which is a container security anti-pattern, (3) dependency management appears sound but should be actively monitored. The Go dependencies listed are from reputable sources and appear to be actively maintained. No hardcoded secrets, SQL injection risks, or obvious injection vulnerabilities were identified in the provided file structure. The project's transparency with contributing guidelines and security documentation is a positive sign.
- Medium · Outdated Go Base Image in Dockerfile —
Dockerfile (line 1). The Dockerfile uses golang:1.22-alpine3.19 as the builder base image. Go 1.22 is no longer receiving security updates (current stable is 1.23+). This could expose the build process to known vulnerabilities in the Go runtime and standard library. Fix: Update to the latest stable Go version (1.23 or later) and corresponding Alpine base image to receive security patches and bug fixes. - Medium · Outdated Alpine Base Image —
Dockerfile (line 6). The final stage uses alpine:3.19, which is no longer the latest stable version. Alpine 3.20+ are available and include security updates. Fix: Update to alpine:3.20 or later to ensure all base OS security patches are applied. - Low · Missing HEALTHCHECK in Docker Image —
Dockerfile. The Dockerfile does not include a HEALTHCHECK instruction. This is not critical for a shell, but makes container orchestration and monitoring more difficult. Fix: Consider adding a HEALTHCHECK instruction if the container will be used in orchestrated environments. - Low · No Non-Root User in Docker Image —
Dockerfile (line 6-8). The final Docker image does not explicitly create or switch to a non-root user. Processes run as root by default, which increases the blast radius of any potential container escape. Fix: Create a dedicated non-root user and switch to it before the CMD instruction. Example: RUN adduser -D -u 1000 elvish && USER elvish - Low · Potential Dependency Update Risk - bbolt —
go.mod (line 6). The codebase uses go.etcd.io/bbolt v1.3.10 for embedded key-value storage. While this version is relatively recent, bbolt should be monitored for security updates as it handles file I/O operations. Fix: Regularly run 'go list -u -m all' and 'go mod tidy' to check for security updates. Consider enabling Dependabot or similar automated dependency monitoring. - Low · Missing Security Policy Documentation —
Repository root. While a security.md file exists in docs/, there is no SECURITY.md in the repository root. This makes it harder for security researchers to report vulnerabilities responsibly. Fix: Create a SECURITY.md file in the repository root following GitHub's recommended format (https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository).
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.