augure 0.5.0 → 0.5.1

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 (2) hide show
  1. package/dist/bin.js +554 -76
  2. package/package.json +20 -20
package/dist/bin.js CHANGED
@@ -153,19 +153,33 @@ async function loadConfig(path) {
153
153
  return AppConfigSchema.parse(parsed);
154
154
  }
155
155
 
156
+ // ../types/dist/logger.js
157
+ var noop = () => {
158
+ };
159
+ var noopLogger = {
160
+ debug: noop,
161
+ info: noop,
162
+ warn: noop,
163
+ error: noop,
164
+ child: () => noopLogger
165
+ };
166
+
156
167
  // ../core/dist/llm.js
157
168
  var OpenRouterClient = class {
158
169
  apiKey;
159
170
  model;
160
171
  maxTokens;
161
172
  baseUrl;
173
+ log;
162
174
  constructor(config) {
163
175
  this.apiKey = config.apiKey;
164
176
  this.model = config.model;
165
177
  this.maxTokens = config.maxTokens;
166
178
  this.baseUrl = config.baseUrl ?? "https://openrouter.ai/api/v1";
179
+ this.log = config.logger ?? noopLogger;
167
180
  }
168
181
  async chat(messages, tools) {
182
+ this.log.debug(`Request: model=${this.model} messages=${messages.length} tools=${tools?.length ?? 0}`);
169
183
  const response = await fetch(`${this.baseUrl}/chat/completions`, {
170
184
  method: "POST",
171
185
  headers: {
@@ -199,6 +213,7 @@ var OpenRouterClient = class {
199
213
  }
200
214
  const data = await response.json();
201
215
  const choice = data.choices[0];
216
+ this.log.debug(`Response: ${response.status} ${data.usage.prompt_tokens}+${data.usage.completion_tokens} tokens`);
202
217
  return {
203
218
  content: choice.message.content ?? "",
204
219
  toolCalls: (choice.message.tool_calls ?? []).map((tc) => {
@@ -206,7 +221,7 @@ var OpenRouterClient = class {
206
221
  try {
207
222
  args = tc.function.arguments ? JSON.parse(tc.function.arguments) : {};
208
223
  } catch {
209
- console.error(`[augure] Failed to parse tool call arguments for ${tc.function.name}:`, tc.function.arguments);
224
+ this.log.warn(`Failed to parse tool call arguments for ${tc.function.name}`);
210
225
  }
211
226
  return { id: tc.id, name: tc.function.name, arguments: args };
212
227
  }),
@@ -222,6 +237,19 @@ var OpenRouterClient = class {
222
237
  function assembleContext(input) {
223
238
  const { systemPrompt, memoryContent, conversationHistory, persona } = input;
224
239
  let system = systemPrompt;
240
+ const now = /* @__PURE__ */ new Date();
241
+ const humanDate = new Intl.DateTimeFormat("en-US", {
242
+ weekday: "long",
243
+ year: "numeric",
244
+ month: "long",
245
+ day: "numeric",
246
+ hour: "2-digit",
247
+ minute: "2-digit",
248
+ timeZoneName: "short"
249
+ }).format(now);
250
+ system += `
251
+
252
+ Current date and time: ${now.toISOString()} (${humanDate})`;
225
253
  if (persona) {
226
254
  system += `
227
255
 
@@ -249,13 +277,15 @@ function summarize(text, maxLen = 200) {
249
277
  }
250
278
  var FileAuditLogger = class {
251
279
  basePath;
280
+ logger;
252
281
  pendingWrite = Promise.resolve();
253
282
  initialized = false;
254
- constructor(basePath) {
283
+ constructor(basePath, logger) {
255
284
  this.basePath = basePath;
285
+ this.logger = logger ?? noopLogger;
256
286
  }
257
287
  log(entry) {
258
- this.pendingWrite = this.pendingWrite.then(() => this.writeEntry(entry)).catch((err2) => console.error("[augure] Audit write error:", err2));
288
+ this.pendingWrite = this.pendingWrite.then(() => this.writeEntry(entry)).catch((err2) => this.logger.error("Audit write error:", err2));
259
289
  }
260
290
  async close() {
261
291
  await this.pendingWrite;
@@ -281,10 +311,12 @@ var NullAuditLogger = class {
281
311
  // ../core/dist/agent.js
282
312
  var Agent = class {
283
313
  config;
314
+ log;
284
315
  conversations = /* @__PURE__ */ new Map();
285
316
  state = "running";
286
317
  constructor(config) {
287
318
  this.config = config;
319
+ this.log = config.logger ?? noopLogger;
288
320
  }
289
321
  getState() {
290
322
  return this.state;
@@ -324,12 +356,14 @@ var Agent = class {
324
356
  conversationHistory: history,
325
357
  persona: this.config.persona
326
358
  });
359
+ this.log.debug(`LLM call #${loopCount + 1} (${messages.length} messages)`);
327
360
  const response = await this.config.llm.chat(messages, toolSchemas);
328
361
  if (response.toolCalls.length === 0) {
329
362
  history.push({
330
363
  role: "assistant",
331
364
  content: response.content
332
365
  });
366
+ this.log.debug(`Response: ${response.usage.inputTokens}+${response.usage.outputTokens} tokens, ${Date.now() - start}ms`);
333
367
  if (this.config.audit) {
334
368
  this.config.audit.log({
335
369
  ts: (/* @__PURE__ */ new Date()).toISOString(),
@@ -347,7 +381,7 @@ var Agent = class {
347
381
  });
348
382
  }
349
383
  if (this.config.ingester) {
350
- this.config.ingester.ingest(history).catch((err2) => console.error("[augure] Ingestion error:", err2));
384
+ this.config.ingester.ingest(history).catch((err2) => this.log.error("Ingestion error:", err2));
351
385
  }
352
386
  return response.content;
353
387
  }
@@ -358,7 +392,9 @@ var Agent = class {
358
392
  });
359
393
  for (const toolCall of response.toolCalls) {
360
394
  const toolStart = Date.now();
395
+ this.log.debug(`Tool: ${toolCall.name}`);
361
396
  const result = await this.config.tools.execute(toolCall.name, toolCall.arguments);
397
+ this.log.debug(`Tool ${toolCall.name}: ${result.success ? "ok" : "fail"} (${Date.now() - toolStart}ms)`);
362
398
  history.push({
363
399
  role: "tool",
364
400
  content: result.output,
@@ -566,6 +602,56 @@ var ContextGuard = class {
566
602
  }
567
603
  };
568
604
 
605
+ // ../core/dist/logger.js
606
+ import { styleText } from "util";
607
+ var LEVELS = {
608
+ debug: 0,
609
+ info: 1,
610
+ warn: 2,
611
+ error: 3,
612
+ silent: 4
613
+ };
614
+ function tag(level) {
615
+ switch (level) {
616
+ case "debug":
617
+ return styleText("magenta", "DBG");
618
+ case "info":
619
+ return styleText("cyan", "INF");
620
+ case "warn":
621
+ return styleText("yellow", "WRN");
622
+ case "error":
623
+ return styleText("red", "ERR");
624
+ }
625
+ }
626
+ function createLogger(opts = {}) {
627
+ const min = LEVELS[opts.level ?? "info"];
628
+ const scope = opts.scope;
629
+ function emit(level, msg, args) {
630
+ if (LEVELS[level] < min)
631
+ return;
632
+ const ts = styleText("dim", (/* @__PURE__ */ new Date()).toISOString().slice(11, 23));
633
+ const lvl = tag(level);
634
+ const sc = scope ? ` ${styleText("dim", scope)}` : "";
635
+ const prefix2 = `${styleText("yellow", "\u25B2")} ${ts} ${lvl}${sc}`;
636
+ const fn = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
637
+ if (args.length > 0) {
638
+ fn(prefix2, msg, ...args);
639
+ } else {
640
+ fn(prefix2, msg);
641
+ }
642
+ }
643
+ return {
644
+ debug: (msg, ...args) => emit("debug", msg, args),
645
+ info: (msg, ...args) => emit("info", msg, args),
646
+ warn: (msg, ...args) => emit("warn", msg, args),
647
+ error: (msg, ...args) => emit("error", msg, args),
648
+ child: (childScope) => createLogger({
649
+ level: opts.level,
650
+ scope: scope ? `${scope}:${childScope}` : childScope
651
+ })
652
+ };
653
+ }
654
+
569
655
  // ../channels/dist/telegram/telegram.js
570
656
  import { Bot } from "grammy";
571
657
 
@@ -786,11 +872,13 @@ var TelegramChannel = class {
786
872
  allowedUsers;
787
873
  handlers = [];
788
874
  sendPipeline;
875
+ log;
789
876
  constructor(config) {
877
+ this.log = config.logger ?? noopLogger;
790
878
  this.bot = new Bot(config.botToken);
791
879
  this.allowedUsers = new Set(config.allowedUsers);
792
880
  this.bot.catch((err2) => {
793
- console.error("[augure:telegram] Bot error:", err2.message ?? err2);
881
+ this.log.error("Bot error:", err2.message ?? err2);
794
882
  });
795
883
  this.bot.on("message:text", async (ctx) => {
796
884
  const userId = ctx.from.id;
@@ -819,7 +907,7 @@ var TelegramChannel = class {
819
907
  ...replyOpts
820
908
  }), { maxRetries: 3, baseDelayMs: 500 }).catch(async () => {
821
909
  await this.bot.api.sendMessage(Number(msg.userId), msg.text, replyOpts).catch((fallbackErr) => {
822
- console.error("[augure:telegram] Fallback send also failed:", fallbackErr);
910
+ this.log.error("Fallback send also failed:", fallbackErr);
823
911
  throw fallbackErr;
824
912
  });
825
913
  });
@@ -830,7 +918,7 @@ var TelegramChannel = class {
830
918
  return this.allowedUsers.has(userId);
831
919
  }
832
920
  handleRejected(userId, unixTimestamp, rejectMessage) {
833
- console.warn(`[augure:telegram] Rejected message from unauthorized user ${userId} at ${new Date(unixTimestamp * 1e3).toISOString()}`);
921
+ this.log.warn(`Rejected message from unauthorized user ${userId} at ${new Date(unixTimestamp * 1e3).toISOString()}`);
834
922
  if (rejectMessage) {
835
923
  this.bot.api.sendMessage(userId, rejectMessage).catch(() => {
836
924
  });
@@ -864,14 +952,27 @@ var ToolRegistry = class {
864
952
  return Array.from(this.tools.values());
865
953
  }
866
954
  toFunctionSchemas() {
867
- return this.list().map((tool) => ({
868
- type: "function",
869
- function: {
870
- name: tool.name,
871
- description: tool.description,
872
- parameters: tool.parameters
955
+ return this.list().map((tool) => {
956
+ let description = tool.description;
957
+ if (tool.configCheck && this.context) {
958
+ try {
959
+ const warning = tool.configCheck(this.context);
960
+ if (warning) {
961
+ description += `
962
+ [NOT CONFIGURED] ${warning}`;
963
+ }
964
+ } catch {
965
+ }
873
966
  }
874
- }));
967
+ return {
968
+ type: "function",
969
+ function: {
970
+ name: tool.name,
971
+ description,
972
+ parameters: tool.parameters
973
+ }
974
+ };
975
+ });
875
976
  }
876
977
  async execute(name, params) {
877
978
  const tool = this.tools.get(name);
@@ -1011,10 +1112,54 @@ var scheduleTool = {
1011
1112
  }
1012
1113
  };
1013
1114
 
1115
+ // ../tools/dist/datetime.js
1116
+ var datetimeTool = {
1117
+ name: "datetime",
1118
+ description: "Get the current date and time, optionally in a specific timezone",
1119
+ parameters: {
1120
+ type: "object",
1121
+ properties: {
1122
+ timezone: {
1123
+ type: "string",
1124
+ description: "IANA timezone (e.g. 'Europe/Paris', 'America/New_York'). Defaults to the system timezone."
1125
+ }
1126
+ }
1127
+ },
1128
+ execute: async (params) => {
1129
+ const { timezone } = params;
1130
+ const now = /* @__PURE__ */ new Date();
1131
+ const options = {
1132
+ weekday: "long",
1133
+ year: "numeric",
1134
+ month: "long",
1135
+ day: "numeric",
1136
+ hour: "2-digit",
1137
+ minute: "2-digit",
1138
+ second: "2-digit",
1139
+ timeZoneName: "longOffset"
1140
+ };
1141
+ if (timezone) {
1142
+ options.timeZone = timezone;
1143
+ }
1144
+ try {
1145
+ const formatted = new Intl.DateTimeFormat("en-US", options).format(now);
1146
+ return {
1147
+ success: true,
1148
+ output: `${formatted}
1149
+ ISO 8601 (UTC): ${now.toISOString()}
1150
+ Unix timestamp: ${Math.floor(now.getTime() / 1e3)}`
1151
+ };
1152
+ } catch {
1153
+ return { success: false, output: `Invalid timezone: ${timezone}` };
1154
+ }
1155
+ }
1156
+ };
1157
+
1014
1158
  // ../tools/dist/web-search.js
1015
1159
  var webSearchTool = {
1016
1160
  name: "web_search",
1017
1161
  description: "Search the web using the configured search provider (Tavily, Exa, or SearXNG)",
1162
+ configCheck: (ctx) => ctx.config.tools?.webSearch ? null : "This tool requires configuration. See https://augure.dev/docs/tools/web-search",
1018
1163
  parameters: {
1019
1164
  type: "object",
1020
1165
  properties: {
@@ -1226,6 +1371,273 @@ ${text}`;
1226
1371
  // ../tools/dist/email.js
1227
1372
  import { ImapFlow } from "imapflow";
1228
1373
  import { createTransport } from "nodemailer";
1374
+ var MAX_BODY_CHARS = 4e3;
1375
+ function stripHtml(html) {
1376
+ return html.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n\n").replace(/<\/div>/gi, "\n").replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/\n{3,}/g, "\n\n").trim();
1377
+ }
1378
+ function findTextPart(structure) {
1379
+ if (structure.type === "text/plain" && structure.part) {
1380
+ return { part: structure.part, isHtml: false };
1381
+ }
1382
+ if (structure.type === "text/html" && structure.part) {
1383
+ return { part: structure.part, isHtml: true };
1384
+ }
1385
+ if (structure.childNodes) {
1386
+ let htmlFallback = null;
1387
+ for (const child of structure.childNodes) {
1388
+ const found = findTextPart(child);
1389
+ if (found && !found.isHtml)
1390
+ return found;
1391
+ if (found && found.isHtml)
1392
+ htmlFallback = found;
1393
+ }
1394
+ return htmlFallback;
1395
+ }
1396
+ return null;
1397
+ }
1398
+ async function withImapClient(config, fn) {
1399
+ const client = new ImapFlow({
1400
+ host: config.host,
1401
+ port: config.port,
1402
+ secure: config.port === 993,
1403
+ auth: { user: config.user, pass: config.password },
1404
+ logger: false
1405
+ });
1406
+ let connected = false;
1407
+ try {
1408
+ await client.connect();
1409
+ connected = true;
1410
+ return await fn(client);
1411
+ } finally {
1412
+ if (connected) {
1413
+ await client.logout();
1414
+ }
1415
+ }
1416
+ }
1417
+ function formatSummaries(emails) {
1418
+ if (emails.length === 0)
1419
+ return "No emails found.";
1420
+ return emails.map((e, i) => `${i + 1}. ${e.seen ? "" : "[UNREAD] "}UID:${e.uid} Subject: ${e.subject} | From: ${e.from} | Date: ${e.date}`).join("\n");
1421
+ }
1422
+ function extractAddress(addr) {
1423
+ if (!addr)
1424
+ return "unknown";
1425
+ if (Array.isArray(addr)) {
1426
+ const first = addr[0];
1427
+ if (first && typeof first === "object" && "address" in first) {
1428
+ return first.address;
1429
+ }
1430
+ return String(first ?? "unknown");
1431
+ }
1432
+ if (typeof addr === "object" && "address" in addr) {
1433
+ return addr.address;
1434
+ }
1435
+ return String(addr);
1436
+ }
1437
+ async function handleList(params, imapConfig) {
1438
+ const folder = params.folder ?? "INBOX";
1439
+ const limit = params.limit ?? 10;
1440
+ return withImapClient(imapConfig, async (client) => {
1441
+ const lock = await client.getMailboxLock(folder);
1442
+ try {
1443
+ const status = client.mailbox;
1444
+ if (!status || status.exists === 0)
1445
+ return "Mailbox is empty.";
1446
+ const start = Math.max(1, status.exists - limit + 1);
1447
+ const emails = [];
1448
+ for await (const msg of client.fetch(`${start}:*`, {
1449
+ envelope: true,
1450
+ flags: true
1451
+ })) {
1452
+ emails.push({
1453
+ uid: msg.uid,
1454
+ subject: msg.envelope?.subject ?? "(no subject)",
1455
+ from: extractAddress(msg.envelope?.from),
1456
+ date: msg.envelope?.date?.toISOString() ?? "unknown",
1457
+ seen: msg.flags?.has("\\Seen") ?? false
1458
+ });
1459
+ }
1460
+ return formatSummaries(emails.slice(-limit));
1461
+ } finally {
1462
+ lock.release();
1463
+ }
1464
+ });
1465
+ }
1466
+ async function handleRead(params, imapConfig) {
1467
+ const folder = params.folder ?? "INBOX";
1468
+ return withImapClient(imapConfig, async (client) => {
1469
+ const lock = await client.getMailboxLock(folder);
1470
+ try {
1471
+ const msg = await client.fetchOne(String(params.uid), {
1472
+ envelope: true,
1473
+ bodyStructure: true,
1474
+ flags: true,
1475
+ uid: true
1476
+ });
1477
+ if (!msg)
1478
+ return `No email found with UID ${params.uid}.`;
1479
+ let bodyText = "";
1480
+ const textPart = msg.bodyStructure ? findTextPart(msg.bodyStructure) : null;
1481
+ if (textPart) {
1482
+ const { content } = await client.download(String(params.uid), textPart.part, {
1483
+ uid: true
1484
+ });
1485
+ const chunks = [];
1486
+ for await (const chunk of content) {
1487
+ chunks.push(Buffer.from(chunk));
1488
+ }
1489
+ bodyText = Buffer.concat(chunks).toString("utf-8");
1490
+ if (textPart.isHtml)
1491
+ bodyText = stripHtml(bodyText);
1492
+ }
1493
+ if (bodyText.length > MAX_BODY_CHARS) {
1494
+ bodyText = bodyText.slice(0, MAX_BODY_CHARS) + "\n[truncated]";
1495
+ }
1496
+ const wasSeen = msg.flags?.has("\\Seen") ?? false;
1497
+ await client.messageFlagsAdd(String(params.uid), ["\\Seen"], { uid: true });
1498
+ const subject = msg.envelope?.subject ?? "(no subject)";
1499
+ const from = extractAddress(msg.envelope?.from);
1500
+ const date = msg.envelope?.date?.toISOString() ?? "unknown";
1501
+ return `UID: ${params.uid}
1502
+ Subject: ${subject}
1503
+ From: ${from}
1504
+ Date: ${date}
1505
+ Status: ${wasSeen ? "was read" : "was unread, marked read"}
1506
+
1507
+ ${bodyText}`;
1508
+ } finally {
1509
+ lock.release();
1510
+ }
1511
+ });
1512
+ }
1513
+ async function handleSearch(params, imapConfig) {
1514
+ const folder = params.folder ?? "INBOX";
1515
+ const limit = params.limit ?? 10;
1516
+ return withImapClient(imapConfig, async (client) => {
1517
+ const lock = await client.getMailboxLock(folder);
1518
+ try {
1519
+ const criteria = {};
1520
+ if (params.from)
1521
+ criteria.from = params.from;
1522
+ if (params.subject)
1523
+ criteria.subject = params.subject;
1524
+ if (params.since)
1525
+ criteria.since = new Date(params.since);
1526
+ if (params.unseen)
1527
+ criteria.seen = false;
1528
+ const result = await client.search(criteria, { uid: true });
1529
+ const uids = Array.isArray(result) ? result : [];
1530
+ if (uids.length === 0)
1531
+ return "No emails match the search criteria.";
1532
+ const selected = uids.slice(-limit);
1533
+ const emails = [];
1534
+ for await (const msg of client.fetch(selected, { envelope: true, flags: true }, { uid: true })) {
1535
+ emails.push({
1536
+ uid: msg.uid,
1537
+ subject: msg.envelope?.subject ?? "(no subject)",
1538
+ from: extractAddress(msg.envelope?.from),
1539
+ date: msg.envelope?.date?.toISOString() ?? "unknown",
1540
+ seen: msg.flags?.has("\\Seen") ?? false
1541
+ });
1542
+ }
1543
+ return formatSummaries(emails);
1544
+ } finally {
1545
+ lock.release();
1546
+ }
1547
+ });
1548
+ }
1549
+ async function handleSend(params, smtpConfig) {
1550
+ const transport = createTransport({
1551
+ host: smtpConfig.host,
1552
+ port: smtpConfig.port,
1553
+ secure: smtpConfig.port === 465,
1554
+ auth: { user: smtpConfig.user, pass: smtpConfig.password }
1555
+ });
1556
+ const info = await transport.sendMail({
1557
+ from: smtpConfig.user,
1558
+ to: params.to,
1559
+ subject: params.subject,
1560
+ text: params.body,
1561
+ cc: params.cc,
1562
+ bcc: params.bcc
1563
+ });
1564
+ return `Email sent. Message ID: ${info.messageId}`;
1565
+ }
1566
+ var emailTool = {
1567
+ name: "email",
1568
+ description: "Manage email: list recent messages, read by UID, search with criteria, or send an email via SMTP",
1569
+ configCheck: (ctx) => ctx.config.tools?.email ? null : "This tool requires configuration. See https://augure.dev/docs/tools/email",
1570
+ parameters: {
1571
+ type: "object",
1572
+ properties: {
1573
+ action: {
1574
+ type: "string",
1575
+ enum: ["list", "read", "search", "send"],
1576
+ description: "The email action to perform"
1577
+ },
1578
+ folder: {
1579
+ type: "string",
1580
+ description: 'IMAP folder (default: "INBOX"). Used by list, read, search.'
1581
+ },
1582
+ limit: {
1583
+ type: "number",
1584
+ description: "Max emails to return (default: 10). Used by list, search."
1585
+ },
1586
+ uid: { type: "number", description: "Email UID to read. Required for read." },
1587
+ from: { type: "string", description: "Filter by sender address. Used by search." },
1588
+ subject: {
1589
+ type: "string",
1590
+ description: "Filter by subject (search) or email subject (send)."
1591
+ },
1592
+ since: {
1593
+ type: "string",
1594
+ description: "ISO 8601 date \u2014 emails since this date. Used by search."
1595
+ },
1596
+ unseen: { type: "boolean", description: "Only unread emails. Used by search." },
1597
+ to: { type: "string", description: "Recipient address. Required for send." },
1598
+ body: { type: "string", description: "Email body text. Required for send." },
1599
+ cc: { type: "string", description: "CC recipients. Used by send." },
1600
+ bcc: { type: "string", description: "BCC recipients. Used by send." }
1601
+ },
1602
+ required: ["action"]
1603
+ },
1604
+ execute: async (params, ctx) => {
1605
+ const p = params;
1606
+ const emailConfig = ctx.config.tools?.email;
1607
+ if (!emailConfig) {
1608
+ return { success: false, output: "Email is not configured. Add tools.email to your config." };
1609
+ }
1610
+ try {
1611
+ switch (p.action) {
1612
+ case "list":
1613
+ return { success: true, output: await handleList(p, emailConfig.imap) };
1614
+ case "read":
1615
+ if (!p.uid)
1616
+ return { success: false, output: "Missing required field: uid" };
1617
+ return { success: true, output: await handleRead(p, emailConfig.imap) };
1618
+ case "search":
1619
+ if (!p.from && !p.subject && !p.since && p.unseen === void 0)
1620
+ return { success: false, output: "At least one search criterion is required (from, subject, since, or unseen)." };
1621
+ return { success: true, output: await handleSearch(p, emailConfig.imap) };
1622
+ case "send":
1623
+ if (!p.to)
1624
+ return { success: false, output: "Missing required field: to" };
1625
+ if (!p.subject)
1626
+ return { success: false, output: "Missing required field: subject" };
1627
+ if (!p.body)
1628
+ return { success: false, output: "Missing required field: body" };
1629
+ return { success: true, output: await handleSend(p, emailConfig.smtp) };
1630
+ default:
1631
+ return { success: false, output: `Unknown action: ${p.action}` };
1632
+ }
1633
+ } catch (err2) {
1634
+ return {
1635
+ success: false,
1636
+ output: err2 instanceof Error ? err2.message : String(err2)
1637
+ };
1638
+ }
1639
+ }
1640
+ };
1229
1641
 
1230
1642
  // ../tools/dist/sandbox-exec.js
1231
1643
  var sandboxExecTool = {
@@ -1304,6 +1716,7 @@ function shellEscape(s) {
1304
1716
  var opencodeTool = {
1305
1717
  name: "opencode",
1306
1718
  description: "Run a code agent (claude-code, opencode, codex CLI) in a Docker container to perform a coding task.",
1719
+ configCheck: (ctx) => ctx.config.sandbox.codeAgent ? null : "This tool requires sandbox.codeAgent configuration. See https://augure.dev/docs/sandbox",
1307
1720
  parameters: {
1308
1721
  type: "object",
1309
1722
  properties: {
@@ -1515,6 +1928,10 @@ var DockerContainer = class {
1515
1928
  }
1516
1929
  });
1517
1930
  this.demux(stream, stdoutPT, stderrPT);
1931
+ stream.on("end", () => {
1932
+ stdoutPT.end();
1933
+ stderrPT.end();
1934
+ });
1518
1935
  });
1519
1936
  }
1520
1937
  };
@@ -1540,6 +1957,7 @@ var DockerContainerPool = class {
1540
1957
  docker;
1541
1958
  image;
1542
1959
  maxTotal;
1960
+ log;
1543
1961
  // C3: idle cache keyed by trust level to prevent cross-trust reuse
1544
1962
  idle = /* @__PURE__ */ new Map([
1545
1963
  ["sandboxed", /* @__PURE__ */ new Set()],
@@ -1551,6 +1969,7 @@ var DockerContainerPool = class {
1551
1969
  this.docker = docker;
1552
1970
  this.image = config.image;
1553
1971
  this.maxTotal = config.maxTotal;
1972
+ this.log = config.logger ?? noopLogger;
1554
1973
  }
1555
1974
  get idleCount() {
1556
1975
  let count = 0;
@@ -1560,18 +1979,22 @@ var DockerContainerPool = class {
1560
1979
  }
1561
1980
  /* ---- acquire ---- */
1562
1981
  async acquire(opts) {
1982
+ this.log.debug(`Acquiring container: trust=${opts.trust} memory=${opts.memory} cpu=${opts.cpu}`);
1563
1983
  const trustIdle = this.idle.get(opts.trust);
1564
1984
  const cached = trustIdle.values().next();
1565
1985
  if (!cached.done) {
1566
1986
  const container2 = cached.value;
1567
1987
  trustIdle.delete(container2);
1568
1988
  this.busy.add(container2);
1989
+ this.log.debug(`Reusing cached container: ${container2.id.slice(0, 12)}`);
1569
1990
  return container2;
1570
1991
  }
1571
1992
  const total = this.idleCount + this.busy.size;
1572
1993
  if (total >= this.maxTotal) {
1994
+ this.log.error(`Pool limit reached: ${total}/${this.maxTotal}`);
1573
1995
  throw new Error("Pool limit reached");
1574
1996
  }
1997
+ this.log.debug("Creating new container...");
1575
1998
  const raw = await this.docker.createContainer(this.buildCreateOpts(opts));
1576
1999
  await raw.start();
1577
2000
  const modem = this.docker.modem;
@@ -1579,6 +2002,7 @@ var DockerContainerPool = class {
1579
2002
  const container = new DockerContainer(raw, demux);
1580
2003
  this.containerTrust.set(container.id, opts.trust);
1581
2004
  this.busy.add(container);
2005
+ this.log.debug(`Container created: ${container.id.slice(0, 12)}`);
1582
2006
  return container;
1583
2007
  }
1584
2008
  /* ---- release ---- */
@@ -1590,6 +2014,7 @@ var DockerContainerPool = class {
1590
2014
  }
1591
2015
  const trust = this.containerTrust.get(container.id) ?? "sandboxed";
1592
2016
  this.idle.get(trust).add(container);
2017
+ this.log.debug(`Container released: ${container.id.slice(0, 12)} \u2192 idle (${trust})`);
1593
2018
  }
1594
2019
  /* ---- destroy ---- */
1595
2020
  async destroy(container) {
@@ -1678,16 +2103,19 @@ function buildTar(content) {
1678
2103
  const end = Buffer.alloc(1024, 0);
1679
2104
  return Buffer.concat([header, dataPadded, end]);
1680
2105
  }
1681
- async function ensureImage(docker, imageName) {
2106
+ async function ensureImage(docker, imageName, logger) {
2107
+ const log = logger ?? noopLogger;
2108
+ log.debug(`Checking image: ${imageName}`);
1682
2109
  try {
1683
2110
  await docker.getImage(imageName).inspect();
2111
+ log.debug("Image exists");
1684
2112
  return;
1685
2113
  } catch (err2) {
1686
2114
  const statusCode = err2.statusCode;
1687
2115
  if (statusCode !== void 0 && statusCode !== 404)
1688
2116
  throw err2;
1689
2117
  }
1690
- console.log(`[augure] Image "${imageName}" not found, building...`);
2118
+ log.info(`Image "${imageName}" not found, building...`);
1691
2119
  const tar = buildTar(DOCKERFILE);
1692
2120
  const stream = await docker.buildImage(Readable.from(tar), {
1693
2121
  t: imageName
@@ -1699,11 +2127,14 @@ async function ensureImage(docker, imageName) {
1699
2127
  else
1700
2128
  resolve5();
1701
2129
  }, (event) => {
1702
- if (event.stream)
1703
- process.stdout.write(event.stream);
2130
+ if (event.stream) {
2131
+ const line = event.stream.trim();
2132
+ if (line)
2133
+ log.debug(line);
2134
+ }
1704
2135
  });
1705
2136
  });
1706
- console.log(`[augure] Image "${imageName}" built successfully`);
2137
+ log.info(`Image "${imageName}" built`);
1707
2138
  }
1708
2139
 
1709
2140
  // ../memory/dist/store.js
@@ -2016,10 +2447,13 @@ Be concise. Only suggest actions that are clearly needed based on the memory con
2016
2447
  var Heartbeat = class {
2017
2448
  config;
2018
2449
  timer;
2450
+ log;
2019
2451
  constructor(config) {
2020
2452
  this.config = config;
2453
+ this.log = config.logger ?? noopLogger;
2021
2454
  }
2022
2455
  async tick() {
2456
+ this.log.debug("Heartbeat tick");
2023
2457
  const memoryContent = await this.loadMemory();
2024
2458
  const messages = [
2025
2459
  { role: "system", content: HEARTBEAT_PROMPT },
@@ -2034,12 +2468,15 @@ ${memoryContent}`
2034
2468
  const response = await this.config.llm.chat(messages);
2035
2469
  const action = this.parseAction(response.content);
2036
2470
  if (action && action.toLowerCase() !== "none") {
2471
+ this.log.debug(`Heartbeat action: ${action}`);
2037
2472
  await this.config.onAction(action);
2473
+ } else {
2474
+ this.log.debug("Heartbeat: no action needed");
2038
2475
  }
2039
2476
  }
2040
2477
  start() {
2041
2478
  this.timer = setInterval(() => {
2042
- this.tick().catch((err2) => console.error("[augure] Heartbeat error:", err2));
2479
+ this.tick().catch((err2) => this.log.error("Heartbeat error:", err2));
2043
2480
  }, this.config.intervalMs);
2044
2481
  }
2045
2482
  stop() {
@@ -2886,9 +3323,11 @@ var JOB_PREFIX = "skill:";
2886
3323
  var SkillSchedulerBridge = class {
2887
3324
  scheduler;
2888
3325
  manager;
2889
- constructor(scheduler, manager) {
3326
+ log;
3327
+ constructor(scheduler, manager, logger) {
2890
3328
  this.scheduler = scheduler;
2891
3329
  this.manager = manager;
3330
+ this.log = logger ?? noopLogger;
2892
3331
  }
2893
3332
  /** Register cron jobs for all active cron-triggered skills */
2894
3333
  async syncAll() {
@@ -2907,7 +3346,7 @@ var SkillSchedulerBridge = class {
2907
3346
  enabled: true
2908
3347
  });
2909
3348
  } catch (err2) {
2910
- console.error(`[skills] Failed to register cron for ${skill.id}:`, err2);
3349
+ this.log.error(`Failed to register cron for ${skill.id}:`, err2);
2911
3350
  }
2912
3351
  }
2913
3352
  existingJobs.delete(jobId);
@@ -3401,27 +3840,48 @@ var VersionChecker = class _VersionChecker {
3401
3840
  };
3402
3841
 
3403
3842
  // ../core/dist/main.js
3404
- var SYSTEM_PROMPT = `You are Augure, a personal AI assistant. You are proactive, helpful, and concise.
3843
+ var BASE_SYSTEM_PROMPT = `You are Augure, a personal AI assistant. You are proactive, helpful, and concise.
3405
3844
  You speak the same language as the user. You have access to tools and persistent memory.
3406
- Always be direct and actionable.`;
3407
- function resolveLLMClient(config, usage) {
3845
+ Always be direct and actionable.
3846
+
3847
+ ## Your capabilities
3848
+
3849
+ You have access to tools that let you interact with the outside world. Use the datetime tool when the user needs precise time information beyond what is shown in the current date above. Use memory tools to remember and recall information across conversations. Use the schedule tool to create recurring or one-shot tasks.
3850
+
3851
+ If a tool is marked as [NOT CONFIGURED], let the user know it needs to be set up first and share the documentation link from the tool description.`;
3852
+ var SKILLS_PROMPT = `
3853
+ ## Skills
3854
+
3855
+ You can create and manage "skills" \u2014 autonomous code units that run in isolated Docker containers. Skills are powerful: they let you automate tasks, run on a schedule, and self-heal when they break.
3856
+
3857
+ - Use skill_list to see existing skills and their status
3858
+ - Use skill_generate to create a new skill from a natural language description
3859
+ - Use skill_run to execute a skill manually
3860
+ - Use skill_heal to fix a broken skill
3861
+ - Use skill_install to install a skill from the hub
3862
+
3863
+ When a user asks to automate a recurring task (e.g. "check this every morning", "send me a summary daily"), suggest creating a skill with a cron trigger. Skills can also be triggered manually or by events.`;
3864
+ function resolveLLMClient(config, usage, logger) {
3408
3865
  const override = usage !== "default" ? config[usage] : void 0;
3409
3866
  return new OpenRouterClient({
3410
3867
  apiKey: override?.apiKey ?? config.default.apiKey,
3411
3868
  model: override?.model ?? config.default.model,
3412
- maxTokens: override?.maxTokens ?? config.default.maxTokens
3869
+ maxTokens: override?.maxTokens ?? config.default.maxTokens,
3870
+ logger: logger.child("llm")
3413
3871
  });
3414
3872
  }
3415
- async function startAgent(configPath) {
3873
+ async function startAgent(configPath, opts) {
3874
+ const log = createLogger({ level: opts?.debug ? "debug" : "info" });
3416
3875
  const config = await loadConfig(configPath);
3417
- console.log(`[augure] Loaded config: ${config.identity.name}`);
3876
+ log.info(`Loaded config: ${config.identity.name}`);
3877
+ log.debug(`Config path: ${configPath}`);
3418
3878
  let telegram;
3419
- const llm = resolveLLMClient(config.llm, "default");
3420
- const ingestionLLM = resolveLLMClient(config.llm, "ingestion");
3421
- const monitoringLLM = resolveLLMClient(config.llm, "monitoring");
3879
+ const llm = resolveLLMClient(config.llm, "default", log);
3880
+ const ingestionLLM = resolveLLMClient(config.llm, "ingestion", log);
3881
+ const monitoringLLM = resolveLLMClient(config.llm, "monitoring", log);
3422
3882
  const memoryPath = resolve(configPath, "..", config.memory.path);
3423
3883
  const memory = new FileMemoryStore(memoryPath);
3424
- console.log(`[augure] Memory store: ${memoryPath}`);
3884
+ log.info(`Memory store: ${memoryPath}`);
3425
3885
  const retriever = new MemoryRetriever(memory, {
3426
3886
  maxTokens: config.memory.maxRetrievalTokens
3427
3887
  });
@@ -3430,15 +3890,17 @@ async function startAgent(configPath) {
3430
3890
  tools.register(memoryReadTool);
3431
3891
  tools.register(memoryWriteTool);
3432
3892
  tools.register(scheduleTool);
3893
+ tools.register(datetimeTool);
3433
3894
  tools.register(webSearchTool);
3434
3895
  tools.register(httpTool);
3896
+ tools.register(emailTool);
3435
3897
  tools.register(sandboxExecTool);
3436
3898
  tools.register(opencodeTool);
3437
3899
  const jobStorePath = resolve(configPath, "..", "jobs.json");
3438
3900
  const jobStore = new JobStore(jobStorePath);
3439
3901
  const scheduler = new CronScheduler(jobStore);
3440
3902
  await scheduler.loadPersistedJobs();
3441
- console.log(`[augure] Loaded ${scheduler.listJobs().length} persisted jobs`);
3903
+ log.info(`Loaded ${scheduler.listJobs().length} persisted jobs`);
3442
3904
  for (const job of config.scheduler.jobs) {
3443
3905
  if (!scheduler.listJobs().some((j) => j.id === job.id)) {
3444
3906
  scheduler.addJob({ ...job, enabled: true });
@@ -3446,17 +3908,19 @@ async function startAgent(configPath) {
3446
3908
  }
3447
3909
  const docker = new Dockerode();
3448
3910
  const sandboxImage = config.sandbox.image ?? "augure-sandbox:latest";
3449
- await ensureImage(docker, sandboxImage);
3911
+ const sandboxLog = log.child("sandbox");
3912
+ await ensureImage(docker, sandboxImage, sandboxLog);
3450
3913
  const pool = new DockerContainerPool(docker, {
3451
3914
  image: sandboxImage,
3452
- maxTotal: config.security.maxConcurrentSandboxes
3915
+ maxTotal: config.security.maxConcurrentSandboxes,
3916
+ logger: sandboxLog
3453
3917
  });
3454
- console.log(`[augure] Container pool created (max: ${config.security.maxConcurrentSandboxes})`);
3918
+ log.info(`Container pool: max=${config.security.maxConcurrentSandboxes}`);
3455
3919
  let skillManagerRef;
3456
3920
  let skillUpdater;
3457
3921
  if (config.skills) {
3458
3922
  const skillsPath = resolve(configPath, "..", config.skills.path);
3459
- const codingLLM = resolveLLMClient(config.llm, "coding");
3923
+ const codingLLM = resolveLLMClient(config.llm, "coding", log);
3460
3924
  const skillManager = new SkillManager(skillsPath);
3461
3925
  const skillGenerator = new SkillGenerator(codingLLM);
3462
3926
  const skillRunner = new SkillRunner({
@@ -3500,29 +3964,29 @@ async function startAgent(configPath) {
3500
3964
  const updated = updateResults.filter((r) => r.success);
3501
3965
  const failed = updateResults.filter((r) => !r.success);
3502
3966
  if (updated.length > 0) {
3503
- console.log(`[augure] Skills updated: ${updated.map((r) => `${r.skillId} (v${r.fromVersion}\u2192v${r.toVersion})`).join(", ")}`);
3967
+ log.info(`Skills updated: ${updated.map((r) => `${r.skillId} (v${r.fromVersion}\u2192v${r.toVersion})`).join(", ")}`);
3504
3968
  }
3505
3969
  if (failed.length > 0) {
3506
- console.log(`[augure] Skill updates failed: ${failed.map((r) => `${r.skillId}: ${r.error}`).join(", ")}`);
3970
+ log.warn(`Skill updates failed: ${failed.map((r) => `${r.skillId}: ${r.error}`).join(", ")}`);
3507
3971
  }
3508
3972
  } catch (err2) {
3509
- console.error("[augure] Skill update check failed:", err2);
3973
+ log.error("Skill update check failed:", err2);
3510
3974
  }
3511
3975
  }
3512
3976
  skillManagerRef = skillManager;
3513
- console.log(`[augure] Skills system initialized at ${skillsPath}`);
3977
+ log.info(`Skills initialized: ${skillsPath}`);
3514
3978
  }
3515
3979
  tools.setContext({ config, memory, scheduler, pool });
3516
3980
  const auditConfig = config.audit ?? { path: "./logs", enabled: true };
3517
3981
  const auditPath = resolve(configPath, "..", auditConfig.path);
3518
- const audit = auditConfig.enabled ? new FileAuditLogger(auditPath) : new NullAuditLogger();
3519
- console.log(`[augure] Audit logger: ${auditConfig.enabled ? auditPath : "disabled"}`);
3982
+ const audit = auditConfig.enabled ? new FileAuditLogger(auditPath, log.child("audit")) : new NullAuditLogger();
3983
+ log.info(`Audit: ${auditConfig.enabled ? auditPath : "disabled"}`);
3520
3984
  let personaResolver;
3521
3985
  if (config.persona) {
3522
3986
  const personaPath = resolve(configPath, "..", config.persona.path);
3523
3987
  personaResolver = new PersonaResolver(personaPath);
3524
3988
  await personaResolver.loadAll();
3525
- console.log(`[augure] Personas loaded from ${personaPath}`);
3989
+ log.info(`Personas: ${personaPath}`);
3526
3990
  }
3527
3991
  let cliVersion;
3528
3992
  try {
@@ -3538,29 +4002,33 @@ async function startAgent(configPath) {
3538
4002
  });
3539
4003
  const versionResult = await versionChecker.check();
3540
4004
  if (versionResult.updateAvailable) {
3541
- console.log(`[augure] Update available: v${versionResult.latestVersion} (current: v${versionResult.currentVersion}). Run: npm update -g augure`);
4005
+ log.warn(`Update available: v${versionResult.latestVersion} (current: v${versionResult.currentVersion}). Run: npm update -g augure`);
3542
4006
  }
3543
4007
  }
3544
4008
  const guard = new ContextGuard({
3545
4009
  maxContextTokens: 2e5,
3546
4010
  reservedForOutput: config.llm.default.maxTokens ?? 8192
3547
4011
  });
4012
+ const systemPrompt = config.skills ? BASE_SYSTEM_PROMPT + SKILLS_PROMPT : BASE_SYSTEM_PROMPT;
3548
4013
  const agent = new Agent({
3549
4014
  llm,
3550
4015
  tools,
3551
- systemPrompt: SYSTEM_PROMPT,
4016
+ systemPrompt,
3552
4017
  memoryContent: "",
3553
4018
  retriever,
3554
4019
  ingester,
3555
4020
  audit,
3556
4021
  guard,
3557
- modelName: config.llm.default.model
4022
+ modelName: config.llm.default.model,
4023
+ logger: log.child("agent")
3558
4024
  });
3559
4025
  if (config.channels.telegram?.enabled) {
4026
+ const telegramLog = log.child("telegram");
3560
4027
  telegram = new TelegramChannel({
3561
4028
  botToken: config.channels.telegram.botToken,
3562
4029
  allowedUsers: config.channels.telegram.allowedUsers,
3563
- rejectMessage: config.channels.telegram.rejectMessage
4030
+ rejectMessage: config.channels.telegram.rejectMessage,
4031
+ logger: telegramLog
3564
4032
  });
3565
4033
  const tg = telegram;
3566
4034
  const commandCtx = {
@@ -3570,7 +4038,7 @@ async function startAgent(configPath) {
3570
4038
  skillManager: skillManagerRef
3571
4039
  };
3572
4040
  tg.onMessage(async (msg) => {
3573
- console.log(`[augure] Message from ${msg.userId}: ${msg.text}`);
4041
+ log.info(`Message from ${msg.userId}: ${msg.text}`);
3574
4042
  try {
3575
4043
  const cmdResult = await handleCommand(msg.text, commandCtx);
3576
4044
  if (cmdResult.handled) {
@@ -3593,7 +4061,7 @@ async function startAgent(configPath) {
3593
4061
  replyTo: msg.id
3594
4062
  });
3595
4063
  } catch (err2) {
3596
- console.error("[augure] Error handling message:", err2);
4064
+ log.error("Error handling message:", err2);
3597
4065
  await tg.send({
3598
4066
  channelType: "telegram",
3599
4067
  userId: msg.userId,
@@ -3602,15 +4070,16 @@ async function startAgent(configPath) {
3602
4070
  }
3603
4071
  });
3604
4072
  await tg.start();
3605
- console.log("[augure] Telegram bot started. Waiting for messages...");
4073
+ log.info("Telegram bot started");
3606
4074
  }
3607
4075
  const heartbeatIntervalMs = parseInterval(config.scheduler.heartbeatInterval);
3608
4076
  const heartbeat = new Heartbeat({
3609
4077
  llm: monitoringLLM,
3610
4078
  memory,
3611
4079
  intervalMs: heartbeatIntervalMs,
4080
+ logger: log.child("heartbeat"),
3612
4081
  onAction: async (action) => {
3613
- console.log(`[augure] Heartbeat action: ${action}`);
4082
+ log.info(`Heartbeat action: ${action}`);
3614
4083
  const response = await agent.handleMessage({
3615
4084
  id: `heartbeat-${Date.now()}`,
3616
4085
  channelType: "system",
@@ -3618,11 +4087,11 @@ async function startAgent(configPath) {
3618
4087
  text: `[Heartbeat] ${action}`,
3619
4088
  timestamp: /* @__PURE__ */ new Date()
3620
4089
  });
3621
- console.log(`[augure] Heartbeat response: ${response}`);
4090
+ log.debug(`Heartbeat response: ${response.slice(0, 200)}`);
3622
4091
  }
3623
4092
  });
3624
4093
  scheduler.onJobTrigger(async (job) => {
3625
- console.log(`[augure] Job triggered: ${job.id}`);
4094
+ log.info(`Job triggered: ${job.id}`);
3626
4095
  const response = await agent.handleMessage({
3627
4096
  id: `job-${job.id}-${Date.now()}`,
3628
4097
  channelType: "system",
@@ -3640,11 +4109,11 @@ async function startAgent(configPath) {
3640
4109
  });
3641
4110
  }
3642
4111
  }
3643
- console.log(`[augure] Job ${job.id} completed`);
4112
+ log.debug(`Job ${job.id} completed`);
3644
4113
  });
3645
4114
  scheduler.start();
3646
4115
  heartbeat.start();
3647
- console.log(`[augure] Scheduler started with ${scheduler.listJobs().length} jobs. Heartbeat every ${config.scheduler.heartbeatInterval}.`);
4116
+ log.info(`Scheduler started: ${scheduler.listJobs().length} jobs, heartbeat every ${config.scheduler.heartbeatInterval}`);
3648
4117
  const updateTimers = [];
3649
4118
  if (skillUpdater && config.updates?.skills?.checkInterval) {
3650
4119
  const su = skillUpdater;
@@ -3654,13 +4123,13 @@ async function startAgent(configPath) {
3654
4123
  const results = await su.checkAndApply();
3655
4124
  for (const r of results) {
3656
4125
  if (r.success) {
3657
- console.log(`[augure] Skill auto-updated: ${r.skillId} v${r.fromVersion}\u2192v${r.toVersion}`);
4126
+ log.info(`Skill auto-updated: ${r.skillId} v${r.fromVersion}\u2192v${r.toVersion}`);
3658
4127
  } else if (r.rolledBack) {
3659
- console.log(`[augure] Skill update rolled back: ${r.skillId} - ${r.error}`);
4128
+ log.warn(`Skill update rolled back: ${r.skillId} - ${r.error}`);
3660
4129
  }
3661
4130
  }
3662
4131
  } catch (err2) {
3663
- console.error("[augure] Periodic skill update check failed:", err2);
4132
+ log.error("Periodic skill update check failed:", err2);
3664
4133
  }
3665
4134
  }, skillCheckMs));
3666
4135
  }
@@ -3685,12 +4154,12 @@ Run: \`npm update -g augure\``
3685
4154
  }
3686
4155
  }
3687
4156
  } catch (err2) {
3688
- console.error("[augure] CLI version check failed:", err2);
4157
+ log.error("CLI version check failed:", err2);
3689
4158
  }
3690
4159
  }, cliCheckMs));
3691
4160
  }
3692
4161
  const shutdown = async () => {
3693
- console.log("\n[augure] Shutting down...");
4162
+ log.info("Shutting down...");
3694
4163
  for (const timer of updateTimers)
3695
4164
  clearInterval(timer);
3696
4165
  heartbeat.stop();
@@ -3699,23 +4168,13 @@ Run: \`npm update -g augure\``
3699
4168
  await telegram.stop();
3700
4169
  await pool.destroyAll();
3701
4170
  await audit.close();
3702
- console.log("[augure] All containers destroyed");
4171
+ log.info("All containers destroyed");
3703
4172
  process.exit(0);
3704
4173
  };
3705
4174
  process.on("SIGINT", shutdown);
3706
4175
  process.on("SIGTERM", shutdown);
3707
4176
  }
3708
4177
 
3709
- // src/colors.ts
3710
- import { styleText } from "util";
3711
- var brand = (s) => styleText("yellow", s);
3712
- var ok = (s) => styleText("green", s);
3713
- var err = (s) => styleText("red", s);
3714
- var dim = (s) => styleText("dim", s);
3715
- var bold = (s) => styleText("bold", s);
3716
- var cyan = (s) => styleText("cyan", s);
3717
- var prefix = brand("\u25B2 augure");
3718
-
3719
4178
  // src/commands/start.ts
3720
4179
  var startCommand = defineCommand({
3721
4180
  meta: {
@@ -3733,21 +4192,28 @@ var startCommand = defineCommand({
3733
4192
  type: "string",
3734
4193
  description: "Path to .env file",
3735
4194
  alias: "e"
4195
+ },
4196
+ debug: {
4197
+ type: "boolean",
4198
+ description: "Enable debug logging",
4199
+ alias: "d",
4200
+ default: false
3736
4201
  }
3737
4202
  },
3738
4203
  async run({ args }) {
3739
4204
  const configPath = resolve2(args.config);
4205
+ const log = createLogger({ level: args.debug ? "debug" : "info" });
3740
4206
  const envPath = args.env ? resolve2(args.env) : join6(dirname4(configPath), ".env");
3741
4207
  try {
3742
4208
  process.loadEnvFile(envPath);
3743
- console.log(`${prefix} Loaded env from ${dim(envPath)}`);
4209
+ log.debug(`Loaded env from ${envPath}`);
3744
4210
  } catch {
3745
4211
  }
3746
- console.log(`${prefix} Starting with config: ${dim(configPath)}`);
4212
+ log.info(`Starting with config: ${configPath}`);
3747
4213
  try {
3748
- await startAgent(configPath);
4214
+ await startAgent(configPath, { debug: args.debug });
3749
4215
  } catch (e) {
3750
- console.error(`${prefix} ${err("Fatal error:")} ${e instanceof Error ? e.message : e}`);
4216
+ log.error("Fatal:", e instanceof Error ? e.message : String(e));
3751
4217
  process.exit(1);
3752
4218
  }
3753
4219
  }
@@ -3757,6 +4223,18 @@ var startCommand = defineCommand({
3757
4223
  import { defineCommand as defineCommand2 } from "citty";
3758
4224
  import { writeFile as writeFile5, access as access3 } from "fs/promises";
3759
4225
  import { resolve as resolve3 } from "path";
4226
+
4227
+ // src/colors.ts
4228
+ import { styleText as styleText2 } from "util";
4229
+ var brand = (s) => styleText2("yellow", s);
4230
+ var ok = (s) => styleText2("green", s);
4231
+ var err = (s) => styleText2("red", s);
4232
+ var dim = (s) => styleText2("dim", s);
4233
+ var bold = (s) => styleText2("bold", s);
4234
+ var cyan = (s) => styleText2("cyan", s);
4235
+ var prefix = brand("\u25B2 augure");
4236
+
4237
+ // src/commands/init.ts
3760
4238
  var CONFIG_TEMPLATE = `{
3761
4239
  // Identity
3762
4240
  identity: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "augure",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Augure — your proactive AI agent",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,15 @@
12
12
  "engines": {
13
13
  "node": ">=22.0.0"
14
14
  },
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "dev": "tsup --watch",
18
+ "test": "vitest run",
19
+ "test:unit": "vitest run",
20
+ "typecheck": "tsc --noEmit",
21
+ "lint": "eslint src/",
22
+ "clean": "rm -rf dist .turbo"
23
+ },
15
24
  "dependencies": {
16
25
  "citty": "^0.2.1",
17
26
  "dockerode": "^4.0.9",
@@ -24,17 +33,17 @@
24
33
  "zod": "^4.3.6"
25
34
  },
26
35
  "devDependencies": {
36
+ "@augure/channels": "workspace:*",
37
+ "@augure/core": "workspace:*",
38
+ "@augure/memory": "workspace:*",
39
+ "@augure/sandbox": "workspace:*",
40
+ "@augure/scheduler": "workspace:*",
41
+ "@augure/skills": "workspace:*",
42
+ "@augure/tools": "workspace:*",
43
+ "@augure/types": "workspace:*",
27
44
  "@types/dockerode": "^4.0.1",
28
45
  "@types/node-cron": "^3.0.11",
29
- "tsup": "^8.5.1",
30
- "@augure/channels": "0.1.1",
31
- "@augure/core": "0.2.0",
32
- "@augure/memory": "0.0.3",
33
- "@augure/sandbox": "0.1.0",
34
- "@augure/scheduler": "0.1.0",
35
- "@augure/skills": "0.1.1",
36
- "@augure/types": "0.1.1",
37
- "@augure/tools": "0.0.3"
46
+ "tsup": "^8.5.1"
38
47
  },
39
48
  "keywords": [
40
49
  "ai",
@@ -52,14 +61,5 @@
52
61
  },
53
62
  "publishConfig": {
54
63
  "access": "public"
55
- },
56
- "scripts": {
57
- "build": "tsup",
58
- "dev": "tsup --watch",
59
- "test": "vitest run",
60
- "test:unit": "vitest run",
61
- "typecheck": "tsc --noEmit",
62
- "lint": "eslint src/",
63
- "clean": "rm -rf dist .turbo"
64
64
  }
65
- }
65
+ }