nothumanallowed 6.5.0 → 6.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "6.5.0",
3
+ "version": "6.5.2",
4
4
  "description": "NotHumanAllowed — 38 AI agents for security, code, DevOps, data & daily ops. Per-agent memory, Telegram + Discord auto-responder, proactive intelligence daemon, voice chat, plugin system.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -125,12 +125,15 @@ TOOLS:
125
125
  18. schedule_draft_email(clientName: string, subject: string, location: string, durationMinutes: number, dateFrom: string, dateTo: string)
126
126
  Same as schedule_meeting but also generates a professional email proposing the top 3 slots to the client.
127
127
 
128
- 19. calendar_update(eventId: string, summary?: string, location?: string, description?: string, start?: string, end?: string)
128
+ 19. calendar_find(query: string, daysAhead?: number)
129
+ Search for a calendar event by name/keyword in the next N days (default 7). Returns matching events with their IDs.
130
+ ALWAYS use this FIRST when the user wants to modify an event — you need the eventId.
131
+
132
+ 20. calendar_update(eventId: string, summary?: string, location?: string, description?: string, start?: string, end?: string)
129
133
  Update ANY field of an existing calendar event: title, location, description, start time, end time.
130
- Use this to modify existing events. You must provide the eventId (from calendar_today or calendar_week).
131
- Only include fields that need to change. ALWAYS confirm with the user before updating.
134
+ You MUST call calendar_find first to get the eventId. Only include fields that need to change. ALWAYS confirm before updating.
132
135
 
133
- 20. maps_directions(from: string, to: string)
136
+ 21. maps_directions(from: string, to: string)
134
137
  Generate a Google Maps directions link between two locations. Returns a clickable URL.
135
138
  Use this when the user asks for directions, route, or "how to get to" somewhere.
136
139
 
@@ -360,6 +363,30 @@ async function executeTool(action, params, config) {
360
363
  return `${proposal}\n\n--- DRAFT EMAIL ---\n\n${email}`;
361
364
  }
362
365
 
366
+ // ── Calendar Find (search event by name) ────────────────────────────
367
+ case 'calendar_find': {
368
+ const { listEvents: listEventsRouter } = await import('../services/mail-router.mjs');
369
+ const query = (params.query || '').toLowerCase();
370
+ const daysAhead = params.daysAhead || 7;
371
+ const from = new Date();
372
+ const to = new Date(from.getTime() + daysAhead * 86400000);
373
+ const events = await listEventsRouter(config, 'primary', from, to);
374
+
375
+ const matches = events.filter(e =>
376
+ (e.summary || '').toLowerCase().includes(query) ||
377
+ (e.description || '').toLowerCase().includes(query)
378
+ );
379
+
380
+ if (matches.length === 0) return `No events found matching "${params.query}" in the next ${daysAhead} days.`;
381
+
382
+ return matches.map((e, i) => {
383
+ const time = e.isAllDay ? 'All day' : `${formatTime(e.start)} - ${formatTime(e.end)}`;
384
+ const date = e.start.split('T')[0];
385
+ const loc = e.location ? ` | Location: ${e.location}` : '';
386
+ return `${i + 1}. [eventId: ${e.id}] ${date} ${time} — ${e.summary}${loc}`;
387
+ }).join('\n');
388
+ }
389
+
363
390
  // ── Calendar Update (modify any field of existing event) ──────────────
364
391
  case 'calendar_update': {
365
392
  const { updateEvent: updateCal } = await import('../services/mail-router.mjs');
@@ -398,10 +425,10 @@ async function executeTool(action, params, config) {
398
425
  function formatEvents(events) {
399
426
  return events.map((e, i) => {
400
427
  const time = e.isAllDay ? 'All day' : `${formatTime(e.start)} - ${formatTime(e.end)}`;
401
- const location = e.location ? ` | ${e.location}` : '';
428
+ const location = e.location ? ` | Location: ${e.location}` : '';
402
429
  const link = e.hangoutLink ? ` | ${e.hangoutLink}` : '';
403
430
  const cal = e.calendarName ? ` (${e.calendarName})` : '';
404
- return `${i + 1}. ${time} — ${e.summary}${location}${link}${cal}`;
431
+ return `${i + 1}. [ID:${e.id}] ${time} — ${e.summary}${location}${link}${cal}`;
405
432
  }).join('\n');
406
433
  }
407
434
 
@@ -173,7 +173,7 @@ async function executeTool(action, params, config) {
173
173
  if (events.length === 0) return 'No events scheduled for today.';
174
174
  return events.map((e, i) => {
175
175
  const time = e.isAllDay ? 'All day' : `${fmtTime(e.start)} - ${fmtTime(e.end)}`;
176
- return `${i + 1}. ${time} — ${e.summary}${e.location ? ' @ ' + e.location : ''}`;
176
+ return `${i + 1}. [ID:${e.id}] ${time} — ${e.summary}${e.location ? ' @ ' + e.location : ''}`;
177
177
  }).join('\n');
178
178
  }
179
179
  case 'calendar_upcoming': {
@@ -209,7 +209,7 @@ async function executeTool(action, params, config) {
209
209
  if (events.length === 0) return 'No events scheduled for tomorrow.';
210
210
  return events.map((e, i) => {
211
211
  const time = e.isAllDay ? 'All day' : `${fmtTime(e.start)} - ${fmtTime(e.end)}`;
212
- return `${i + 1}. ${time} — ${e.summary}${e.location ? ' @ ' + e.location : ''}`;
212
+ return `${i + 1}. [ID:${e.id}] ${time} — ${e.summary}${e.location ? ' @ ' + e.location : ''}`;
213
213
  }).join('\n');
214
214
  }
215
215
  case 'task_move': {
@@ -306,6 +306,20 @@ async function executeTool(action, params, config) {
306
306
  return `${proposal}\n\n--- DRAFT EMAIL ---\n\n${email}`;
307
307
  }
308
308
 
309
+ case 'calendar_find': {
310
+ const { listEvents: listEventsR } = await import('../services/mail-router.mjs');
311
+ const query = (params.query || '').toLowerCase();
312
+ const daysAhead = params.daysAhead || 7;
313
+ const from = new Date();
314
+ const to = new Date(from.getTime() + daysAhead * 86400000);
315
+ const events = await listEventsR(config, 'primary', from, to);
316
+ const matches = events.filter(e => (e.summary || '').toLowerCase().includes(query) || (e.description || '').toLowerCase().includes(query));
317
+ if (matches.length === 0) return `No events matching "${params.query}" in the next ${daysAhead} days.`;
318
+ return matches.map((e, i) => {
319
+ const time = e.isAllDay ? 'All day' : `${fmtTime(e.start)} - ${fmtTime(e.end)}`;
320
+ return `${i + 1}. [eventId: ${e.id}] ${e.start.split('T')[0]} ${time} — ${e.summary}${e.location ? ' @ ' + e.location : ''}`;
321
+ }).join('\n');
322
+ }
309
323
  case 'calendar_update': {
310
324
  const { updateEvent: updateCal } = await import('../services/mail-router.mjs');
311
325
  const patch = {};
@@ -218,6 +218,16 @@ async function executeTool(action, params, config) {
218
218
  });
219
219
  return formatSlotProposal(slots, params.clientName || 'the client', params.subject || 'meeting', params.location || '');
220
220
  }
221
+ case 'calendar_find': {
222
+ const { listEvents: listEventsR } = await import('../services/mail-router.mjs');
223
+ const query = (params.query || '').toLowerCase();
224
+ const from = new Date();
225
+ const to = new Date(from.getTime() + 7 * 86400000);
226
+ const events = await listEventsR(config, 'primary', from, to);
227
+ const matches = events.filter(e => (e.summary || '').toLowerCase().includes(query));
228
+ if (matches.length === 0) return 'No events matching that name found this week.';
229
+ return matches.map((e, i) => `${i + 1}. Event ID ${e.id}, ${e.start.split('T')[0]}, ${e.summary}`).join('. ');
230
+ }
221
231
  case 'calendar_update': {
222
232
  const { updateEvent: updateCal } = await import('../services/mail-router.mjs');
223
233
  const patch = {};
package/src/config.mjs CHANGED
@@ -123,7 +123,18 @@ const DEFAULT_CONFIG = {
123
123
  export function loadConfig() {
124
124
  if (fs.existsSync(CONFIG_FILE)) {
125
125
  try {
126
- return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
126
+ const saved = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
127
+ // Deep merge with defaults so new config sections (profile, responder, etc.)
128
+ // are always present even if the file was created before they existed
129
+ const merged = structuredClone(DEFAULT_CONFIG);
130
+ for (const [key, value] of Object.entries(saved)) {
131
+ if (typeof value === 'object' && value !== null && !Array.isArray(value) && typeof merged[key] === 'object' && merged[key] !== null) {
132
+ merged[key] = { ...merged[key], ...value };
133
+ } else {
134
+ merged[key] = value;
135
+ }
136
+ }
137
+ return merged;
127
138
  } catch {
128
139
  return structuredClone(DEFAULT_CONFIG);
129
140
  }
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '6.5.0';
8
+ export const VERSION = '6.5.2';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11