bernard-agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +21 -0
- package/LICENSE +21 -0
- package/README.md +629 -0
- package/dist/agent.d.ts +24 -0
- package/dist/agent.js +174 -0
- package/dist/agent.js.map +1 -0
- package/dist/config.d.ts +44 -0
- package/dist/config.js +267 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +37 -0
- package/dist/context.js +245 -0
- package/dist/context.js.map +1 -0
- package/dist/cron/client.d.ts +4 -0
- package/dist/cron/client.js +113 -0
- package/dist/cron/client.js.map +1 -0
- package/dist/cron/daemon.d.ts +1 -0
- package/dist/cron/daemon.js +132 -0
- package/dist/cron/daemon.js.map +1 -0
- package/dist/cron/log-store.d.ts +51 -0
- package/dist/cron/log-store.js +135 -0
- package/dist/cron/log-store.js.map +1 -0
- package/dist/cron/notify.d.ts +7 -0
- package/dist/cron/notify.js +136 -0
- package/dist/cron/notify.js.map +1 -0
- package/dist/cron/runner.d.ts +6 -0
- package/dist/cron/runner.js +219 -0
- package/dist/cron/runner.js.map +1 -0
- package/dist/cron/scheduler.d.ts +16 -0
- package/dist/cron/scheduler.js +105 -0
- package/dist/cron/scheduler.js.map +1 -0
- package/dist/cron/store.d.ts +20 -0
- package/dist/cron/store.js +170 -0
- package/dist/cron/store.js.map +1 -0
- package/dist/cron/types.d.ts +21 -0
- package/dist/cron/types.js +3 -0
- package/dist/cron/types.js.map +1 -0
- package/dist/embeddings.d.ts +14 -0
- package/dist/embeddings.js +61 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/history.d.ts +6 -0
- package/dist/history.js +71 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +231 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +24 -0
- package/dist/logger.js.map +1 -0
- package/dist/mcp.d.ts +43 -0
- package/dist/mcp.js +303 -0
- package/dist/mcp.js.map +1 -0
- package/dist/memory.d.ts +17 -0
- package/dist/memory.js +106 -0
- package/dist/memory.js.map +1 -0
- package/dist/output.d.ts +13 -0
- package/dist/output.js +151 -0
- package/dist/output.js.map +1 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.js +19 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/types.d.ts +5 -0
- package/dist/providers/types.js +3 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/rag-worker.d.ts +10 -0
- package/dist/rag-worker.js +84 -0
- package/dist/rag-worker.js.map +1 -0
- package/dist/rag.d.ts +53 -0
- package/dist/rag.js +242 -0
- package/dist/rag.js.map +1 -0
- package/dist/repl.d.ts +2 -0
- package/dist/repl.js +531 -0
- package/dist/repl.js.map +1 -0
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +104 -0
- package/dist/setup.js.map +1 -0
- package/dist/tools/cron-logs.d.ts +67 -0
- package/dist/tools/cron-logs.js +131 -0
- package/dist/tools/cron-logs.js.map +1 -0
- package/dist/tools/cron.d.ts +98 -0
- package/dist/tools/cron.js +248 -0
- package/dist/tools/cron.js.map +1 -0
- package/dist/tools/datetime.d.ts +4 -0
- package/dist/tools/datetime.js +25 -0
- package/dist/tools/datetime.js.map +1 -0
- package/dist/tools/index.d.ts +317 -0
- package/dist/tools/index.js +28 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/mcp-url.d.ts +16 -0
- package/dist/tools/mcp-url.js +27 -0
- package/dist/tools/mcp-url.js.map +1 -0
- package/dist/tools/mcp.d.ts +28 -0
- package/dist/tools/mcp.js +107 -0
- package/dist/tools/mcp.js.map +1 -0
- package/dist/tools/memory.d.ts +40 -0
- package/dist/tools/memory.js +99 -0
- package/dist/tools/memory.js.map +1 -0
- package/dist/tools/shell.d.ts +15 -0
- package/dist/tools/shell.js +60 -0
- package/dist/tools/shell.js.map +1 -0
- package/dist/tools/subagent.d.ts +21 -0
- package/dist/tools/subagent.js +81 -0
- package/dist/tools/subagent.js.map +1 -0
- package/dist/tools/time.d.ts +50 -0
- package/dist/tools/time.js +61 -0
- package/dist/tools/time.js.map +1 -0
- package/dist/tools/types.d.ts +8 -0
- package/dist/tools/types.js +3 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/web.d.ts +16 -0
- package/dist/tools/web.js +136 -0
- package/dist/tools/web.js.map +1 -0
- package/package.json +73 -0
package/.env.example
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Bernard CLI Configuration
|
|
2
|
+
|
|
3
|
+
# Provider: anthropic | openai | xai
|
|
4
|
+
BERNARD_PROVIDER=anthropic
|
|
5
|
+
|
|
6
|
+
# Model name (provider-specific)
|
|
7
|
+
# Anthropic: claude-sonnet-4-5-20250929, claude-haiku-4-5-20251001
|
|
8
|
+
# OpenAI: gpt-4o, gpt-4o-mini
|
|
9
|
+
# xAI: grok-3, grok-3-mini
|
|
10
|
+
BERNARD_MODEL=claude-sonnet-4-5-20250929
|
|
11
|
+
|
|
12
|
+
# Max tokens for responses
|
|
13
|
+
BERNARD_MAX_TOKENS=4096
|
|
14
|
+
|
|
15
|
+
# Shell command timeout in milliseconds
|
|
16
|
+
BERNARD_SHELL_TIMEOUT=30000
|
|
17
|
+
|
|
18
|
+
# API Keys (set the one(s) for your provider)
|
|
19
|
+
ANTHROPIC_API_KEY=
|
|
20
|
+
OPENAI_API_KEY=
|
|
21
|
+
XAI_API_KEY=
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 phillt
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
# Bernard
|
|
2
|
+
|
|
3
|
+
A local CLI AI agent that executes terminal commands, manages scheduled tasks, remembers context across sessions, and connects to external tool servers — all through natural language. Supports multiple LLM providers (Anthropic, OpenAI, xAI) via the Vercel AI SDK.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Quick Start](#quick-start)
|
|
9
|
+
- [Configuration](#configuration)
|
|
10
|
+
- [API Keys](#api-keys)
|
|
11
|
+
- [Environment Variables](#environment-variables)
|
|
12
|
+
- [Providers and Models](#providers-and-models)
|
|
13
|
+
- [Runtime Options](#runtime-options)
|
|
14
|
+
- [Usage](#usage)
|
|
15
|
+
- [CLI Flags](#cli-flags)
|
|
16
|
+
- [CLI Management Commands](#cli-management-commands)
|
|
17
|
+
- [Interactive REPL](#interactive-repl)
|
|
18
|
+
- [REPL Slash Commands](#repl-slash-commands)
|
|
19
|
+
- [Tools](#tools)
|
|
20
|
+
- [Shell Execution](#shell-execution)
|
|
21
|
+
- [Web Reading](#web-reading)
|
|
22
|
+
- [Memory (Persistent)](#memory-persistent)
|
|
23
|
+
- [Scratch Notes (Session)](#scratch-notes-session)
|
|
24
|
+
- [Date and Time](#date-and-time)
|
|
25
|
+
- [Time Range Calculations](#time-range-calculations)
|
|
26
|
+
- [Sub-Agents](#sub-agents)
|
|
27
|
+
- [Cron Jobs (Scheduled Tasks)](#cron-jobs-scheduled-tasks)
|
|
28
|
+
- [Creating Jobs](#creating-jobs)
|
|
29
|
+
- [Managing Jobs](#managing-jobs)
|
|
30
|
+
- [Execution Logs](#execution-logs)
|
|
31
|
+
- [Notifications](#notifications)
|
|
32
|
+
- [MCP Servers (External Tools)](#mcp-servers-external-tools)
|
|
33
|
+
- [Stdio Servers](#stdio-servers)
|
|
34
|
+
- [URL-Based Servers (SSE/HTTP)](#url-based-servers-ssehttp)
|
|
35
|
+
- [Managing MCP Servers](#managing-mcp-servers)
|
|
36
|
+
- [Context Management](#context-management)
|
|
37
|
+
- [Automatic Compression](#automatic-compression)
|
|
38
|
+
- [RAG Memory](#rag-memory)
|
|
39
|
+
- [Conversation Resume](#conversation-resume)
|
|
40
|
+
- [File Structure](#file-structure)
|
|
41
|
+
- [Development](#development)
|
|
42
|
+
- [Building](#building)
|
|
43
|
+
- [Testing](#testing)
|
|
44
|
+
- [Debug Logging](#debug-logging)
|
|
45
|
+
- [Adding a New Provider](#adding-a-new-provider)
|
|
46
|
+
- [Adding a New Tool](#adding-a-new-tool)
|
|
47
|
+
- [License](#license)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install -g bernard-agent
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This installs `bernard` as a global command available from any directory.
|
|
58
|
+
|
|
59
|
+
### From Source
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
git clone https://github.com/phillt/bernard.git && cd bernard
|
|
63
|
+
npm install
|
|
64
|
+
npm run build
|
|
65
|
+
npm link
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Quick Start
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Store an API key
|
|
72
|
+
bernard add-key anthropic sk-ant-...
|
|
73
|
+
|
|
74
|
+
# Start the REPL
|
|
75
|
+
bernard
|
|
76
|
+
|
|
77
|
+
# Or use a specific provider
|
|
78
|
+
bernard -p openai -m gpt-4o
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Once inside the REPL, just type naturally:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
bernard> what's in this directory?
|
|
85
|
+
▶ shell: ls -la
|
|
86
|
+
...
|
|
87
|
+
|
|
88
|
+
bernard> show me the git log for the last week
|
|
89
|
+
▶ shell: git log --since="1 week ago" --oneline
|
|
90
|
+
...
|
|
91
|
+
|
|
92
|
+
bernard> remember that this project uses pnpm, not npm
|
|
93
|
+
▶ memory: write "project-conventions" ...
|
|
94
|
+
Got it — I'll remember that for future sessions.
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Configuration
|
|
100
|
+
|
|
101
|
+
### API Keys
|
|
102
|
+
|
|
103
|
+
The recommended way to store API keys is with the `add-key` command:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
bernard add-key anthropic sk-ant-api03-...
|
|
107
|
+
bernard add-key openai sk-...
|
|
108
|
+
bernard add-key xai xai-...
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Keys are stored in `~/.bernard/keys.json` with restricted file permissions (mode 0600). You can also set keys via environment variables if you prefer.
|
|
112
|
+
|
|
113
|
+
Check which providers have keys configured:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
bernard providers
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Environment Variables
|
|
120
|
+
|
|
121
|
+
Bernard loads `.env` from the current directory first, then falls back to `~/.bernard/.env`.
|
|
122
|
+
|
|
123
|
+
| Variable | Description | Default |
|
|
124
|
+
|----------|-------------|---------|
|
|
125
|
+
| `BERNARD_PROVIDER` | LLM provider (`anthropic`, `openai`, `xai`) | `anthropic` |
|
|
126
|
+
| `BERNARD_MODEL` | Model name | Provider-specific default |
|
|
127
|
+
| `BERNARD_MAX_TOKENS` | Max response tokens | `4096` |
|
|
128
|
+
| `BERNARD_SHELL_TIMEOUT` | Shell command timeout (ms) | `30000` |
|
|
129
|
+
| `BERNARD_RAG_ENABLED` | Enable the RAG memory system | `true` |
|
|
130
|
+
| `BERNARD_DEBUG` | Enable debug logging | unset |
|
|
131
|
+
| `ANTHROPIC_API_KEY` | Anthropic API key | — |
|
|
132
|
+
| `OPENAI_API_KEY` | OpenAI API key | — |
|
|
133
|
+
| `XAI_API_KEY` | xAI API key | — |
|
|
134
|
+
|
|
135
|
+
### Providers and Models
|
|
136
|
+
|
|
137
|
+
| Provider | Default Model | Available Models |
|
|
138
|
+
|----------|--------------|------------------|
|
|
139
|
+
| `anthropic` | `claude-sonnet-4-5-20250929` | `claude-sonnet-4-5-20250929`, `claude-opus-4-20250514`, `claude-sonnet-4-20250514`, `claude-3-5-haiku-latest` |
|
|
140
|
+
| `openai` | `gpt-4o` | `gpt-4o`, `gpt-4o-mini`, `o3`, `o3-mini`, `o4-mini`, `gpt-4.1`, `gpt-4.1-mini`, `gpt-4.1-nano` |
|
|
141
|
+
| `xai` | `grok-3` | `grok-3`, `grok-3-fast`, `grok-3-mini`, `grok-3-mini-fast` |
|
|
142
|
+
|
|
143
|
+
You can switch providers and models at any time during a session with `/provider` and `/model`.
|
|
144
|
+
|
|
145
|
+
### Runtime Options
|
|
146
|
+
|
|
147
|
+
Options can be changed during a session with `/options` or persisted to `~/.bernard/preferences.json`:
|
|
148
|
+
|
|
149
|
+
| Option | Default | Description |
|
|
150
|
+
|--------|---------|-------------|
|
|
151
|
+
| `max-tokens` | `4096` | Maximum tokens per AI response |
|
|
152
|
+
| `shell-timeout` | `30000` | Shell command timeout in milliseconds |
|
|
153
|
+
|
|
154
|
+
From the CLI:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
bernard list-options # Show current option values
|
|
158
|
+
bernard reset-option max-tokens # Reset a single option
|
|
159
|
+
bernard reset-options # Reset all options to defaults
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Usage
|
|
165
|
+
|
|
166
|
+
### CLI Flags
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
bernard # Start with defaults
|
|
170
|
+
bernard -p openai -m gpt-4o # Specify provider and model
|
|
171
|
+
bernard -r # Resume previous conversation
|
|
172
|
+
bernard --alert <id> # Open with cron alert context
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
| Flag | Description |
|
|
176
|
+
|------|-------------|
|
|
177
|
+
| `-p, --provider <name>` | LLM provider (anthropic, openai, xai) |
|
|
178
|
+
| `-m, --model <name>` | Model name |
|
|
179
|
+
| `-r, --resume` | Resume the previous conversation |
|
|
180
|
+
| `--alert <id>` | Load context from a cron alert |
|
|
181
|
+
|
|
182
|
+
### CLI Management Commands
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
bernard add-key <provider> <key> # Store an API key securely
|
|
186
|
+
bernard providers # List providers and key status
|
|
187
|
+
bernard list-options # Show configurable options
|
|
188
|
+
bernard reset-option <option> # Reset one option to default
|
|
189
|
+
bernard reset-options # Reset all options (with confirmation)
|
|
190
|
+
bernard mcp-list # List configured MCP servers
|
|
191
|
+
bernard remove-mcp <key> # Remove an MCP server
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Interactive REPL
|
|
195
|
+
|
|
196
|
+
Once running, Bernard presents an interactive prompt where you type natural language requests. Bernard has access to a suite of tools it can call autonomously — shell commands, memory, web fetching, and more.
|
|
197
|
+
|
|
198
|
+
Features:
|
|
199
|
+
- **Multi-line paste support** — paste code blocks directly; Bernard detects bracket paste mode
|
|
200
|
+
- **Live command hints** — type `/` and matching slash commands appear as suggestions
|
|
201
|
+
- **Abort in progress** — press Escape to cancel an in-flight request
|
|
202
|
+
- **Ctrl+C** — graceful exit with cleanup
|
|
203
|
+
|
|
204
|
+
### REPL Slash Commands
|
|
205
|
+
|
|
206
|
+
| Command | Description |
|
|
207
|
+
|---------|-------------|
|
|
208
|
+
| `/help` | Show available commands |
|
|
209
|
+
| `/clear` | Clear conversation history and scratch notes |
|
|
210
|
+
| `/memory` | List all persistent memories |
|
|
211
|
+
| `/scratch` | List session scratch notes |
|
|
212
|
+
| `/mcp` | List connected MCP servers and their tools |
|
|
213
|
+
| `/cron` | Show cron jobs and daemon status |
|
|
214
|
+
| `/rag` | Show RAG memory stats and recent facts |
|
|
215
|
+
| `/provider` | Switch LLM provider interactively |
|
|
216
|
+
| `/model` | Switch model for the current provider |
|
|
217
|
+
| `/options` | View and modify runtime options |
|
|
218
|
+
| `/exit` | Quit Bernard (also: `exit`, `quit`) |
|
|
219
|
+
|
|
220
|
+
Prefix with `\` to send a `/`-prefixed message as text instead of a command (e.g., `\/etc/hosts` sends the literal string).
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Tools
|
|
225
|
+
|
|
226
|
+
Bernard has access to the following tools, which it calls automatically based on your requests.
|
|
227
|
+
|
|
228
|
+
### Shell Execution
|
|
229
|
+
|
|
230
|
+
Execute any terminal command in the current working directory.
|
|
231
|
+
|
|
232
|
+
```
|
|
233
|
+
bernard> what git branch am I on?
|
|
234
|
+
▶ shell: git branch --show-current
|
|
235
|
+
main
|
|
236
|
+
|
|
237
|
+
You're on the main branch.
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Dangerous command protection:** Bernard detects risky patterns (`rm -rf`, `sudo`, `mkfs`, `dd`, `chmod 777`, `reboot`, `kill -9`, etc.) and asks for your confirmation before executing.
|
|
241
|
+
|
|
242
|
+
**Timeout:** Commands time out after 30 seconds by default (configurable via `shell-timeout` option).
|
|
243
|
+
|
|
244
|
+
**Output limit:** Command output is capped at 10MB.
|
|
245
|
+
|
|
246
|
+
### Web Reading
|
|
247
|
+
|
|
248
|
+
Fetch any web page and convert it to markdown for analysis.
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
bernard> read https://docs.example.com/api and summarize it
|
|
252
|
+
▶ web_read: https://docs.example.com/api
|
|
253
|
+
...
|
|
254
|
+
|
|
255
|
+
Here's a summary of the API docs: ...
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Supports an optional CSS selector to target specific content (e.g., `article`, `main`, `.post-body`). Strips scripts, styles, navigation, footers, and other non-content elements.
|
|
259
|
+
|
|
260
|
+
### Memory (Persistent)
|
|
261
|
+
|
|
262
|
+
Long-term memory that persists across sessions. Stored as markdown files in `~/.bernard/memory/`.
|
|
263
|
+
|
|
264
|
+
```
|
|
265
|
+
bernard> remember that the staging server is at 10.0.1.50
|
|
266
|
+
▶ memory: write "staging-server" ...
|
|
267
|
+
|
|
268
|
+
bernard> what's the staging server IP?
|
|
269
|
+
(reads from memory automatically via system prompt)
|
|
270
|
+
The staging server is at 10.0.1.50.
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Actions: `list`, `read`, `write`, `delete`. All persistent memories are automatically injected into Bernard's system prompt each turn, so they're always available without needing to be explicitly recalled.
|
|
274
|
+
|
|
275
|
+
### Scratch Notes (Session)
|
|
276
|
+
|
|
277
|
+
Temporary working notes that survive context compression but are discarded when the session ends. Useful for tracking multi-step task progress.
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
bernard> I need to migrate 5 database tables — track progress in scratch
|
|
281
|
+
▶ scratch: write "migration-progress" ...
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Actions: `list`, `read`, `write`, `delete`. Scratch notes are also injected into the system prompt, so Bernard always knows the current session state.
|
|
285
|
+
|
|
286
|
+
### Date and Time
|
|
287
|
+
|
|
288
|
+
Returns the current date, time, and timezone. Bernard calls this automatically when needed.
|
|
289
|
+
|
|
290
|
+
### Time Range Calculations
|
|
291
|
+
|
|
292
|
+
Calculate durations between military times and total durations across multiple time ranges. Handles overnight wrapping (e.g., 2300 to 0100 = 2 hours).
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
bernard> how long is a shift from 0800 to 1730?
|
|
296
|
+
▶ time_range: 800 → 1730
|
|
297
|
+
9 hours 30 minutes
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Sub-Agents
|
|
301
|
+
|
|
302
|
+
Bernard can delegate independent tasks to parallel sub-agents, each with their own tool set. Sub-agents run concurrently and report back when done.
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
bernard> check the disk usage on /, look up the weather in Austin, and count lines of code in this project
|
|
306
|
+
▶ agent: "Check disk usage on /"
|
|
307
|
+
▶ agent: "Look up weather in Austin"
|
|
308
|
+
▶ agent: "Count lines of code"
|
|
309
|
+
[sub:1] ▶ shell: df -h /
|
|
310
|
+
[sub:2] ▶ web_read: ...
|
|
311
|
+
[sub:3] ▶ shell: find . -name "*.ts" | xargs wc -l
|
|
312
|
+
...
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Up to 4 concurrent sub-agents. Each gets 10 max steps. Color-coded output in the terminal.
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Cron Jobs (Scheduled Tasks)
|
|
320
|
+
|
|
321
|
+
Bernard can create and manage scheduled background tasks that run on a cron schedule. Jobs are executed by a background daemon process with their own AI agent instance.
|
|
322
|
+
|
|
323
|
+
### Creating Jobs
|
|
324
|
+
|
|
325
|
+
Ask Bernard to set up a scheduled task:
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
bernard> every hour, check if the API at https://api.example.com/health returns 200 and notify me if it doesn't
|
|
329
|
+
▶ cron_create: { name: "api-health-check", schedule: "0 * * * *", prompt: "..." }
|
|
330
|
+
|
|
331
|
+
Created cron job "api-health-check" (runs hourly).
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Or be explicit about the schedule:
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
bernard> create a cron job called "disk-check" that runs every 5 minutes and alerts me if disk usage exceeds 90%
|
|
338
|
+
▶ cron_create: { name: "disk-check", schedule: "*/5 * * * *", prompt: "..." }
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Managing Jobs
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
bernard> list my cron jobs
|
|
345
|
+
▶ cron_list
|
|
346
|
+
|
|
347
|
+
bernard> disable the disk-check job
|
|
348
|
+
▶ cron_disable: { id: "abc123" }
|
|
349
|
+
|
|
350
|
+
bernard> update the api health check to run every 30 minutes instead
|
|
351
|
+
▶ cron_update: { id: "def456", schedule: "*/30 * * * *" }
|
|
352
|
+
|
|
353
|
+
bernard> delete the disk-check job
|
|
354
|
+
▶ cron_delete: { id: "abc123" }
|
|
355
|
+
|
|
356
|
+
bernard> what's the cron daemon status?
|
|
357
|
+
▶ cron_status
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Use `/cron` in the REPL for a quick status overview.
|
|
361
|
+
|
|
362
|
+
Available cron tools: `cron_create`, `cron_list`, `cron_get`, `cron_update`, `cron_delete`, `cron_enable`, `cron_disable`, `cron_status`, `cron_bounce`.
|
|
363
|
+
|
|
364
|
+
### Execution Logs
|
|
365
|
+
|
|
366
|
+
Every cron job run is logged with full execution traces:
|
|
367
|
+
|
|
368
|
+
```
|
|
369
|
+
bernard> show me the last 5 runs of the api health check
|
|
370
|
+
▶ cron_logs_list: { job_id: "def456", limit: 5 }
|
|
371
|
+
|
|
372
|
+
bernard> show me the full trace of that failed run
|
|
373
|
+
▶ cron_logs_get: { job_id: "def456", run_id: "run789" }
|
|
374
|
+
|
|
375
|
+
bernard> give me a summary of the api health check job performance
|
|
376
|
+
▶ cron_logs_summary: { job_id: "def456" }
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Logs include: step-by-step traces, tool calls and results, token usage, durations, success/error status.
|
|
380
|
+
|
|
381
|
+
Log management: `cron_logs_cleanup` supports `rotate` (keep N recent entries) and `delete` (remove all logs for a job).
|
|
382
|
+
|
|
383
|
+
### Notifications
|
|
384
|
+
|
|
385
|
+
Cron jobs can send desktop notifications when they need your attention. The daemon uses `node-notifier` for cross-platform notification support. When you receive an alert, start Bernard with `--alert <id>` to load the alert context.
|
|
386
|
+
|
|
387
|
+
---
|
|
388
|
+
|
|
389
|
+
## MCP Servers (External Tools)
|
|
390
|
+
|
|
391
|
+
Bernard supports the [Model Context Protocol (MCP)](https://modelcontextprotocol.io) for connecting to external tool servers. MCP servers provide additional tools that Bernard can use alongside its built-in tools.
|
|
392
|
+
|
|
393
|
+
Configuration is stored in `~/.bernard/mcp.json`.
|
|
394
|
+
|
|
395
|
+
### Stdio Servers
|
|
396
|
+
|
|
397
|
+
Stdio-based MCP servers run as child processes:
|
|
398
|
+
|
|
399
|
+
```
|
|
400
|
+
bernard> add an MCP server for filesystem access using npx @modelcontextprotocol/server-filesystem with /home/user as the root
|
|
401
|
+
▶ mcp_config: add { key: "filesystem", command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"] }
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Resulting config:
|
|
405
|
+
```json
|
|
406
|
+
{
|
|
407
|
+
"mcpServers": {
|
|
408
|
+
"filesystem": {
|
|
409
|
+
"command": "npx",
|
|
410
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user"]
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
### URL-Based Servers (SSE/HTTP)
|
|
417
|
+
|
|
418
|
+
URL-based MCP servers connect over SSE or HTTP transport:
|
|
419
|
+
|
|
420
|
+
```
|
|
421
|
+
bernard> add this MCP server: http://localhost:6288/web/sse
|
|
422
|
+
▶ mcp_add_url: { key: "my-mcp", url: "http://localhost:6288/web/sse" }
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Resulting config:
|
|
426
|
+
```json
|
|
427
|
+
{
|
|
428
|
+
"mcpServers": {
|
|
429
|
+
"my-mcp": {
|
|
430
|
+
"url": "http://localhost:6288/web/sse"
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
URL servers support optional fields:
|
|
437
|
+
- `type` — `"sse"` (default) or `"http"` for Streamable HTTP transport
|
|
438
|
+
- `headers` — for authentication tokens or custom headers
|
|
439
|
+
|
|
440
|
+
Example with all fields:
|
|
441
|
+
```json
|
|
442
|
+
{
|
|
443
|
+
"mcpServers": {
|
|
444
|
+
"authenticated-server": {
|
|
445
|
+
"url": "https://example.com/mcp",
|
|
446
|
+
"type": "http",
|
|
447
|
+
"headers": { "Authorization": "Bearer token123" }
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Managing MCP Servers
|
|
454
|
+
|
|
455
|
+
```
|
|
456
|
+
bernard> list my MCP servers
|
|
457
|
+
▶ mcp_config: list
|
|
458
|
+
|
|
459
|
+
bernard> show details for the filesystem server
|
|
460
|
+
▶ mcp_config: get { key: "filesystem" }
|
|
461
|
+
|
|
462
|
+
bernard> remove the filesystem server
|
|
463
|
+
▶ mcp_config: remove { key: "filesystem" }
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
From the CLI:
|
|
467
|
+
```bash
|
|
468
|
+
bernard mcp-list # List all configured servers
|
|
469
|
+
bernard remove-mcp <key> # Remove a server
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Use `/mcp` in the REPL to see connected servers and their available tools.
|
|
473
|
+
|
|
474
|
+
**Note:** MCP server changes take effect after restarting Bernard. Servers are connected at startup.
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## Context Management
|
|
479
|
+
|
|
480
|
+
### Automatic Compression
|
|
481
|
+
|
|
482
|
+
Bernard automatically compresses conversation history when it approaches 75% of the model's context window. During compression:
|
|
483
|
+
|
|
484
|
+
1. Recent messages (last 4 turns) are preserved in full
|
|
485
|
+
2. Older messages are summarized by the LLM into a concise recap
|
|
486
|
+
3. Key facts are extracted and stored in the RAG memory system
|
|
487
|
+
4. The conversation continues seamlessly with the compressed context
|
|
488
|
+
|
|
489
|
+
Scratch notes survive compression, so multi-step task progress is never lost.
|
|
490
|
+
|
|
491
|
+
### RAG Memory
|
|
492
|
+
|
|
493
|
+
Bernard has a Retrieval-Augmented Generation (RAG) system that provides long-term memory beyond the current session:
|
|
494
|
+
|
|
495
|
+
- **Automatic fact extraction** — when context is compressed, key facts are extracted and stored with embeddings
|
|
496
|
+
- **Semantic search** — on each new user message, relevant facts are retrieved and injected into the system prompt as "Recalled Context"
|
|
497
|
+
- **Local embeddings** — uses FastEmbed (`AllMiniLML6V2`, 384 dimensions) for fully local embedding computation
|
|
498
|
+
- **Deduplication** — facts too similar to existing ones (>92% cosine similarity) are skipped
|
|
499
|
+
- **Pruning** — older, less-accessed facts decay over time (90-day half-life); the store caps at 5000 facts
|
|
500
|
+
|
|
501
|
+
Use `/rag` in the REPL to see RAG stats and recent facts.
|
|
502
|
+
|
|
503
|
+
Storage: `~/.bernard/rag/memories.json`
|
|
504
|
+
|
|
505
|
+
To disable RAG: set `BERNARD_RAG_ENABLED=false`.
|
|
506
|
+
|
|
507
|
+
### Conversation Resume
|
|
508
|
+
|
|
509
|
+
Bernard saves your conversation history when you exit. Resume where you left off:
|
|
510
|
+
|
|
511
|
+
```bash
|
|
512
|
+
bernard -r
|
|
513
|
+
# or
|
|
514
|
+
bernard --resume
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
The previous conversation is replayed in the terminal (truncated for readability) and the full context is restored.
|
|
518
|
+
|
|
519
|
+
Storage: `~/.bernard/conversation-history.json`
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
|
|
523
|
+
## File Structure
|
|
524
|
+
|
|
525
|
+
Bernard stores all data in `~/.bernard/`:
|
|
526
|
+
|
|
527
|
+
```
|
|
528
|
+
~/.bernard/
|
|
529
|
+
├── keys.json # API keys (mode 0600)
|
|
530
|
+
├── preferences.json # Provider, model, options
|
|
531
|
+
├── .env # Fallback environment config
|
|
532
|
+
├── mcp.json # MCP server configuration
|
|
533
|
+
├── conversation-history.json # Last session (for --resume)
|
|
534
|
+
├── memory/ # Persistent memories (*.md)
|
|
535
|
+
├── rag/
|
|
536
|
+
│ └── memories.json # RAG fact embeddings
|
|
537
|
+
└── cron/
|
|
538
|
+
├── jobs.json # Scheduled jobs
|
|
539
|
+
├── daemon.pid # Daemon process ID
|
|
540
|
+
├── daemon.log # Daemon output (rotates at 1MB)
|
|
541
|
+
├── logs/ # Per-job execution logs
|
|
542
|
+
└── alerts/ # Cron alert files
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
---
|
|
546
|
+
|
|
547
|
+
## Development
|
|
548
|
+
|
|
549
|
+
### Building
|
|
550
|
+
|
|
551
|
+
```bash
|
|
552
|
+
npm run build # Compile TypeScript to dist/
|
|
553
|
+
npm run dev # Run via tsx with debug logging (no build needed)
|
|
554
|
+
npm start # Run compiled output
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Testing
|
|
558
|
+
|
|
559
|
+
```bash
|
|
560
|
+
npm test # Run all tests once
|
|
561
|
+
npm run test:watch # Run tests in watch mode
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
Uses [Vitest](https://vitest.dev) as the test runner.
|
|
565
|
+
|
|
566
|
+
### Debug Logging
|
|
567
|
+
|
|
568
|
+
Set `BERNARD_DEBUG=1` to enable verbose logging:
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
BERNARD_DEBUG=1 bernard
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
Logs are written to `.logs/YYYY-MM-DD.log` in JSON format, covering agent processing, RAG operations, context compression, tool execution, and MCP operations.
|
|
575
|
+
|
|
576
|
+
### Adding a New Provider
|
|
577
|
+
|
|
578
|
+
1. Install the AI SDK provider package (e.g., `npm install @ai-sdk/google`)
|
|
579
|
+
2. Add a case to `getModel()` in `src/providers/index.ts`
|
|
580
|
+
3. Add the API key variable to `src/config.ts`
|
|
581
|
+
|
|
582
|
+
### Adding a New Tool
|
|
583
|
+
|
|
584
|
+
1. Create `src/tools/newtool.ts` using the `tool()` helper from `ai` with a Zod schema for parameters
|
|
585
|
+
2. Register it in `src/tools/index.ts`
|
|
586
|
+
|
|
587
|
+
### Project Structure
|
|
588
|
+
|
|
589
|
+
```
|
|
590
|
+
src/
|
|
591
|
+
├── index.ts # CLI entry point (Commander)
|
|
592
|
+
├── repl.ts # Interactive REPL loop
|
|
593
|
+
├── agent.ts # Agent class (generateText loop)
|
|
594
|
+
├── config.ts # Config loading and validation
|
|
595
|
+
├── output.ts # Terminal formatting (Chalk)
|
|
596
|
+
├── memory.ts # MemoryStore (persistent + scratch)
|
|
597
|
+
├── context.ts # Context compression
|
|
598
|
+
├── rag.ts # RAG store (embeddings + search)
|
|
599
|
+
├── embeddings.ts # FastEmbed wrapper
|
|
600
|
+
├── mcp.ts # MCP server manager
|
|
601
|
+
├── history.ts # Conversation save/load
|
|
602
|
+
├── logger.ts # Debug file logger
|
|
603
|
+
├── providers/
|
|
604
|
+
│ └── index.ts # getModel() factory
|
|
605
|
+
├── tools/
|
|
606
|
+
│ ├── index.ts # Tool registry
|
|
607
|
+
│ ├── shell.ts # Shell execution
|
|
608
|
+
│ ├── memory.ts # Memory + scratch tools
|
|
609
|
+
│ ├── web.ts # Web page fetching
|
|
610
|
+
│ ├── datetime.ts # Date/time
|
|
611
|
+
│ ├── time.ts # Time range calculations
|
|
612
|
+
│ ├── cron.ts # Cron job management
|
|
613
|
+
│ ├── cron-logs.ts # Cron execution logs
|
|
614
|
+
│ ├── mcp.ts # MCP config (stdio)
|
|
615
|
+
│ ├── mcp-url.ts # MCP config (URL-based)
|
|
616
|
+
│ └── subagent.ts # Parallel sub-agents
|
|
617
|
+
└── cron/
|
|
618
|
+
├── store.ts # Job + alert persistence
|
|
619
|
+
├── daemon.ts # Background daemon process
|
|
620
|
+
├── runner.ts # Job execution
|
|
621
|
+
├── scheduler.ts # Cron scheduling
|
|
622
|
+
├── client.ts # Daemon lifecycle
|
|
623
|
+
├── log-store.ts # Execution log storage
|
|
624
|
+
└── notify.ts # Desktop notifications
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
## License
|
|
628
|
+
|
|
629
|
+
MIT
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type CoreMessage } from 'ai';
|
|
2
|
+
import { type ToolOptions } from './tools/index.js';
|
|
3
|
+
import type { BernardConfig } from './config.js';
|
|
4
|
+
import type { MemoryStore } from './memory.js';
|
|
5
|
+
import type { RAGStore, RAGSearchResult } from './rag.js';
|
|
6
|
+
/** @internal */
|
|
7
|
+
export declare function buildSystemPrompt(config: BernardConfig, memoryStore: MemoryStore, mcpServerNames?: string[], ragResults?: RAGSearchResult[]): string;
|
|
8
|
+
export declare class Agent {
|
|
9
|
+
private history;
|
|
10
|
+
private config;
|
|
11
|
+
private toolOptions;
|
|
12
|
+
private memoryStore;
|
|
13
|
+
private mcpTools?;
|
|
14
|
+
private mcpServerNames?;
|
|
15
|
+
private alertContext?;
|
|
16
|
+
private ragStore?;
|
|
17
|
+
private abortController;
|
|
18
|
+
private lastPromptTokens;
|
|
19
|
+
constructor(config: BernardConfig, toolOptions: ToolOptions, memoryStore: MemoryStore, mcpTools?: Record<string, any>, mcpServerNames?: string[], alertContext?: string, initialHistory?: CoreMessage[], ragStore?: RAGStore);
|
|
20
|
+
getHistory(): CoreMessage[];
|
|
21
|
+
abort(): void;
|
|
22
|
+
processInput(userInput: string): Promise<void>;
|
|
23
|
+
clearHistory(): void;
|
|
24
|
+
}
|