atris 2.2.1 → 2.3.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/atris/skills/drive/SKILL.md +32 -2
- package/atris/skills/notion/SKILL.md +478 -0
- package/atris/skills/slack/SKILL.md +320 -0
- package/atris/skills/slides/SKILL.md +355 -0
- package/bin/atris.js +5 -3
- package/package.json +1 -1
- package/utils/update-check.js +33 -0
|
@@ -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.1.0
|
|
5
5
|
tags:
|
|
6
6
|
- drive
|
|
7
7
|
- backend
|
|
@@ -108,6 +108,14 @@ All requests require: `-H "Authorization: Bearer $TOKEN"`
|
|
|
108
108
|
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
+
### List Shared Drives
|
|
112
|
+
```bash
|
|
113
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/shared-drives" \
|
|
114
|
+
-H "Authorization: Bearer $TOKEN"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Returns all shared/team drives the user has access to with `id` and `name`.
|
|
118
|
+
|
|
111
119
|
### List Files
|
|
112
120
|
```bash
|
|
113
121
|
curl -s "https://api.atris.ai/api/integrations/google-drive/files?page_size=20" \
|
|
@@ -120,6 +128,14 @@ curl -s "https://api.atris.ai/api/integrations/google-drive/files?folder_id=FOLD
|
|
|
120
128
|
-H "Authorization: Bearer $TOKEN"
|
|
121
129
|
```
|
|
122
130
|
|
|
131
|
+
**List files in a shared drive:**
|
|
132
|
+
```bash
|
|
133
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/files?shared_drive_id=DRIVE_ID&page_size=20" \
|
|
134
|
+
-H "Authorization: Bearer $TOKEN"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**NOTE:** All file operations (list, search, get, download, export) automatically include shared drive files. Use `shared_drive_id` only to scope results to a specific shared drive.
|
|
138
|
+
|
|
123
139
|
### Search Files
|
|
124
140
|
```bash
|
|
125
141
|
curl -s "https://api.atris.ai/api/integrations/google-drive/search?q=quarterly+report&page_size=20" \
|
|
@@ -266,6 +282,17 @@ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/sheets/{spre
|
|
|
266
282
|
4. **Show user what will be appended, get approval**
|
|
267
283
|
5. Append: `POST /google-drive/sheets/{id}/append`
|
|
268
284
|
|
|
285
|
+
### "Browse a shared drive"
|
|
286
|
+
1. Run bootstrap
|
|
287
|
+
2. List shared drives: `GET /google-drive/shared-drives`
|
|
288
|
+
3. Display drive names and IDs
|
|
289
|
+
4. List files in chosen drive: `GET /google-drive/files?shared_drive_id=DRIVE_ID`
|
|
290
|
+
|
|
291
|
+
### "Find a file across all drives"
|
|
292
|
+
1. Run bootstrap
|
|
293
|
+
2. Search: `GET /google-drive/search?q=QUERY` (automatically searches My Drive + all shared drives)
|
|
294
|
+
3. Display results
|
|
295
|
+
|
|
269
296
|
### "Upload a file to Drive"
|
|
270
297
|
1. Run bootstrap
|
|
271
298
|
2. Read the local file content
|
|
@@ -309,7 +336,10 @@ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
|
309
336
|
# Check connection
|
|
310
337
|
curl -s "https://api.atris.ai/api/integrations/google-drive/status" -H "Authorization: Bearer $TOKEN"
|
|
311
338
|
|
|
312
|
-
# List
|
|
339
|
+
# List shared drives
|
|
340
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/shared-drives" -H "Authorization: Bearer $TOKEN"
|
|
341
|
+
|
|
342
|
+
# List files (includes shared drive files)
|
|
313
343
|
curl -s "https://api.atris.ai/api/integrations/google-drive/files" -H "Authorization: Bearer $TOKEN"
|
|
314
344
|
|
|
315
345
|
# Search files
|
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: notion
|
|
3
|
+
description: Notion integration via AtrisOS API. Search pages, read/create/update pages, query databases, manage blocks and comments. Use when user asks about Notion, pages, databases, wikis, or docs.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags:
|
|
6
|
+
- notion
|
|
7
|
+
- backend
|
|
8
|
+
- productivity
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Notion Agent
|
|
12
|
+
|
|
13
|
+
> Drop this in `~/.claude/skills/notion/SKILL.md` and Claude Code becomes your Notion assistant.
|
|
14
|
+
|
|
15
|
+
## Bootstrap (ALWAYS Run First)
|
|
16
|
+
|
|
17
|
+
Before any Notion operation, run this bootstrap to ensure everything is set up:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
#!/bin/bash
|
|
21
|
+
set -e
|
|
22
|
+
|
|
23
|
+
# 1. Check if atris CLI is installed
|
|
24
|
+
if ! command -v atris &> /dev/null; then
|
|
25
|
+
echo "Installing atris CLI..."
|
|
26
|
+
npm install -g atris
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# 2. Check if logged in to AtrisOS
|
|
30
|
+
if [ ! -f ~/.atris/credentials.json ]; then
|
|
31
|
+
echo "Not logged in to AtrisOS."
|
|
32
|
+
echo ""
|
|
33
|
+
echo "Option 1 (interactive): Run 'atris login' and follow prompts"
|
|
34
|
+
echo "Option 2 (non-interactive): Get token from https://atris.ai/auth/cli"
|
|
35
|
+
echo " Then run: atris login --token YOUR_TOKEN"
|
|
36
|
+
echo ""
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# 3. Extract token
|
|
41
|
+
if command -v node &> /dev/null; then
|
|
42
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
43
|
+
elif command -v python3 &> /dev/null; then
|
|
44
|
+
TOKEN=$(python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.atris/credentials.json')))['token'])")
|
|
45
|
+
elif command -v jq &> /dev/null; then
|
|
46
|
+
TOKEN=$(jq -r '.token' ~/.atris/credentials.json)
|
|
47
|
+
else
|
|
48
|
+
echo "Error: Need node, python3, or jq to read credentials"
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# 4. Check Notion connection status
|
|
53
|
+
STATUS=$(curl -s "https://api.atris.ai/api/integrations/notion/status" \
|
|
54
|
+
-H "Authorization: Bearer $TOKEN")
|
|
55
|
+
|
|
56
|
+
if echo "$STATUS" | grep -q "Token expired\|Not authenticated"; then
|
|
57
|
+
echo "Token expired. Please re-authenticate:"
|
|
58
|
+
echo " Run: atris login --force"
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if command -v node &> /dev/null; then
|
|
63
|
+
CONNECTED=$(node -e "try{console.log(JSON.parse('$STATUS').connected||false)}catch(e){console.log(false)}")
|
|
64
|
+
elif command -v python3 &> /dev/null; then
|
|
65
|
+
CONNECTED=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('connected', False))")
|
|
66
|
+
else
|
|
67
|
+
CONNECTED=$(echo "$STATUS" | jq -r '.connected // false')
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
if [ "$CONNECTED" != "true" ] && [ "$CONNECTED" != "True" ]; then
|
|
71
|
+
echo "Notion not connected. Getting authorization URL..."
|
|
72
|
+
AUTH=$(curl -s -X POST "https://api.atris.ai/api/integrations/notion/start" \
|
|
73
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
74
|
+
-H "Content-Type: application/json" \
|
|
75
|
+
-d '{"return_url":"https://atris.ai/dashboard/settings"}')
|
|
76
|
+
|
|
77
|
+
if command -v node &> /dev/null; then
|
|
78
|
+
URL=$(node -e "try{console.log(JSON.parse('$AUTH').auth_url||'')}catch(e){console.log('')}")
|
|
79
|
+
elif command -v python3 &> /dev/null; then
|
|
80
|
+
URL=$(echo "$AUTH" | python3 -c "import sys,json; print(json.load(sys.stdin).get('auth_url', ''))")
|
|
81
|
+
else
|
|
82
|
+
URL=$(echo "$AUTH" | jq -r '.auth_url // empty')
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
echo ""
|
|
86
|
+
echo "Open this URL to connect your Notion:"
|
|
87
|
+
echo "$URL"
|
|
88
|
+
echo ""
|
|
89
|
+
echo "After authorizing, run your command again."
|
|
90
|
+
exit 0
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
echo "Ready. Notion is connected."
|
|
94
|
+
export ATRIS_TOKEN="$TOKEN"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## API Reference
|
|
100
|
+
|
|
101
|
+
Base: `https://api.atris.ai/api/integrations`
|
|
102
|
+
|
|
103
|
+
All requests require: `-H "Authorization: Bearer $TOKEN"`
|
|
104
|
+
|
|
105
|
+
### Get Token (after bootstrap)
|
|
106
|
+
```bash
|
|
107
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Search
|
|
113
|
+
|
|
114
|
+
### Search Pages & Databases
|
|
115
|
+
```bash
|
|
116
|
+
curl -s "https://api.atris.ai/api/integrations/notion/search?q=meeting+notes&page_size=20" \
|
|
117
|
+
-H "Authorization: Bearer $TOKEN"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**Filter by type:**
|
|
121
|
+
```bash
|
|
122
|
+
# Only pages
|
|
123
|
+
curl -s "https://api.atris.ai/api/integrations/notion/search?q=roadmap&filter_type=page" \
|
|
124
|
+
-H "Authorization: Bearer $TOKEN"
|
|
125
|
+
|
|
126
|
+
# Only databases
|
|
127
|
+
curl -s "https://api.atris.ai/api/integrations/notion/search?q=tasks&filter_type=database" \
|
|
128
|
+
-H "Authorization: Bearer $TOKEN"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Pages
|
|
134
|
+
|
|
135
|
+
### Get a Page
|
|
136
|
+
```bash
|
|
137
|
+
curl -s "https://api.atris.ai/api/integrations/notion/pages/{page_id}" \
|
|
138
|
+
-H "Authorization: Bearer $TOKEN"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Create a Page (under a page)
|
|
142
|
+
```bash
|
|
143
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/notion/pages" \
|
|
144
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
145
|
+
-H "Content-Type: application/json" \
|
|
146
|
+
-d '{
|
|
147
|
+
"parent_id": "PARENT_PAGE_ID",
|
|
148
|
+
"parent_type": "page_id",
|
|
149
|
+
"title": "Meeting Notes - Feb 14"
|
|
150
|
+
}'
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Create a Page (in a database)
|
|
154
|
+
```bash
|
|
155
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/notion/pages" \
|
|
156
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
157
|
+
-H "Content-Type: application/json" \
|
|
158
|
+
-d '{
|
|
159
|
+
"parent_id": "DATABASE_ID",
|
|
160
|
+
"parent_type": "database_id",
|
|
161
|
+
"title": "New Task",
|
|
162
|
+
"properties": {
|
|
163
|
+
"Status": {"select": {"name": "In Progress"}},
|
|
164
|
+
"Priority": {"select": {"name": "High"}}
|
|
165
|
+
}
|
|
166
|
+
}'
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Create a Page with Content
|
|
170
|
+
```bash
|
|
171
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/notion/pages" \
|
|
172
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
173
|
+
-H "Content-Type: application/json" \
|
|
174
|
+
-d '{
|
|
175
|
+
"parent_id": "PARENT_PAGE_ID",
|
|
176
|
+
"parent_type": "page_id",
|
|
177
|
+
"title": "Project Brief",
|
|
178
|
+
"children": [
|
|
179
|
+
{
|
|
180
|
+
"object": "block",
|
|
181
|
+
"type": "heading_2",
|
|
182
|
+
"heading_2": {"rich_text": [{"type": "text", "text": {"content": "Overview"}}]}
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
"object": "block",
|
|
186
|
+
"type": "paragraph",
|
|
187
|
+
"paragraph": {"rich_text": [{"type": "text", "text": {"content": "This project aims to..."}}]}
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"object": "block",
|
|
191
|
+
"type": "bulleted_list_item",
|
|
192
|
+
"bulleted_list_item": {"rich_text": [{"type": "text", "text": {"content": "Goal 1: Ship MVP"}}]}
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}'
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Update a Page
|
|
199
|
+
```bash
|
|
200
|
+
curl -s -X PATCH "https://api.atris.ai/api/integrations/notion/pages/{page_id}" \
|
|
201
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
202
|
+
-H "Content-Type: application/json" \
|
|
203
|
+
-d '{
|
|
204
|
+
"properties": {
|
|
205
|
+
"Status": {"select": {"name": "Done"}}
|
|
206
|
+
}
|
|
207
|
+
}'
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Archive a Page
|
|
211
|
+
```bash
|
|
212
|
+
curl -s -X PATCH "https://api.atris.ai/api/integrations/notion/pages/{page_id}" \
|
|
213
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
214
|
+
-H "Content-Type: application/json" \
|
|
215
|
+
-d '{"archived": true}'
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## Databases
|
|
221
|
+
|
|
222
|
+
### Get Database Schema
|
|
223
|
+
```bash
|
|
224
|
+
curl -s "https://api.atris.ai/api/integrations/notion/databases/{database_id}" \
|
|
225
|
+
-H "Authorization: Bearer $TOKEN"
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Query a Database
|
|
229
|
+
```bash
|
|
230
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/notion/databases/{database_id}/query" \
|
|
231
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
232
|
+
-H "Content-Type: application/json" \
|
|
233
|
+
-d '{"page_size": 20}'
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**With filters:**
|
|
237
|
+
```bash
|
|
238
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/notion/databases/{database_id}/query" \
|
|
239
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
240
|
+
-H "Content-Type: application/json" \
|
|
241
|
+
-d '{
|
|
242
|
+
"filter": {
|
|
243
|
+
"property": "Status",
|
|
244
|
+
"select": {"equals": "In Progress"}
|
|
245
|
+
},
|
|
246
|
+
"sorts": [
|
|
247
|
+
{"property": "Created", "direction": "descending"}
|
|
248
|
+
],
|
|
249
|
+
"page_size": 50
|
|
250
|
+
}'
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Create a Database
|
|
254
|
+
```bash
|
|
255
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/notion/databases" \
|
|
256
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
257
|
+
-H "Content-Type: application/json" \
|
|
258
|
+
-d '{
|
|
259
|
+
"parent_page_id": "PAGE_ID",
|
|
260
|
+
"title": "Task Tracker",
|
|
261
|
+
"properties": {
|
|
262
|
+
"Name": {"title": {}},
|
|
263
|
+
"Status": {"select": {"options": [{"name": "To Do"}, {"name": "In Progress"}, {"name": "Done"}]}},
|
|
264
|
+
"Priority": {"select": {"options": [{"name": "High"}, {"name": "Medium"}, {"name": "Low"}]}},
|
|
265
|
+
"Due Date": {"date": {}}
|
|
266
|
+
}
|
|
267
|
+
}'
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Blocks (Page Content)
|
|
273
|
+
|
|
274
|
+
### Read Page Content
|
|
275
|
+
```bash
|
|
276
|
+
curl -s "https://api.atris.ai/api/integrations/notion/blocks/{page_id}/children?page_size=50" \
|
|
277
|
+
-H "Authorization: Bearer $TOKEN"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Append Content to a Page
|
|
281
|
+
```bash
|
|
282
|
+
curl -s -X PATCH "https://api.atris.ai/api/integrations/notion/blocks/{page_id}/children" \
|
|
283
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
284
|
+
-H "Content-Type: application/json" \
|
|
285
|
+
-d '{
|
|
286
|
+
"children": [
|
|
287
|
+
{
|
|
288
|
+
"object": "block",
|
|
289
|
+
"type": "paragraph",
|
|
290
|
+
"paragraph": {"rich_text": [{"type": "text", "text": {"content": "Added via Atris!"}}]}
|
|
291
|
+
}
|
|
292
|
+
]
|
|
293
|
+
}'
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Delete a Block
|
|
297
|
+
```bash
|
|
298
|
+
curl -s -X DELETE "https://api.atris.ai/api/integrations/notion/blocks/{block_id}" \
|
|
299
|
+
-H "Authorization: Bearer $TOKEN"
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
## Users
|
|
305
|
+
|
|
306
|
+
### List Workspace Users
|
|
307
|
+
```bash
|
|
308
|
+
curl -s "https://api.atris.ai/api/integrations/notion/users" \
|
|
309
|
+
-H "Authorization: Bearer $TOKEN"
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Get Integration Info
|
|
313
|
+
```bash
|
|
314
|
+
curl -s "https://api.atris.ai/api/integrations/notion/me" \
|
|
315
|
+
-H "Authorization: Bearer $TOKEN"
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Comments
|
|
321
|
+
|
|
322
|
+
### Get Comments on a Page
|
|
323
|
+
```bash
|
|
324
|
+
curl -s "https://api.atris.ai/api/integrations/notion/comments/{page_id}" \
|
|
325
|
+
-H "Authorization: Bearer $TOKEN"
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Add a Comment
|
|
329
|
+
```bash
|
|
330
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/notion/comments" \
|
|
331
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
332
|
+
-H "Content-Type: application/json" \
|
|
333
|
+
-d '{
|
|
334
|
+
"parent_id": "PAGE_ID",
|
|
335
|
+
"text": "This looks great! Approved."
|
|
336
|
+
}'
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Reply to a Discussion
|
|
340
|
+
```bash
|
|
341
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/notion/comments" \
|
|
342
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
343
|
+
-H "Content-Type: application/json" \
|
|
344
|
+
-d '{
|
|
345
|
+
"parent_id": "PAGE_ID",
|
|
346
|
+
"text": "Thanks for the feedback!",
|
|
347
|
+
"discussion_id": "DISCUSSION_ID"
|
|
348
|
+
}'
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Workflows
|
|
354
|
+
|
|
355
|
+
### "Search my Notion for X"
|
|
356
|
+
1. Run bootstrap
|
|
357
|
+
2. Search: `GET /notion/search?q=X`
|
|
358
|
+
3. Display: title, type (page/database), last edited
|
|
359
|
+
|
|
360
|
+
### "What's in my task database?"
|
|
361
|
+
1. Run bootstrap
|
|
362
|
+
2. Search databases: `GET /notion/search?q=tasks&filter_type=database`
|
|
363
|
+
3. Query: `POST /notion/databases/{id}/query` with filters
|
|
364
|
+
4. Display rows with properties
|
|
365
|
+
|
|
366
|
+
### "Create a page with notes"
|
|
367
|
+
1. Run bootstrap
|
|
368
|
+
2. Search for parent page: `GET /notion/search?q=parent+name&filter_type=page`
|
|
369
|
+
3. Create page: `POST /notion/pages` with title + children blocks
|
|
370
|
+
4. Confirm: "Page created!" with link
|
|
371
|
+
|
|
372
|
+
### "Add a row to a database"
|
|
373
|
+
1. Run bootstrap
|
|
374
|
+
2. Get database schema: `GET /notion/databases/{id}` (to see property names/types)
|
|
375
|
+
3. Create page in database: `POST /notion/pages` with `parent_type=database_id` and matching properties
|
|
376
|
+
4. Confirm with row details
|
|
377
|
+
|
|
378
|
+
### "Read a page"
|
|
379
|
+
1. Run bootstrap
|
|
380
|
+
2. Get page metadata: `GET /notion/pages/{id}`
|
|
381
|
+
3. Get page content: `GET /notion/blocks/{id}/children`
|
|
382
|
+
4. Display title + content blocks
|
|
383
|
+
|
|
384
|
+
### "Update task status"
|
|
385
|
+
1. Run bootstrap
|
|
386
|
+
2. Find the task: search or query database
|
|
387
|
+
3. Update: `PATCH /notion/pages/{id}` with new property values
|
|
388
|
+
4. Confirm the change
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
## Block Types Reference
|
|
393
|
+
|
|
394
|
+
Common block types for creating content:
|
|
395
|
+
|
|
396
|
+
| Type | Key | Rich text field |
|
|
397
|
+
|------|-----|----------------|
|
|
398
|
+
| Paragraph | `paragraph` | `rich_text` |
|
|
399
|
+
| Heading 1 | `heading_1` | `rich_text` |
|
|
400
|
+
| Heading 2 | `heading_2` | `rich_text` |
|
|
401
|
+
| Heading 3 | `heading_3` | `rich_text` |
|
|
402
|
+
| Bullet list | `bulleted_list_item` | `rich_text` |
|
|
403
|
+
| Numbered list | `numbered_list_item` | `rich_text` |
|
|
404
|
+
| To-do | `to_do` | `rich_text` + `checked` |
|
|
405
|
+
| Code | `code` | `rich_text` + `language` |
|
|
406
|
+
| Quote | `quote` | `rich_text` |
|
|
407
|
+
| Divider | `divider` | (none) |
|
|
408
|
+
| Callout | `callout` | `rich_text` + `icon` |
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Important Notes
|
|
413
|
+
|
|
414
|
+
- **Notion tokens don't expire** — once connected, it stays connected
|
|
415
|
+
- **Page IDs**: Notion uses UUIDs like `12345678-abcd-1234-abcd-123456789abc`. You can get them from URLs or search results
|
|
416
|
+
- **Database pages**: When adding rows to a database, the "page" properties must match the database schema
|
|
417
|
+
- **Content = Blocks**: A page's visible content is a list of blocks. Use the blocks endpoint to read/write content
|
|
418
|
+
- **Search scope**: Only pages/databases shared with the Atris integration are visible
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## Error Handling
|
|
423
|
+
|
|
424
|
+
| Error | Meaning | Solution |
|
|
425
|
+
|-------|---------|----------|
|
|
426
|
+
| `Token expired` | AtrisOS session expired | Run `atris login` |
|
|
427
|
+
| `Notion not connected` | OAuth not completed | Re-run bootstrap |
|
|
428
|
+
| `401 Unauthorized` | Invalid/expired token | Run `atris login` |
|
|
429
|
+
| `object_not_found` | Page/database not shared with integration | Share the page with the Atris integration in Notion |
|
|
430
|
+
| `validation_error` | Bad request format | Check property names match database schema |
|
|
431
|
+
| `restricted_resource` | No access to resource | Share with Atris integration in Notion settings |
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
## Security Model
|
|
436
|
+
|
|
437
|
+
1. **Local token** (`~/.atris/credentials.json`): Your AtrisOS auth token, stored locally.
|
|
438
|
+
2. **Notion credentials**: Access token stored **server-side** in AtrisOS encrypted vault.
|
|
439
|
+
3. **Access control**: AtrisOS API enforces that you can only access your own Notion.
|
|
440
|
+
4. **Scoped access**: Only pages/databases explicitly shared with the Atris integration are accessible.
|
|
441
|
+
5. **HTTPS only**: All API communication encrypted in transit.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Quick Reference
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
# Setup (one time)
|
|
449
|
+
npm install -g atris && atris login
|
|
450
|
+
|
|
451
|
+
# Get token
|
|
452
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
453
|
+
|
|
454
|
+
# Check connection
|
|
455
|
+
curl -s "https://api.atris.ai/api/integrations/notion/status" -H "Authorization: Bearer $TOKEN"
|
|
456
|
+
|
|
457
|
+
# Search
|
|
458
|
+
curl -s "https://api.atris.ai/api/integrations/notion/search?q=meeting" -H "Authorization: Bearer $TOKEN"
|
|
459
|
+
|
|
460
|
+
# Read a page
|
|
461
|
+
curl -s "https://api.atris.ai/api/integrations/notion/pages/PAGE_ID" -H "Authorization: Bearer $TOKEN"
|
|
462
|
+
|
|
463
|
+
# Read page content
|
|
464
|
+
curl -s "https://api.atris.ai/api/integrations/notion/blocks/PAGE_ID/children" -H "Authorization: Bearer $TOKEN"
|
|
465
|
+
|
|
466
|
+
# Create a page
|
|
467
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/notion/pages" \
|
|
468
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
469
|
+
-d '{"parent_id":"PAGE_ID","parent_type":"page_id","title":"New Page"}'
|
|
470
|
+
|
|
471
|
+
# Query a database
|
|
472
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/notion/databases/DB_ID/query" \
|
|
473
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
474
|
+
-d '{"page_size":20}'
|
|
475
|
+
|
|
476
|
+
# List users
|
|
477
|
+
curl -s "https://api.atris.ai/api/integrations/notion/users" -H "Authorization: Bearer $TOKEN"
|
|
478
|
+
```
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: slack
|
|
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.0.0
|
|
5
|
+
tags:
|
|
6
|
+
- slack
|
|
7
|
+
- backend
|
|
8
|
+
- messaging
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Slack Agent
|
|
12
|
+
|
|
13
|
+
> Drop this in `~/.claude/skills/slack/SKILL.md` and Claude Code becomes your Slack assistant.
|
|
14
|
+
|
|
15
|
+
## Bootstrap (ALWAYS Run First)
|
|
16
|
+
|
|
17
|
+
Before any Slack operation, run this bootstrap to ensure everything is set up:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
#!/bin/bash
|
|
21
|
+
set -e
|
|
22
|
+
|
|
23
|
+
# 1. Check if atris CLI is installed
|
|
24
|
+
if ! command -v atris &> /dev/null; then
|
|
25
|
+
echo "Installing atris CLI..."
|
|
26
|
+
npm install -g atris
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# 2. Check if logged in to AtrisOS
|
|
30
|
+
if [ ! -f ~/.atris/credentials.json ]; then
|
|
31
|
+
echo "Not logged in to AtrisOS."
|
|
32
|
+
echo ""
|
|
33
|
+
echo "Option 1 (interactive): Run 'atris login' and follow prompts"
|
|
34
|
+
echo "Option 2 (non-interactive): Get token from https://atris.ai/auth/cli"
|
|
35
|
+
echo " Then run: atris login --token YOUR_TOKEN"
|
|
36
|
+
echo ""
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# 3. Extract token
|
|
41
|
+
if command -v node &> /dev/null; then
|
|
42
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
43
|
+
elif command -v python3 &> /dev/null; then
|
|
44
|
+
TOKEN=$(python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.atris/credentials.json')))['token'])")
|
|
45
|
+
elif command -v jq &> /dev/null; then
|
|
46
|
+
TOKEN=$(jq -r '.token' ~/.atris/credentials.json)
|
|
47
|
+
else
|
|
48
|
+
echo "Error: Need node, python3, or jq to read credentials"
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# 4. Check Slack connection status
|
|
53
|
+
STATUS=$(curl -s "https://api.atris.ai/api/integrations/slack/status" \
|
|
54
|
+
-H "Authorization: Bearer $TOKEN")
|
|
55
|
+
|
|
56
|
+
if echo "$STATUS" | grep -q "Token expired\|Not authenticated"; then
|
|
57
|
+
echo "Token expired. Please re-authenticate:"
|
|
58
|
+
echo " Run: atris login --force"
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
if command -v node &> /dev/null; then
|
|
63
|
+
CONNECTED=$(node -e "try{console.log(JSON.parse('$STATUS').connected||false)}catch(e){console.log(false)}")
|
|
64
|
+
elif command -v python3 &> /dev/null; then
|
|
65
|
+
CONNECTED=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('connected', False))")
|
|
66
|
+
else
|
|
67
|
+
CONNECTED=$(echo "$STATUS" | jq -r '.connected // false')
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
if [ "$CONNECTED" != "true" ] && [ "$CONNECTED" != "True" ]; then
|
|
71
|
+
echo "Slack not connected. Getting authorization URL..."
|
|
72
|
+
AUTH=$(curl -s -X POST "https://api.atris.ai/api/integrations/slack/start" \
|
|
73
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
74
|
+
-H "Content-Type: application/json" \
|
|
75
|
+
-d '{"mode":"personal"}')
|
|
76
|
+
|
|
77
|
+
if command -v node &> /dev/null; then
|
|
78
|
+
URL=$(node -e "try{console.log(JSON.parse('$AUTH').auth_url||'')}catch(e){console.log('')}")
|
|
79
|
+
elif command -v python3 &> /dev/null; then
|
|
80
|
+
URL=$(echo "$AUTH" | python3 -c "import sys,json; print(json.load(sys.stdin).get('auth_url', ''))")
|
|
81
|
+
else
|
|
82
|
+
URL=$(echo "$AUTH" | jq -r '.auth_url // empty')
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
echo ""
|
|
86
|
+
echo "Open this URL to connect your Slack:"
|
|
87
|
+
echo "$URL"
|
|
88
|
+
echo ""
|
|
89
|
+
echo "After authorizing, run your command again."
|
|
90
|
+
exit 0
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
echo "Ready. Slack is connected."
|
|
94
|
+
export ATRIS_TOKEN="$TOKEN"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## API Reference
|
|
100
|
+
|
|
101
|
+
Base: `https://api.atris.ai/api/integrations`
|
|
102
|
+
|
|
103
|
+
All requests require: `-H "Authorization: Bearer $TOKEN"`
|
|
104
|
+
|
|
105
|
+
### Get Token (after bootstrap)
|
|
106
|
+
```bash
|
|
107
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Personal Endpoints (send/read as yourself)
|
|
113
|
+
|
|
114
|
+
These use your personal Slack token. Messages appear as YOU, not a bot.
|
|
115
|
+
|
|
116
|
+
### List Your Channels & DMs
|
|
117
|
+
```bash
|
|
118
|
+
curl -s "https://api.atris.ai/api/integrations/slack/me/channels" \
|
|
119
|
+
-H "Authorization: Bearer $TOKEN"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### List Your DMs
|
|
123
|
+
```bash
|
|
124
|
+
curl -s "https://api.atris.ai/api/integrations/slack/me/dms" \
|
|
125
|
+
-H "Authorization: Bearer $TOKEN"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Read Messages from a Channel or DM
|
|
129
|
+
```bash
|
|
130
|
+
curl -s "https://api.atris.ai/api/integrations/slack/me/messages/{channel_id}?limit=20" \
|
|
131
|
+
-H "Authorization: Bearer $TOKEN"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Send Message as Yourself
|
|
135
|
+
```bash
|
|
136
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/slack/me/send" \
|
|
137
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
138
|
+
-H "Content-Type: application/json" \
|
|
139
|
+
-d '{
|
|
140
|
+
"channel": "C0123CHANNEL",
|
|
141
|
+
"text": "Hey team, here is the update..."
|
|
142
|
+
}'
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Reply in a thread:**
|
|
146
|
+
```bash
|
|
147
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/slack/me/send" \
|
|
148
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
149
|
+
-H "Content-Type: application/json" \
|
|
150
|
+
-d '{
|
|
151
|
+
"channel": "C0123CHANNEL",
|
|
152
|
+
"text": "Following up on this...",
|
|
153
|
+
"thread_ts": "1234567890.123456"
|
|
154
|
+
}'
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### DM Someone as Yourself
|
|
158
|
+
```bash
|
|
159
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/slack/me/dm" \
|
|
160
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
161
|
+
-H "Content-Type: application/json" \
|
|
162
|
+
-d '{
|
|
163
|
+
"slack_user_id": "U0123USER",
|
|
164
|
+
"text": "Hey, quick question about the project..."
|
|
165
|
+
}'
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Search Messages
|
|
169
|
+
```bash
|
|
170
|
+
curl -s "https://api.atris.ai/api/integrations/slack/me/search?q=quarterly+report&count=20" \
|
|
171
|
+
-H "Authorization: Bearer $TOKEN"
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Workspace Endpoints
|
|
177
|
+
|
|
178
|
+
### List Channels (bot view)
|
|
179
|
+
```bash
|
|
180
|
+
curl -s "https://api.atris.ai/api/integrations/slack/channels" \
|
|
181
|
+
-H "Authorization: Bearer $TOKEN"
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### List Users
|
|
185
|
+
```bash
|
|
186
|
+
curl -s "https://api.atris.ai/api/integrations/slack/users" \
|
|
187
|
+
-H "Authorization: Bearer $TOKEN"
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Send as Bot
|
|
191
|
+
```bash
|
|
192
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/slack/test-send" \
|
|
193
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
194
|
+
-H "Content-Type: application/json" \
|
|
195
|
+
-d '{
|
|
196
|
+
"channel": "C0123CHANNEL",
|
|
197
|
+
"message": "Hello from Atris!"
|
|
198
|
+
}'
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Check Connection Status
|
|
202
|
+
```bash
|
|
203
|
+
curl -s "https://api.atris.ai/api/integrations/slack/status" \
|
|
204
|
+
-H "Authorization: Bearer $TOKEN"
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Disconnect Slack
|
|
208
|
+
```bash
|
|
209
|
+
curl -s -X DELETE "https://api.atris.ai/api/integrations/slack" \
|
|
210
|
+
-H "Authorization: Bearer $TOKEN"
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Workflows
|
|
216
|
+
|
|
217
|
+
### "Check my Slack messages"
|
|
218
|
+
1. Run bootstrap
|
|
219
|
+
2. List DMs: `GET /slack/me/dms`
|
|
220
|
+
3. For each open DM, read recent messages: `GET /slack/me/messages/{channel_id}?limit=5`
|
|
221
|
+
4. Resolve user names: `GET /slack/users`
|
|
222
|
+
5. Display: who messaged, what they said, when
|
|
223
|
+
|
|
224
|
+
### "Send a message to someone"
|
|
225
|
+
1. Run bootstrap
|
|
226
|
+
2. Find the user: `GET /slack/users` (search by name/email)
|
|
227
|
+
3. **Show user the draft for approval**
|
|
228
|
+
4. Send DM: `POST /slack/me/dm` with `{user_id, text}`
|
|
229
|
+
5. Confirm: "Message sent!"
|
|
230
|
+
|
|
231
|
+
### "Reply in a channel"
|
|
232
|
+
1. Run bootstrap
|
|
233
|
+
2. List channels: `GET /slack/me/channels`
|
|
234
|
+
3. Find the right channel
|
|
235
|
+
4. Read recent messages: `GET /slack/me/messages/{channel_id}`
|
|
236
|
+
5. **Show user the draft for approval**
|
|
237
|
+
6. Send: `POST /slack/me/send` with `{channel, text}`
|
|
238
|
+
|
|
239
|
+
### "Find a conversation about X"
|
|
240
|
+
1. Run bootstrap
|
|
241
|
+
2. Search: `GET /slack/me/search?q=X`
|
|
242
|
+
3. Display matching messages with channel, sender, permalink
|
|
243
|
+
|
|
244
|
+
### "What did [person] say to me?"
|
|
245
|
+
1. Run bootstrap
|
|
246
|
+
2. List users: `GET /slack/users` (find user ID)
|
|
247
|
+
3. List DMs: `GET /slack/me/dms` (find DM channel with that user)
|
|
248
|
+
4. Read messages: `GET /slack/me/messages/{channel_id}`
|
|
249
|
+
5. Display conversation
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Important Notes
|
|
254
|
+
|
|
255
|
+
- **Personal endpoints** (`/slack/me/*`) send messages as YOU, not a bot
|
|
256
|
+
- **Bot endpoints** (`/slack/channels`, `/slack/test-send`) use the bot token
|
|
257
|
+
- **Always get approval** before sending messages on behalf of the user
|
|
258
|
+
- **Thread replies**: include `thread_ts` to reply in a thread instead of creating a new message
|
|
259
|
+
- **User IDs**: Slack uses IDs like `U0123ABC`. Get them from `/slack/users` endpoint
|
|
260
|
+
- **Channel IDs**: Use IDs like `C0123ABC`. Get them from `/slack/me/channels`
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Error Handling
|
|
265
|
+
|
|
266
|
+
| Error | Meaning | Solution |
|
|
267
|
+
|-------|---------|----------|
|
|
268
|
+
| `Token expired` | AtrisOS session expired | Run `atris login` |
|
|
269
|
+
| `Slack not connected` | OAuth not completed | Re-run bootstrap |
|
|
270
|
+
| `Personal Slack not connected` | No user token | Re-connect Slack (needs re-auth for personal access) |
|
|
271
|
+
| `401 Unauthorized` | Invalid/expired token | Run `atris login` |
|
|
272
|
+
| `channel_not_found` | Invalid channel ID | Use `/slack/me/channels` to find correct ID |
|
|
273
|
+
| `not_in_channel` | User not in channel | Join the channel first |
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## Security Model
|
|
278
|
+
|
|
279
|
+
1. **Local token** (`~/.atris/credentials.json`): Your AtrisOS auth token, stored locally.
|
|
280
|
+
2. **Slack credentials**: Bot token and user token stored **server-side** in AtrisOS encrypted vault.
|
|
281
|
+
3. **Two token types**: Bot token (xoxb-) for workspace operations, User token (xoxp-) for personal operations.
|
|
282
|
+
4. **Access control**: AtrisOS API enforces that you can only access your own Slack.
|
|
283
|
+
5. **HTTPS only**: All API communication encrypted in transit.
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Quick Reference
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
# Setup (one time)
|
|
291
|
+
npm install -g atris && atris login
|
|
292
|
+
|
|
293
|
+
# Get token
|
|
294
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
295
|
+
|
|
296
|
+
# Check connection
|
|
297
|
+
curl -s "https://api.atris.ai/api/integrations/slack/status" -H "Authorization: Bearer $TOKEN"
|
|
298
|
+
|
|
299
|
+
# List your DMs
|
|
300
|
+
curl -s "https://api.atris.ai/api/integrations/slack/me/dms" -H "Authorization: Bearer $TOKEN"
|
|
301
|
+
|
|
302
|
+
# Read messages
|
|
303
|
+
curl -s "https://api.atris.ai/api/integrations/slack/me/messages/CHANNEL_ID" -H "Authorization: Bearer $TOKEN"
|
|
304
|
+
|
|
305
|
+
# Send as yourself
|
|
306
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/slack/me/send" \
|
|
307
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
308
|
+
-d '{"channel":"C0123","text":"Hello!"}'
|
|
309
|
+
|
|
310
|
+
# DM someone as yourself
|
|
311
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/slack/me/dm" \
|
|
312
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
313
|
+
-d '{"user_id":"U0123","text":"Hey!"}'
|
|
314
|
+
|
|
315
|
+
# Search messages
|
|
316
|
+
curl -s "https://api.atris.ai/api/integrations/slack/me/search?q=project+update" -H "Authorization: Bearer $TOKEN"
|
|
317
|
+
|
|
318
|
+
# List workspace users
|
|
319
|
+
curl -s "https://api.atris.ai/api/integrations/slack/users" -H "Authorization: Bearer $TOKEN"
|
|
320
|
+
```
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: slides
|
|
3
|
+
description: Google Slides integration via AtrisOS API. List, create, read, and update presentations. Add slides, text, shapes, images. Export to PDF. Use when user asks about slides, presentations, decks, or pitch decks.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
tags:
|
|
6
|
+
- slides
|
|
7
|
+
- google
|
|
8
|
+
- productivity
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Google Slides Agent
|
|
12
|
+
|
|
13
|
+
> Drop this in `~/.claude/skills/slides/SKILL.md` and Claude Code becomes your presentation assistant.
|
|
14
|
+
|
|
15
|
+
## Prerequisites
|
|
16
|
+
|
|
17
|
+
Google Slides shares OAuth with Google Drive. If Drive is connected, Slides works automatically. If not:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
21
|
+
|
|
22
|
+
# Check if Drive is connected (Slides piggybacks on Drive)
|
|
23
|
+
curl -s "https://api.atris.ai/api/integrations/google-drive/status" -H "Authorization: Bearer $TOKEN"
|
|
24
|
+
|
|
25
|
+
# If not connected, start Drive OAuth (includes Slides scope)
|
|
26
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/start" \
|
|
27
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
28
|
+
-d '{"next_url":"https://atris.ai/dashboard/settings"}'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## API Reference
|
|
34
|
+
|
|
35
|
+
Base: `https://api.atris.ai/api/integrations/google-slides`
|
|
36
|
+
|
|
37
|
+
All requests require: `-H "Authorization: Bearer $TOKEN"`
|
|
38
|
+
|
|
39
|
+
### Get Token
|
|
40
|
+
```bash
|
|
41
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Presentations
|
|
47
|
+
|
|
48
|
+
### List Presentations
|
|
49
|
+
```bash
|
|
50
|
+
curl -s "https://api.atris.ai/api/integrations/google-slides/presentations?page_size=20" \
|
|
51
|
+
-H "Authorization: Bearer $TOKEN"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Get a Presentation (with all slides)
|
|
55
|
+
```bash
|
|
56
|
+
curl -s "https://api.atris.ai/api/integrations/google-slides/presentations/{presentation_id}" \
|
|
57
|
+
-H "Authorization: Bearer $TOKEN"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Create a Presentation
|
|
61
|
+
```bash
|
|
62
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/google-slides/presentations" \
|
|
63
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
64
|
+
-H "Content-Type: application/json" \
|
|
65
|
+
-d '{"title": "Q1 2026 Review"}'
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Updating Slides (batch-update)
|
|
71
|
+
|
|
72
|
+
All slide mutations use the batch-update endpoint. This is the most powerful endpoint — it can add slides, insert text, add shapes, images, change formatting, and more.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/google-slides/presentations/{id}/batch-update" \
|
|
76
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
77
|
+
-H "Content-Type: application/json" \
|
|
78
|
+
-d '{"requests": [...]}'
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Add a Blank Slide
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"requests": [
|
|
85
|
+
{
|
|
86
|
+
"createSlide": {
|
|
87
|
+
"insertionIndex": 1,
|
|
88
|
+
"slideLayoutReference": {
|
|
89
|
+
"predefinedLayout": "BLANK"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Available layouts:** `BLANK`, `TITLE`, `TITLE_AND_BODY`, `TITLE_AND_TWO_COLUMNS`, `TITLE_ONLY`, `SECTION_HEADER`, `SECTION_TITLE_AND_DESCRIPTION`, `ONE_COLUMN_TEXT`, `MAIN_POINT`, `BIG_NUMBER`
|
|
98
|
+
|
|
99
|
+
### Add a Title Slide
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"requests": [
|
|
103
|
+
{
|
|
104
|
+
"createSlide": {
|
|
105
|
+
"insertionIndex": 0,
|
|
106
|
+
"slideLayoutReference": {
|
|
107
|
+
"predefinedLayout": "TITLE"
|
|
108
|
+
},
|
|
109
|
+
"placeholderIdMappings": [
|
|
110
|
+
{"layoutPlaceholder": {"type": "TITLE"}, "objectId": "titleId"},
|
|
111
|
+
{"layoutPlaceholder": {"type": "SUBTITLE"}, "objectId": "subtitleId"}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"insertText": {
|
|
117
|
+
"objectId": "titleId",
|
|
118
|
+
"text": "Q1 2026 Review"
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"insertText": {
|
|
123
|
+
"objectId": "subtitleId",
|
|
124
|
+
"text": "Atris Labs - Confidential"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Insert Text into an Element
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"requests": [
|
|
135
|
+
{
|
|
136
|
+
"insertText": {
|
|
137
|
+
"objectId": "ELEMENT_ID",
|
|
138
|
+
"text": "Hello, world!"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Add a Text Box
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"requests": [
|
|
149
|
+
{
|
|
150
|
+
"createShape": {
|
|
151
|
+
"objectId": "myTextBox1",
|
|
152
|
+
"shapeType": "TEXT_BOX",
|
|
153
|
+
"elementProperties": {
|
|
154
|
+
"pageObjectId": "SLIDE_ID",
|
|
155
|
+
"size": {
|
|
156
|
+
"width": {"magnitude": 400, "unit": "PT"},
|
|
157
|
+
"height": {"magnitude": 50, "unit": "PT"}
|
|
158
|
+
},
|
|
159
|
+
"transform": {
|
|
160
|
+
"scaleX": 1, "scaleY": 1,
|
|
161
|
+
"translateX": 100, "translateY": 200,
|
|
162
|
+
"unit": "PT"
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"insertText": {
|
|
169
|
+
"objectId": "myTextBox1",
|
|
170
|
+
"text": "Custom text here"
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Add an Image
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"requests": [
|
|
181
|
+
{
|
|
182
|
+
"createImage": {
|
|
183
|
+
"objectId": "myImage1",
|
|
184
|
+
"url": "https://example.com/image.png",
|
|
185
|
+
"elementProperties": {
|
|
186
|
+
"pageObjectId": "SLIDE_ID",
|
|
187
|
+
"size": {
|
|
188
|
+
"width": {"magnitude": 300, "unit": "PT"},
|
|
189
|
+
"height": {"magnitude": 200, "unit": "PT"}
|
|
190
|
+
},
|
|
191
|
+
"transform": {
|
|
192
|
+
"scaleX": 1, "scaleY": 1,
|
|
193
|
+
"translateX": 150, "translateY": 100,
|
|
194
|
+
"unit": "PT"
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
]
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Replace All Text (find & replace)
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"requests": [
|
|
207
|
+
{
|
|
208
|
+
"replaceAllText": {
|
|
209
|
+
"containsText": {"text": "{{company_name}}"},
|
|
210
|
+
"replaceText": "Atris Labs"
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Delete a Slide or Element
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"requests": [
|
|
221
|
+
{
|
|
222
|
+
"deleteObject": {
|
|
223
|
+
"objectId": "SLIDE_OR_ELEMENT_ID"
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Style Text
|
|
231
|
+
```json
|
|
232
|
+
{
|
|
233
|
+
"requests": [
|
|
234
|
+
{
|
|
235
|
+
"updateTextStyle": {
|
|
236
|
+
"objectId": "ELEMENT_ID",
|
|
237
|
+
"style": {
|
|
238
|
+
"bold": true,
|
|
239
|
+
"fontSize": {"magnitude": 24, "unit": "PT"},
|
|
240
|
+
"foregroundColor": {
|
|
241
|
+
"opaqueColor": {"rgbColor": {"red": 0.2, "green": 0.2, "blue": 0.8}}
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
"fields": "bold,fontSize,foregroundColor"
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Pages (Individual Slides)
|
|
254
|
+
|
|
255
|
+
### Get a Single Slide
|
|
256
|
+
```bash
|
|
257
|
+
curl -s "https://api.atris.ai/api/integrations/google-slides/presentations/{id}/pages/{page_id}" \
|
|
258
|
+
-H "Authorization: Bearer $TOKEN"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Get Slide Thumbnail
|
|
262
|
+
```bash
|
|
263
|
+
curl -s "https://api.atris.ai/api/integrations/google-slides/presentations/{id}/pages/{page_id}/thumbnail" \
|
|
264
|
+
-H "Authorization: Bearer $TOKEN"
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Export
|
|
270
|
+
|
|
271
|
+
### Export as PDF
|
|
272
|
+
```bash
|
|
273
|
+
curl -s "https://api.atris.ai/api/integrations/google-slides/presentations/{id}/export" \
|
|
274
|
+
-H "Authorization: Bearer $TOKEN"
|
|
275
|
+
```
|
|
276
|
+
Returns `{"pdf_base64": "...", "content_type": "application/pdf"}`.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## Workflows
|
|
281
|
+
|
|
282
|
+
### "Create a pitch deck"
|
|
283
|
+
1. Create presentation: `POST /presentations` with title
|
|
284
|
+
2. Get the presentation to find the first slide ID
|
|
285
|
+
3. Batch update: add title slide, content slides, closing slide
|
|
286
|
+
4. Each slide: createSlide + insertText for content
|
|
287
|
+
5. Return the presentation URL: `https://docs.google.com/presentation/d/{id}`
|
|
288
|
+
|
|
289
|
+
### "List my presentations"
|
|
290
|
+
1. `GET /presentations`
|
|
291
|
+
2. Display: name, last modified, link
|
|
292
|
+
|
|
293
|
+
### "Add a slide to an existing deck"
|
|
294
|
+
1. `GET /presentations/{id}` to see current slides
|
|
295
|
+
2. Batch update: createSlide at the desired index
|
|
296
|
+
3. Add content with insertText, createShape, createImage
|
|
297
|
+
|
|
298
|
+
### "Export deck to PDF"
|
|
299
|
+
1. `GET /presentations/{id}/export`
|
|
300
|
+
2. Decode base64 and save to file
|
|
301
|
+
|
|
302
|
+
### "Update text in a deck"
|
|
303
|
+
1. `GET /presentations/{id}` to find element IDs
|
|
304
|
+
2. Batch update with replaceAllText for template variables
|
|
305
|
+
3. Or insertText/deleteText for specific elements
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Important Notes
|
|
310
|
+
|
|
311
|
+
- **Shares Drive OAuth** — no separate connection needed. If Drive is connected, Slides works
|
|
312
|
+
- **Batch update is everything** — all slide mutations (add, edit, delete, style) go through batch-update
|
|
313
|
+
- **Object IDs** — every slide, shape, text box, image has an ID. Get them from `GET /presentations/{id}`
|
|
314
|
+
- **Slide size** — default is 10x5.63 inches (720x406.5 PT). Position elements accordingly
|
|
315
|
+
- **Images** — must be publicly accessible URLs. For private images, upload to Drive first
|
|
316
|
+
- **Templates** — use replaceAllText with `{{placeholder}}` patterns for template-based decks
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Error Handling
|
|
321
|
+
|
|
322
|
+
| Error | Meaning | Solution |
|
|
323
|
+
|-------|---------|----------|
|
|
324
|
+
| `Drive not connected` | No Drive OAuth token | Connect Google Drive first |
|
|
325
|
+
| `403 insufficient_scope` | Token missing presentations scope | Reconnect Drive to get updated scopes |
|
|
326
|
+
| `404 not_found` | Presentation doesn't exist | Check presentation ID |
|
|
327
|
+
| `400 invalid_request` | Bad batch update request | Check request format against API docs |
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Quick Reference
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
# Get token
|
|
335
|
+
TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
|
|
336
|
+
|
|
337
|
+
# List presentations
|
|
338
|
+
curl -s "https://api.atris.ai/api/integrations/google-slides/presentations" -H "Authorization: Bearer $TOKEN"
|
|
339
|
+
|
|
340
|
+
# Create presentation
|
|
341
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/google-slides/presentations" \
|
|
342
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
343
|
+
-d '{"title":"My Deck"}'
|
|
344
|
+
|
|
345
|
+
# Get presentation (with all slides)
|
|
346
|
+
curl -s "https://api.atris.ai/api/integrations/google-slides/presentations/PRES_ID" -H "Authorization: Bearer $TOKEN"
|
|
347
|
+
|
|
348
|
+
# Add a slide with text
|
|
349
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/google-slides/presentations/PRES_ID/batch-update" \
|
|
350
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
351
|
+
-d '{"requests":[{"createSlide":{"slideLayoutReference":{"predefinedLayout":"TITLE_AND_BODY"}}}]}'
|
|
352
|
+
|
|
353
|
+
# Export as PDF
|
|
354
|
+
curl -s "https://api.atris.ai/api/integrations/google-slides/presentations/PRES_ID/export" -H "Authorization: Bearer $TOKEN"
|
|
355
|
+
```
|
package/bin/atris.js
CHANGED
|
@@ -25,7 +25,7 @@ const DEFAULT_CLIENT_ID = `AtrisCLI/${CLI_VERSION}`;
|
|
|
25
25
|
const DEFAULT_USER_AGENT = `${DEFAULT_CLIENT_ID} (node ${process.version}; ${os.platform()} ${os.release()} ${os.arch()})`;
|
|
26
26
|
|
|
27
27
|
// Update check utility
|
|
28
|
-
const { checkForUpdates, showUpdateNotification } = require('../utils/update-check');
|
|
28
|
+
const { checkForUpdates, showUpdateNotification, autoUpdate } = require('../utils/update-check');
|
|
29
29
|
|
|
30
30
|
// State detection for smart default
|
|
31
31
|
const { detectWorkspaceState, loadContext } = require('../lib/state-detection');
|
|
@@ -39,9 +39,11 @@ if (!skipUpdateCheck && (!process.argv[2] || (process.argv[2] && !['version', 'u
|
|
|
39
39
|
.then((updateInfo) => {
|
|
40
40
|
// Show notification if update available (after command completes)
|
|
41
41
|
if (updateInfo) {
|
|
42
|
-
//
|
|
42
|
+
// Auto-update in background, fall back to notification if it fails
|
|
43
43
|
setTimeout(() => {
|
|
44
|
-
|
|
44
|
+
if (!autoUpdate(updateInfo)) {
|
|
45
|
+
showUpdateNotification(updateInfo);
|
|
46
|
+
}
|
|
45
47
|
}, 100);
|
|
46
48
|
}
|
|
47
49
|
return updateInfo;
|
package/package.json
CHANGED
package/utils/update-check.js
CHANGED
|
@@ -175,8 +175,41 @@ function showUpdateNotification(updateInfo) {
|
|
|
175
175
|
console.log('');
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
function autoUpdate(updateInfo) {
|
|
179
|
+
if (!updateInfo || !updateInfo.needsUpdate) return false;
|
|
180
|
+
|
|
181
|
+
const { execSync } = require('child_process');
|
|
182
|
+
|
|
183
|
+
console.log('');
|
|
184
|
+
console.log(`⬆️ Updating atris ${updateInfo.installed} → ${updateInfo.latest}...`);
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
execSync('npm update -g atris', { stdio: 'pipe', timeout: 30000 });
|
|
188
|
+
console.log(`✅ Updated to ${updateInfo.latest}`);
|
|
189
|
+
|
|
190
|
+
// Auto-sync skills into current project if atris/skills/ exists
|
|
191
|
+
try {
|
|
192
|
+
if (fs.existsSync(path.join(process.cwd(), 'atris', 'skills'))) {
|
|
193
|
+
execSync('atris sync', { stdio: 'pipe', timeout: 10000 });
|
|
194
|
+
console.log(`✅ Skills synced`);
|
|
195
|
+
}
|
|
196
|
+
} catch (e) {
|
|
197
|
+
// Sync failed — not critical
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
console.log('');
|
|
201
|
+
return true;
|
|
202
|
+
} catch (error) {
|
|
203
|
+
// npm update failed (permissions, network, etc) — fall back to notification
|
|
204
|
+
console.log(`⚠️ Auto-update failed. Run manually: npm update -g atris`);
|
|
205
|
+
console.log('');
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
178
210
|
module.exports = {
|
|
179
211
|
checkForUpdates,
|
|
180
212
|
showUpdateNotification,
|
|
213
|
+
autoUpdate,
|
|
181
214
|
};
|
|
182
215
|
|