@stevederico/dotbot 0.22.0 → 0.24.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/CHANGELOG.md CHANGED
@@ -1,3 +1,18 @@
1
+ 0.24
2
+
3
+ Add --system flag for custom prompts
4
+ Add tools command
5
+ Add stats command
6
+ Add memory command
7
+ Add jobs command
8
+ Add tasks command
9
+ Add sessions command
10
+ Add events command
11
+
12
+ 0.23
13
+
14
+ Fix no-args launches interactive
15
+
1
16
  0.22
2
17
 
3
18
  Simplify CLI, no args for interactive
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  <h1 align="center" style="border-bottom: none; margin-bottom: 0;">dotbot</h1>
4
4
  <h3 align="center" style="margin-top: 0; font-weight: normal;">
5
5
  The ultra-lean AI agent.<br>
6
- 11k lines. 47 tools. 0 dependencies.
6
+ 11k lines. 53 tools. 0 dependencies.
7
7
  </h3>
8
8
  <p align="center">
9
9
  <a href="https://opensource.org/licenses/mit">
@@ -13,7 +13,7 @@
13
13
  <img src="https://img.shields.io/github/stars/stevederico/dotbot?style=social" alt="GitHub stars">
14
14
  </a>
15
15
  <a href="https://github.com/stevederico/dotbot">
16
- <img src="https://img.shields.io/badge/version-0.20-green" alt="version">
16
+ <img src="https://img.shields.io/badge/version-0.24-green" alt="version">
17
17
  </a>
18
18
  <img src="https://img.shields.io/badge/LOC-11k-orange" alt="Lines of Code">
19
19
  </p>
@@ -28,7 +28,7 @@
28
28
  | | dotbot | nanobot | OpenClaw |
29
29
  |---|:---:|:---:|:---:|
30
30
  | **Lines of Code** | **11k** | 22k | 1M+ |
31
- | **Tools** | **47** | ~10 | ~50 |
31
+ | **Tools** | **53** | ~10 | ~50 |
32
32
  | **Dependencies** | Minimal | Heavy | Heavy |
33
33
 
34
34
  Everything you need for AI agents. Nothing you don't. No bloated abstractions. No dependency hell. Just a clean, focused agent that works.
@@ -42,8 +42,9 @@ A **streaming AI agent** with tool execution, autonomous tasks, and scheduled jo
42
42
  **As a CLI:**
43
43
  ```bash
44
44
  dotbot "What's the weather in San Francisco?"
45
- dotbot repl
45
+ dotbot # Interactive mode
46
46
  dotbot serve --port 3000
47
+ dotbot tools # List all 53 tools
47
48
  ```
48
49
 
49
50
  **As a library:**
@@ -67,11 +68,16 @@ export XAI_API_KEY=xai-...
67
68
  # Chat
68
69
  dotbot "Summarize the top 3 AI news stories today"
69
70
 
70
- # Interactive REPL
71
- dotbot repl
71
+ # Interactive mode
72
+ dotbot
72
73
 
73
74
  # Start HTTP server
74
75
  dotbot serve --port 3000
76
+
77
+ # Inspect data
78
+ dotbot tools
79
+ dotbot stats
80
+ dotbot memory
75
81
  ```
76
82
 
77
83
  ### Library Usage
@@ -116,7 +122,7 @@ for await (const event of agent.chat({
116
122
  - **Abort support** via AbortSignal
117
123
  - **Automatic retries** with provider failover
118
124
 
119
- ### 🔧 **47 Built-in Tools**
125
+ ### 🔧 **53 Built-in Tools**
120
126
  - **Memory** — save, search, update, delete long-term memory
121
127
  - **Web** — search, fetch, browser automation with Playwright
122
128
  - **Files** — read, write, list, delete, move files
@@ -148,18 +154,29 @@ for await (const event of agent.chat({
148
154
  ## CLI Reference
149
155
 
150
156
  ```
151
- dotbot v0.19 — AI agent CLI
157
+ dotbot v0.24 — AI agent CLI
152
158
 
153
159
  Usage:
154
- dotbot "message" Send a message (default)
155
- dotbot repl Interactive chat session
160
+ dotbot "message" One-shot query
161
+ dotbot Interactive chat
156
162
  dotbot serve [--port N] Start HTTP server (default: 3000)
157
163
 
164
+ Commands:
165
+ tools List all available tools
166
+ stats Show database statistics
167
+ memory [list|search <q>] Manage saved memories
168
+ jobs List scheduled jobs
169
+ tasks List active tasks
170
+ sessions List chat sessions
171
+ events [--summary] View audit log
172
+
158
173
  Options:
159
174
  --provider, -p AI provider: xai, anthropic, openai, ollama (default: xai)
160
175
  --model, -m Model name (default: grok-4-1-fast-reasoning)
176
+ --system, -s Custom system prompt (prepended to default)
161
177
  --db SQLite database path (default: ./dotbot.db)
162
178
  --port Server port for 'serve' command
179
+ --verbose Show initialization logs
163
180
  --help, -h Show help
164
181
  --version, -v Show version
165
182
 
@@ -230,7 +247,7 @@ for await (const event of agent.chat({
230
247
 
231
248
  <br />
232
249
 
233
- ## Built-in Tools (47)
250
+ ## Built-in Tools (53)
234
251
 
235
252
  | Category | Tools |
236
253
  |----------|-------|
package/bin/dotbot.js CHANGED
@@ -94,9 +94,19 @@ Usage:
94
94
  dotbot Interactive chat
95
95
  dotbot serve [--port N] Start HTTP server (default: ${DEFAULT_PORT})
96
96
 
97
+ Commands:
98
+ tools List all available tools
99
+ stats Show database statistics
100
+ memory [list|search <q>] Manage saved memories
101
+ jobs List scheduled jobs
102
+ tasks List active tasks
103
+ sessions List chat sessions
104
+ events [--summary] View audit log
105
+
97
106
  Options:
98
107
  --provider, -p AI provider: xai, anthropic, openai, ollama (default: xai)
99
108
  --model, -m Model name (default: grok-4-1-fast-reasoning)
109
+ --system, -s Custom system prompt (prepended to default)
100
110
  --db SQLite database path (default: ${DEFAULT_DB})
101
111
  --port Server port for 'serve' command (default: ${DEFAULT_PORT})
102
112
  --verbose Show initialization logs
@@ -113,6 +123,9 @@ Examples:
113
123
  dotbot "What's the weather in SF?"
114
124
  dotbot
115
125
  dotbot serve --port 8080
126
+ dotbot tools
127
+ dotbot memory search "preferences"
128
+ dotbot --system "You are a pirate" "Hello"
116
129
  `);
117
130
  }
118
131
 
@@ -129,6 +142,8 @@ function parseCliArgs() {
129
142
  verbose: { type: 'boolean', default: false },
130
143
  provider: { type: 'string', short: 'p', default: 'xai' },
131
144
  model: { type: 'string', short: 'm', default: 'grok-4-1-fast-reasoning' },
145
+ system: { type: 'string', short: 's', default: '' },
146
+ summary: { type: 'boolean', default: false },
132
147
  db: { type: 'string', default: DEFAULT_DB },
133
148
  port: { type: 'string', default: String(DEFAULT_PORT) },
134
149
  },
@@ -179,20 +194,30 @@ async function getProviderConfig(providerId) {
179
194
  *
180
195
  * @param {string} dbPath - Path to SQLite database
181
196
  * @param {boolean} verbose - Show initialization logs
197
+ * @param {string} customSystemPrompt - Custom system prompt to prepend
182
198
  * @returns {Promise<Object>} Initialized stores
183
199
  */
184
- async function initStores(dbPath, verbose = false) {
200
+ async function initStores(dbPath, verbose = false, customSystemPrompt = '') {
185
201
  await loadModules();
186
202
 
203
+ // Import defaultSystemPrompt for custom builder
204
+ const { defaultSystemPrompt } = await import('../storage/SQLiteAdapter.js');
205
+
187
206
  // Suppress init logs unless verbose
188
207
  const originalLog = console.log;
189
208
  if (!verbose) {
190
209
  console.log = () => {};
191
210
  }
192
211
 
212
+ // Build custom systemPromptBuilder that prepends user's text
213
+ const systemPromptBuilder = customSystemPrompt
214
+ ? (prefs) => `${customSystemPrompt}\n\n${defaultSystemPrompt(prefs)}`
215
+ : undefined;
216
+
193
217
  const sessionStore = new stores.SQLiteSessionStore();
194
218
  await sessionStore.init(dbPath, {
195
219
  prefsFetcher: async () => ({ agentName: 'Dotbot', agentPersonality: '' }),
220
+ ...(systemPromptBuilder && { systemPromptBuilder }),
196
221
  });
197
222
 
198
223
  const cronStore = new stores.SQLiteCronStore();
@@ -223,7 +248,7 @@ async function initStores(dbPath, verbose = false) {
223
248
  * @param {Object} options - CLI options
224
249
  */
225
250
  async function runChat(message, options) {
226
- const storesObj = await initStores(options.db, options.verbose);
251
+ const storesObj = await initStores(options.db, options.verbose, options.system);
227
252
  const provider = await getProviderConfig(options.provider);
228
253
 
229
254
  const session = await storesObj.sessionStore.createSession('cli-user', options.model, options.provider);
@@ -282,7 +307,7 @@ async function runChat(message, options) {
282
307
  * @param {Object} options - CLI options
283
308
  */
284
309
  async function runRepl(options) {
285
- const storesObj = await initStores(options.db, options.verbose);
310
+ const storesObj = await initStores(options.db, options.verbose, options.system);
286
311
  const provider = await getProviderConfig(options.provider);
287
312
 
288
313
  const session = await storesObj.sessionStore.createSession('cli-user', options.model, options.provider);
@@ -388,7 +413,7 @@ async function runRepl(options) {
388
413
  */
389
414
  async function runServer(options) {
390
415
  const port = parseInt(options.port, 10);
391
- const storesObj = await initStores(options.db, options.verbose);
416
+ const storesObj = await initStores(options.db, options.verbose, options.system);
392
417
 
393
418
  const server = createServer(async (req, res) => {
394
419
  // CORS headers
@@ -482,6 +507,192 @@ async function runServer(options) {
482
507
  });
483
508
  }
484
509
 
510
+ /**
511
+ * List all available tools.
512
+ */
513
+ async function runTools() {
514
+ await loadModules();
515
+ console.log(`\ndotbot tools (${coreTools.length})\n`);
516
+
517
+ // Group tools by category based on name prefix
518
+ const categories = {};
519
+ for (const tool of coreTools) {
520
+ const prefix = tool.name.split('_')[0];
521
+ if (!categories[prefix]) categories[prefix] = [];
522
+ categories[prefix].push(tool.name);
523
+ }
524
+
525
+ for (const [category, tools] of Object.entries(categories).sort()) {
526
+ console.log(` ${category} (${tools.length})`);
527
+ for (const name of tools.sort()) {
528
+ console.log(` ${name}`);
529
+ }
530
+ }
531
+ console.log();
532
+ }
533
+
534
+ /**
535
+ * Show database statistics.
536
+ *
537
+ * @param {Object} options - CLI options
538
+ */
539
+ async function runStats(options) {
540
+ const storesObj = await initStores(options.db, options.verbose, options.system);
541
+
542
+ console.log(`\ndotbot stats\n`);
543
+ console.log(` Database: ${options.db}`);
544
+
545
+ // Sessions
546
+ const sessions = await storesObj.sessionStore.listSessions('cli-user');
547
+ console.log(` Sessions: ${sessions.length}`);
548
+
549
+ // Memory
550
+ const memories = await storesObj.memoryStore.getAllMemories('cli-user');
551
+ console.log(` Memories: ${memories.length}`);
552
+
553
+ // Jobs (need to get session IDs first)
554
+ const jobs = await storesObj.cronStore.listTasksBySessionIds(['default'], 'cli-user');
555
+ console.log(` Jobs: ${jobs.length}`);
556
+
557
+ // Tasks
558
+ const tasks = await storesObj.taskStore.getTasks('cli-user');
559
+ console.log(` Tasks: ${tasks.length}`);
560
+
561
+ // Triggers
562
+ const triggers = await storesObj.triggerStore.listTriggers('cli-user');
563
+ console.log(` Triggers: ${triggers.length}`);
564
+
565
+ console.log();
566
+ }
567
+
568
+ /**
569
+ * Manage memories.
570
+ *
571
+ * @param {Object} options - CLI options
572
+ * @param {string} subcommand - list or search
573
+ * @param {string} query - Search query
574
+ */
575
+ async function runMemory(options, subcommand, query) {
576
+ const storesObj = await initStores(options.db, options.verbose, options.system);
577
+
578
+ if (subcommand === 'search' && query) {
579
+ const results = await storesObj.memoryStore.readMemoryPattern('cli-user', `%${query}%`);
580
+ console.log(`\nMemory search: "${query}" (${results.length} results)\n`);
581
+ for (const mem of results) {
582
+ const val = typeof mem.value === 'string' ? mem.value : JSON.stringify(mem.value);
583
+ console.log(` [${mem.key}] ${val.substring(0, 60)}${val.length > 60 ? '...' : ''}`);
584
+ }
585
+ } else {
586
+ const memories = await storesObj.memoryStore.getAllMemories('cli-user');
587
+ console.log(`\nMemories (${memories.length})\n`);
588
+ for (const mem of memories) {
589
+ const val = typeof mem.value === 'string' ? mem.value : JSON.stringify(mem.value);
590
+ console.log(` [${mem.key}] ${val.substring(0, 60)}${val.length > 60 ? '...' : ''}`);
591
+ }
592
+ }
593
+ console.log();
594
+ }
595
+
596
+ /**
597
+ * List scheduled jobs.
598
+ *
599
+ * @param {Object} options - CLI options
600
+ */
601
+ async function runJobs(options) {
602
+ const storesObj = await initStores(options.db, options.verbose, options.system);
603
+
604
+ const jobs = await storesObj.cronStore.listTasksBySessionIds(['default'], 'cli-user');
605
+ console.log(`\nScheduled jobs (${jobs.length})\n`);
606
+
607
+ for (const job of jobs) {
608
+ const status = job.enabled ? 'active' : 'paused';
609
+ const next = job.nextRunAt ? job.nextRunAt.toLocaleString() : 'N/A';
610
+ const interval = job.intervalMs ? `${Math.round(job.intervalMs / 60000)}m` : 'once';
611
+ console.log(` [${job.id}] ${job.name} (${status})`);
612
+ console.log(` Interval: ${interval}`);
613
+ console.log(` Next: ${next}`);
614
+ console.log(` Prompt: ${job.prompt.substring(0, 50)}${job.prompt.length > 50 ? '...' : ''}`);
615
+ console.log();
616
+ }
617
+ }
618
+
619
+ /**
620
+ * List active tasks.
621
+ *
622
+ * @param {Object} options - CLI options
623
+ */
624
+ async function runTasks(options) {
625
+ const storesObj = await initStores(options.db, options.verbose, options.system);
626
+
627
+ const tasks = await storesObj.taskStore.getTasks('cli-user');
628
+ console.log(`\nTasks (${tasks.length})\n`);
629
+
630
+ for (const task of tasks) {
631
+ const steps = task.steps ? JSON.parse(task.steps) : [];
632
+ const progress = `${task.current_step || 0}/${steps.length}`;
633
+ console.log(` [${task.id}] ${task.status} (${progress})`);
634
+ console.log(` Description: ${task.description?.substring(0, 50) || 'N/A'}${task.description?.length > 50 ? '...' : ''}`);
635
+ console.log(` Mode: ${task.mode || 'auto'}`);
636
+ console.log(` Created: ${new Date(task.created_at).toLocaleString()}`);
637
+ console.log();
638
+ }
639
+ }
640
+
641
+ /**
642
+ * List chat sessions.
643
+ *
644
+ * @param {Object} options - CLI options
645
+ */
646
+ async function runSessions(options) {
647
+ const storesObj = await initStores(options.db, options.verbose, options.system);
648
+
649
+ const sessions = await storesObj.sessionStore.listSessions('cli-user');
650
+ console.log(`\nSessions (${sessions.length})\n`);
651
+
652
+ for (const session of sessions) {
653
+ const updated = new Date(session.updatedAt).toLocaleString();
654
+ const msgCount = session.messageCount || 0;
655
+ console.log(` [${session.id}]`);
656
+ console.log(` Title: ${session.title || 'Untitled'}`);
657
+ console.log(` Messages: ${msgCount}`);
658
+ console.log(` Updated: ${updated}`);
659
+ console.log();
660
+ }
661
+ }
662
+
663
+ /**
664
+ * View audit log events.
665
+ *
666
+ * @param {Object} options - CLI options
667
+ */
668
+ async function runEvents(options) {
669
+ const storesObj = await initStores(options.db, options.verbose, options.system);
670
+
671
+ if (options.summary) {
672
+ const summary = await storesObj.eventStore.summary({ userId: 'cli-user' });
673
+ console.log(`\nEvent summary\n`);
674
+ console.log(` Total events: ${summary.total || 0}`);
675
+ if (summary.breakdown) {
676
+ for (const [type, count] of Object.entries(summary.breakdown)) {
677
+ console.log(` ${type}: ${count}`);
678
+ }
679
+ }
680
+ } else {
681
+ const events = await storesObj.eventStore.query({ userId: 'cli-user', limit: 20 });
682
+ console.log(`\nRecent events (${events.length})\n`);
683
+
684
+ for (const event of events) {
685
+ const time = new Date(event.timestamp).toLocaleString();
686
+ console.log(` [${time}] ${event.type}`);
687
+ if (event.data) {
688
+ const data = typeof event.data === 'string' ? event.data : JSON.stringify(event.data);
689
+ console.log(` ${data.substring(0, 60)}${data.length > 60 ? '...' : ''}`);
690
+ }
691
+ }
692
+ }
693
+ console.log();
694
+ }
695
+
485
696
  /**
486
697
  * Main entry point.
487
698
  */
@@ -493,21 +704,45 @@ async function main() {
493
704
  process.exit(0);
494
705
  }
495
706
 
496
- if (args.help || args.positionals.length === 0) {
707
+ if (args.help) {
497
708
  printHelp();
498
709
  process.exit(0);
499
710
  }
500
711
 
501
712
  const command = args.positionals[0];
502
713
 
503
- if (command === 'serve') {
504
- await runServer(args);
505
- } else {
506
- const message = args.positionals.join(' ');
507
- if (message) {
508
- await runChat(message, args);
509
- } else {
510
- await runRepl(args);
714
+ switch (command) {
715
+ case 'serve':
716
+ await runServer(args);
717
+ break;
718
+ case 'tools':
719
+ await runTools();
720
+ break;
721
+ case 'stats':
722
+ await runStats(args);
723
+ break;
724
+ case 'memory':
725
+ await runMemory(args, args.positionals[1], args.positionals.slice(2).join(' '));
726
+ break;
727
+ case 'jobs':
728
+ await runJobs(args);
729
+ break;
730
+ case 'tasks':
731
+ await runTasks(args);
732
+ break;
733
+ case 'sessions':
734
+ await runSessions(args);
735
+ break;
736
+ case 'events':
737
+ await runEvents(args);
738
+ break;
739
+ default: {
740
+ const message = args.positionals.join(' ');
741
+ if (message) {
742
+ await runChat(message, args);
743
+ } else {
744
+ await runRepl(args);
745
+ }
511
746
  }
512
747
  }
513
748
  }
package/dotbot.db CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stevederico/dotbot",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "AI agent CLI and library for Node.js — streaming, multi-provider, tool execution, autonomous tasks",
5
5
  "type": "module",
6
6
  "main": "index.js",