atris 2.3.1 → 2.3.3
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: atris
|
|
3
|
-
description: Codebase intelligence — generates structured navigation maps with file:line references so agents stop re-scanning the same files every session. Use when exploring code, answering
|
|
3
|
+
description: "Codebase intelligence — generates structured navigation maps with file:line references so agents stop re-scanning the same files every session. Use when exploring code, answering 'where is X?', or onboarding to a new codebase."
|
|
4
4
|
version: 1.1.0
|
|
5
5
|
requires:
|
|
6
6
|
bins:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: drive
|
|
3
3
|
description: Google Drive integration via AtrisOS API. Browse, search, read, upload files and work with Google Sheets. Use when user asks about Drive, files, docs, sheets, or spreadsheets.
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
tags:
|
|
6
6
|
- drive
|
|
7
7
|
- backend
|
|
@@ -199,6 +199,29 @@ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/files" \
|
|
|
199
199
|
}'
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
+
### Update Existing File
|
|
203
|
+
```bash
|
|
204
|
+
curl -s -X PUT "https://api.atris.ai/api/integrations/google-drive/files/{file_id}" \
|
|
205
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
206
|
+
-H "Content-Type: application/json" \
|
|
207
|
+
-d '{
|
|
208
|
+
"content": "Updated file content here",
|
|
209
|
+
"mime_type": "text/plain"
|
|
210
|
+
}'
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Update content and rename:**
|
|
214
|
+
```bash
|
|
215
|
+
curl -s -X PUT "https://api.atris.ai/api/integrations/google-drive/files/{file_id}" \
|
|
216
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
217
|
+
-H "Content-Type: application/json" \
|
|
218
|
+
-d '{
|
|
219
|
+
"content": "New content",
|
|
220
|
+
"mime_type": "text/plain",
|
|
221
|
+
"name": "renamed-file.txt"
|
|
222
|
+
}'
|
|
223
|
+
```
|
|
224
|
+
|
|
202
225
|
---
|
|
203
226
|
|
|
204
227
|
## Google Sheets
|
|
@@ -299,6 +322,14 @@ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/sheets/{spre
|
|
|
299
322
|
3. **Confirm with user**: "Upload {filename} to Drive?"
|
|
300
323
|
4. Upload: `POST /google-drive/files` with `{name, content, mime_type}`
|
|
301
324
|
|
|
325
|
+
### "Edit a file on Drive"
|
|
326
|
+
1. Run bootstrap
|
|
327
|
+
2. Find the file: `GET /google-drive/search?q=FILENAME`
|
|
328
|
+
3. Read current content: `GET /google-drive/files/{id}/export?mime_type=text/plain`
|
|
329
|
+
4. Make edits
|
|
330
|
+
5. **Show user the changes for approval**
|
|
331
|
+
6. Update: `PUT /google-drive/files/{id}` with `{content, mime_type}`
|
|
332
|
+
|
|
302
333
|
---
|
|
303
334
|
|
|
304
335
|
## Error Handling
|
|
@@ -356,8 +387,13 @@ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/sheets/{id}/
|
|
|
356
387
|
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
357
388
|
-d '{"range":"Sheet1","values":[["Alice",95]]}'
|
|
358
389
|
|
|
359
|
-
# Upload a file
|
|
390
|
+
# Upload a new file
|
|
360
391
|
curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/files" \
|
|
361
392
|
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
362
393
|
-d '{"name":"notes.txt","content":"Hello world","mime_type":"text/plain"}'
|
|
394
|
+
|
|
395
|
+
# Update an existing file
|
|
396
|
+
curl -s -X PUT "https://api.atris.ai/api/integrations/google-drive/files/{file_id}" \
|
|
397
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
398
|
+
-d '{"content":"Updated content","mime_type":"text/plain"}'
|
|
363
399
|
```
|
|
@@ -250,6 +250,17 @@ curl -s -X DELETE "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}
|
|
|
250
250
|
-H "Authorization: Bearer $TOKEN"
|
|
251
251
|
```
|
|
252
252
|
|
|
253
|
+
### Mark as Read / Unread
|
|
254
|
+
```bash
|
|
255
|
+
# Mark as read
|
|
256
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/read" \
|
|
257
|
+
-H "Authorization: Bearer $TOKEN"
|
|
258
|
+
|
|
259
|
+
# Mark as unread
|
|
260
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/unread" \
|
|
261
|
+
-H "Authorization: Bearer $TOKEN"
|
|
262
|
+
```
|
|
263
|
+
|
|
253
264
|
### Archive Email
|
|
254
265
|
```bash
|
|
255
266
|
# Single message
|
|
@@ -263,6 +274,19 @@ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/batch-arch
|
|
|
263
274
|
-d '{"message_ids": ["id1", "id2", "id3"]}'
|
|
264
275
|
```
|
|
265
276
|
|
|
277
|
+
### Trash Email
|
|
278
|
+
```bash
|
|
279
|
+
# Single message
|
|
280
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/trash" \
|
|
281
|
+
-H "Authorization: Bearer $TOKEN"
|
|
282
|
+
|
|
283
|
+
# Batch trash
|
|
284
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/batch-trash" \
|
|
285
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
286
|
+
-H "Content-Type: application/json" \
|
|
287
|
+
-d '{"message_ids": ["id1", "id2", "id3"]}'
|
|
288
|
+
```
|
|
289
|
+
|
|
266
290
|
### Check Status
|
|
267
291
|
```bash
|
|
268
292
|
curl -s "https://api.atris.ai/api/integrations/gmail/status" \
|
|
@@ -410,6 +434,12 @@ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts" \
|
|
|
410
434
|
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
411
435
|
-d '{"to":"email@example.com","subject":"Hi","body":"Draft text"}'
|
|
412
436
|
|
|
437
|
+
# Mark as read
|
|
438
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/read" -H "Authorization: Bearer $TOKEN"
|
|
439
|
+
|
|
440
|
+
# Trash an email
|
|
441
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/trash" -H "Authorization: Bearer $TOKEN"
|
|
442
|
+
|
|
413
443
|
# Send a draft
|
|
414
444
|
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}/send" \
|
|
415
445
|
-H "Authorization: Bearer $TOKEN"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: slack
|
|
3
3
|
description: Slack integration via AtrisOS API. Read messages, send as yourself, search conversations, manage DMs. Use when user asks about Slack, messages, channels, or team communication.
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
tags:
|
|
6
6
|
- slack
|
|
7
7
|
- backend
|
|
@@ -187,6 +187,14 @@ curl -s "https://api.atris.ai/api/integrations/slack/users" \
|
|
|
187
187
|
-H "Authorization: Bearer $TOKEN"
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
+
### Search Users
|
|
191
|
+
```bash
|
|
192
|
+
curl -s "https://api.atris.ai/api/integrations/slack/users/search?q=justin" \
|
|
193
|
+
-H "Authorization: Bearer $TOKEN"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Searches by name, display name, or email. Much faster than pulling the full user list.
|
|
197
|
+
|
|
190
198
|
### Send as Bot
|
|
191
199
|
```bash
|
|
192
200
|
curl -s -X POST "https://api.atris.ai/api/integrations/slack/test-send" \
|
|
@@ -223,9 +231,9 @@ curl -s -X DELETE "https://api.atris.ai/api/integrations/slack" \
|
|
|
223
231
|
|
|
224
232
|
### "Send a message to someone"
|
|
225
233
|
1. Run bootstrap
|
|
226
|
-
2. Find the user: `GET /slack/users`
|
|
234
|
+
2. Find the user: `GET /slack/users/search?q=NAME`
|
|
227
235
|
3. **Show user the draft for approval**
|
|
228
|
-
4. Send DM: `POST /slack/me/dm` with `{
|
|
236
|
+
4. Send DM: `POST /slack/me/dm` with `{slack_user_id, text}`
|
|
229
237
|
5. Confirm: "Message sent!"
|
|
230
238
|
|
|
231
239
|
### "Reply in a channel"
|
|
@@ -243,7 +251,7 @@ curl -s -X DELETE "https://api.atris.ai/api/integrations/slack" \
|
|
|
243
251
|
|
|
244
252
|
### "What did [person] say to me?"
|
|
245
253
|
1. Run bootstrap
|
|
246
|
-
2.
|
|
254
|
+
2. Find user: `GET /slack/users/search?q=NAME` (get user ID)
|
|
247
255
|
3. List DMs: `GET /slack/me/dms` (find DM channel with that user)
|
|
248
256
|
4. Read messages: `GET /slack/me/messages/{channel_id}`
|
|
249
257
|
5. Display conversation
|
|
@@ -317,4 +325,7 @@ curl -s "https://api.atris.ai/api/integrations/slack/me/search?q=project+update"
|
|
|
317
325
|
|
|
318
326
|
# List workspace users
|
|
319
327
|
curl -s "https://api.atris.ai/api/integrations/slack/users" -H "Authorization: Bearer $TOKEN"
|
|
328
|
+
|
|
329
|
+
# Search users by name
|
|
330
|
+
curl -s "https://api.atris.ai/api/integrations/slack/users/search?q=justin" -H "Authorization: Bearer $TOKEN"
|
|
320
331
|
```
|
package/commands/sync.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
3
4
|
|
|
4
5
|
function syncAtris() {
|
|
5
6
|
const targetDir = path.join(process.cwd(), 'atris');
|
|
@@ -293,83 +294,114 @@ After displaying the boot output, respond to the user naturally.
|
|
|
293
294
|
}
|
|
294
295
|
|
|
295
296
|
/**
|
|
296
|
-
*
|
|
297
|
-
|
|
297
|
+
* Recursively sync files from src to dest. Returns count of files updated.
|
|
298
|
+
*/
|
|
299
|
+
function syncRecursiveCount(src, dest, label, silent) {
|
|
300
|
+
let count = 0;
|
|
301
|
+
if (!fs.existsSync(dest)) {
|
|
302
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
303
|
+
}
|
|
304
|
+
const entries = fs.readdirSync(src);
|
|
305
|
+
for (const entry of entries) {
|
|
306
|
+
const srcPath = path.join(src, entry);
|
|
307
|
+
const destPath = path.join(dest, entry);
|
|
308
|
+
|
|
309
|
+
if (fs.statSync(srcPath).isDirectory()) {
|
|
310
|
+
count += syncRecursiveCount(srcPath, destPath, `${label}/${entry}`, silent);
|
|
311
|
+
} else {
|
|
312
|
+
const srcContent = fs.readFileSync(srcPath, 'utf8');
|
|
313
|
+
const destContent = fs.existsSync(destPath) ? fs.readFileSync(destPath, 'utf8') : '';
|
|
314
|
+
if (srcContent !== destContent) {
|
|
315
|
+
fs.writeFileSync(destPath, srcContent);
|
|
316
|
+
if (entry.endsWith('.sh')) {
|
|
317
|
+
fs.chmodSync(destPath, 0o755);
|
|
318
|
+
}
|
|
319
|
+
if (!silent) {
|
|
320
|
+
console.log(`✓ Updated ${label}/${entry}`);
|
|
321
|
+
}
|
|
322
|
+
count++;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return count;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Lightweight skill-only sync. Syncs skills from the npm package to:
|
|
331
|
+
* 1. Global skill dirs (~/.claude/skills/, ~/.codex/skills/) — always, if they exist
|
|
332
|
+
* 2. Project-level (atris/skills/ + .claude/skills/ symlinks) — if in a project
|
|
333
|
+
*
|
|
334
|
+
* Global = baseline truth. Project = optional override.
|
|
298
335
|
* Returns number of files updated (0 = already current).
|
|
299
336
|
*/
|
|
300
337
|
function syncSkills({ silent = false } = {}) {
|
|
301
|
-
const targetDir = path.join(process.cwd(), 'atris');
|
|
302
338
|
const packageSkillsDir = path.join(__dirname, '..', 'atris', 'skills');
|
|
303
|
-
|
|
304
|
-
const claudeSkillsBaseDir = path.join(process.cwd(), '.claude', 'skills');
|
|
305
|
-
|
|
306
|
-
if (!fs.existsSync(targetDir) || !fs.existsSync(packageSkillsDir)) {
|
|
339
|
+
if (!fs.existsSync(packageSkillsDir)) {
|
|
307
340
|
return 0;
|
|
308
341
|
}
|
|
309
342
|
|
|
310
|
-
if (!fs.existsSync(userSkillsDir)) {
|
|
311
|
-
fs.mkdirSync(userSkillsDir, { recursive: true });
|
|
312
|
-
}
|
|
313
|
-
if (!fs.existsSync(claudeSkillsBaseDir)) {
|
|
314
|
-
fs.mkdirSync(claudeSkillsBaseDir, { recursive: true });
|
|
315
|
-
}
|
|
316
|
-
|
|
317
343
|
let updated = 0;
|
|
344
|
+
const homeDir = os.homedir();
|
|
318
345
|
|
|
319
346
|
const skillFolders = fs.readdirSync(packageSkillsDir).filter(f =>
|
|
320
347
|
fs.statSync(path.join(packageSkillsDir, f)).isDirectory()
|
|
321
348
|
);
|
|
322
349
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
350
|
+
// --- 1. Global skill directories (sync if they exist) ---
|
|
351
|
+
const globalSkillDirs = [
|
|
352
|
+
path.join(homeDir, '.claude', 'skills'),
|
|
353
|
+
path.join(homeDir, '.codex', 'skills'),
|
|
354
|
+
];
|
|
327
355
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (fs.statSync(srcPath).isDirectory()) {
|
|
339
|
-
syncRecursive(srcPath, destPath, skillName, relPath);
|
|
340
|
-
} else {
|
|
341
|
-
const srcContent = fs.readFileSync(srcPath, 'utf8');
|
|
342
|
-
const destContent = fs.existsSync(destPath) ? fs.readFileSync(destPath, 'utf8') : '';
|
|
343
|
-
if (srcContent !== destContent) {
|
|
344
|
-
fs.writeFileSync(destPath, srcContent);
|
|
345
|
-
if (entry.endsWith('.sh')) {
|
|
346
|
-
fs.chmodSync(destPath, 0o755);
|
|
347
|
-
}
|
|
348
|
-
if (!silent) {
|
|
349
|
-
console.log(`✓ Updated atris/skills/${skillName}/${relPath}`);
|
|
350
|
-
}
|
|
351
|
-
updated++;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
356
|
+
for (const globalDir of globalSkillDirs) {
|
|
357
|
+
if (!fs.existsSync(globalDir)) continue;
|
|
358
|
+
const dirName = path.basename(path.dirname(globalDir)); // .claude or .codex
|
|
359
|
+
|
|
360
|
+
for (const skill of skillFolders) {
|
|
361
|
+
const srcSkillDir = path.join(packageSkillsDir, skill);
|
|
362
|
+
const destSkillDir = path.join(globalDir, skill);
|
|
363
|
+
|
|
364
|
+
if (fs.existsSync(destSkillDir) || fs.existsSync(globalDir)) {
|
|
365
|
+
updated += syncRecursiveCount(srcSkillDir, destSkillDir, `~/${dirName}/skills/${skill}`, silent);
|
|
354
366
|
}
|
|
355
|
-
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
356
369
|
|
|
357
|
-
|
|
370
|
+
// --- 2. Project-level (only if inside an atris project) ---
|
|
371
|
+
const targetDir = path.join(process.cwd(), 'atris');
|
|
372
|
+
if (fs.existsSync(targetDir)) {
|
|
373
|
+
const userSkillsDir = path.join(targetDir, 'skills');
|
|
374
|
+
const claudeSkillsBaseDir = path.join(process.cwd(), '.claude', 'skills');
|
|
358
375
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
376
|
+
if (!fs.existsSync(userSkillsDir)) {
|
|
377
|
+
fs.mkdirSync(userSkillsDir, { recursive: true });
|
|
378
|
+
}
|
|
379
|
+
if (!fs.existsSync(claudeSkillsBaseDir)) {
|
|
380
|
+
fs.mkdirSync(claudeSkillsBaseDir, { recursive: true });
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
for (const skill of skillFolders) {
|
|
384
|
+
const srcSkillDir = path.join(packageSkillsDir, skill);
|
|
385
|
+
const destSkillDir = path.join(userSkillsDir, skill);
|
|
386
|
+
const symlinkPath = path.join(claudeSkillsBaseDir, skill);
|
|
387
|
+
|
|
388
|
+
updated += syncRecursiveCount(srcSkillDir, destSkillDir, `atris/skills/${skill}`, silent);
|
|
389
|
+
|
|
390
|
+
// Create symlink if doesn't exist
|
|
391
|
+
if (!fs.existsSync(symlinkPath)) {
|
|
392
|
+
const relativePath = path.join('..', '..', 'atris', 'skills', skill);
|
|
393
|
+
try {
|
|
394
|
+
fs.symlinkSync(relativePath, symlinkPath);
|
|
395
|
+
if (!silent) {
|
|
396
|
+
console.log(`✓ Linked .claude/skills/${skill}`);
|
|
397
|
+
}
|
|
398
|
+
} catch (e) {
|
|
399
|
+
// Fallback: copy instead of symlink
|
|
400
|
+
fs.mkdirSync(symlinkPath, { recursive: true });
|
|
401
|
+
const skillFile = path.join(destSkillDir, 'SKILL.md');
|
|
402
|
+
if (fs.existsSync(skillFile)) {
|
|
403
|
+
fs.copyFileSync(skillFile, path.join(symlinkPath, 'SKILL.md'));
|
|
404
|
+
}
|
|
373
405
|
}
|
|
374
406
|
}
|
|
375
407
|
}
|
package/package.json
CHANGED