atris 3.15.56 → 3.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/GETTING_STARTED.md +1 -1
- package/PERSONA.md +4 -4
- package/README.md +11 -11
- package/atris/skills/copy-editor/SKILL.md +30 -4
- package/atris/skills/improve/SKILL.md +18 -20
- package/atris/wiki/concepts/agent-activation-contract.md +5 -3
- package/atris/wiki/concepts/workspace-initialization-contract.md +4 -4
- package/atris/wiki/index.md +1 -0
- package/ax +522 -73
- package/bin/atris.js +32 -31
- package/commands/align.js +0 -14
- package/commands/apps.js +102 -1
- package/commands/autopilot.js +197 -22
- package/commands/brain.js +219 -34
- package/commands/brainstorm.js +0 -829
- package/commands/computer.js +45 -83
- package/commands/improve.js +501 -0
- package/commands/integrations.js +228 -0
- package/commands/lesson.js +44 -0
- package/commands/member.js +4498 -226
- package/commands/mission.js +302 -27
- package/commands/now.js +89 -1
- package/commands/radar.js +181 -56
- package/commands/skill.js +37 -6
- package/commands/soul.js +0 -4
- package/commands/task.js +5582 -517
- package/commands/terminal.js +14 -10
- package/commands/wiki.js +87 -1
- package/commands/workflow.js +288 -73
- package/commands/worktree.js +52 -15
- package/commands/xp.js +41 -65
- package/lib/auto-accept-certified.js +294 -0
- package/lib/file-ops.js +0 -184
- package/lib/member-alive.js +232 -0
- package/lib/policy-lessons.js +280 -0
- package/lib/receipt-evidence.js +64 -0
- package/lib/state-detection.js +34 -0
- package/lib/task-db.js +568 -16
- package/lib/task-proof.js +43 -0
- package/package.json +1 -1
- package/utils/auth.js +13 -4
- package/commands/research.js +0 -52
- package/lib/section-merge.js +0 -196
package/commands/integrations.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* atris calendar yesterday - Show yesterday's events
|
|
9
9
|
* atris calendar week - Show this week's events
|
|
10
10
|
* atris calendar date YYYY-MM-DD - Show events on a date
|
|
11
|
+
* atris calendar search <query> [--days 30] [--limit 20] [--timeout-ms 10000] [--json] - Search upcoming events
|
|
11
12
|
* atris twitter post - Post a tweet (interactive)
|
|
12
13
|
* atris slack channels - List Slack channels
|
|
13
14
|
* atris slack messages <channel> [--limit 20] - Read recent messages
|
|
@@ -21,6 +22,8 @@ const os = require('os');
|
|
|
21
22
|
const path = require('path');
|
|
22
23
|
const { spawnSync } = require('child_process');
|
|
23
24
|
|
|
25
|
+
const CALENDAR_CACHE_PATH = path.join(os.homedir(), '.atris', 'calendar-events-cache.json');
|
|
26
|
+
|
|
24
27
|
async function getAuth() {
|
|
25
28
|
const ensured = await ensureValidCredentials(apiRequestJson);
|
|
26
29
|
const creds = ensured.error ? null : ensured.credentials;
|
|
@@ -209,6 +212,166 @@ function formatCalendarEvents(label, events) {
|
|
|
209
212
|
console.log('─'.repeat(50));
|
|
210
213
|
}
|
|
211
214
|
|
|
215
|
+
function parseCalendarSearchArgs(args = []) {
|
|
216
|
+
const values = [];
|
|
217
|
+
const options = { days: 30, limit: 20, timeoutMs: 10000, json: false };
|
|
218
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
219
|
+
const arg = args[i];
|
|
220
|
+
if (arg === '--json') {
|
|
221
|
+
options.json = true;
|
|
222
|
+
} else if (arg === '--days' || arg === '-d') {
|
|
223
|
+
const parsed = Number.parseInt(args[i + 1], 10);
|
|
224
|
+
if (Number.isFinite(parsed) && parsed > 0) options.days = Math.min(parsed, 365);
|
|
225
|
+
i += 1;
|
|
226
|
+
} else if (arg === '--limit' || arg === '-n') {
|
|
227
|
+
const parsed = Number.parseInt(args[i + 1], 10);
|
|
228
|
+
if (Number.isFinite(parsed) && parsed > 0) options.limit = Math.min(parsed, 100);
|
|
229
|
+
i += 1;
|
|
230
|
+
} else if (arg === '--timeout-ms' || arg === '--timeout') {
|
|
231
|
+
const parsed = Number.parseInt(args[i + 1], 10);
|
|
232
|
+
if (Number.isFinite(parsed) && parsed > 0) options.timeoutMs = Math.min(parsed, 60000);
|
|
233
|
+
i += 1;
|
|
234
|
+
} else {
|
|
235
|
+
values.push(arg);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return { query: values.join(' ').trim(), ...options };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function calendarAttendeesText(attendees) {
|
|
242
|
+
if (!Array.isArray(attendees)) return '';
|
|
243
|
+
return attendees
|
|
244
|
+
.map((attendee) => {
|
|
245
|
+
if (typeof attendee === 'string') return attendee;
|
|
246
|
+
return [
|
|
247
|
+
attendee.email,
|
|
248
|
+
attendee.displayName,
|
|
249
|
+
attendee.name,
|
|
250
|
+
attendee.responseStatus,
|
|
251
|
+
].filter(Boolean).join(' ');
|
|
252
|
+
})
|
|
253
|
+
.filter(Boolean)
|
|
254
|
+
.join(' ');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function calendarSearchText(event = {}) {
|
|
258
|
+
return [
|
|
259
|
+
event.summary,
|
|
260
|
+
event.title,
|
|
261
|
+
event.location,
|
|
262
|
+
event.description,
|
|
263
|
+
calendarAttendeesText(event.attendees),
|
|
264
|
+
].filter(Boolean).join(' ').toLowerCase();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function filterCalendarEventsForSearch(events = [], query = '', limit = 20) {
|
|
268
|
+
const terms = String(query || '').toLowerCase().split(/\s+/).filter(Boolean);
|
|
269
|
+
if (!terms.length) return [];
|
|
270
|
+
return events
|
|
271
|
+
.filter((event) => {
|
|
272
|
+
const haystack = calendarSearchText(event);
|
|
273
|
+
return terms.every((term) => haystack.includes(term));
|
|
274
|
+
})
|
|
275
|
+
.slice(0, limit);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function compactCalendarEvent(event = {}) {
|
|
279
|
+
return {
|
|
280
|
+
id: event.id || event.event_id || '',
|
|
281
|
+
start: calendarEventTimeValue(event.start || event.start_time || event.startTime),
|
|
282
|
+
end: calendarEventTimeValue(event.end || event.end_time || event.endTime),
|
|
283
|
+
summary: event.summary || event.title || '(no title)',
|
|
284
|
+
location: event.location || '',
|
|
285
|
+
htmlLink: event.htmlLink || event.link || '',
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function calendarCachePacket(events = [], meta = {}) {
|
|
290
|
+
return {
|
|
291
|
+
ok: true,
|
|
292
|
+
updatedAt: new Date().toISOString(),
|
|
293
|
+
source: meta.source || 'live',
|
|
294
|
+
days: meta.days || null,
|
|
295
|
+
count: events.length,
|
|
296
|
+
events: events.map(compactCalendarEvent),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function writeCalendarEventsCache(events = [], meta = {}) {
|
|
301
|
+
const rows = Array.isArray(events) ? events : [];
|
|
302
|
+
try {
|
|
303
|
+
fs.mkdirSync(path.dirname(CALENDAR_CACHE_PATH), { recursive: true });
|
|
304
|
+
fs.writeFileSync(CALENDAR_CACHE_PATH, `${JSON.stringify(calendarCachePacket(rows, meta), null, 2)}\n`, 'utf8');
|
|
305
|
+
return CALENDAR_CACHE_PATH;
|
|
306
|
+
} catch {
|
|
307
|
+
return '';
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function readCalendarEventsCache() {
|
|
312
|
+
try {
|
|
313
|
+
const packet = JSON.parse(fs.readFileSync(CALENDAR_CACHE_PATH, 'utf8'));
|
|
314
|
+
const events = Array.isArray(packet.events) ? packet.events : [];
|
|
315
|
+
return {
|
|
316
|
+
ok: true,
|
|
317
|
+
path: CALENDAR_CACHE_PATH,
|
|
318
|
+
updatedAt: packet.updatedAt || '',
|
|
319
|
+
events,
|
|
320
|
+
};
|
|
321
|
+
} catch {
|
|
322
|
+
return { ok: false, path: CALENDAR_CACHE_PATH, updatedAt: '', events: [] };
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function formatCalendarSearchResults(query, matches, {
|
|
327
|
+
json = false,
|
|
328
|
+
total = matches.length,
|
|
329
|
+
source = 'live',
|
|
330
|
+
liveOk = true,
|
|
331
|
+
cacheUpdatedAt = '',
|
|
332
|
+
error = '',
|
|
333
|
+
} = {}) {
|
|
334
|
+
if (json) {
|
|
335
|
+
console.log(JSON.stringify({
|
|
336
|
+
ok: true,
|
|
337
|
+
query,
|
|
338
|
+
source,
|
|
339
|
+
liveOk,
|
|
340
|
+
cacheUpdatedAt,
|
|
341
|
+
error,
|
|
342
|
+
totalEvents: total,
|
|
343
|
+
matchCount: matches.length,
|
|
344
|
+
matches: matches.map(compactCalendarEvent),
|
|
345
|
+
}, null, 2));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (!matches.length) {
|
|
350
|
+
console.log(`No upcoming events matched "${query}".`);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
console.log(`Found ${matches.length} upcoming event(s) matching "${query}":\n`);
|
|
355
|
+
formatCalendarEvents('matching your search', matches);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function formatCalendarSearchError(query, message, { json = false, status = 0 } = {}) {
|
|
359
|
+
if (json) {
|
|
360
|
+
console.log(JSON.stringify({
|
|
361
|
+
ok: false,
|
|
362
|
+
query,
|
|
363
|
+
status,
|
|
364
|
+
error: message || 'Failed to fetch events',
|
|
365
|
+
totalEvents: 0,
|
|
366
|
+
matchCount: 0,
|
|
367
|
+
matches: [],
|
|
368
|
+
}, null, 2));
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
console.error(`Error: ${message || 'Failed to fetch events'}`);
|
|
372
|
+
process.exit(1);
|
|
373
|
+
}
|
|
374
|
+
|
|
212
375
|
async function calendarRange(label, { timeMin, timeMax, days } = {}) {
|
|
213
376
|
const { token, email } = await getAuth();
|
|
214
377
|
|
|
@@ -236,9 +399,63 @@ async function calendarRange(label, { timeMin, timeMax, days } = {}) {
|
|
|
236
399
|
}
|
|
237
400
|
|
|
238
401
|
const events = result.data?.events || result.data || [];
|
|
402
|
+
writeCalendarEventsCache(events, { source: 'calendarRange', days: days || null });
|
|
239
403
|
formatCalendarEvents(label.toLowerCase(), events);
|
|
240
404
|
}
|
|
241
405
|
|
|
406
|
+
async function calendarSearch(args = []) {
|
|
407
|
+
const { query, days, limit, timeoutMs, json } = parseCalendarSearchArgs(args);
|
|
408
|
+
if (!query) {
|
|
409
|
+
console.error('Usage: atris calendar search <query> [--days 30] [--limit 20] [--timeout-ms 10000] [--json]');
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const credentials = loadCredentials();
|
|
414
|
+
const token = credentials && credentials.token;
|
|
415
|
+
const email = credentials && credentials.email || 'unknown';
|
|
416
|
+
if (!token) {
|
|
417
|
+
formatCalendarSearchError(query, 'Not logged in. Run: atris login', { json, status: 401 });
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const result = await apiRequestJson(`/integrations/google-calendar/events?days=${encodeURIComponent(String(days))}`, {
|
|
421
|
+
method: 'GET',
|
|
422
|
+
token,
|
|
423
|
+
timeoutMs,
|
|
424
|
+
retries: 0,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
if (!result.ok) {
|
|
428
|
+
const cache = readCalendarEventsCache();
|
|
429
|
+
if (json && cache.ok) {
|
|
430
|
+
const matches = filterCalendarEventsForSearch(cache.events, query, limit);
|
|
431
|
+
formatCalendarSearchResults(query, matches, {
|
|
432
|
+
json,
|
|
433
|
+
total: cache.events.length,
|
|
434
|
+
source: 'cache',
|
|
435
|
+
liveOk: false,
|
|
436
|
+
cacheUpdatedAt: cache.updatedAt,
|
|
437
|
+
error: result.error || 'Failed to fetch events',
|
|
438
|
+
});
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
if (result.status === 400 || result.status === 401) {
|
|
442
|
+
formatCalendarSearchError(
|
|
443
|
+
query,
|
|
444
|
+
`Calendar not connected for ${email}. Connect at: https://atris.ai/dashboard/settings`,
|
|
445
|
+
{ json, status: result.status },
|
|
446
|
+
);
|
|
447
|
+
} else {
|
|
448
|
+
formatCalendarSearchError(query, result.error || 'Failed to fetch events', { json, status: result.status || 0 });
|
|
449
|
+
}
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const events = result.data?.events || result.data || [];
|
|
454
|
+
writeCalendarEventsCache(events, { source: 'calendarSearch', days });
|
|
455
|
+
const matches = filterCalendarEventsForSearch(events, query, limit);
|
|
456
|
+
formatCalendarSearchResults(query, matches, { json, total: events.length, source: 'live', liveOk: true });
|
|
457
|
+
}
|
|
458
|
+
|
|
242
459
|
async function calendarToday() {
|
|
243
460
|
await calendarRange("Today's events", { days: 1 });
|
|
244
461
|
}
|
|
@@ -272,12 +489,16 @@ async function calendarCommand(subcommand, ...args) {
|
|
|
272
489
|
case 'date':
|
|
273
490
|
await calendarDate(args[0]);
|
|
274
491
|
break;
|
|
492
|
+
case 'search':
|
|
493
|
+
await calendarSearch(args);
|
|
494
|
+
break;
|
|
275
495
|
default:
|
|
276
496
|
console.log('Calendar commands:');
|
|
277
497
|
console.log(' atris calendar today - Show today\'s events');
|
|
278
498
|
console.log(' atris calendar yesterday - Show yesterday\'s events');
|
|
279
499
|
console.log(' atris calendar week - Show this week\'s events');
|
|
280
500
|
console.log(' atris calendar date YYYY-MM-DD - Show events on a date');
|
|
501
|
+
console.log(' atris calendar search <query> [--days 30] [--limit 20] [--timeout-ms 10000] [--json] - Search upcoming events');
|
|
281
502
|
}
|
|
282
503
|
}
|
|
283
504
|
|
|
@@ -1233,4 +1454,11 @@ module.exports = {
|
|
|
1233
1454
|
imessageCommand,
|
|
1234
1455
|
imessageDoctor,
|
|
1235
1456
|
integrationsStatus,
|
|
1457
|
+
parseCalendarSearchArgs,
|
|
1458
|
+
filterCalendarEventsForSearch,
|
|
1459
|
+
formatCalendarSearchResults,
|
|
1460
|
+
formatCalendarSearchError,
|
|
1461
|
+
writeCalendarEventsCache,
|
|
1462
|
+
readCalendarEventsCache,
|
|
1463
|
+
CALENDAR_CACHE_PATH,
|
|
1236
1464
|
};
|
package/commands/lesson.js
CHANGED
|
@@ -2,6 +2,44 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { writeLesson } = require('./autopilot');
|
|
4
4
|
|
|
5
|
+
function mineLessons(args) {
|
|
6
|
+
const { loadHistory, mineProofPolicy, writePolicyLessons, syncLessonsMd } = require('../lib/policy-lessons');
|
|
7
|
+
const root = process.cwd();
|
|
8
|
+
const json = args.includes('--json');
|
|
9
|
+
const dryRun = args.includes('--dry-run');
|
|
10
|
+
const history = loadHistory(root);
|
|
11
|
+
const mined = mineProofPolicy(history);
|
|
12
|
+
let statePath = null;
|
|
13
|
+
let lessonsMd = { path: null, written: [] };
|
|
14
|
+
if (!dryRun) {
|
|
15
|
+
statePath = writePolicyLessons(root, mined);
|
|
16
|
+
lessonsMd = syncLessonsMd(root, mined);
|
|
17
|
+
}
|
|
18
|
+
if (json) {
|
|
19
|
+
console.log(JSON.stringify({
|
|
20
|
+
ok: true,
|
|
21
|
+
action: 'lesson_mine',
|
|
22
|
+
dry_run: dryRun,
|
|
23
|
+
state_path: statePath,
|
|
24
|
+
lessons_md_path: lessonsMd.path,
|
|
25
|
+
lessons_md_written: lessonsMd.written,
|
|
26
|
+
...mined,
|
|
27
|
+
}, null, 2));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const { sources } = mined;
|
|
31
|
+
console.log(`mined ${mined.lessons.length} policy lesson(s) from ${sources.career_xp_receipts} receipts / ${sources.task_episodes} episodes / ${sources.scorecards} scorecards (${sources.human_reviewed_episodes} human-reviewed)`);
|
|
32
|
+
for (const lesson of mined.lessons) {
|
|
33
|
+
console.log(`- policy-${lesson.id}: ${lesson.lesson}`);
|
|
34
|
+
}
|
|
35
|
+
if (dryRun) {
|
|
36
|
+
console.log('(dry-run: nothing written)');
|
|
37
|
+
} else {
|
|
38
|
+
console.log(`state: ${statePath}`);
|
|
39
|
+
console.log(`lessons.md: ${lessonsMd.written.length ? lessonsMd.written.map((id) => `policy-${id}`).join(', ') : 'no entries written'}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
5
43
|
function lessonAtris(subcommand, ...args) {
|
|
6
44
|
const atrisDir = path.join(process.cwd(), 'atris');
|
|
7
45
|
if (!fs.existsSync(atrisDir)) {
|
|
@@ -9,9 +47,15 @@ function lessonAtris(subcommand, ...args) {
|
|
|
9
47
|
process.exit(1);
|
|
10
48
|
}
|
|
11
49
|
|
|
50
|
+
if (subcommand === 'mine') {
|
|
51
|
+
mineLessons(args);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
12
55
|
if (subcommand !== 'add') {
|
|
13
56
|
console.log('');
|
|
14
57
|
console.log(' Usage: atris lesson add <slug> <pass|fail> "<text>"');
|
|
58
|
+
console.log(' atris lesson mine [--json] [--dry-run]');
|
|
15
59
|
console.log('');
|
|
16
60
|
process.exit(subcommand ? 1 : 0);
|
|
17
61
|
}
|