Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,13 @@ jobs:
otp_versions: '["26", "27"]'
elixir_versions: '["1.17", "1.18"]'
test_command: mix test

coverage:
name: Coverage
uses: agentjido/github-actions/.github/workflows/elixir-test.yml@main
with:
otp_versions: '["27"]'
elixir_versions: '["1.18"]'
test_command: >-
mix coveralls.json &&
MIX_ENV=test mix run -e 'data = Jason.decode!(File.read!("cover/excoveralls.json")); coverages = data["source_files"] |> Enum.flat_map(& &1["coverage"]); relevant = Enum.count(coverages, &(!is_nil(&1))); missed = Enum.count(coverages, &(&1 == 0)); percent = (relevant - missed) * 100.0 / relevant; rounded = Float.round(percent, 2); IO.puts("Coverage gate: #{rounded}%"); if percent < 90.0, do: System.halt(1)'
158 changes: 37 additions & 121 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,150 +2,66 @@

## Purpose

Jido.Shell is an Elixir-native virtual shell system that provides multi-instance interactive sessions with virtual file system support. It's designed to be embedded in any BEAM application, offering an interactive REPL and full programmatic API for spawning sessions, evaluating commands, and manipulating virtual file systems.
Jido.Shell is an Elixir-native virtual shell for multi-session, sandboxed command execution over virtual filesystems.

## Commands
## Key Commands

```bash
# Development
mix setup # Install deps and git hooks
mix setup
mix compile --warnings-as-errors
mix test # Run tests (excludes flaky)
mix test --include flaky # Run all tests
mix coveralls.html # Test coverage report

# Quality
mix quality # All checks (format, compile, credo, dialyzer)
mix q # Alias for quality
mix format # Auto-format code
mix credo # Linting
mix dialyzer # Type checking

# Documentation
mix docs # Generate docs

# Interactive
mix kodo # IEx-style shell
mix kodo --ui # Rich terminal UI
mix test
mix test --include flaky
mix coveralls
mix quality
mix docs
mix jido_shell
```

## Architecture

### Supervision Tree
## Core Architecture

```
Jido.Shell.Supervisor
├── Jido.Shell.VFS.MountTable
├── Registry (Jido.Shell.SessionRegistry)
├── DynamicSupervisor (Jido.Shell.SessionSupervisor)
│ └── SessionServer processes
├── DynamicSupervisor (Jido.Shell.FilesystemSupervisor)
│ └── Jido.VFS adapter processes
└── Task.Supervisor (Jido.Shell.CommandTaskSupervisor)
└── Command task processes
```

### Key Modules

| Module | Purpose |
|--------|---------|
| `Jido.Shell.Agent` | Programmatic API for agents (synchronous) |
| `Jido.Shell.Session` | Session lifecycle management |
| `Jido.Shell.SessionServer` | Per-session GenServer with state and subscriptions |
| `Jido.Shell.Command` | Command behaviour definition |
| `Jido.Shell.Command.Registry` | Command lookup and registration |
| `Jido.Shell.CommandRunner` | Task-based command execution |
| `Jido.Shell.VFS` | Virtual filesystem router |
| `Jido.Shell.VFS.MountTable` | ETS-backed mount table |
| `Jido.Shell.Transport.IEx` | Interactive IEx transport |
| `Jido.Shell.Transport.TermUI` | Rich terminal UI transport |

### Command Pattern
## Main Modules

Commands implement `Jido.Shell.Command` behaviour:

```elixir
defmodule Jido.Shell.Command.Example do
@behaviour Jido.Shell.Command

@impl true
def name, do: "example"

@impl true
def summary, do: "Example command"

@impl true
def schema do
Zoi.map(%{
args: Zoi.array(Zoi.string()) |> Zoi.default([])
})
end

@impl true
def run(state, args, emit) do
emit.({:output, "Hello\n"})
{:ok, nil} # or {:ok, {:state_update, %{cwd: "/new/path"}}}
end
end
```
- `Jido.Shell.Agent` - synchronous API for agents
- `Jido.Shell.Session` - session lifecycle
- `Jido.Shell.SessionServer` - per-session GenServer
- `Jido.Shell.CommandRunner` - command execution and chaining
- `Jido.Shell.VFS` - mounted filesystem router
- `Jido.Shell.Transport.IEx` - interactive shell transport

### Session Events
## Session Events

```elixir
{:kodo_session, session_id, event}

# Events:
{:command_started, line}
{:output, chunk}
{:error, %Jido.Shell.Error{}}
{:cwd_changed, path}
:command_done
:command_cancelled
{:command_crashed, reason}
{:jido_shell_session, session_id, event}
```

## Code Style
Events:

- Use `mix format` before committing
- Elixir naming: snake_case functions, PascalCase modules
- Pattern match with `{:ok, result}` | `{:error, reason}`
- Add `@spec` type annotations for public functions
- Test with ExUnit in `describe` blocks
- Use `Jido.Shell.Error` for structured errors
- Follow conventional commits for git messages
- `{:command_started, line}`
- `{:output, chunk}`
- `{:error, %Jido.Shell.Error{}}`
- `{:cwd_changed, path}`
- `:command_done`
- `:command_cancelled`
- `{:command_crashed, reason}`

## Testing
## Test Layout

```elixir
# Use Jido.Shell.TestShell for E2E tests
shell = Jido.Shell.TestShell.start!()
assert {:ok, "/"} = Jido.Shell.TestShell.run(shell, "pwd")
```
- Unit/integration: `test/jido/shell/**/*.exs`
- End-to-end: `test/jido/shell/e2e_test.exs`
- Support helpers: `test/support/*.ex`

## File Structure
## Conventions

```
lib/
├── kodo.ex # Version and utilities
├── kodo/
│ ├── application.ex # OTP application
│ ├── agent.ex # Agent API
│ ├── session.ex # Session management
│ ├── session_server.ex # Session GenServer
│ ├── session/state.ex # Session state struct
│ ├── command.ex # Command behaviour
│ ├── command/ # Built-in commands
│ ├── command_runner.ex # Task execution
│ ├── vfs.ex # Virtual filesystem
│ ├── vfs/ # VFS internals
│ ├── transport/ # IEx and TermUI
│ └── error.ex # Error handling
├── mix/tasks/
│ └── kodo.ex # mix kodo task
test/
├── support/
│ ├── case.ex # Test case template
│ └── test_shell.ex # E2E test helper
├── kodo/
│ ├── e2e_test.exs # End-to-end tests
│ └── ... # Unit tests
```
- Prefer tuple-based APIs: `{:ok, ...}` / `{:error, ...}`
- Use `Jido.Shell.Error` for structured errors
- Keep workspace IDs as strings (`String.t()`)
- Avoid runtime atom generation from user input
54 changes: 36 additions & 18 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,46 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Changed
- Hardened identifier model to use binary workspace IDs across public APIs.
- Removed runtime-generated atom usage from session/VFS workflows.
- Updated `SessionServer` and `Agent` APIs to return explicit structured errors for missing sessions and invalid identifiers instead of crashing callers.
- Added deterministic mount lifecycle behavior:
- duplicate mount path rejection,
- typed mount startup failures,
- owned filesystem process termination on unmount/workspace teardown.
- Added workspace teardown API wiring for deterministic resource cleanup.
- Upgraded command parsing to support quote/escape-aware tokenization and top-level chaining (`;`, `&&`).
- Hardened `sleep` and `seq` argument parsing to return validation errors for invalid numerics.
- Expanded sandbox network policy endpoint handling and chaining-aware enforcement.
- Added optional per-command runtime/output limits in execution context.
- Removed the alternate rich UI mode from the V1 public release surface and CLI flags.
- Updated docs and examples to current names/event tuples and current package surface.

### Added
- `MIGRATION.md` documenting V1-facing breaking API changes and upgrade steps.
- New hardening tests for:
- workspace identifier validation and atom leak regression,
- session API resilience/error shaping,
- mount lifecycle/cleanup behavior,
- parser/chaining behavior and syntax errors,
- command numeric validation,
- network policy edge cases,
- transport and helper branch behavior.
- CI coverage job with enforced coverage gate.

## [3.0.0] - 2024-12-23

### Added
- Complete v3 reimplementation from scratch
- `Jido.Shell.Session` - Session management with Registry and DynamicSupervisor
- `Jido.Shell.SessionServer` - GenServer per session with state and subscriptions
- `Jido.Shell.Command` behaviour - Unified command interface with streaming support
- `Jido.Shell.CommandRunner` - Task-based command execution
- `Jido.Shell.VFS` - Virtual filesystem with Hako adapters and mount table
- `Jido.Shell.Agent` - Agent-friendly programmatic API
- `Jido.Shell.Transport.IEx` - Interactive IEx shell transport
- `Jido.Shell.Transport.TermUI` - Rich terminal UI transport
- Built-in commands: echo, pwd, cd, ls, cat, write, mkdir, rm, cp, env, help, sleep, seq
- `mix kodo` task for easy shell access
- Zoi schema validation for command arguments
- Structured errors with `Jido.Shell.Error`
- Session events protocol for streaming output
- Command cancellation support
- Complete v3 reimplementation from scratch.
- `Jido.Shell.Session`, `Jido.Shell.SessionServer`, `Jido.Shell.Command`, `Jido.Shell.CommandRunner`, `Jido.Shell.VFS`, `Jido.Shell.Agent`, `Jido.Shell.Transport.IEx`.
- Built-in commands: `echo`, `pwd`, `cd`, `ls`, `cat`, `write`, `mkdir`, `rm`, `cp`, `env`, `help`, `sleep`, `seq`.
- Structured shell errors and session event protocol.

### Changed
- Complete architecture redesign for streaming and agent integration
- GenServer-based sessions replace stateless execution
- Architecture redesign for streaming and agent integration.

### Removed
- Legacy v2 implementation
- Legacy v2 implementation.
Loading
Loading