Commit 481194e

mo khan <mo@mokhan.ca>
2026-01-28 17:58:46
docs: update CHANGELOG and README
1 parent e4afa5f
Changed files (2)
CHANGELOG.md
@@ -2,18 +2,30 @@
 
 ### Added
 - **Async MCP loading** for faster startup - tools load in background thread
+- **HTTP MCP servers** with SSE support and session management
+- **OAuth authentication** for MCP servers with PKCE, automatic token refresh
+- **Global hooks** - `toolbox.before`/`toolbox.after` without tool name applies to all tools
 - **`/context` improvements**: `/context <n>` to view entry, `/context json` for full dump
 - **ast-grep (`sg`) support** for building repo maps - faster and more accurate than ctags
-- **OAuth authentication** for MCP servers with automatic token refresh
-- **New tools**: `glob`, `grep`, `list`, `git` for structured file operations
+- **New tools**: `glob`, `grep`, `list`, `git`, `task`, `/tools` command
 - **Permissions system** (`lib/elelem/permissions.rb`) for tool access control
 - **OpenAI reasoning mode** - enables `Reasoning: high` for o-series models
+- **Test coverage** for OAuth, token storage, HTTP MCP, SSE parsing, global hooks
 
 ### Changed
+- **BREAKING: Plugin API** - plugins now receive `agent` instead of `toolbox`
+  - Old: `Elelem::Plugins.register(:name) { |toolbox| toolbox.add(...) }`
+  - New: `Elelem::Plugins.register(:name) { |agent| agent.toolbox.add(...) }`
+  - Plugins can now access `agent.terminal`, `agent.commands`, `agent.conversation`
 - Extracted `Conversation` class from `Agent` for better separation of concerns
+- Extracted `Commands` class for slash command handling
 - Refactored LLM fetch interface to emit separate events for thinking/content/tool_calls
 - Simplified system prompt with inline ERB template
 - Renamed confirm plugin to `zz_confirm` to ensure it loads last
+- MCP logs now write to `~/.elelem/mcp.log` instead of working directory
+- Tool schema now frozen to prevent mutation
+- Uses `Open3.capture2` instead of backticks for thread safety
+- Improved ANSI escape sequence stripping in `/shell` transcripts
 
 ## [0.9.2] - 2026-01-22
 
README.md
@@ -129,18 +129,94 @@ Each provider reads its configuration from environment variables:
 * **Conversation History** – persists across turns; can be cleared.
 * **Context Dump** – `/context` shows the current conversation state.
 
-## Toolbox Overview
-
-The `Toolbox` class is defined in `lib/elelem/toolbox.rb`. It supplies
-three tools, each represented by a JSON schema that the LLM can call.
+## Tools
+
+Built-in tools available to the LLM:
+
+| Tool      | Purpose                    | Parameters                |
+| --------- | -------------------------- | ------------------------- |
+| `read`    | Read file contents         | `path`                    |
+| `write`   | Write file                 | `path`, `content`         |
+| `edit`    | Replace text in file       | `path`, `old`, `new`      |
+| `execute` | Run shell command          | `command`                 |
+| `eval`    | Execute Ruby code          | `ruby`                    |
+| `glob`    | Find files by pattern      | `pattern`, `path`         |
+| `grep`    | Search file contents       | `pattern`, `path`, `glob` |
+| `list`    | List directory             | `path`, `recursive`       |
+| `git`     | Run git command            | `command`, `args`         |
+| `task`    | Delegate to sub-agent      | `prompt`                  |
+| `verify`  | Check syntax and run tests | `path`                    |
+
+Aliases: `bash`, `sh`, `exec` → `execute`; `open` → `read`; `ls` → `list`
+
+## Plugins
+
+Plugins extend elelem with custom tools and commands. They are loaded from:
+- `lib/elelem/plugins/` (built-in)
+- `~/.elelem/plugins/` (user global)
+- `.elelem/plugins/` (project local)
+
+### Writing a Plugin
+
+```ruby
+# ~/.elelem/plugins/hello.rb
+Elelem::Plugins.register(:hello) do |agent|
+  # Add a tool
+  agent.toolbox.add("hello",
+    description: "Say hello",
+    params: { name: { type: "string" } },
+    required: ["name"]
+  ) do |args|
+    { message: "Hello, #{args["name"]}!" }
+  end
+
+  # Add a command
+  agent.commands.register("greet", description: "Greet the user") do
+    agent.terminal.say "Hello!"
+  end
+
+  # Add hooks
+  agent.toolbox.before("execute") { |args| puts "Running: #{args["command"]}" }
+  agent.toolbox.after("execute") { |args, result| puts "Exit: #{result[:exit_status]}" }
+
+  # Global hook (runs for all tools)
+  agent.toolbox.before { |args, tool_name:| puts "Calling #{tool_name}" }
+end
+```
 
-| Tool      | Purpose            | Parameters         |
-| --------- | ------------------ | ------------------ |
-| `read`    | Read file contents | `path`             |
-| `write`   | Write file         | `path`, `content`  |
-| `execute` | Run shell command  | `command`          |
+### Plugin API
+
+Plugins receive an `agent` object with access to:
+- `agent.toolbox` - add tools, register hooks
+- `agent.terminal` - output to the user (`say`, `ask`, `markdown`)
+- `agent.commands` - register slash commands
+- `agent.conversation` - access message history
+- `agent.client` - the LLM client
+- `agent.fork(system_prompt:)` - create a sub-agent
+
+## MCP Configuration
+
+Configure MCP servers in `~/.elelem/mcp.json` or `.elelem/mcp.json`:
+
+```json
+{
+  "mcpServers": {
+    "gitlab": {
+      "command": "npx",
+      "args": ["-y", "@anthropics/gitlab-mcp"],
+      "env": {
+        "GITLAB_TOKEN": "${GITLAB_TOKEN}"
+      }
+    },
+    "remote": {
+      "type": "http",
+      "url": "https://mcp.example.com/sse"
+    }
+  }
+}
+```
 
-Aliases: `bash`, `sh`, `exec` → `execute`; `open` → `read`
+HTTP servers support OAuth authentication automatically.
 
 ## Known Limitations