zubo 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.
Files changed (222) hide show
  1. package/.github/workflows/ci.yml +35 -0
  2. package/README.md +149 -0
  3. package/bun.lock +216 -0
  4. package/desktop/README.md +57 -0
  5. package/desktop/package.json +12 -0
  6. package/desktop/src-tauri/Cargo.toml +25 -0
  7. package/desktop/src-tauri/build.rs +3 -0
  8. package/desktop/src-tauri/icons/README.md +17 -0
  9. package/desktop/src-tauri/icons/icon.png +0 -0
  10. package/desktop/src-tauri/src/main.rs +189 -0
  11. package/desktop/src-tauri/tauri.conf.json +68 -0
  12. package/docs/ROADMAP.md +490 -0
  13. package/migrations/001_init.sql +9 -0
  14. package/migrations/002_memory.sql +33 -0
  15. package/migrations/003_cron.sql +24 -0
  16. package/migrations/004_usage.sql +12 -0
  17. package/migrations/005_secrets.sql +8 -0
  18. package/migrations/006_agents.sql +1 -0
  19. package/migrations/007_workflows.sql +22 -0
  20. package/migrations/008_proactive.sql +24 -0
  21. package/migrations/009_uploads.sql +9 -0
  22. package/migrations/010_observability.sql +22 -0
  23. package/migrations/011_api_keys.sql +7 -0
  24. package/migrations/012_indexes.sql +5 -0
  25. package/migrations/013_budget.sql +11 -0
  26. package/migrations/014_usage_session_idx.sql +2 -0
  27. package/package.json +39 -0
  28. package/site/404.html +156 -0
  29. package/site/CNAME +1 -0
  30. package/site/docs/agents.html +294 -0
  31. package/site/docs/api.html +446 -0
  32. package/site/docs/channels.html +345 -0
  33. package/site/docs/cli.html +238 -0
  34. package/site/docs/config.html +1034 -0
  35. package/site/docs/index.html +433 -0
  36. package/site/docs/integrations.html +381 -0
  37. package/site/docs/memory.html +254 -0
  38. package/site/docs/security.html +375 -0
  39. package/site/docs/skills.html +322 -0
  40. package/site/docs.css +412 -0
  41. package/site/index.html +638 -0
  42. package/site/install.sh +98 -0
  43. package/site/logo.svg +1 -0
  44. package/site/og-image.png +0 -0
  45. package/site/robots.txt +4 -0
  46. package/site/script.js +361 -0
  47. package/site/sitemap.xml +63 -0
  48. package/site/skills.html +532 -0
  49. package/site/style.css +1686 -0
  50. package/src/agent/agents.ts +159 -0
  51. package/src/agent/compaction.ts +53 -0
  52. package/src/agent/context.ts +18 -0
  53. package/src/agent/delegate.ts +118 -0
  54. package/src/agent/loop.ts +318 -0
  55. package/src/agent/prompts.ts +111 -0
  56. package/src/agent/session.ts +87 -0
  57. package/src/agent/teams.ts +116 -0
  58. package/src/agent/workflow-executor.ts +192 -0
  59. package/src/agent/workflow.ts +175 -0
  60. package/src/channels/adapter.ts +21 -0
  61. package/src/channels/dashboard.html.ts +2969 -0
  62. package/src/channels/discord.ts +137 -0
  63. package/src/channels/optional-deps.d.ts +17 -0
  64. package/src/channels/router.ts +199 -0
  65. package/src/channels/signal.ts +133 -0
  66. package/src/channels/slack.ts +101 -0
  67. package/src/channels/telegram.ts +102 -0
  68. package/src/channels/utils.ts +18 -0
  69. package/src/channels/webchat.ts +1797 -0
  70. package/src/channels/whatsapp.ts +119 -0
  71. package/src/config/loader.ts +22 -0
  72. package/src/config/paths.ts +43 -0
  73. package/src/config/schema.ts +121 -0
  74. package/src/db/connection.ts +20 -0
  75. package/src/db/export.ts +148 -0
  76. package/src/db/migrations.ts +42 -0
  77. package/src/index.ts +261 -0
  78. package/src/llm/claude.ts +193 -0
  79. package/src/llm/factory.ts +115 -0
  80. package/src/llm/failover.ts +101 -0
  81. package/src/llm/openai-compat.ts +409 -0
  82. package/src/llm/provider.ts +83 -0
  83. package/src/llm/smart-router.ts +241 -0
  84. package/src/logs.ts +53 -0
  85. package/src/memory/chunker.ts +58 -0
  86. package/src/memory/document-parser.ts +115 -0
  87. package/src/memory/embedder.ts +235 -0
  88. package/src/memory/engine.ts +170 -0
  89. package/src/memory/fts-index.ts +55 -0
  90. package/src/memory/hybrid-search.ts +72 -0
  91. package/src/memory/store.ts +56 -0
  92. package/src/memory/vector-index.ts +72 -0
  93. package/src/model.ts +118 -0
  94. package/src/registry/cli.ts +43 -0
  95. package/src/registry/client.ts +54 -0
  96. package/src/registry/installer.ts +67 -0
  97. package/src/scheduler/briefing.ts +71 -0
  98. package/src/scheduler/cron.ts +258 -0
  99. package/src/scheduler/heartbeat.ts +58 -0
  100. package/src/scheduler/memory-triggers.ts +100 -0
  101. package/src/scheduler/natural-cron.ts +163 -0
  102. package/src/scheduler/proactive.ts +25 -0
  103. package/src/scheduler/recipes.ts +110 -0
  104. package/src/secrets/store.ts +64 -0
  105. package/src/setup.ts +413 -0
  106. package/src/skills.ts +293 -0
  107. package/src/start.ts +373 -0
  108. package/src/status.ts +165 -0
  109. package/src/tools/builtin/connect-service.ts +205 -0
  110. package/src/tools/builtin/cron.ts +126 -0
  111. package/src/tools/builtin/datetime.ts +36 -0
  112. package/src/tools/builtin/delegate-task.ts +81 -0
  113. package/src/tools/builtin/delegate.ts +42 -0
  114. package/src/tools/builtin/diagnose.ts +41 -0
  115. package/src/tools/builtin/google-oauth.ts +379 -0
  116. package/src/tools/builtin/manage-agents.ts +149 -0
  117. package/src/tools/builtin/manage-skills.ts +294 -0
  118. package/src/tools/builtin/manage-teams.ts +89 -0
  119. package/src/tools/builtin/manage-triggers.ts +94 -0
  120. package/src/tools/builtin/manage-workflows.ts +119 -0
  121. package/src/tools/builtin/memory-search.ts +38 -0
  122. package/src/tools/builtin/memory-write.ts +30 -0
  123. package/src/tools/builtin/run-workflow.ts +36 -0
  124. package/src/tools/builtin/secrets.ts +122 -0
  125. package/src/tools/builtin/skill-registry.ts +75 -0
  126. package/src/tools/builtin-integrations/api-helpers.ts +26 -0
  127. package/src/tools/builtin-integrations/github/github_issues/SKILL.md +56 -0
  128. package/src/tools/builtin-integrations/github/github_issues/handler.ts +108 -0
  129. package/src/tools/builtin-integrations/github/github_prs/SKILL.md +57 -0
  130. package/src/tools/builtin-integrations/github/github_prs/handler.ts +113 -0
  131. package/src/tools/builtin-integrations/github/github_repos/SKILL.md +37 -0
  132. package/src/tools/builtin-integrations/github/github_repos/handler.ts +88 -0
  133. package/src/tools/builtin-integrations/google/gmail/SKILL.md +51 -0
  134. package/src/tools/builtin-integrations/google/gmail/handler.ts +125 -0
  135. package/src/tools/builtin-integrations/google/google_calendar/SKILL.md +35 -0
  136. package/src/tools/builtin-integrations/google/google_calendar/handler.ts +105 -0
  137. package/src/tools/builtin-integrations/google/google_docs/SKILL.md +35 -0
  138. package/src/tools/builtin-integrations/google/google_docs/handler.ts +108 -0
  139. package/src/tools/builtin-integrations/google/google_drive/SKILL.md +39 -0
  140. package/src/tools/builtin-integrations/google/google_drive/handler.ts +106 -0
  141. package/src/tools/builtin-integrations/google/google_sheets/SKILL.md +36 -0
  142. package/src/tools/builtin-integrations/google/google_sheets/handler.ts +116 -0
  143. package/src/tools/builtin-integrations/jira/jira_boards/SKILL.md +21 -0
  144. package/src/tools/builtin-integrations/jira/jira_boards/handler.ts +74 -0
  145. package/src/tools/builtin-integrations/jira/jira_issues/SKILL.md +28 -0
  146. package/src/tools/builtin-integrations/jira/jira_issues/handler.ts +140 -0
  147. package/src/tools/builtin-integrations/linear/linear_issues/SKILL.md +30 -0
  148. package/src/tools/builtin-integrations/linear/linear_issues/handler.ts +75 -0
  149. package/src/tools/builtin-integrations/linear/linear_projects/SKILL.md +21 -0
  150. package/src/tools/builtin-integrations/linear/linear_projects/handler.ts +43 -0
  151. package/src/tools/builtin-integrations/notion/notion_databases/SKILL.md +39 -0
  152. package/src/tools/builtin-integrations/notion/notion_databases/handler.ts +83 -0
  153. package/src/tools/builtin-integrations/notion/notion_pages/SKILL.md +43 -0
  154. package/src/tools/builtin-integrations/notion/notion_pages/handler.ts +130 -0
  155. package/src/tools/builtin-integrations/notion/notion_search/SKILL.md +27 -0
  156. package/src/tools/builtin-integrations/notion/notion_search/handler.ts +69 -0
  157. package/src/tools/builtin-integrations/slack/slack_messages/SKILL.md +42 -0
  158. package/src/tools/builtin-integrations/slack/slack_messages/handler.ts +72 -0
  159. package/src/tools/builtin-integrations/twitter/twitter_posts/SKILL.md +24 -0
  160. package/src/tools/builtin-integrations/twitter/twitter_posts/handler.ts +133 -0
  161. package/src/tools/builtin-skills/file-read/SKILL.md +26 -0
  162. package/src/tools/builtin-skills/file-read/handler.ts +66 -0
  163. package/src/tools/builtin-skills/file-write/SKILL.md +30 -0
  164. package/src/tools/builtin-skills/file-write/handler.ts +64 -0
  165. package/src/tools/builtin-skills/http-request/SKILL.md +34 -0
  166. package/src/tools/builtin-skills/http-request/handler.ts +87 -0
  167. package/src/tools/builtin-skills/shell/SKILL.md +26 -0
  168. package/src/tools/builtin-skills/shell/handler.ts +96 -0
  169. package/src/tools/builtin-skills/url-fetch/SKILL.md +26 -0
  170. package/src/tools/builtin-skills/url-fetch/handler.ts +37 -0
  171. package/src/tools/builtin-skills/web-search/SKILL.md +26 -0
  172. package/src/tools/builtin-skills/web-search/handler.ts +50 -0
  173. package/src/tools/executor.ts +205 -0
  174. package/src/tools/integration-installer.ts +106 -0
  175. package/src/tools/permissions.ts +45 -0
  176. package/src/tools/registry.ts +39 -0
  177. package/src/tools/sandbox-runner.ts +56 -0
  178. package/src/tools/sandbox.ts +82 -0
  179. package/src/tools/skill-installer.ts +52 -0
  180. package/src/tools/skill-loader.ts +259 -0
  181. package/src/types/optional-deps.d.ts +23 -0
  182. package/src/util/auth.ts +121 -0
  183. package/src/util/costs.ts +59 -0
  184. package/src/util/error-buffer.ts +32 -0
  185. package/src/util/google-tokens.ts +180 -0
  186. package/src/util/logger.ts +73 -0
  187. package/src/util/perf-collector.ts +35 -0
  188. package/src/util/rate-limiter.ts +70 -0
  189. package/src/util/tokens.ts +17 -0
  190. package/src/voice/stt.ts +57 -0
  191. package/src/voice/tts.ts +103 -0
  192. package/tests/agent/session.test.ts +109 -0
  193. package/tests/agent-loop.test.ts +54 -0
  194. package/tests/auth.test.ts +89 -0
  195. package/tests/channels.test.ts +67 -0
  196. package/tests/compaction.test.ts +44 -0
  197. package/tests/config.test.ts +51 -0
  198. package/tests/costs.test.ts +19 -0
  199. package/tests/cron.test.ts +55 -0
  200. package/tests/db/export.test.ts +219 -0
  201. package/tests/executor.test.ts +144 -0
  202. package/tests/export.test.ts +137 -0
  203. package/tests/helpers/mock-llm.ts +34 -0
  204. package/tests/helpers/test-db.ts +74 -0
  205. package/tests/integration/chat-flow.test.ts +48 -0
  206. package/tests/integrations.test.ts +97 -0
  207. package/tests/memory/engine.test.ts +114 -0
  208. package/tests/memory-engine.test.ts +57 -0
  209. package/tests/permissions.test.ts +21 -0
  210. package/tests/rate-limiter.test.ts +70 -0
  211. package/tests/registry.test.ts +67 -0
  212. package/tests/router.test.ts +36 -0
  213. package/tests/session.test.ts +58 -0
  214. package/tests/skill-loader.test.ts +44 -0
  215. package/tests/tokens.test.ts +30 -0
  216. package/tests/tools/executor.test.ts +130 -0
  217. package/tests/util/auth.test.ts +75 -0
  218. package/tests/util/rate-limiter.test.ts +73 -0
  219. package/tests/voice.test.ts +60 -0
  220. package/tests/webchat.test.ts +88 -0
  221. package/tests/workflow.test.ts +38 -0
  222. package/tsconfig.json +16 -0
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Parse natural language schedule descriptions into cron expressions.
3
+ * Returns a standard 5-field cron expression (minute hour day month weekday).
4
+ * Throws if the input can't be parsed.
5
+ */
6
+ export function parseNaturalSchedule(input: string): string {
7
+ const text = input.toLowerCase().trim();
8
+
9
+ // Direct cron expression passthrough — only accept standard 5-field cron
10
+ if (/^[\d*\/,\-]+(\s+[\d*\/,\-]+){4}$/.test(text)) return text;
11
+
12
+ // Time parsing helper
13
+ function parseTime(s: string): { hour: number; minute: number } | null {
14
+ // "9am", "9:30am", "9:30 am", "14:00", "2pm", "noon", "midnight"
15
+ if (/noon/i.test(s)) return { hour: 12, minute: 0 };
16
+ if (/midnight/i.test(s)) return { hour: 0, minute: 0 };
17
+
18
+ const match = s.match(/(\d{1,2})(?::(\d{2}))?\s*(am|pm)?/i);
19
+ if (!match) return null;
20
+
21
+ let hour = parseInt(match[1], 10);
22
+ const minute = match[2] ? parseInt(match[2], 10) : 0;
23
+ const ampm = match[3]?.toLowerCase();
24
+
25
+ if (ampm === "pm" && hour < 12) hour += 12;
26
+ if (ampm === "am" && hour === 12) hour = 0;
27
+
28
+ if (hour > 23 || minute > 59) return null;
29
+ return { hour, minute };
30
+ }
31
+
32
+ // Day of week mapping
33
+ const dayMap: Record<string, number> = {
34
+ sunday: 0, sun: 0,
35
+ monday: 1, mon: 1,
36
+ tuesday: 2, tue: 2, tues: 2,
37
+ wednesday: 3, wed: 3,
38
+ thursday: 4, thu: 4, thur: 4, thurs: 4,
39
+ friday: 5, fri: 5,
40
+ saturday: 6, sat: 6,
41
+ };
42
+
43
+ // Extract time from the text
44
+ function findTime(): { hour: number; minute: number } {
45
+ // Look for "at <time>" pattern
46
+ const atMatch = text.match(/at\s+(\d{1,2}(?::\d{2})?\s*(?:am|pm)?)/i);
47
+ if (atMatch) {
48
+ const t = parseTime(atMatch[1]);
49
+ if (t) return t;
50
+ }
51
+ // Look for standalone time
52
+ const timeMatch = text.match(/(\d{1,2}(?::\d{2})\s*(?:am|pm)?)/i) || text.match(/(\d{1,2}\s*(?:am|pm))/i);
53
+ if (timeMatch) {
54
+ const t = parseTime(timeMatch[1]);
55
+ if (t) return t;
56
+ }
57
+ // "noon", "midnight"
58
+ if (/noon/.test(text)) return { hour: 12, minute: 0 };
59
+ if (/midnight/.test(text)) return { hour: 0, minute: 0 };
60
+ // "morning" = 9am, "evening" = 6pm, "night" = 9pm
61
+ if (/\bmorning\b/.test(text)) return { hour: 9, minute: 0 };
62
+ if (/\bevening\b/.test(text)) return { hour: 18, minute: 0 };
63
+ if (/\bnight\b/.test(text)) return { hour: 21, minute: 0 };
64
+ // Default to 9am
65
+ return { hour: 9, minute: 0 };
66
+ }
67
+
68
+ // --- Pattern matching ---
69
+
70
+ // "every X minutes/hours"
71
+ const everyMinMatch = text.match(/every\s+(\d+)\s*min(?:ute)?s?/i);
72
+ if (everyMinMatch) {
73
+ const mins = parseInt(everyMinMatch[1], 10);
74
+ if (mins < 1 || mins > 59) throw new Error("Interval must be between 1-59 minutes");
75
+ return `*/${mins} * * * *`;
76
+ }
77
+
78
+ const everyHourMatch = text.match(/every\s+(\d+)\s*hours?/i);
79
+ if (everyHourMatch) {
80
+ const hrs = parseInt(everyHourMatch[1], 10);
81
+ if (hrs < 1 || hrs > 23) throw new Error("Interval must be between 1-23 hours");
82
+ return `0 */${hrs} * * *`;
83
+ }
84
+
85
+ // "every hour"
86
+ if (/every\s+hour\b/i.test(text)) return "0 * * * *";
87
+
88
+ // "every day at <time>" or "daily at <time>"
89
+ if (/(?:every\s+day|daily)\b/i.test(text)) {
90
+ const t = findTime();
91
+ return `${t.minute} ${t.hour} * * *`;
92
+ }
93
+
94
+ // "every weekday at <time>" or "weekdays at <time>"
95
+ if (/(?:every\s+)?weekday/i.test(text)) {
96
+ const t = findTime();
97
+ return `${t.minute} ${t.hour} * * 1-5`;
98
+ }
99
+
100
+ // "every weekend at <time>"
101
+ if (/(?:every\s+)?weekend/i.test(text)) {
102
+ const t = findTime();
103
+ return `${t.minute} ${t.hour} * * 0,6`;
104
+ }
105
+
106
+ // "every monday at <time>", "every mon, wed, fri at <time>"
107
+ // Extract everything between "every" and "at" (or end) to find day names
108
+ const everyDayMatch = text.match(/every\s+(.+?)(?:\s+at\s+|$)/i);
109
+ if (everyDayMatch) {
110
+ const dayStr = everyDayMatch[1].toLowerCase();
111
+ const days: number[] = [];
112
+ for (const [name, num] of Object.entries(dayMap)) {
113
+ const wordPattern = new RegExp(`\\b${name}\\b`);
114
+ if (wordPattern.test(dayStr)) {
115
+ if (!days.includes(num)) days.push(num);
116
+ }
117
+ }
118
+ if (days.length > 0) {
119
+ const t = findTime();
120
+ return `${t.minute} ${t.hour} * * ${days.sort().join(",")}`;
121
+ }
122
+ }
123
+
124
+ // "<weekday> at <time>" — single day with explicit "at" keyword
125
+ for (const [name, num] of Object.entries(dayMap)) {
126
+ const wordPattern = new RegExp(`\\b${name}\\b`);
127
+ if (wordPattern.test(text) && /\bat\b/.test(text)) {
128
+ const t = findTime();
129
+ return `${t.minute} ${t.hour} * * ${num}`;
130
+ }
131
+ }
132
+
133
+ // "every month on the <nth> at <time>" or "monthly on <nth>"
134
+ const monthlyMatch = text.match(/(?:every\s+month|monthly)\s+(?:on\s+)?(?:the\s+)?(\d{1,2})(?:st|nd|rd|th)?/i);
135
+ if (monthlyMatch) {
136
+ const day = parseInt(monthlyMatch[1], 10);
137
+ if (day < 1 || day > 31) throw new Error("Day must be between 1-31");
138
+ const t = findTime();
139
+ return `${t.minute} ${t.hour} ${day} * *`;
140
+ }
141
+
142
+ // "twice a day" -> 9am and 5pm
143
+ if (/twice\s+a\s+day/i.test(text)) return "0 9,17 * * *";
144
+
145
+ // "every morning" -> 9am daily
146
+ if (/every\s+morning/i.test(text)) return "0 9 * * *";
147
+
148
+ // "every evening" -> 6pm daily
149
+ if (/every\s+evening/i.test(text)) return "0 18 * * *";
150
+
151
+ // "every night" -> 9pm daily
152
+ if (/every\s+night/i.test(text)) return "0 21 * * *";
153
+
154
+ // Fallback: if there's just a time, assume daily
155
+ const t = findTime();
156
+ if (text.includes("at") || /\d{1,2}\s*(?:am|pm)/i.test(text)) {
157
+ return `${t.minute} ${t.hour} * * *`;
158
+ }
159
+
160
+ throw new Error(
161
+ `Could not parse schedule: "${input}". Try formats like "every day at 9am", "every weekday at 8:30am", "every monday and friday at noon", or "every 30 minutes".`
162
+ );
163
+ }
@@ -0,0 +1,25 @@
1
+ import { Database } from "bun:sqlite";
2
+ import type { MessageRouter } from "../channels/router";
3
+ import type { LlmProvider } from "../llm/provider";
4
+ import type { ZuboConfig } from "../config/schema";
5
+ import { onHeartbeat } from "./heartbeat";
6
+ import { checkMemoryTriggers } from "./memory-triggers";
7
+ import { logger } from "../util/logger";
8
+
9
+ export function initProactiveIntelligence(
10
+ db: Database,
11
+ router: MessageRouter,
12
+ llm: LlmProvider,
13
+ _config: ZuboConfig
14
+ ): void {
15
+ // Register heartbeat handler for proactive checks
16
+ onHeartbeat(async () => {
17
+ try {
18
+ await checkMemoryTriggers(db, router, llm);
19
+ } catch (err: any) {
20
+ logger.error("Proactive trigger check failed", { error: err.message });
21
+ }
22
+ });
23
+
24
+ logger.info("Proactive intelligence initialized");
25
+ }
@@ -0,0 +1,110 @@
1
+ export interface WorkflowRecipe {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ category: "productivity" | "communication" | "development" | "monitoring";
6
+ icon: string;
7
+ schedule: string; // cron expression
8
+ scheduleHuman: string; // human-readable
9
+ task: string; // the prompt sent to the agent
10
+ requires?: string[]; // optional: integration names needed
11
+ }
12
+
13
+ export const WORKFLOW_RECIPES: WorkflowRecipe[] = [
14
+ {
15
+ id: "morning-briefing",
16
+ name: "Morning Briefing",
17
+ description: "Get a daily summary of your calendar, emails, and pending tasks every morning.",
18
+ category: "productivity",
19
+ icon: "☀️",
20
+ schedule: "0 9 * * 1-5",
21
+ scheduleHuman: "Weekdays at 9:00am",
22
+ task: "Generate a morning briefing. Check my calendar for today's events, summarize any new unread emails, and list any pending or urgent tasks. Format it as a concise digest with sections: 📅 Today's Calendar, 📧 Email Summary, ✅ Tasks.",
23
+ },
24
+ {
25
+ id: "email-triage",
26
+ name: "Email Triage",
27
+ description: "Automatically categorize and summarize incoming emails every few hours.",
28
+ category: "communication",
29
+ icon: "📧",
30
+ schedule: "0 */3 * * *",
31
+ scheduleHuman: "Every 3 hours",
32
+ task: "Check my recent emails from the last 3 hours. Categorize them as: 🔴 Urgent (needs reply today), 🟡 Important (needs reply this week), 🟢 FYI (no action needed), 🗑️ Skip (newsletters, spam). Give a one-line summary for each email.",
33
+ requires: ["gmail"],
34
+ },
35
+ {
36
+ id: "weekly-report",
37
+ name: "Weekly Report",
38
+ description: "Generate a weekly summary of activity, tasks completed, and key metrics.",
39
+ category: "productivity",
40
+ icon: "📊",
41
+ schedule: "0 17 * * 5",
42
+ scheduleHuman: "Fridays at 5:00pm",
43
+ task: "Generate a weekly report summarizing this week's activity. Include: conversations held, tools used, tasks completed via cron jobs, and any notable events. Format as a clean report with sections and bullet points.",
44
+ },
45
+ {
46
+ id: "github-digest",
47
+ name: "GitHub Digest",
48
+ description: "Daily summary of new issues, PRs, and activity across your repositories.",
49
+ category: "development",
50
+ icon: "🐙",
51
+ schedule: "0 10 * * 1-5",
52
+ scheduleHuman: "Weekdays at 10:00am",
53
+ task: "Check my GitHub repositories for activity in the last 24 hours. Summarize new issues, pull requests, and any PRs that need my review. Format as: 🔔 Needs Review, 🆕 New Issues, 📝 New PRs.",
54
+ requires: ["github"],
55
+ },
56
+ {
57
+ id: "standup-prep",
58
+ name: "Standup Prep",
59
+ description: "Prepare your daily standup notes based on recent activity.",
60
+ category: "development",
61
+ icon: "🎯",
62
+ schedule: "0 8 * * 1-5",
63
+ scheduleHuman: "Weekdays at 8:00am",
64
+ task: "Help me prepare for my daily standup. Based on my recent conversations and any tools used yesterday, draft standup notes in the format: ✅ What I did yesterday, 🔜 What I'm doing today, 🚧 Any blockers.",
65
+ },
66
+ {
67
+ id: "system-health",
68
+ name: "System Health Check",
69
+ description: "Monitor Zubo's health — database size, memory usage, error rates.",
70
+ category: "monitoring",
71
+ icon: "🏥",
72
+ schedule: "0 8 * * *",
73
+ scheduleHuman: "Daily at 8:00am",
74
+ task: "Run a system health check using the diagnose tool. Report on: database size, memory count, recent errors, cron job status, and any issues. Flag anything that needs attention.",
75
+ },
76
+ {
77
+ id: "calendar-reminder",
78
+ name: "Calendar Reminder",
79
+ description: "Get notified about upcoming meetings 15 minutes before they start.",
80
+ category: "productivity",
81
+ icon: "📅",
82
+ schedule: "*/15 * * * *",
83
+ scheduleHuman: "Every 15 minutes",
84
+ task: "Check my Google Calendar for any events starting in the next 15 minutes. If there are any, send me a brief reminder with the event name, time, and meeting link if available.",
85
+ requires: ["google_calendar"],
86
+ },
87
+ {
88
+ id: "news-digest",
89
+ name: "News & Trends Digest",
90
+ description: "Daily briefing of top news and trends relevant to your interests.",
91
+ category: "productivity",
92
+ icon: "📰",
93
+ schedule: "0 7 * * *",
94
+ scheduleHuman: "Daily at 7:00am",
95
+ task: "Search the web for today's top technology and AI news. Summarize the 5 most important stories in 1-2 sentences each. Focus on: AI developments, major tech announcements, and cybersecurity news.",
96
+ },
97
+ ];
98
+
99
+ export function getRecipeById(id: string): WorkflowRecipe | undefined {
100
+ return WORKFLOW_RECIPES.find(r => r.id === id);
101
+ }
102
+
103
+ export function getRecipesByCategory(): Record<string, WorkflowRecipe[]> {
104
+ const result: Record<string, WorkflowRecipe[]> = {};
105
+ for (const recipe of WORKFLOW_RECIPES) {
106
+ if (!result[recipe.category]) result[recipe.category] = [];
107
+ result[recipe.category].push(recipe);
108
+ }
109
+ return result;
110
+ }
@@ -0,0 +1,64 @@
1
+ import { getDb } from "../db/connection";
2
+
3
+ export interface SecretInfo {
4
+ name: string;
5
+ service: string | null;
6
+ updated_at: string;
7
+ }
8
+
9
+ export function setSecret(name: string, value: string, service?: string): void {
10
+ const db = getDb();
11
+ db.prepare(
12
+ `INSERT INTO secrets (name, value, service, updated_at)
13
+ VALUES (?, ?, ?, datetime('now'))
14
+ ON CONFLICT(name) DO UPDATE SET value = excluded.value, service = excluded.service, updated_at = datetime('now')`
15
+ ).run(name, value, service ?? null);
16
+ }
17
+
18
+ export function getSecret(name: string): string | null {
19
+ const db = getDb();
20
+ const row = db.query("SELECT value FROM secrets WHERE name = ?").get(name) as
21
+ | { value: string }
22
+ | null;
23
+ return row?.value ?? null;
24
+ }
25
+
26
+ export function listSecrets(service?: string): SecretInfo[] {
27
+ const db = getDb();
28
+ if (service) {
29
+ return db
30
+ .query("SELECT name, service, updated_at FROM secrets WHERE service = ? ORDER BY name")
31
+ .all(service) as SecretInfo[];
32
+ }
33
+ return db
34
+ .query("SELECT name, service, updated_at FROM secrets ORDER BY name")
35
+ .all() as SecretInfo[];
36
+ }
37
+
38
+ export function deleteSecret(name: string): boolean {
39
+ const db = getDb();
40
+ const result = db.prepare("DELETE FROM secrets WHERE name = ?").run(name);
41
+ return result.changes > 0;
42
+ }
43
+
44
+ /**
45
+ * Exposes getSecret (and helpers) on globalThis.Zubo so user-created
46
+ * skill handlers can access secrets without relative imports.
47
+ */
48
+ export function exposeSecretsRuntime(): void {
49
+ const g = globalThis as any;
50
+ if (!g.Zubo) g.Zubo = {};
51
+ g.Zubo.getSecret = getSecret;
52
+ }
53
+
54
+ /**
55
+ * Exposes getGoogleToken on globalThis.Zubo so installed Google skill
56
+ * handlers can get a valid OAuth access token without importing from src.
57
+ */
58
+ export function exposeGoogleTokenRuntime(
59
+ getGoogleAccessToken: () => Promise<string>
60
+ ): void {
61
+ const g = globalThis as any;
62
+ if (!g.Zubo) g.Zubo = {};
63
+ g.Zubo.getGoogleToken = getGoogleAccessToken;
64
+ }