beecork 1.2.0 → 1.3.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.
Files changed (113) hide show
  1. package/dist/channels/command-handler.d.ts.map +1 -1
  2. package/dist/channels/command-handler.js +26 -0
  3. package/dist/channels/command-handler.js.map +1 -1
  4. package/dist/channels/discord.d.ts +1 -0
  5. package/dist/channels/discord.d.ts.map +1 -1
  6. package/dist/channels/discord.js +6 -0
  7. package/dist/channels/discord.js.map +1 -1
  8. package/dist/channels/telegram.d.ts +1 -0
  9. package/dist/channels/telegram.d.ts.map +1 -1
  10. package/dist/channels/telegram.js +41 -0
  11. package/dist/channels/telegram.js.map +1 -1
  12. package/dist/channels/whatsapp.d.ts +1 -0
  13. package/dist/channels/whatsapp.d.ts.map +1 -1
  14. package/dist/channels/whatsapp.js +8 -0
  15. package/dist/channels/whatsapp.js.map +1 -1
  16. package/dist/cli/commands.d.ts +2 -0
  17. package/dist/cli/commands.d.ts.map +1 -1
  18. package/dist/cli/commands.js +40 -12
  19. package/dist/cli/commands.js.map +1 -1
  20. package/dist/cli/setup.js +2 -1
  21. package/dist/cli/setup.js.map +1 -1
  22. package/dist/cli/store.d.ts +4 -0
  23. package/dist/cli/store.d.ts.map +1 -0
  24. package/dist/cli/store.js +92 -0
  25. package/dist/cli/store.js.map +1 -0
  26. package/dist/daemon.js +20 -9
  27. package/dist/daemon.js.map +1 -1
  28. package/dist/dashboard/html.d.ts.map +1 -1
  29. package/dist/dashboard/html.js +130 -10
  30. package/dist/dashboard/html.js.map +1 -1
  31. package/dist/dashboard/server.d.ts.map +1 -1
  32. package/dist/dashboard/server.js +107 -11
  33. package/dist/dashboard/server.js.map +1 -1
  34. package/dist/db/migrations.d.ts.map +1 -1
  35. package/dist/db/migrations.js +39 -0
  36. package/dist/db/migrations.js.map +1 -1
  37. package/dist/index.js +107 -4
  38. package/dist/index.js.map +1 -1
  39. package/dist/knowledge/index.d.ts +3 -0
  40. package/dist/knowledge/index.d.ts.map +1 -0
  41. package/dist/knowledge/index.js +2 -0
  42. package/dist/knowledge/index.js.map +1 -0
  43. package/dist/knowledge/manager.d.ts +18 -0
  44. package/dist/knowledge/manager.d.ts.map +1 -0
  45. package/dist/knowledge/manager.js +137 -0
  46. package/dist/knowledge/manager.js.map +1 -0
  47. package/dist/knowledge/types.d.ts +8 -0
  48. package/dist/knowledge/types.d.ts.map +1 -0
  49. package/dist/knowledge/types.js +2 -0
  50. package/dist/knowledge/types.js.map +1 -0
  51. package/dist/mcp/server.js +247 -28
  52. package/dist/mcp/server.js.map +1 -1
  53. package/dist/observability/analytics.js +1 -1
  54. package/dist/observability/analytics.js.map +1 -1
  55. package/dist/session/manager.d.ts.map +1 -1
  56. package/dist/session/manager.js +16 -3
  57. package/dist/session/manager.js.map +1 -1
  58. package/dist/{cron → tasks}/scheduler.d.ts +7 -3
  59. package/dist/tasks/scheduler.d.ts.map +1 -0
  60. package/dist/{cron → tasks}/scheduler.js +29 -24
  61. package/dist/tasks/scheduler.js.map +1 -0
  62. package/dist/tasks/store.d.ts +14 -0
  63. package/dist/tasks/store.d.ts.map +1 -0
  64. package/dist/{cron → tasks}/store.js +14 -12
  65. package/dist/{cron → tasks}/store.js.map +1 -1
  66. package/dist/timeline/index.d.ts +4 -0
  67. package/dist/timeline/index.d.ts.map +1 -0
  68. package/dist/timeline/index.js +3 -0
  69. package/dist/timeline/index.js.map +1 -0
  70. package/dist/timeline/logger.d.ts +9 -0
  71. package/dist/timeline/logger.d.ts.map +1 -0
  72. package/dist/timeline/logger.js +10 -0
  73. package/dist/timeline/logger.js.map +1 -0
  74. package/dist/timeline/query.d.ts +12 -0
  75. package/dist/timeline/query.d.ts.map +1 -0
  76. package/dist/timeline/query.js +57 -0
  77. package/dist/timeline/query.js.map +1 -0
  78. package/dist/timeline/types.d.ts +13 -0
  79. package/dist/timeline/types.d.ts.map +1 -0
  80. package/dist/timeline/types.js +2 -0
  81. package/dist/timeline/types.js.map +1 -0
  82. package/dist/types.d.ts +11 -5
  83. package/dist/types.d.ts.map +1 -1
  84. package/dist/types.js.map +1 -1
  85. package/dist/voice/stt.d.ts +2 -0
  86. package/dist/voice/stt.d.ts.map +1 -1
  87. package/dist/voice/stt.js +10 -0
  88. package/dist/voice/stt.js.map +1 -1
  89. package/dist/watchers/evaluator.d.ts +6 -0
  90. package/dist/watchers/evaluator.d.ts.map +1 -0
  91. package/dist/watchers/evaluator.js +30 -0
  92. package/dist/watchers/evaluator.js.map +1 -0
  93. package/dist/watchers/index.d.ts +5 -0
  94. package/dist/watchers/index.d.ts.map +1 -0
  95. package/dist/watchers/index.js +4 -0
  96. package/dist/watchers/index.js.map +1 -0
  97. package/dist/watchers/scheduler.d.ts +14 -0
  98. package/dist/watchers/scheduler.d.ts.map +1 -0
  99. package/dist/watchers/scheduler.js +134 -0
  100. package/dist/watchers/scheduler.js.map +1 -0
  101. package/dist/watchers/store.d.ts +11 -0
  102. package/dist/watchers/store.d.ts.map +1 -0
  103. package/dist/watchers/store.js +57 -0
  104. package/dist/watchers/store.js.map +1 -0
  105. package/dist/watchers/types.d.ts +16 -0
  106. package/dist/watchers/types.d.ts.map +1 -0
  107. package/dist/watchers/types.js +2 -0
  108. package/dist/watchers/types.js.map +1 -0
  109. package/package.json +1 -1
  110. package/dist/cron/scheduler.d.ts.map +0 -1
  111. package/dist/cron/scheduler.js.map +0 -1
  112. package/dist/cron/store.d.ts +0 -12
  113. package/dist/cron/store.d.ts.map +0 -1
@@ -0,0 +1,137 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { getDb } from '../db/index.js';
4
+ import { getBeecorkHome } from '../util/paths.js';
5
+ import { logger } from '../util/logger.js';
6
+ const GLOBAL_KNOWLEDGE_DIR = path.join(getBeecorkHome(), 'knowledge');
7
+ const GLOBAL_CATEGORIES = ['people', 'preferences', 'routines', 'general'];
8
+ /** Ensure global knowledge directory exists */
9
+ function ensureGlobalDir() {
10
+ fs.mkdirSync(GLOBAL_KNOWLEDGE_DIR, { recursive: true });
11
+ }
12
+ /** Read a knowledge file, return content or empty string */
13
+ function readKnowledgeFile(filePath) {
14
+ try {
15
+ if (fs.existsSync(filePath))
16
+ return fs.readFileSync(filePath, 'utf-8');
17
+ }
18
+ catch { }
19
+ return '';
20
+ }
21
+ /** Append to a knowledge file */
22
+ function appendToFile(filePath, content) {
23
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
24
+ const existing = readKnowledgeFile(filePath);
25
+ const separator = existing.endsWith('\n') || existing === '' ? '' : '\n';
26
+ fs.writeFileSync(filePath, existing + separator + content + '\n');
27
+ }
28
+ // ─── Layer 1: Global Knowledge ───
29
+ export function getGlobalKnowledge() {
30
+ ensureGlobalDir();
31
+ const entries = [];
32
+ for (const category of GLOBAL_CATEGORIES) {
33
+ const filePath = path.join(GLOBAL_KNOWLEDGE_DIR, `${category}.md`);
34
+ const content = readKnowledgeFile(filePath);
35
+ if (content.trim()) {
36
+ entries.push({ content, scope: 'global', source: `${category}.md`, category });
37
+ }
38
+ }
39
+ return entries;
40
+ }
41
+ export function addGlobalKnowledge(content, category = 'general') {
42
+ ensureGlobalDir();
43
+ const validCategory = GLOBAL_CATEGORIES.includes(category) ? category : 'general';
44
+ const filePath = path.join(GLOBAL_KNOWLEDGE_DIR, `${validCategory}.md`);
45
+ appendToFile(filePath, content);
46
+ logger.info(`Global knowledge added to ${validCategory}.md`);
47
+ }
48
+ // ─── Layer 2: Project Knowledge ───
49
+ export function getProjectKnowledge(projectPath) {
50
+ const filePath = path.join(projectPath, '.beecork', 'knowledge.md');
51
+ const content = readKnowledgeFile(filePath);
52
+ if (!content.trim())
53
+ return [];
54
+ return [{ content, scope: 'project', source: filePath }];
55
+ }
56
+ export function addProjectKnowledge(projectPath, content) {
57
+ const filePath = path.join(projectPath, '.beecork', 'knowledge.md');
58
+ appendToFile(filePath, content);
59
+ logger.info(`Project knowledge added to ${filePath}`);
60
+ }
61
+ // ─── Layer 3: Tab Knowledge (database) ───
62
+ export function getTabKnowledge(tabName) {
63
+ const db = getDb();
64
+ const rows = db.prepare('SELECT content FROM memories WHERE tab_name = ? ORDER BY created_at DESC LIMIT 50').all(tabName);
65
+ return rows.map(r => ({ content: r.content, scope: 'tab', source: tabName }));
66
+ }
67
+ // ─── Combined Knowledge ───
68
+ export function getAllKnowledge(projectPath, tabName) {
69
+ const entries = [];
70
+ // Layer 1: Global
71
+ entries.push(...getGlobalKnowledge());
72
+ // Layer 2: Project (if provided)
73
+ if (projectPath) {
74
+ entries.push(...getProjectKnowledge(projectPath));
75
+ }
76
+ // Layer 3: Tab (if provided)
77
+ if (tabName) {
78
+ entries.push(...getTabKnowledge(tabName));
79
+ }
80
+ return entries;
81
+ }
82
+ /** Format knowledge for injection into Claude's context */
83
+ export function formatKnowledgeForContext(entries) {
84
+ if (entries.length === 0)
85
+ return '';
86
+ const sections = [];
87
+ const global = entries.filter(e => e.scope === 'global');
88
+ if (global.length > 0) {
89
+ sections.push('[Your knowledge (global)]');
90
+ for (const entry of global) {
91
+ if (entry.category)
92
+ sections.push(`[${entry.category}]`);
93
+ sections.push(entry.content);
94
+ }
95
+ }
96
+ const project = entries.filter(e => e.scope === 'project');
97
+ if (project.length > 0) {
98
+ sections.push('\n[Project knowledge]');
99
+ for (const entry of project) {
100
+ sections.push(entry.content);
101
+ }
102
+ }
103
+ const tab = entries.filter(e => e.scope === 'tab');
104
+ if (tab.length > 0) {
105
+ sections.push('\n[Context from memory]');
106
+ for (const entry of tab) {
107
+ sections.push(`- ${entry.content}`);
108
+ }
109
+ }
110
+ return sections.join('\n');
111
+ }
112
+ /** Add knowledge to the right scope */
113
+ export function addKnowledge(content, scope, options) {
114
+ switch (scope) {
115
+ case 'global':
116
+ addGlobalKnowledge(content, options?.category);
117
+ break;
118
+ case 'project':
119
+ if (!options?.projectPath)
120
+ throw new Error('projectPath required for project scope');
121
+ addProjectKnowledge(options.projectPath, content);
122
+ break;
123
+ case 'tab': {
124
+ // Use existing memory system
125
+ const db = getDb();
126
+ db.prepare('INSERT INTO memories (content, tab_name, source) VALUES (?, ?, ?)').run(content, options?.tabName || null, 'tool');
127
+ break;
128
+ }
129
+ }
130
+ }
131
+ /** Search knowledge across all layers */
132
+ export function searchKnowledge(query, projectPath, tabName) {
133
+ const all = getAllKnowledge(projectPath, tabName);
134
+ const lower = query.toLowerCase();
135
+ return all.filter(e => e.content.toLowerCase().includes(lower));
136
+ }
137
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/knowledge/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export type KnowledgeScope = 'global' | 'project' | 'tab';
2
+ export interface KnowledgeEntry {
3
+ content: string;
4
+ scope: KnowledgeScope;
5
+ source: string;
6
+ category?: string;
7
+ }
8
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/knowledge/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,cAAc,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/knowledge/types.ts"],"names":[],"mappings":""}
@@ -14,6 +14,7 @@ function fail(text) { return { content: [{ type: 'text', text }], isError: true
14
14
  const BEECORK_HOME = process.env.BEECORK_HOME || path.join(os.homedir(), '.beecork');
15
15
  const DB_PATH = path.join(BEECORK_HOME, 'memory.db');
16
16
  const CRON_RELOAD_SIGNAL = path.join(BEECORK_HOME, '.cron-reload');
17
+ const WATCHER_RELOAD_SIGNAL = path.join(BEECORK_HOME, '.watcher-reload');
17
18
  // Cached singleton connection (lives for the MCP server's lifetime)
18
19
  let cachedDb = null;
19
20
  function getDb() {
@@ -45,6 +46,9 @@ const VALID_SCHEDULE_TYPES = ['at', 'every', 'cron'];
45
46
  function signalCronReload() {
46
47
  fs.writeFileSync(CRON_RELOAD_SIGNAL, String(Date.now()));
47
48
  }
49
+ function signalWatcherReload() {
50
+ fs.writeFileSync(WATCHER_RELOAD_SIGNAL, String(Date.now()));
51
+ }
48
52
  import { VERSION } from '../version.js';
49
53
  import { getConfig, validateTabName } from '../config.js';
50
54
  const server = new Server({ name: 'beecork', version: VERSION }, { capabilities: { tools: {} } });
@@ -57,45 +61,110 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
57
61
  type: 'object',
58
62
  properties: {
59
63
  content: { type: 'string', description: 'The fact or information to remember' },
60
- category: { type: 'string', description: 'Optional category for organizing memories (e.g., "preference", "server", "decision")' },
64
+ scope: { type: 'string', enum: ['global', 'project', 'tab', 'auto'], description: 'Where to store: global (about the user), project (about this project), tab (about this conversation), or auto (Claude decides)' },
65
+ category: { type: 'string', description: 'For global scope: people, preferences, routines, or general' },
61
66
  },
62
67
  required: ['content'],
63
68
  },
64
69
  },
65
70
  {
66
- name: 'beecork_cron_create',
71
+ name: 'beecork_task_create',
67
72
  description: 'Schedule a task that will run automatically. The task sends a message to a Beecork tab at the scheduled time.',
68
73
  inputSchema: {
69
74
  type: 'object',
70
75
  properties: {
71
- name: { type: 'string', description: 'Human-readable name for the job' },
76
+ name: { type: 'string', description: 'Human-readable name for the task' },
72
77
  scheduleType: {
73
78
  type: 'string',
74
79
  enum: ['at', 'every', 'cron'],
75
80
  description: '"at" = one-time ISO datetime, "every" = interval like "30m"/"2h"/"1d", "cron" = cron expression like "0 9 * * 1"',
76
81
  },
77
82
  schedule: { type: 'string', description: 'The schedule value (ISO datetime, interval, or cron expression)' },
78
- message: { type: 'string', description: 'The prompt/message to send when the job fires' },
83
+ message: { type: 'string', description: 'The prompt/message to send when the task fires' },
79
84
  tabName: { type: 'string', description: 'Which tab to send the message to (default: "default")' },
80
85
  },
81
86
  required: ['name', 'scheduleType', 'schedule', 'message'],
82
87
  },
83
88
  },
84
89
  {
85
- name: 'beecork_cron_list',
86
- description: 'List all scheduled cron jobs.',
90
+ name: 'beecork_task_list',
91
+ description: 'List all scheduled tasks.',
87
92
  inputSchema: {
88
93
  type: 'object',
89
94
  properties: {},
90
95
  },
91
96
  },
97
+ {
98
+ name: 'beecork_task_delete',
99
+ description: 'Delete a scheduled task by ID.',
100
+ inputSchema: {
101
+ type: 'object',
102
+ properties: {
103
+ id: { type: 'string', description: 'The ID of the task to delete' },
104
+ },
105
+ required: ['id'],
106
+ },
107
+ },
108
+ // Backward-compatible aliases
109
+ {
110
+ name: 'beecork_cron_create',
111
+ description: '[Alias for beecork_task_create] Schedule a task that will run automatically.',
112
+ inputSchema: {
113
+ type: 'object',
114
+ properties: {
115
+ name: { type: 'string', description: 'Human-readable name for the job' },
116
+ scheduleType: { type: 'string', enum: ['at', 'every', 'cron'] },
117
+ schedule: { type: 'string' },
118
+ message: { type: 'string' },
119
+ tabName: { type: 'string' },
120
+ },
121
+ required: ['name', 'scheduleType', 'schedule', 'message'],
122
+ },
123
+ },
124
+ {
125
+ name: 'beecork_cron_list',
126
+ description: '[Alias for beecork_task_list] List all scheduled tasks.',
127
+ inputSchema: { type: 'object', properties: {} },
128
+ },
92
129
  {
93
130
  name: 'beecork_cron_delete',
94
- description: 'Delete a scheduled cron job by ID.',
131
+ description: '[Alias for beecork_task_delete] Delete a scheduled task by ID.',
132
+ inputSchema: {
133
+ type: 'object',
134
+ properties: { id: { type: 'string' } },
135
+ required: ['id'],
136
+ },
137
+ },
138
+ // Watcher tools
139
+ {
140
+ name: 'beecork_watch_create',
141
+ description: 'Create a watcher that periodically runs a check command and triggers an action when a condition is met.',
95
142
  inputSchema: {
96
143
  type: 'object',
97
144
  properties: {
98
- id: { type: 'string', description: 'The ID of the cron job to delete' },
145
+ name: { type: 'string', description: 'Human-readable name for the watcher' },
146
+ description: { type: 'string', description: 'What to watch (natural language)' },
147
+ checkCommand: { type: 'string', description: 'Shell command to run for checking' },
148
+ condition: { type: 'string', description: 'When to trigger: "contains X", "not contains X", "> N", "< N", "any", "error"' },
149
+ action: { type: 'string', enum: ['notify', 'fix', 'delegate'], description: 'What to do when triggered (default: notify)' },
150
+ actionDetails: { type: 'string', description: 'For fix: command to run. For delegate: tab name + message.' },
151
+ schedule: { type: 'string', description: 'How often: "every 5m", "every 1h", or cron expression' },
152
+ },
153
+ required: ['name', 'checkCommand', 'condition', 'schedule'],
154
+ },
155
+ },
156
+ {
157
+ name: 'beecork_watch_list',
158
+ description: 'List all watchers with their status.',
159
+ inputSchema: { type: 'object', properties: {} },
160
+ },
161
+ {
162
+ name: 'beecork_watch_delete',
163
+ description: 'Delete a watcher by ID.',
164
+ inputSchema: {
165
+ type: 'object',
166
+ properties: {
167
+ id: { type: 'string', description: 'The ID of the watcher to delete' },
99
168
  },
100
169
  required: ['id'],
101
170
  },
@@ -334,11 +403,55 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
334
403
  description: 'List configured media generation providers and their capabilities',
335
404
  inputSchema: { type: 'object', properties: {} },
336
405
  },
406
+ {
407
+ name: 'beecork_knowledge',
408
+ description: 'List all knowledge Beecork has about the current context (global + project + tab)',
409
+ inputSchema: {
410
+ type: 'object',
411
+ properties: {
412
+ scope: { type: 'string', enum: ['global', 'project', 'tab', 'all'], description: 'Which layer to show (default: all)' },
413
+ },
414
+ },
415
+ },
337
416
  {
338
417
  name: 'beecork_capabilities',
339
418
  description: 'List available and enabled capability packs (email, calendar, github, etc.)',
340
419
  inputSchema: { type: 'object', properties: {} },
341
420
  },
421
+ {
422
+ name: 'beecork_history',
423
+ description: 'Show activity timeline — what Beecork has been doing',
424
+ inputSchema: {
425
+ type: 'object',
426
+ properties: {
427
+ date: { type: 'string', description: 'Date filter (YYYY-MM-DD). Default: today' },
428
+ tabName: { type: 'string', description: 'Filter by tab name' },
429
+ limit: { type: 'number', description: 'Max events (default 50)' },
430
+ },
431
+ },
432
+ },
433
+ {
434
+ name: 'beecork_replay',
435
+ description: 'Re-run a past task by its event ID',
436
+ inputSchema: {
437
+ type: 'object',
438
+ properties: {
439
+ eventId: { type: 'string', description: 'Activity event ID to replay' },
440
+ },
441
+ required: ['eventId'],
442
+ },
443
+ },
444
+ {
445
+ name: 'beecork_store_search',
446
+ description: 'Search the Beecork store for community packages (capabilities, media generators, channels)',
447
+ inputSchema: {
448
+ type: 'object',
449
+ properties: {
450
+ query: { type: 'string', description: 'Search query' },
451
+ },
452
+ required: ['query'],
453
+ },
454
+ },
342
455
  ],
343
456
  }));
344
457
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -347,18 +460,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
347
460
  const db = getDb();
348
461
  switch (name) {
349
462
  case 'beecork_remember': {
350
- const { content, category } = args;
463
+ const { content, scope, category } = args;
351
464
  if (!content || content.length > MAX_CONTENT_LENGTH) {
352
465
  return fail(`Content is required and must be under ${MAX_CONTENT_LENGTH} characters.`);
353
466
  }
467
+ if (scope && scope !== 'tab' && scope !== 'auto') {
468
+ const { addKnowledge } = await import('../knowledge/index.js');
469
+ // Determine tab info for project scope
470
+ const currentTab = db.prepare("SELECT working_dir FROM tabs ORDER BY last_activity_at DESC LIMIT 1").get();
471
+ addKnowledge(content, scope, { category, projectPath: currentTab?.working_dir, tabName: undefined });
472
+ return ok(`Remembered (${scope}): ${content.slice(0, 100)}`);
473
+ }
474
+ // Default: existing tab memory behavior
354
475
  const fullContent = category ? `[${category}] ${content}` : content;
355
476
  db.prepare('INSERT INTO memories (content, source) VALUES (?, ?)').run(fullContent, 'tool');
356
477
  return ok(`Remembered: "${fullContent}"`);
357
478
  }
479
+ case 'beecork_task_create':
358
480
  case 'beecork_cron_create': {
359
481
  const { name: jobName, scheduleType, schedule, message, tabName } = args;
360
482
  if (!jobName || jobName.length > MAX_NAME_LENGTH) {
361
- return fail(`Job name is required and must be under ${MAX_NAME_LENGTH} characters.`);
483
+ return fail(`Task name is required and must be under ${MAX_NAME_LENGTH} characters.`);
362
484
  }
363
485
  if (!VALID_SCHEDULE_TYPES.includes(scheduleType)) {
364
486
  return fail(`Invalid scheduleType "${scheduleType}". Must be one of: ${VALID_SCHEDULE_TYPES.join(', ')}`);
@@ -374,27 +496,63 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
374
496
  return fail(tabError);
375
497
  }
376
498
  }
377
- db.prepare(`INSERT INTO cron_jobs (id, name, schedule_type, schedule, tab_name, message, enabled, user_id, created_at)
499
+ db.prepare(`INSERT INTO tasks (id, name, schedule_type, schedule, tab_name, message, enabled, user_id, created_at)
378
500
  VALUES (?, ?, ?, ?, ?, ?, 1, 'local', ?)`).run(id, jobName, scheduleType, schedule, tab, message, new Date().toISOString());
379
501
  signalCronReload();
380
- return ok(`Cron job created: "${jobName}" (${scheduleType}: ${schedule}) tab:${tab}\nID: ${id}`);
502
+ return ok(`Task created: "${jobName}" (${scheduleType}: ${schedule}) -> tab:${tab}\nID: ${id}`);
381
503
  }
504
+ case 'beecork_task_list':
382
505
  case 'beecork_cron_list': {
383
- const jobs = db.prepare('SELECT * FROM cron_jobs WHERE user_id = ? ORDER BY created_at').all('local');
506
+ const jobs = db.prepare('SELECT * FROM tasks WHERE user_id = ? ORDER BY created_at').all('local');
384
507
  if (jobs.length === 0) {
385
- return ok('No cron jobs scheduled.');
508
+ return ok('No tasks scheduled.');
386
509
  }
387
- const lines = jobs.map(j => `- ${j.name} [${j.enabled ? 'enabled' : 'disabled'}] (${j.schedule_type}: ${j.schedule}) tab:${j.tab_name} (ID: ${j.id})`);
510
+ const lines = jobs.map(j => `- ${j.name} [${j.enabled ? 'enabled' : 'disabled'}] (${j.schedule_type}: ${j.schedule}) -> tab:${j.tab_name} (ID: ${j.id})`);
388
511
  return ok(lines.join('\n'));
389
512
  }
513
+ case 'beecork_task_delete':
390
514
  case 'beecork_cron_delete': {
391
515
  const { id } = args;
392
- const result = db.prepare('DELETE FROM cron_jobs WHERE id = ?').run(id);
516
+ const result = db.prepare('DELETE FROM tasks WHERE id = ?').run(id);
393
517
  if (result.changes === 0) {
394
- return ok(`No cron job found with ID: ${id}`);
518
+ return ok(`No task found with ID: ${id}`);
395
519
  }
396
520
  signalCronReload();
397
- return ok(`Deleted cron job: ${id}`);
521
+ return ok(`Deleted task: ${id}`);
522
+ }
523
+ case 'beecork_watch_create': {
524
+ const { name: watchName, description: watchDesc, checkCommand, condition, action, actionDetails, schedule: watchSchedule } = args;
525
+ if (!watchName || watchName.length > MAX_NAME_LENGTH) {
526
+ return fail(`Watcher name is required and must be under ${MAX_NAME_LENGTH} characters.`);
527
+ }
528
+ if (!checkCommand)
529
+ return fail('checkCommand is required.');
530
+ if (!condition)
531
+ return fail('condition is required.');
532
+ if (!watchSchedule)
533
+ return fail('schedule is required.');
534
+ const watchId = uuidv4();
535
+ db.prepare(`INSERT INTO watchers (id, name, description, check_command, condition, action, action_details, schedule)
536
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(watchId, watchName, watchDesc || null, checkCommand, condition, action || 'notify', actionDetails || null, watchSchedule);
537
+ signalWatcherReload();
538
+ return ok(`Watcher created: "${watchName}" (${watchSchedule})\nID: ${watchId}`);
539
+ }
540
+ case 'beecork_watch_list': {
541
+ const watchers = db.prepare('SELECT * FROM watchers ORDER BY created_at').all();
542
+ if (watchers.length === 0) {
543
+ return ok('No watchers configured.');
544
+ }
545
+ const watchLines = watchers.map(w => `- ${w.name} [${w.enabled ? 'enabled' : 'disabled'}] ${w.schedule} | action: ${w.action} | triggers: ${w.trigger_count} (ID: ${w.id})`);
546
+ return ok(watchLines.join('\n'));
547
+ }
548
+ case 'beecork_watch_delete': {
549
+ const { id: watchDelId } = args;
550
+ const watchDelResult = db.prepare('DELETE FROM watchers WHERE id = ?').run(watchDelId);
551
+ if (watchDelResult.changes === 0) {
552
+ return ok(`No watcher found with ID: ${watchDelId}`);
553
+ }
554
+ signalWatcherReload();
555
+ return ok(`Deleted watcher: ${watchDelId}`);
398
556
  }
399
557
  case 'beecork_tab_create': {
400
558
  const { name: tabName, workingDir, template: templateName, systemPrompt } = args;
@@ -461,14 +619,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
461
619
  const { query, limit } = args;
462
620
  const maxResults = Math.min(limit ?? 10, 50);
463
621
  const memories = db.prepare('SELECT content, tab_name, source, created_at FROM memories WHERE content LIKE ? ORDER BY created_at DESC LIMIT ?').all(`%${query}%`, maxResults);
464
- if (memories.length === 0) {
465
- return ok(`No memories found matching "${query}".`);
622
+ // Also search knowledge files
623
+ const { searchKnowledge } = await import('../knowledge/index.js');
624
+ const knowledgeResults = searchKnowledge(query);
625
+ // Merge and return
626
+ const allResults = [
627
+ ...knowledgeResults.map(k => k.content),
628
+ ...memories.map(m => m.content),
629
+ ];
630
+ if (allResults.length === 0) {
631
+ return ok(`No relevant knowledge found matching "${query}".`);
466
632
  }
467
- const lines = memories.map(m => {
468
- const scope = m.tab_name ? `tab:${m.tab_name}` : 'global';
469
- return `- [${m.source}, ${scope}, ${m.created_at}] ${m.content}`;
470
- });
471
- return ok(lines.join('\n'));
633
+ return ok(allResults.join('\n---\n'));
472
634
  }
473
635
  case 'beecork_notify': {
474
636
  const { message, urgent } = args;
@@ -482,11 +644,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
482
644
  case 'beecork_status': {
483
645
  const tabCount = db.prepare('SELECT COUNT(*) as c FROM tabs').get().c;
484
646
  const activeTabs = db.prepare("SELECT COUNT(*) as c FROM tabs WHERE status = 'running'").get().c;
485
- const cronCount = db.prepare('SELECT COUNT(*) as c FROM cron_jobs WHERE enabled = 1').get().c;
647
+ const taskCount = db.prepare('SELECT COUNT(*) as c FROM tasks WHERE enabled = 1').get().c;
648
+ const watcherCount = db.prepare('SELECT COUNT(*) as c FROM watchers WHERE enabled = 1').get().c;
486
649
  const memoryCount = db.prepare('SELECT COUNT(*) as c FROM memories').get().c;
487
650
  const lines = [
488
651
  `Tabs: ${tabCount} total, ${activeTabs} running`,
489
- `Cron jobs: ${cronCount} active`,
652
+ `Tasks: ${taskCount} active`,
653
+ `Watchers: ${watcherCount} active`,
490
654
  `Memories: ${memoryCount} stored`,
491
655
  ];
492
656
  return ok(lines.join('\n'));
@@ -556,7 +720,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
556
720
  data = db.prepare("SELECT m.role, m.content, m.cost_usd, m.created_at, t.name as tab FROM messages m JOIN tabs t ON t.id = m.tab_id WHERE m.created_at > ? ORDER BY m.created_at DESC LIMIT 500").all(since);
557
721
  break;
558
722
  case 'crons':
559
- data = db.prepare("SELECT * FROM cron_jobs ORDER BY created_at").all();
723
+ data = db.prepare("SELECT * FROM tasks ORDER BY created_at").all();
560
724
  break;
561
725
  default:
562
726
  return fail('Invalid type. Use: costs, messages, or crons');
@@ -681,6 +845,29 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
681
845
  const lines = generators.map(g => `- ${g.name} (${g.id}): ${g.supportedTypes.join(', ')}`);
682
846
  return ok(lines.join('\n'));
683
847
  }
848
+ case 'beecork_knowledge': {
849
+ const { scope: knowledgeScope } = (args || {});
850
+ const { getGlobalKnowledge, getProjectKnowledge, getTabKnowledge, getAllKnowledge, formatKnowledgeForContext } = await import('../knowledge/index.js');
851
+ let entries;
852
+ if (knowledgeScope === 'global') {
853
+ entries = getGlobalKnowledge();
854
+ }
855
+ else if (knowledgeScope === 'project') {
856
+ const currentTab = db.prepare("SELECT working_dir FROM tabs ORDER BY last_activity_at DESC LIMIT 1").get();
857
+ entries = currentTab ? getProjectKnowledge(currentTab.working_dir) : [];
858
+ }
859
+ else if (knowledgeScope === 'tab') {
860
+ const currentTab = db.prepare("SELECT name FROM tabs ORDER BY last_activity_at DESC LIMIT 1").get();
861
+ entries = currentTab ? getTabKnowledge(currentTab.name) : [];
862
+ }
863
+ else {
864
+ entries = getAllKnowledge();
865
+ }
866
+ if (entries.length === 0) {
867
+ return ok('No knowledge stored yet.');
868
+ }
869
+ return ok(formatKnowledgeForContext(entries));
870
+ }
684
871
  case 'beecork_capabilities': {
685
872
  const { getAvailablePacks, isEnabled } = await import('../capabilities/index.js');
686
873
  const packs = getAvailablePacks();
@@ -690,6 +877,38 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
690
877
  });
691
878
  return ok(capLines.join('\n'));
692
879
  }
880
+ case 'beecork_history': {
881
+ const { date, tabName, limit } = (args || {});
882
+ const { getTimeline, formatTimeline } = await import('../timeline/index.js');
883
+ const events = getTimeline({ date: date || new Date().toISOString().slice(0, 10), tabName, limit });
884
+ return ok(formatTimeline(events));
885
+ }
886
+ case 'beecork_replay': {
887
+ const { eventId } = args;
888
+ const { getReplayInfo } = await import('../timeline/index.js');
889
+ const info = getReplayInfo(eventId);
890
+ if (!info)
891
+ return fail('Event not found or not replayable.');
892
+ db.prepare('INSERT INTO pending_messages (tab_name, message, type) VALUES (?, ?, ?)').run(info.tabName, info.message, 'replay');
893
+ return ok(`Replaying in tab "${info.tabName}": ${info.message.slice(0, 200)}`);
894
+ }
895
+ case 'beecork_store_search': {
896
+ const { query } = args;
897
+ try {
898
+ const response = await fetch(`https://registry.npmjs.org/-/v1/search?text=beecork+${encodeURIComponent(query)}&size=10`, { signal: AbortSignal.timeout(10000) });
899
+ if (!response.ok)
900
+ return fail('npm registry search failed');
901
+ const data = await response.json();
902
+ const packages = data.objects?.filter((o) => o.package.name.startsWith('beecork-')) || [];
903
+ if (packages.length === 0)
904
+ return ok(`No beecork packages found for "${query}". You can create one with: beecork channel create <name> or beecork media create <name>`);
905
+ const lines = packages.map((o) => `${o.package.name}@${o.package.version} — ${o.package.description || 'No description'}`);
906
+ return ok(`${packages.length} package(s):\n${lines.join('\n')}\n\nInstall: npm install -g <package-name>`);
907
+ }
908
+ catch {
909
+ return fail('Failed to search npm registry');
910
+ }
911
+ }
693
912
  default:
694
913
  return fail(`Unknown tool: ${name}`);
695
914
  }