atris 2.1.0 → 2.2.1

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.
Files changed (44) hide show
  1. package/AGENT.md +35 -0
  2. package/AGENTS.md +46 -0
  3. package/GETTING_STARTED.md +2 -2
  4. package/PERSONA.md +5 -1
  5. package/README.md +16 -8
  6. package/atris/AGENTS.md +25 -0
  7. package/atris/GEMINI.md +8 -0
  8. package/atris/GETTING_STARTED.md +2 -2
  9. package/atris/atris.md +4 -3
  10. package/atris/features/README.md +41 -15
  11. package/atris/policies/LESSONS.md +1 -0
  12. package/atris/skills/README.md +45 -14
  13. package/atris/skills/atris/SKILL.md +7 -0
  14. package/atris/skills/autopilot/SKILL.md +9 -4
  15. package/atris/skills/autopilot/atris-autopilot.md +71 -0
  16. package/atris/skills/autopilot/hooks/stop-hook.sh +79 -0
  17. package/atris/skills/backend/SKILL.md +6 -1
  18. package/atris/skills/calendar/SKILL.md +301 -0
  19. package/atris/skills/clawhub/atris/SKILL.md +121 -0
  20. package/atris/skills/copy-editor/SKILL.md +470 -0
  21. package/atris/skills/design/SKILL.md +5 -1
  22. package/atris/skills/drive/SKILL.md +333 -0
  23. package/atris/skills/email-agent/SKILL.md +376 -0
  24. package/atris/skills/memory/SKILL.md +8 -0
  25. package/atris/skills/meta/SKILL.md +4 -0
  26. package/atris/skills/skill-improver/SKILL.md +147 -0
  27. package/atris/skills/writing/SKILL.md +3 -0
  28. package/atris/team/brainstormer.md +1 -0
  29. package/atris/team/executor.md +27 -0
  30. package/atris/team/launcher.md +1 -0
  31. package/atris/team/navigator.md +44 -5
  32. package/atris/team/validator.md +44 -3
  33. package/atris.md +37 -1
  34. package/bin/atris.js +58 -5
  35. package/commands/auth.js +24 -4
  36. package/commands/init.js +140 -17
  37. package/commands/integrations.js +330 -0
  38. package/commands/skill.js +496 -0
  39. package/commands/status.js +9 -1
  40. package/commands/sync.js +64 -19
  41. package/commands/workflow.js +7 -0
  42. package/package.json +4 -2
  43. package/utils/auth.js +33 -0
  44. package/commands/stubs.txt +0 -10
@@ -0,0 +1,333 @@
1
+ ---
2
+ name: drive
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.0.0
5
+ tags:
6
+ - drive
7
+ - backend
8
+ - google-drive
9
+ - sheets
10
+ ---
11
+
12
+ # Drive Agent
13
+
14
+ > Drop this in `~/.claude/skills/drive/SKILL.md` and Claude Code becomes your Google Drive assistant.
15
+
16
+ ## Bootstrap (ALWAYS Run First)
17
+
18
+ Before any Drive operation, run this bootstrap to ensure everything is set up:
19
+
20
+ ```bash
21
+ #!/bin/bash
22
+ set -e
23
+
24
+ # 1. Check if atris CLI is installed
25
+ if ! command -v atris &> /dev/null; then
26
+ echo "Installing atris CLI..."
27
+ npm install -g atris
28
+ fi
29
+
30
+ # 2. Check if logged in to AtrisOS
31
+ if [ ! -f ~/.atris/credentials.json ]; then
32
+ echo "Not logged in to AtrisOS."
33
+ echo ""
34
+ echo "Option 1 (interactive): Run 'atris login' and follow prompts"
35
+ echo "Option 2 (non-interactive): Get token from https://atris.ai/auth/cli"
36
+ echo " Then run: atris login --token YOUR_TOKEN"
37
+ echo ""
38
+ exit 1
39
+ fi
40
+
41
+ # 3. Extract token
42
+ if command -v node &> /dev/null; then
43
+ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
44
+ elif command -v python3 &> /dev/null; then
45
+ TOKEN=$(python3 -c "import json,os; print(json.load(open(os.path.expanduser('~/.atris/credentials.json')))['token'])")
46
+ elif command -v jq &> /dev/null; then
47
+ TOKEN=$(jq -r '.token' ~/.atris/credentials.json)
48
+ else
49
+ echo "Error: Need node, python3, or jq to read credentials"
50
+ exit 1
51
+ fi
52
+
53
+ # 4. Check Google Drive connection status
54
+ STATUS=$(curl -s "https://api.atris.ai/api/integrations/google-drive/status" \
55
+ -H "Authorization: Bearer $TOKEN")
56
+
57
+ if echo "$STATUS" | grep -q "Token expired\|Not authenticated"; then
58
+ echo "Token expired. Please re-authenticate:"
59
+ echo " Run: atris login --force"
60
+ exit 1
61
+ fi
62
+
63
+ if command -v node &> /dev/null; then
64
+ CONNECTED=$(node -e "try{console.log(JSON.parse('$STATUS').connected||false)}catch(e){console.log(false)}")
65
+ elif command -v python3 &> /dev/null; then
66
+ CONNECTED=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('connected', False))")
67
+ else
68
+ CONNECTED=$(echo "$STATUS" | jq -r '.connected // false')
69
+ fi
70
+
71
+ if [ "$CONNECTED" != "true" ] && [ "$CONNECTED" != "True" ]; then
72
+ echo "Google Drive not connected. Getting authorization URL..."
73
+ AUTH=$(curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/start" \
74
+ -H "Authorization: Bearer $TOKEN" \
75
+ -H "Content-Type: application/json" \
76
+ -d '{}')
77
+
78
+ if command -v node &> /dev/null; then
79
+ URL=$(node -e "try{console.log(JSON.parse('$AUTH').auth_url||'')}catch(e){console.log('')}")
80
+ elif command -v python3 &> /dev/null; then
81
+ URL=$(echo "$AUTH" | python3 -c "import sys,json; print(json.load(sys.stdin).get('auth_url', ''))")
82
+ else
83
+ URL=$(echo "$AUTH" | jq -r '.auth_url // empty')
84
+ fi
85
+
86
+ echo ""
87
+ echo "Open this URL to connect your Google Drive:"
88
+ echo "$URL"
89
+ echo ""
90
+ echo "After authorizing, run your command again."
91
+ exit 0
92
+ fi
93
+
94
+ echo "Ready. Google Drive is connected."
95
+ export ATRIS_TOKEN="$TOKEN"
96
+ ```
97
+
98
+ ---
99
+
100
+ ## API Reference
101
+
102
+ Base: `https://api.atris.ai/api/integrations`
103
+
104
+ All requests require: `-H "Authorization: Bearer $TOKEN"`
105
+
106
+ ### Get Token (after bootstrap)
107
+ ```bash
108
+ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
109
+ ```
110
+
111
+ ### List Files
112
+ ```bash
113
+ curl -s "https://api.atris.ai/api/integrations/google-drive/files?page_size=20" \
114
+ -H "Authorization: Bearer $TOKEN"
115
+ ```
116
+
117
+ **List files in a folder:**
118
+ ```bash
119
+ curl -s "https://api.atris.ai/api/integrations/google-drive/files?folder_id=FOLDER_ID&page_size=20" \
120
+ -H "Authorization: Bearer $TOKEN"
121
+ ```
122
+
123
+ ### Search Files
124
+ ```bash
125
+ curl -s "https://api.atris.ai/api/integrations/google-drive/search?q=quarterly+report&page_size=20" \
126
+ -H "Authorization: Bearer $TOKEN"
127
+ ```
128
+
129
+ Simple queries search by file name. For advanced queries, use Drive query syntax:
130
+ - `name contains 'budget'` — name search
131
+ - `mimeType = 'application/vnd.google-apps.spreadsheet'` — only sheets
132
+ - `mimeType = 'application/vnd.google-apps.document'` — only docs
133
+ - `modifiedTime > '2026-01-01'` — recently modified
134
+
135
+ ### Get File Metadata
136
+ ```bash
137
+ curl -s "https://api.atris.ai/api/integrations/google-drive/files/{file_id}" \
138
+ -H "Authorization: Bearer $TOKEN"
139
+ ```
140
+
141
+ ### Download File
142
+ ```bash
143
+ curl -s "https://api.atris.ai/api/integrations/google-drive/files/{file_id}/download" \
144
+ -H "Authorization: Bearer $TOKEN"
145
+ ```
146
+
147
+ Returns base64-encoded content. For Google Docs/Sheets/Slides, use export instead.
148
+
149
+ ### Export Google Docs/Sheets/Slides
150
+ ```bash
151
+ curl -s "https://api.atris.ai/api/integrations/google-drive/files/{file_id}/export?mime_type=text/plain" \
152
+ -H "Authorization: Bearer $TOKEN"
153
+ ```
154
+
155
+ **Export formats:**
156
+ - `text/plain` — plain text (default, good for Docs)
157
+ - `text/html` — HTML
158
+ - `application/pdf` — PDF
159
+ - `text/csv` — CSV (for Sheets)
160
+
161
+ ### Upload File
162
+ ```bash
163
+ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/files" \
164
+ -H "Authorization: Bearer $TOKEN" \
165
+ -H "Content-Type: application/json" \
166
+ -d '{
167
+ "name": "notes.txt",
168
+ "content": "File content here",
169
+ "mime_type": "text/plain"
170
+ }'
171
+ ```
172
+
173
+ **Upload to a specific folder:**
174
+ ```bash
175
+ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/files" \
176
+ -H "Authorization: Bearer $TOKEN" \
177
+ -H "Content-Type: application/json" \
178
+ -d '{
179
+ "name": "report.txt",
180
+ "content": "File content here",
181
+ "mime_type": "text/plain",
182
+ "folder_id": "FOLDER_ID"
183
+ }'
184
+ ```
185
+
186
+ ---
187
+
188
+ ## Google Sheets
189
+
190
+ Full read/write access to Google Sheets.
191
+
192
+ ### Get Spreadsheet Info
193
+ ```bash
194
+ curl -s "https://api.atris.ai/api/integrations/google-drive/sheets/{spreadsheet_id}" \
195
+ -H "Authorization: Bearer $TOKEN"
196
+ ```
197
+
198
+ Returns sheet names, title, and metadata.
199
+
200
+ ### Read Cells
201
+ ```bash
202
+ curl -s "https://api.atris.ai/api/integrations/google-drive/sheets/{spreadsheet_id}/values?range=Sheet1" \
203
+ -H "Authorization: Bearer $TOKEN"
204
+ ```
205
+
206
+ **Range uses A1 notation:**
207
+ - `Sheet1` — entire sheet
208
+ - `Sheet1!A1:D10` — specific range
209
+ - `Sheet1!A:A` — entire column A
210
+ - `Sheet1!1:1` — entire row 1
211
+
212
+ ### Update Cells
213
+ ```bash
214
+ curl -s -X PUT "https://api.atris.ai/api/integrations/google-drive/sheets/{spreadsheet_id}/values" \
215
+ -H "Authorization: Bearer $TOKEN" \
216
+ -H "Content-Type: application/json" \
217
+ -d '{
218
+ "range": "Sheet1!A1:B2",
219
+ "values": [
220
+ ["Name", "Score"],
221
+ ["Alice", 95]
222
+ ]
223
+ }'
224
+ ```
225
+
226
+ ### Append Rows
227
+ ```bash
228
+ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/sheets/{spreadsheet_id}/append" \
229
+ -H "Authorization: Bearer $TOKEN" \
230
+ -H "Content-Type: application/json" \
231
+ -d '{
232
+ "range": "Sheet1",
233
+ "values": [
234
+ ["Bob", 88],
235
+ ["Carol", 92]
236
+ ]
237
+ }'
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Workflows
243
+
244
+ ### "Find a file in my Drive"
245
+ 1. Run bootstrap
246
+ 2. Search: `GET /google-drive/search?q=QUERY`
247
+ 3. Display: name, type, modified date for each result
248
+
249
+ ### "Read a Google Doc"
250
+ 1. Run bootstrap
251
+ 2. Search for the doc: `GET /google-drive/search?q=DOC_NAME`
252
+ 3. Export as text: `GET /google-drive/files/{id}/export?mime_type=text/plain`
253
+ 4. Display content
254
+
255
+ ### "Read a spreadsheet"
256
+ 1. Run bootstrap
257
+ 2. Search for the sheet: `GET /google-drive/search?q=SHEET_NAME`
258
+ 3. Get sheet info: `GET /google-drive/sheets/{id}` (to see sheet names)
259
+ 4. Read values: `GET /google-drive/sheets/{id}/values?range=Sheet1`
260
+ 5. Display as a table
261
+
262
+ ### "Add rows to a spreadsheet"
263
+ 1. Run bootstrap
264
+ 2. Find the sheet
265
+ 3. Read current data to understand the columns: `GET /google-drive/sheets/{id}/values?range=Sheet1!1:1`
266
+ 4. **Show user what will be appended, get approval**
267
+ 5. Append: `POST /google-drive/sheets/{id}/append`
268
+
269
+ ### "Upload a file to Drive"
270
+ 1. Run bootstrap
271
+ 2. Read the local file content
272
+ 3. **Confirm with user**: "Upload {filename} to Drive?"
273
+ 4. Upload: `POST /google-drive/files` with `{name, content, mime_type}`
274
+
275
+ ---
276
+
277
+ ## Error Handling
278
+
279
+ | Error | Meaning | Solution |
280
+ |-------|---------|----------|
281
+ | `Token expired` | AtrisOS session expired | Run `atris login` |
282
+ | `Google Drive not connected` | OAuth not completed | Re-run bootstrap |
283
+ | `401 Unauthorized` | Invalid/expired token | Run `atris login` |
284
+ | `400 Drive not connected` | No Drive credentials | Complete OAuth via bootstrap |
285
+ | `429 Rate limited` | Too many requests | Wait 60s, retry |
286
+ | `Invalid grant` | Google revoked access | Re-connect via bootstrap |
287
+
288
+ ---
289
+
290
+ ## Security Model
291
+
292
+ 1. **Local token** (`~/.atris/credentials.json`): Your AtrisOS auth token, stored locally with 600 permissions.
293
+ 2. **Drive credentials**: Google Drive refresh token is stored **server-side** in AtrisOS encrypted vault.
294
+ 3. **Access control**: AtrisOS API enforces that you can only access your own Drive.
295
+ 4. **OAuth scopes**: Only requests necessary Drive permissions (read, write files).
296
+ 5. **HTTPS only**: All API communication encrypted in transit.
297
+
298
+ ---
299
+
300
+ ## Quick Reference
301
+
302
+ ```bash
303
+ # Setup (one time)
304
+ npm install -g atris && atris login
305
+
306
+ # Get token
307
+ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
308
+
309
+ # Check connection
310
+ curl -s "https://api.atris.ai/api/integrations/google-drive/status" -H "Authorization: Bearer $TOKEN"
311
+
312
+ # List files
313
+ curl -s "https://api.atris.ai/api/integrations/google-drive/files" -H "Authorization: Bearer $TOKEN"
314
+
315
+ # Search files
316
+ curl -s "https://api.atris.ai/api/integrations/google-drive/search?q=budget" -H "Authorization: Bearer $TOKEN"
317
+
318
+ # Read a Google Doc as text
319
+ curl -s "https://api.atris.ai/api/integrations/google-drive/files/{file_id}/export?mime_type=text/plain" -H "Authorization: Bearer $TOKEN"
320
+
321
+ # Read a spreadsheet
322
+ curl -s "https://api.atris.ai/api/integrations/google-drive/sheets/{id}/values?range=Sheet1" -H "Authorization: Bearer $TOKEN"
323
+
324
+ # Append rows to a sheet
325
+ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/sheets/{id}/append" \
326
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
327
+ -d '{"range":"Sheet1","values":[["Alice",95]]}'
328
+
329
+ # Upload a file
330
+ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/files" \
331
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
332
+ -d '{"name":"notes.txt","content":"Hello world","mime_type":"text/plain"}'
333
+ ```
@@ -0,0 +1,376 @@
1
+ ---
2
+ name: email-agent
3
+ description: Gmail integration via AtrisOS API. Read, send, archive emails. Use when user asks about email, inbox, or wants to send/check messages.
4
+ version: 1.1.0
5
+ tags:
6
+ - email-agent
7
+ - backend
8
+ - email
9
+ ---
10
+
11
+ # Email Agent
12
+
13
+ > Drop this in `~/.claude/skills/email-agent/SKILL.md` and Claude Code becomes your email assistant.
14
+
15
+ ## Bootstrap (ALWAYS Run First)
16
+
17
+ Before any email 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 (try node first, then python3, then jq)
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 Gmail connection status (also validates token)
53
+ STATUS=$(curl -s "https://api.atris.ai/api/integrations/gmail/status" \
54
+ -H "Authorization: Bearer $TOKEN")
55
+
56
+ # Check for token expiry
57
+ if echo "$STATUS" | grep -q "Token expired\|Not authenticated"; then
58
+ echo "Token expired. Please re-authenticate:"
59
+ echo " Run: atris login --force"
60
+ echo " Or get new token from: https://atris.ai/auth/cli"
61
+ exit 1
62
+ fi
63
+
64
+ # Parse connected status
65
+ if command -v node &> /dev/null; then
66
+ CONNECTED=$(node -e "console.log(JSON.parse('$STATUS').connected || false)")
67
+ elif command -v python3 &> /dev/null; then
68
+ CONNECTED=$(echo "$STATUS" | python3 -c "import sys,json; print(json.load(sys.stdin).get('connected', False))")
69
+ else
70
+ CONNECTED=$(echo "$STATUS" | jq -r '.connected // false')
71
+ fi
72
+
73
+ if [ "$CONNECTED" != "true" ] && [ "$CONNECTED" != "True" ]; then
74
+ echo "Gmail not connected. Getting authorization URL..."
75
+ AUTH=$(curl -s -X POST "https://api.atris.ai/api/integrations/gmail/start" \
76
+ -H "Authorization: Bearer $TOKEN" \
77
+ -H "Content-Type: application/json" \
78
+ -d '{}')
79
+
80
+ if command -v node &> /dev/null; then
81
+ URL=$(node -e "console.log(JSON.parse('$AUTH').auth_url || '')")
82
+ elif command -v python3 &> /dev/null; then
83
+ URL=$(echo "$AUTH" | python3 -c "import sys,json; print(json.load(sys.stdin).get('auth_url', ''))")
84
+ else
85
+ URL=$(echo "$AUTH" | jq -r '.auth_url // empty')
86
+ fi
87
+
88
+ echo ""
89
+ echo "Open this URL to connect your Gmail:"
90
+ echo "$URL"
91
+ echo ""
92
+ echo "After authorizing, run your email command again."
93
+ exit 0
94
+ fi
95
+
96
+ echo "Ready. Gmail is connected."
97
+ export ATRIS_TOKEN="$TOKEN"
98
+ ```
99
+
100
+ **Important**: Run this script ONCE before email operations. If it exits with instructions, follow them, then run again.
101
+
102
+ ---
103
+
104
+ ## API Reference
105
+
106
+ Base: `https://api.atris.ai/api/integrations/gmail`
107
+
108
+ All requests require: `-H "Authorization: Bearer $TOKEN"`
109
+
110
+ ### Get Token (after bootstrap)
111
+ ```bash
112
+ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
113
+ ```
114
+
115
+ ### List Emails
116
+ ```bash
117
+ curl -s "https://api.atris.ai/api/integrations/gmail/messages?query=in:inbox&max_results=20" \
118
+ -H "Authorization: Bearer $TOKEN"
119
+ ```
120
+
121
+ **Query syntax** (Gmail search):
122
+ - `in:inbox` — inbox only
123
+ - `in:inbox newer_than:1d` — today's emails
124
+ - `is:unread` — unread only
125
+ - `from:someone@example.com` — from specific sender
126
+ - `subject:invoice` — subject contains word
127
+ - `has:attachment` — emails with attachments
128
+
129
+ ### Read Single Email
130
+ ```bash
131
+ curl -s "https://api.atris.ai/api/integrations/gmail/messages/{message_id}" \
132
+ -H "Authorization: Bearer $TOKEN"
133
+ ```
134
+
135
+ ### Send Email
136
+ ```bash
137
+ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
138
+ -H "Authorization: Bearer $TOKEN" \
139
+ -H "Content-Type: application/json" \
140
+ -d '{
141
+ "to": "recipient@example.com",
142
+ "subject": "Subject line",
143
+ "body": "Email body text"
144
+ }'
145
+ ```
146
+
147
+ **With CC/BCC:**
148
+ ```bash
149
+ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
150
+ -H "Authorization: Bearer $TOKEN" \
151
+ -H "Content-Type: application/json" \
152
+ -d '{
153
+ "to": "recipient@example.com",
154
+ "cc": "copy@example.com",
155
+ "bcc": ["hidden1@example.com", "hidden2@example.com"],
156
+ "subject": "Subject line",
157
+ "body": "Email body text"
158
+ }'
159
+ ```
160
+
161
+ **With attachments:**
162
+ ```bash
163
+ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
164
+ -H "Authorization: Bearer $TOKEN" \
165
+ -H "Content-Type: application/json" \
166
+ -d '{
167
+ "to": "recipient@example.com",
168
+ "subject": "With attachment",
169
+ "body": "See attached.",
170
+ "attachments": [{"filename": "report.txt", "content": "base64-encoded-content", "mime_type": "text/plain"}]
171
+ }'
172
+ ```
173
+
174
+ ### Drafts
175
+
176
+ **List drafts:**
177
+ ```bash
178
+ curl -s "https://api.atris.ai/api/integrations/gmail/drafts?max_results=20" \
179
+ -H "Authorization: Bearer $TOKEN"
180
+ ```
181
+
182
+ **Read a draft:**
183
+ ```bash
184
+ curl -s "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}" \
185
+ -H "Authorization: Bearer $TOKEN"
186
+ ```
187
+
188
+ **Create a draft:**
189
+ ```bash
190
+ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts" \
191
+ -H "Authorization: Bearer $TOKEN" \
192
+ -H "Content-Type: application/json" \
193
+ -d '{
194
+ "to": "recipient@example.com",
195
+ "subject": "Subject line",
196
+ "body": "Draft body text"
197
+ }'
198
+ ```
199
+
200
+ Supports same fields as send: `cc`, `bcc`, `attachments`, plus `thread_id` to attach to an existing thread.
201
+
202
+ **Update a draft:**
203
+ ```bash
204
+ curl -s -X PUT "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}" \
205
+ -H "Authorization: Bearer $TOKEN" \
206
+ -H "Content-Type: application/json" \
207
+ -d '{
208
+ "to": "recipient@example.com",
209
+ "subject": "Updated subject",
210
+ "body": "Updated body"
211
+ }'
212
+ ```
213
+
214
+ **Send a draft:**
215
+ ```bash
216
+ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}/send" \
217
+ -H "Authorization: Bearer $TOKEN"
218
+ ```
219
+
220
+ **Delete a draft:**
221
+ ```bash
222
+ curl -s -X DELETE "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}" \
223
+ -H "Authorization: Bearer $TOKEN"
224
+ ```
225
+
226
+ ### Archive Email
227
+ ```bash
228
+ # Single message
229
+ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/archive" \
230
+ -H "Authorization: Bearer $TOKEN"
231
+
232
+ # Batch archive
233
+ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/batch-archive" \
234
+ -H "Authorization: Bearer $TOKEN" \
235
+ -H "Content-Type: application/json" \
236
+ -d '{"message_ids": ["id1", "id2", "id3"]}'
237
+ ```
238
+
239
+ ### Check Status
240
+ ```bash
241
+ curl -s "https://api.atris.ai/api/integrations/gmail/status" \
242
+ -H "Authorization: Bearer $TOKEN"
243
+ ```
244
+
245
+ ### Disconnect Gmail
246
+ ```bash
247
+ curl -s -X DELETE "https://api.atris.ai/api/integrations/gmail" \
248
+ -H "Authorization: Bearer $TOKEN"
249
+ ```
250
+
251
+ ---
252
+
253
+ ## Workflows
254
+
255
+ ### "Check my emails"
256
+ 1. Run bootstrap
257
+ 2. List messages: `GET /messages?query=in:inbox%20newer_than:1d&max_results=20`
258
+ 3. Display: sender, subject, snippet for each
259
+
260
+ ### "Send email to X about Y"
261
+ 1. Run bootstrap
262
+ 2. Draft email content
263
+ 3. **Show user the draft for approval**
264
+ 4. On approval: `POST /send` with `{to, subject, body}`
265
+ 5. Confirm: "Email sent!"
266
+
267
+ ### "Clean up my inbox"
268
+ 1. Run bootstrap
269
+ 2. List: `GET /messages?query=in:inbox&max_results=50`
270
+ 3. Identify archivable emails (see rules below)
271
+ 4. **Show user what will be archived, get approval**
272
+ 5. Batch archive: `POST /batch-archive`
273
+
274
+ ### "Show my drafts"
275
+ 1. Run bootstrap
276
+ 2. List drafts: `GET /gmail/drafts?max_results=20`
277
+ 3. Display: to, subject, snippet for each
278
+
279
+ ### "Draft an email to X about Y"
280
+ 1. Run bootstrap
281
+ 2. Compose email content
282
+ 3. **Show user the draft for review**
283
+ 4. On approval: `POST /gmail/drafts` with `{to, subject, body}`
284
+ 5. Confirm: "Draft saved! You can find it in Gmail."
285
+
286
+ ### "Send draft about X"
287
+ 1. Run bootstrap
288
+ 2. List drafts: `GET /gmail/drafts`
289
+ 3. Find matching draft by subject/recipient
290
+ 4. **Show user the draft content, confirm they want to send it**
291
+ 5. Send: `POST /gmail/drafts/{draft_id}/send`
292
+
293
+ ### "Archive all from [sender]"
294
+ 1. Run bootstrap
295
+ 2. Search: `GET /messages?query=from:{sender}`
296
+ 3. Collect message IDs
297
+ 4. **Confirm with user**: "Found N emails from {sender}. Archive all?"
298
+ 5. Batch archive
299
+
300
+ ---
301
+
302
+ ## Auto-Archive Rules
303
+
304
+ **Safe to suggest archiving:**
305
+ - From: `noreply@`, `notifications@`, `newsletter@`, `no-reply@`
306
+ - Subject contains: digest, newsletter, notification, weekly update, daily summary
307
+ - Marketing: promotional, unsubscribe link present
308
+
309
+ **NEVER auto-archive (always keep):**
310
+ - Subject contains: invoice, receipt, payment, urgent, action required, password, verification, security
311
+ - From known contacts (check if user has replied to them)
312
+ - Flagged/starred messages
313
+
314
+ **Always ask before archiving.** Never archive without explicit user approval.
315
+
316
+ ---
317
+
318
+ ## Error Handling
319
+
320
+ | Error | Meaning | Solution |
321
+ |-------|---------|----------|
322
+ | `Token expired` | AtrisOS session expired | Run `atris login` |
323
+ | `Gmail not connected` | OAuth not completed | Re-run bootstrap, complete OAuth flow |
324
+ | `401 Unauthorized` | Invalid/expired token | Run `atris login` |
325
+ | `400 Gmail not connected` | No Gmail credentials | Complete OAuth via bootstrap |
326
+ | `429 Rate limited` | Too many requests | Wait 60s, retry |
327
+ | `Invalid grant` | Google revoked access | Re-connect Gmail via bootstrap |
328
+
329
+ ---
330
+
331
+ ## Security Model
332
+
333
+ 1. **Local token** (`~/.atris/credentials.json`): Your AtrisOS auth token, stored locally with 600 permissions. Same model as AWS CLI, GitHub CLI.
334
+
335
+ 2. **Gmail credentials**: Your Gmail refresh token is stored **server-side** in AtrisOS encrypted vault. Never stored on your local machine.
336
+
337
+ 3. **Access control**: AtrisOS API enforces that you can only access your own email. No cross-user access possible.
338
+
339
+ 4. **OAuth scopes**: Only requests necessary Gmail permissions (read, send, modify labels).
340
+
341
+ 5. **HTTPS only**: All API communication encrypted in transit.
342
+
343
+ ---
344
+
345
+ ## Quick Reference
346
+
347
+ ```bash
348
+ # Setup (one time)
349
+ npm install -g atris && atris login
350
+
351
+ # Get token
352
+ TOKEN=$(node -e "console.log(require('$HOME/.atris/credentials.json').token)")
353
+
354
+ # Check connection
355
+ curl -s "https://api.atris.ai/api/integrations/gmail/status" -H "Authorization: Bearer $TOKEN"
356
+
357
+ # List inbox
358
+ curl -s "https://api.atris.ai/api/integrations/gmail/messages?query=in:inbox&max_results=10" -H "Authorization: Bearer $TOKEN"
359
+
360
+ # Send email
361
+ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
362
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
363
+ -d '{"to":"email@example.com","subject":"Hi","body":"Hello!"}'
364
+
365
+ # List drafts
366
+ curl -s "https://api.atris.ai/api/integrations/gmail/drafts" -H "Authorization: Bearer $TOKEN"
367
+
368
+ # Create draft
369
+ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts" \
370
+ -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
371
+ -d '{"to":"email@example.com","subject":"Hi","body":"Draft text"}'
372
+
373
+ # Send a draft
374
+ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}/send" \
375
+ -H "Authorization: Bearer $TOKEN"
376
+ ```