This tutorial walks you through setting up GRASP, mounting data sources, and running shell commands. By the end, you'll have a working virtual filesystem with local files, in-memory tools, and a shell interface.
- Go 1.24+
- A Go module project
go get github.com/jackfish212/grasp@latestCreate a main.go:
package main
import (
"context"
"fmt"
"github.com/jackfish212/grasp"
"github.com/jackfish212/grasp/builtins"
"github.com/jackfish212/grasp/mounts"
)
func main() {
// 1. Create the virtual OS
v := grasp.New()
// 2. Configure standard filesystem layout (/bin, /usr, /etc, /proc, ...)
rootFS, err := grasp.Configure(v)
if err != nil {
panic(err)
}
// 3. Register built-in commands (ls, cat, grep, search, ...)
builtins.RegisterBuiltinsOnFS(v, rootFS)
// 4. Mount a local directory
v.Mount("/data", mounts.NewLocalFS(".", grasp.PermRW))
// 5. Create a shell and run commands
sh := v.Shell("agent")
ctx := context.Background()
result := sh.Execute(ctx, "ls /data")
fmt.Println(result.Output)
result = sh.Execute(ctx, "cat /proc/version")
fmt.Println(result.Output)
}Run it:
go run main.goYou'll see a listing of your current directory under /data and the GRASP version string.
You can add files directly to the root MemFS:
rootFS.AddFile("etc/motd", []byte("Welcome to GRASP\n"), grasp.PermRO)
result := sh.Execute(ctx, "cat /etc/motd")
// Output: Welcome to GRASPRegister Go functions as executable commands:
rootFS.AddExecFunc("usr/bin/greet", func(ctx context.Context, args []string, stdin io.Reader) (io.ReadCloser, error) {
name := "world"
if len(args) > 0 {
name = args[0]
}
msg := fmt.Sprintf("Hello, %s!\n", name)
return io.NopCloser(strings.NewReader(msg)), nil
}, mounts.FuncMeta{
Description: "Greet someone",
Usage: "greet [name]",
})
result := sh.Execute(ctx, "greet Agent")
// Output: Hello, Agent!Because greet is at /usr/bin/greet and /usr/bin is on PATH, the shell finds it automatically.
Shell commands compose through pipes:
sh.Execute(ctx, "ls /data | head -5")
sh.Execute(ctx, "cat /data/main.go | grep import")
sh.Execute(ctx, "search TODO --scope /data | head -3")Use redirection to write output to files:
sh.Execute(ctx, "echo 'task done' > /tmp/log.md")
sh.Execute(ctx, "cat /tmp/log.md")
// Output: task doneLogical operators for conditional execution:
sh.Execute(ctx, "mkdir /tmp/work && echo 'created' || echo 'failed'")Each Shell instance is independent — different working directories, different environment variables, different history. Create one per agent session:
sh1 := v.Shell("alice")
sh2 := v.Shell("bob")
sh1.Execute(ctx, "cd /data")
sh2.Execute(ctx, "cd /memory")
// sh1.Cwd() == "/data"
// sh2.Cwd() == "/memory"// List all mount points
sh.Execute(ctx, "mount")
// System information
sh.Execute(ctx, "uname -a")
// Find a command
sh.Execute(ctx, "which grep")
// Environment variables
sh.Execute(ctx, "env")- Read Architecture to understand how VirtualOS, MountTable, and Shell work together.
- Read Provider Model to learn how capability-based interfaces work.
- Read Create a Custom Provider for a step-by-step guide to building your own provider.
- Read Shell as Interface to understand why shell commands are the optimal agent interface.