atris 2.2.2 → 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.
@@ -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,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
- // Wait a bit for command output to finish, then show notification
42
+ // Auto-update in background, fall back to notification if it fails
43
43
  setTimeout(() => {
44
- showUpdateNotification(updateInfo);
44
+ if (!autoUpdate(updateInfo)) {
45
+ showUpdateNotification(updateInfo);
46
+ }
45
47
  }, 100);
46
48
  }
47
49
  return updateInfo;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "2.2.2",
3
+ "version": "2.3.0",
4
4
  "description": "atrisDev (atris dev) - CLI for AI coding agents. Works with Claude Code, Cursor, Windsurf. Make any codebase AI-navigable.",
5
5
  "main": "bin/atris.js",
6
6
  "bin": {
@@ -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