Compare commits
1 Commits
3893d88365
...
feat/skill
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66fb080297 |
32
README.md
32
README.md
@@ -42,7 +42,7 @@ mkdir -p ~/.nanobot
|
|||||||
**2. Chat**
|
**2. Chat**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run nanobot agent
|
bun run start agent
|
||||||
```
|
```
|
||||||
|
|
||||||
That's it.
|
That's it.
|
||||||
@@ -54,21 +54,22 @@ That's it.
|
|||||||
Chat with the agent from your terminal. Does not require a running gateway.
|
Chat with the agent from your terminal. Does not require a running gateway.
|
||||||
|
|
||||||
```
|
```
|
||||||
bun run nanobot agent [options]
|
bun run start agent [options]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
|--------|-------------|
|
|--------|-------------|
|
||||||
| `-c, --config <path>` | Path to `config.json` (default: `~/.nanobot/config.json`) |
|
| `-c, --config <path>` | Path to `config.json` (default: `~/.nanobot/config.json`) |
|
||||||
| `-m, --message <text>` | Send a single message and exit (non-interactive) |
|
| `-m, --message <text>` | Send a single message and exit (non-interactive) |
|
||||||
|
| `-w, --workspace <path>` | Override the workspace directory |
|
||||||
| `-M, --model <model>` | Override the model for this session |
|
| `-M, --model <model>` | Override the model for this session |
|
||||||
|
|
||||||
**Interactive mode** (default when no `-m` is given):
|
**Interactive mode** (default when no `-m` is given):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run nanobot agent
|
bun run start agent
|
||||||
bun run nanobot agent -c ~/.nanobot-work/config.json
|
bun run start agent -c ~/.nanobot-work/config.json
|
||||||
bun run nanobot agent -w /tmp/scratch
|
bun run start agent -w /tmp/scratch
|
||||||
```
|
```
|
||||||
|
|
||||||
Press `Ctrl+C` to exit.
|
Press `Ctrl+C` to exit.
|
||||||
@@ -76,8 +77,8 @@ Press `Ctrl+C` to exit.
|
|||||||
**Single-shot mode:**
|
**Single-shot mode:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run nanobot agent -m "What time is it in Tokyo?"
|
bun run start agent -m "What time is it in Tokyo?"
|
||||||
bun run nanobot agent -m "Summarize the file ./notes.md"
|
bun run start agent -m "Summarize the file ./notes.md"
|
||||||
```
|
```
|
||||||
|
|
||||||
### `gateway` — Mattermost bot
|
### `gateway` — Mattermost bot
|
||||||
@@ -85,7 +86,7 @@ bun run nanobot agent -m "Summarize the file ./notes.md"
|
|||||||
Runs the full stack: Mattermost WebSocket channel, agent loop, cron scheduler, and heartbeat.
|
Runs the full stack: Mattermost WebSocket channel, agent loop, cron scheduler, and heartbeat.
|
||||||
|
|
||||||
```
|
```
|
||||||
bun run nanobot gateway [options]
|
bun run start gateway [options]
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Description |
|
| Option | Description |
|
||||||
@@ -93,8 +94,8 @@ bun run nanobot gateway [options]
|
|||||||
| `-c, --config <path>` | Path to `config.json` (default: `~/.nanobot/config.json`) |
|
| `-c, --config <path>` | Path to `config.json` (default: `~/.nanobot/config.json`) |
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run nanobot gateway
|
bun run start gateway
|
||||||
bun run nanobot gateway -c ~/.nanobot-work/config.json
|
bun run start gateway -c ~/.nanobot-work/config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Handles `SIGINT` / `SIGTERM` for graceful shutdown.
|
Handles `SIGINT` / `SIGTERM` for graceful shutdown.
|
||||||
@@ -109,6 +110,7 @@ Environment variable overrides:
|
|||||||
|----------|-------------------|
|
|----------|-------------------|
|
||||||
| `NANOBOT_CONFIG` | path to config file |
|
| `NANOBOT_CONFIG` | path to config file |
|
||||||
| `NANOBOT_MODEL` | `agent.model` |
|
| `NANOBOT_MODEL` | `agent.model` |
|
||||||
|
| `NANOBOT_WORKSPACE` | `agent.workspacePath` |
|
||||||
|
|
||||||
### Full config reference
|
### Full config reference
|
||||||
|
|
||||||
@@ -196,7 +198,7 @@ For Ollama, set `providers.ollama.apiBase` (default: `http://localhost:11434/api
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Run `bun run nanobot gateway`
|
4. Run `bun run start gateway`
|
||||||
|
|
||||||
`allowFrom` controls which users the bot responds to. Use `["*"]` to allow all users.
|
`allowFrom` controls which users the bot responds to. Use `["*"]` to allow all users.
|
||||||
|
|
||||||
@@ -231,10 +233,10 @@ Run separate instances with different configs — useful for isolated workspaces
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Instance A
|
# Instance A
|
||||||
bun run nanobot gateway -c ~/.nanobot-a/config.json
|
bun run start gateway -c ~/.nanobot-a/config.json
|
||||||
|
|
||||||
# Instance B
|
# Instance B
|
||||||
bun run nanobot gateway -c ~/.nanobot-b/config.json
|
bun run start gateway -c ~/.nanobot-b/config.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Each instance needs its own config file. Set a different `agent.workspacePath` per instance to keep memory, sessions, and cron jobs isolated:
|
Each instance needs its own config file. Set a different `agent.workspacePath` per instance to keep memory, sessions, and cron jobs isolated:
|
||||||
@@ -250,10 +252,10 @@ Each instance needs its own config file. Set a different `agent.workspacePath` p
|
|||||||
To run a local CLI session against a specific instance:
|
To run a local CLI session against a specific instance:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run nanobot agent -c ~/.nanobot-a/config.json -m "Hello"
|
bun run start agent -c ~/.nanobot-a/config.json -m "Hello"
|
||||||
|
|
||||||
# Temporarily override the workspace for a one-off run
|
# Temporarily override the workspace for a one-off run
|
||||||
bun run nanobot agent -c ~/.nanobot-a/config.json -w /tmp/scratch
|
bun run start agent -c ~/.nanobot-a/config.json -w /tmp/scratch
|
||||||
```
|
```
|
||||||
|
|
||||||
## Linux service (systemd)
|
## Linux service (systemd)
|
||||||
|
|||||||
@@ -49,28 +49,6 @@ Inbound and outbound messages are passed through a typed `AsyncQueue<T>`. The qu
|
|||||||
## Logging Pattern
|
## Logging Pattern
|
||||||
Use `console.error` / `console.warn` / `console.info` / `console.debug` — no external logger. Color via `picocolors` in CLI output only.
|
Use `console.error` / `console.warn` / `console.info` / `console.debug` — no external logger. Color via `picocolors` in CLI output only.
|
||||||
|
|
||||||
## CLI Command Pattern
|
|
||||||
Each command is in its own file with a registration function:
|
|
||||||
```ts
|
|
||||||
// src/cli/agent.ts
|
|
||||||
export function agentCommand(program: Command, config: Config, workspace: string): void {
|
|
||||||
program.command('agent')
|
|
||||||
.description('...')
|
|
||||||
.option('-m, --message <text>', 'Single message to process')
|
|
||||||
.action(async (opts) => { /* ... */ })
|
|
||||||
}
|
|
||||||
|
|
||||||
// src/cli/commands.ts (bootstrap)
|
|
||||||
export function createCli(): Command {
|
|
||||||
const program = new Command('nanobot')...
|
|
||||||
const config = loadConfig(opts.config);
|
|
||||||
const workspace = resolveWorkspacePath(config.agent.workspacePath);
|
|
||||||
gatewayCommand(program, config, workspace);
|
|
||||||
agentCommand(program, config, workspace);
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## File Layout
|
## File Layout
|
||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
@@ -89,11 +67,7 @@ src/
|
|||||||
tools/base.ts + filesystem.ts + shell.ts + web.ts + message.ts + spawn.ts + cron.ts
|
tools/base.ts + filesystem.ts + shell.ts + web.ts + message.ts + spawn.ts + cron.ts
|
||||||
channels/
|
channels/
|
||||||
base.ts + mattermost.ts + manager.ts
|
base.ts + mattermost.ts + manager.ts
|
||||||
cli/
|
cli/commands.ts
|
||||||
types.ts # CommandHandler type
|
|
||||||
commands.ts # Bootstrap - loads config, registers commands
|
|
||||||
agent.ts # agentCommand() - interactive/single-shot mode
|
|
||||||
gateway.ts # gatewayCommand() - full runtime with Mattermost
|
|
||||||
index.ts
|
index.ts
|
||||||
templates/ (SOUL.md, AGENTS.md, USER.md, TOOLS.md, HEARTBEAT.md, memory/MEMORY.md)
|
templates/ (SOUL.md, AGENTS.md, USER.md, TOOLS.md, HEARTBEAT.md, memory/MEMORY.md)
|
||||||
skills/ (copied from Python repo)
|
skills/ (copied from Python repo)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"nanobot": "bun run index.ts",
|
"start": "bun run index.ts",
|
||||||
"dev": "bun --watch run index.ts",
|
"dev": "bun --watch run index.ts",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"fmt": "oxfmt --check",
|
"fmt": "oxfmt --check",
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { mkdirSync } from 'node:fs';
|
||||||
import { createInterface } from 'node:readline';
|
import { createInterface } from 'node:readline';
|
||||||
import { Command } from 'commander';
|
import { Command } from 'commander';
|
||||||
import pc from 'picocolors';
|
import pc from 'picocolors';
|
||||||
@@ -7,12 +8,17 @@ import type { Config } from '../config/types.ts';
|
|||||||
import { makeProvider } from '../provider/index.ts';
|
import { makeProvider } from '../provider/index.ts';
|
||||||
|
|
||||||
export function agentCommand(program: Command, config: Config, workspace: string): void {
|
export function agentCommand(program: Command, config: Config, workspace: string): void {
|
||||||
|
mkdirSync(workspace, { recursive: true });
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('agent')
|
.command('agent')
|
||||||
.description('Run the agent interactively or send a single message.')
|
.description('Run the agent interactively or send a single message.')
|
||||||
|
.option('-c, --config <path>', 'Path to config.json')
|
||||||
.option('-m, --message <text>', 'Single message to process (non-interactive)')
|
.option('-m, --message <text>', 'Single message to process (non-interactive)')
|
||||||
|
.option('-w, --workspace <path>', 'Workspace path override')
|
||||||
.option('-M, --model <model>', 'Model override')
|
.option('-M, --model <model>', 'Model override')
|
||||||
.action(async (opts: { config?: string; message?: string; model?: string }) => {
|
.action(
|
||||||
|
async (opts: { config?: string; message?: string; workspace?: string; model?: string }) => {
|
||||||
const model = opts.model ?? config.agent.model;
|
const model = opts.model ?? config.agent.model;
|
||||||
const provider = makeProvider(
|
const provider = makeProvider(
|
||||||
config.providers,
|
config.providers,
|
||||||
@@ -81,5 +87,6 @@ export function agentCommand(program: Command, config: Config, workspace: string
|
|||||||
});
|
});
|
||||||
|
|
||||||
promptUser();
|
promptUser();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,15 @@ import { Command } from 'commander';
|
|||||||
import { loadConfig, resolveWorkspacePath } from '../config/loader.ts';
|
import { loadConfig, resolveWorkspacePath } from '../config/loader.ts';
|
||||||
import { agentCommand } from './agent.ts';
|
import { agentCommand } from './agent.ts';
|
||||||
import { gatewayCommand } from './gateway.ts';
|
import { gatewayCommand } from './gateway.ts';
|
||||||
import pc from 'picocolors';
|
|
||||||
|
|
||||||
export function createCli(): Command {
|
export function createCli(): Command {
|
||||||
const program = new Command('nanobot')
|
const program = new Command('nanobot')
|
||||||
.description('nanobot — personal AI assistant')
|
.description('nanobot — personal AI assistant')
|
||||||
.option('-c, --config <path>', 'Path to config.json')
|
|
||||||
.version('1.0.0');
|
.version('1.0.0');
|
||||||
|
|
||||||
const globalOpts = program.opts();
|
const globalOpts = program.opts();
|
||||||
const config = loadConfig(globalOpts.config);
|
const config = loadConfig(globalOpts.config);
|
||||||
const workspace = resolveWorkspacePath(config.agent.workspacePath);
|
const workspace = resolveWorkspacePath(config.agent.workspacePath);
|
||||||
|
|
||||||
console.info(pc.magenta(`workspace path: ${workspace}`));
|
|
||||||
mkdirSync(workspace, { recursive: true });
|
mkdirSync(workspace, { recursive: true });
|
||||||
|
|
||||||
gatewayCommand(program, config, workspace);
|
gatewayCommand(program, config, workspace);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { mkdirSync } from 'node:fs';
|
||||||
import { Command } from 'commander';
|
import { Command } from 'commander';
|
||||||
import pc from 'picocolors';
|
import pc from 'picocolors';
|
||||||
import { AgentLoop } from '../agent/loop.ts';
|
import { AgentLoop } from '../agent/loop.ts';
|
||||||
@@ -10,9 +11,12 @@ import { HeartbeatService } from '../heartbeat/service.ts';
|
|||||||
import { makeProvider } from '../provider/index.ts';
|
import { makeProvider } from '../provider/index.ts';
|
||||||
|
|
||||||
export function gatewayCommand(program: Command, config: Config, workspace: string): void {
|
export function gatewayCommand(program: Command, config: Config, workspace: string): void {
|
||||||
|
mkdirSync(workspace, { recursive: true });
|
||||||
|
|
||||||
program
|
program
|
||||||
.command('gateway')
|
.command('gateway')
|
||||||
.description('Start the full gateway: Mattermost channel, agent loop, cron, and heartbeat.')
|
.description('Start the full gateway: Mattermost channel, agent loop, cron, and heartbeat.')
|
||||||
|
.option('-c, --config <path>', 'Path to config.json')
|
||||||
.action(async (_opts: { config?: string }) => {
|
.action(async (_opts: { config?: string }) => {
|
||||||
const provider = makeProvider(
|
const provider = makeProvider(
|
||||||
config.providers,
|
config.providers,
|
||||||
|
|||||||
Reference in New Issue
Block a user