# Elps

> Use when asked to add a new standard library package (e.g., file I/O, crypto, HTTP client, etc.).

- Skill: `tomevault-io/elps` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/elps`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/elps/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/elps

---

# /add-stdlib-package — New Standard Library Package Skill

Creates a new stdlib package for the ELPS interpreter. Follows the established pattern from existing packages like `libmath`.

## Trigger

Use when asked to add a new standard library package (e.g., file I/O, crypto, HTTP client, etc.).

## Workflow

### 1. Plan the Package

Before writing code, define:
- Package name (lowercase, e.g., `"file"`, `"crypto"`)
- Functions to export with their signatures
- Any global constants to expose
- Package-level documentation

### 2. Create the Package Directory

Create `lisp/lisplib/lib<name>/lib<name>.go`:

```go
package lib<name>

import (
	"github.com/luthersystems/elps/lisp"
	"github.com/luthersystems/elps/lisp/lisplib/libutil"
)

const DefaultPackageName = "<name>"

func LoadPackage(env *lisp.LEnv) *lisp.LVal {
	name := lisp.Symbol(DefaultPackageName)

	e := env.DefinePackage(name)
	if !e.IsNil() {
		return e
	}

	e = env.InPackage(name)
	if !e.IsNil() {
		return e
	}

	env.SetPackageDoc(`One-line description of the package.

Detailed description of what the package provides.`)

	// Optional: global constants
	// env.PutGlobal(lisp.Symbol("my-const"), lisp.Int(42))
	// env.SetSymbolDoc("my-const", "Description of the constant.")
	// env.Runtime.Package.Exports("my-const")

	for _, fn := range builtins {
		env.AddBuiltins(true, fn)
	}

	return lisp.Nil()
}

var builtins = []*libutil.Builtin{
	libutil.FunctionDoc("my-func",
		lisp.Formals("arg1", "arg2"),
		builtinMyFunc,
		`Description of what my-func does.

Returns the result of ...`),
}

func builtinMyFunc(env *lisp.LEnv, args *lisp.LVal) *lisp.LVal {
	arg1 := args.Cells[0]
	arg2 := args.Cells[1]
	// Implementation
	_ = arg1
	_ = arg2
	return lisp.Nil()
}
```

### Key Rules

- **Always use `libutil.FunctionDoc()`** — never `libutil.Function()`. All functions must have docstrings. CI enforces this via `elps doc -m`.
- **Use `lisp.Formals()`** for argument declarations. Available modifiers:
  - Required: `lisp.Formals("arg1", "arg2")`
  - Optional: `lisp.Formals("required", lisp.FmtString("&optional"), "opt-arg")`
  - Variadic: `lisp.Formals("required", lisp.FmtString("&rest"), "rest-args")`
  - Keyword: `lisp.Formals("required", lisp.FmtString("&key"), "key-arg")`
- **Error propagation**: Return `env.Errorf("message: %v", detail)` for errors — never use Go's `error` interface
- **Export symbols**: Call `env.Runtime.Package.Exports("func1", "func2", ...)` for all public symbols

### 3. Register in `LoadLibrary()`

Edit `lisp/lisplib/lisplib.go`:

1. Add import: `"github.com/luthersystems/elps/lisp/lisplib/lib<name>"`
2. Add to the package loader list in `LoadLibrary()`:

```go
e = lib<name>.LoadPackage(env)
if !e.IsNil() {
    return e
}
```

### 4. Write Tests

Create a lisp test file at `lisp/lisplib/lib<name>/lib<name>_test.go`:

```go
package lib<name>_test

import (
	"testing"

	"github.com/luthersystems/elps/elpstest"
)

func TestLib<Name>(t *testing.T) {
	runner := &elpstest.Runner{}
	runner.RunTestFile(t, "testdata/<name>_test.lisp")
}
```

Create the lisp test at `lisp/lisplib/lib<name>/testdata/<name>_test.lisp`:

```lisp
(use-package 'testing)
(use-package '<name>)

(test "my-func basic usage"
  (assert-equal (my-func "arg1" "arg2") expected-result))

(test "my-func edge case"
  (assert-nil (my-func "" "")))

(test "my-func error handling"
  (assert-error (my-func)))  ; missing args
```

### 5. Verify

```bash
go test ./lisp/lisplib/lib<name>/...    # Package tests pass
make test                                # Full suite passes
./elps doc -m                            # No missing docstrings
./elps doc <name>                        # Package docs render correctly
```

## Checklist

- [ ] Package directory created at `lisp/lisplib/lib<name>/`
- [ ] `LoadPackage()` follows DefinePackage → InPackage → SetPackageDoc → AddBuiltins pattern
- [ ] All functions use `libutil.FunctionDoc()` (not `Function()`)
- [ ] All public symbols exported via `Exports()`
- [ ] Package registered in `lisplib.go` `LoadLibrary()`
- [ ] Tests written with `elpstest.Runner` + `.lisp` test file
- [ ] `elps doc -m` passes (no missing docstrings)
- [ ] `make test` passes
- [ ] `docs/lang.md` updated if package is user-facing

---
> Source: [luthersystems/elps](https://github.com/luthersystems/elps) — distributed by [TomeVault](https://tomevault.io).
<!-- tomevault:4.0:skill_md:2026-06-21 -->

