atris 2.3.0 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/atris/skills/calendar/SKILL.md +3 -3
- package/atris/skills/clawhub/atris/SKILL.md +1 -1
- package/atris/skills/drive/SKILL.md +38 -2
- package/atris/skills/email-agent/SKILL.md +72 -2
- package/atris/skills/slack/SKILL.md +15 -4
- package/atris/skills/writing/SKILL.md +1 -1
- package/bin/atris.js +30 -106
- package/commands/auth.js +97 -2
- package/commands/integrations.js +26 -12
- package/commands/plugin.js +450 -0
- package/commands/sync.js +119 -1
- package/package.json +1 -1
- package/utils/auth.js +72 -3
|
@@ -144,12 +144,12 @@ curl -s -X POST "https://api.atris.ai/api/integrations/google-calendar/events" \
|
|
|
144
144
|
-H "Authorization: Bearer $TOKEN" \
|
|
145
145
|
-H "Content-Type: application/json" \
|
|
146
146
|
-d '{
|
|
147
|
-
"summary": "Meeting with
|
|
147
|
+
"summary": "Meeting with Hugo",
|
|
148
148
|
"start": "2026-02-15T14:00:00-08:00",
|
|
149
149
|
"end": "2026-02-15T15:00:00-08:00",
|
|
150
|
-
"description": "Discuss
|
|
150
|
+
"description": "Discuss project roadmap",
|
|
151
151
|
"location": "Zoom",
|
|
152
|
-
"attendees": ["
|
|
152
|
+
"attendees": ["hugo@atrismail.com"],
|
|
153
153
|
"timezone": "America/Los_Angeles"
|
|
154
154
|
}'
|
|
155
155
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: atris
|
|
3
|
-
description: Codebase intelligence — generates structured navigation maps with file:line references so agents stop re-scanning the same files every session. Use when exploring code, answering
|
|
3
|
+
description: "Codebase intelligence — generates structured navigation maps with file:line references so agents stop re-scanning the same files every session. Use when exploring code, answering 'where is X?', or onboarding to a new codebase."
|
|
4
4
|
version: 1.1.0
|
|
5
5
|
requires:
|
|
6
6
|
bins:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: drive
|
|
3
3
|
description: Google Drive integration via AtrisOS API. Browse, search, read, upload files and work with Google Sheets. Use when user asks about Drive, files, docs, sheets, or spreadsheets.
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
tags:
|
|
6
6
|
- drive
|
|
7
7
|
- backend
|
|
@@ -199,6 +199,29 @@ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/files" \
|
|
|
199
199
|
}'
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
+
### Update Existing File
|
|
203
|
+
```bash
|
|
204
|
+
curl -s -X PUT "https://api.atris.ai/api/integrations/google-drive/files/{file_id}" \
|
|
205
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
206
|
+
-H "Content-Type: application/json" \
|
|
207
|
+
-d '{
|
|
208
|
+
"content": "Updated file content here",
|
|
209
|
+
"mime_type": "text/plain"
|
|
210
|
+
}'
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Update content and rename:**
|
|
214
|
+
```bash
|
|
215
|
+
curl -s -X PUT "https://api.atris.ai/api/integrations/google-drive/files/{file_id}" \
|
|
216
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
217
|
+
-H "Content-Type: application/json" \
|
|
218
|
+
-d '{
|
|
219
|
+
"content": "New content",
|
|
220
|
+
"mime_type": "text/plain",
|
|
221
|
+
"name": "renamed-file.txt"
|
|
222
|
+
}'
|
|
223
|
+
```
|
|
224
|
+
|
|
202
225
|
---
|
|
203
226
|
|
|
204
227
|
## Google Sheets
|
|
@@ -299,6 +322,14 @@ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/sheets/{spre
|
|
|
299
322
|
3. **Confirm with user**: "Upload {filename} to Drive?"
|
|
300
323
|
4. Upload: `POST /google-drive/files` with `{name, content, mime_type}`
|
|
301
324
|
|
|
325
|
+
### "Edit a file on Drive"
|
|
326
|
+
1. Run bootstrap
|
|
327
|
+
2. Find the file: `GET /google-drive/search?q=FILENAME`
|
|
328
|
+
3. Read current content: `GET /google-drive/files/{id}/export?mime_type=text/plain`
|
|
329
|
+
4. Make edits
|
|
330
|
+
5. **Show user the changes for approval**
|
|
331
|
+
6. Update: `PUT /google-drive/files/{id}` with `{content, mime_type}`
|
|
332
|
+
|
|
302
333
|
---
|
|
303
334
|
|
|
304
335
|
## Error Handling
|
|
@@ -356,8 +387,13 @@ curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/sheets/{id}/
|
|
|
356
387
|
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
357
388
|
-d '{"range":"Sheet1","values":[["Alice",95]]}'
|
|
358
389
|
|
|
359
|
-
# Upload a file
|
|
390
|
+
# Upload a new file
|
|
360
391
|
curl -s -X POST "https://api.atris.ai/api/integrations/google-drive/files" \
|
|
361
392
|
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
362
393
|
-d '{"name":"notes.txt","content":"Hello world","mime_type":"text/plain"}'
|
|
394
|
+
|
|
395
|
+
# Update an existing file
|
|
396
|
+
curl -s -X PUT "https://api.atris.ai/api/integrations/google-drive/files/{file_id}" \
|
|
397
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
398
|
+
-d '{"content":"Updated content","mime_type":"text/plain"}'
|
|
363
399
|
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: email-agent
|
|
3
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.
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
tags:
|
|
6
6
|
- email-agent
|
|
7
7
|
- backend
|
|
@@ -158,6 +158,33 @@ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
|
|
|
158
158
|
}'
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
+
**Reply in thread (IMPORTANT — use this for all replies):**
|
|
162
|
+
|
|
163
|
+
To reply within an existing email thread, you MUST pass `thread_id` and `reply_to_message_id`. Without these, Gmail creates a new thread.
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# 1. First, get the message you're replying to (extract thread_id and id)
|
|
167
|
+
curl -s "https://api.atris.ai/api/integrations/gmail/messages/{message_id}" \
|
|
168
|
+
-H "Authorization: Bearer $TOKEN"
|
|
169
|
+
# Response includes: id, thread_id, subject, from, etc.
|
|
170
|
+
|
|
171
|
+
# 2. Send reply in the same thread
|
|
172
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
|
|
173
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
174
|
+
-H "Content-Type: application/json" \
|
|
175
|
+
-d '{
|
|
176
|
+
"to": "original-sender@example.com",
|
|
177
|
+
"subject": "Re: Original Subject",
|
|
178
|
+
"body": "Your reply text here",
|
|
179
|
+
"thread_id": "THREAD_ID_FROM_STEP_1",
|
|
180
|
+
"reply_to_message_id": "MESSAGE_ID_FROM_STEP_1"
|
|
181
|
+
}'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
- `thread_id` — The thread ID from the original message. Tells Gmail which thread to add this to.
|
|
185
|
+
- `reply_to_message_id` — The message ID you're replying to. The backend uses this to set `In-Reply-To` and `References` headers so Gmail threads it correctly.
|
|
186
|
+
- `subject` — Must match the original subject with "Re: " prefix.
|
|
187
|
+
|
|
161
188
|
**With attachments:**
|
|
162
189
|
```bash
|
|
163
190
|
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
|
|
@@ -223,6 +250,17 @@ curl -s -X DELETE "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}
|
|
|
223
250
|
-H "Authorization: Bearer $TOKEN"
|
|
224
251
|
```
|
|
225
252
|
|
|
253
|
+
### Mark as Read / Unread
|
|
254
|
+
```bash
|
|
255
|
+
# Mark as read
|
|
256
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/read" \
|
|
257
|
+
-H "Authorization: Bearer $TOKEN"
|
|
258
|
+
|
|
259
|
+
# Mark as unread
|
|
260
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/unread" \
|
|
261
|
+
-H "Authorization: Bearer $TOKEN"
|
|
262
|
+
```
|
|
263
|
+
|
|
226
264
|
### Archive Email
|
|
227
265
|
```bash
|
|
228
266
|
# Single message
|
|
@@ -236,6 +274,19 @@ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/batch-arch
|
|
|
236
274
|
-d '{"message_ids": ["id1", "id2", "id3"]}'
|
|
237
275
|
```
|
|
238
276
|
|
|
277
|
+
### Trash Email
|
|
278
|
+
```bash
|
|
279
|
+
# Single message
|
|
280
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/trash" \
|
|
281
|
+
-H "Authorization: Bearer $TOKEN"
|
|
282
|
+
|
|
283
|
+
# Batch trash
|
|
284
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/batch-trash" \
|
|
285
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
286
|
+
-H "Content-Type: application/json" \
|
|
287
|
+
-d '{"message_ids": ["id1", "id2", "id3"]}'
|
|
288
|
+
```
|
|
289
|
+
|
|
239
290
|
### Check Status
|
|
240
291
|
```bash
|
|
241
292
|
curl -s "https://api.atris.ai/api/integrations/gmail/status" \
|
|
@@ -264,6 +315,14 @@ curl -s -X DELETE "https://api.atris.ai/api/integrations/gmail" \
|
|
|
264
315
|
4. On approval: `POST /send` with `{to, subject, body}`
|
|
265
316
|
5. Confirm: "Email sent!"
|
|
266
317
|
|
|
318
|
+
### "Reply to this email"
|
|
319
|
+
1. Run bootstrap
|
|
320
|
+
2. Read the message: `GET /messages/{message_id}` — extract `id`, `thread_id`, `from`, `subject`
|
|
321
|
+
3. Draft reply content
|
|
322
|
+
4. **Show user the reply for approval**
|
|
323
|
+
5. On approval: `POST /send` with `{to, subject: "Re: ...", body, thread_id, reply_to_message_id}`
|
|
324
|
+
6. Verify: response `thread_id` matches original thread_id (if it doesn't, something went wrong)
|
|
325
|
+
|
|
267
326
|
### "Clean up my inbox"
|
|
268
327
|
1. Run bootstrap
|
|
269
328
|
2. List: `GET /messages?query=in:inbox&max_results=50`
|
|
@@ -357,11 +416,16 @@ curl -s "https://api.atris.ai/api/integrations/gmail/status" -H "Authorization:
|
|
|
357
416
|
# List inbox
|
|
358
417
|
curl -s "https://api.atris.ai/api/integrations/gmail/messages?query=in:inbox&max_results=10" -H "Authorization: Bearer $TOKEN"
|
|
359
418
|
|
|
360
|
-
# Send email
|
|
419
|
+
# Send new email
|
|
361
420
|
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
|
|
362
421
|
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
363
422
|
-d '{"to":"email@example.com","subject":"Hi","body":"Hello!"}'
|
|
364
423
|
|
|
424
|
+
# Reply in thread (pass thread_id + reply_to_message_id)
|
|
425
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/send" \
|
|
426
|
+
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
427
|
+
-d '{"to":"sender@example.com","subject":"Re: Original","body":"Reply text","thread_id":"THREAD_ID","reply_to_message_id":"MSG_ID"}'
|
|
428
|
+
|
|
365
429
|
# List drafts
|
|
366
430
|
curl -s "https://api.atris.ai/api/integrations/gmail/drafts" -H "Authorization: Bearer $TOKEN"
|
|
367
431
|
|
|
@@ -370,6 +434,12 @@ curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts" \
|
|
|
370
434
|
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
|
371
435
|
-d '{"to":"email@example.com","subject":"Hi","body":"Draft text"}'
|
|
372
436
|
|
|
437
|
+
# Mark as read
|
|
438
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/read" -H "Authorization: Bearer $TOKEN"
|
|
439
|
+
|
|
440
|
+
# Trash an email
|
|
441
|
+
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/messages/{message_id}/trash" -H "Authorization: Bearer $TOKEN"
|
|
442
|
+
|
|
373
443
|
# Send a draft
|
|
374
444
|
curl -s -X POST "https://api.atris.ai/api/integrations/gmail/drafts/{draft_id}/send" \
|
|
375
445
|
-H "Authorization: Bearer $TOKEN"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: slack
|
|
3
3
|
description: Slack integration via AtrisOS API. Read messages, send as yourself, search conversations, manage DMs. Use when user asks about Slack, messages, channels, or team communication.
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
tags:
|
|
6
6
|
- slack
|
|
7
7
|
- backend
|
|
@@ -187,6 +187,14 @@ curl -s "https://api.atris.ai/api/integrations/slack/users" \
|
|
|
187
187
|
-H "Authorization: Bearer $TOKEN"
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
+
### Search Users
|
|
191
|
+
```bash
|
|
192
|
+
curl -s "https://api.atris.ai/api/integrations/slack/users/search?q=justin" \
|
|
193
|
+
-H "Authorization: Bearer $TOKEN"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Searches by name, display name, or email. Much faster than pulling the full user list.
|
|
197
|
+
|
|
190
198
|
### Send as Bot
|
|
191
199
|
```bash
|
|
192
200
|
curl -s -X POST "https://api.atris.ai/api/integrations/slack/test-send" \
|
|
@@ -223,9 +231,9 @@ curl -s -X DELETE "https://api.atris.ai/api/integrations/slack" \
|
|
|
223
231
|
|
|
224
232
|
### "Send a message to someone"
|
|
225
233
|
1. Run bootstrap
|
|
226
|
-
2. Find the user: `GET /slack/users`
|
|
234
|
+
2. Find the user: `GET /slack/users/search?q=NAME`
|
|
227
235
|
3. **Show user the draft for approval**
|
|
228
|
-
4. Send DM: `POST /slack/me/dm` with `{
|
|
236
|
+
4. Send DM: `POST /slack/me/dm` with `{slack_user_id, text}`
|
|
229
237
|
5. Confirm: "Message sent!"
|
|
230
238
|
|
|
231
239
|
### "Reply in a channel"
|
|
@@ -243,7 +251,7 @@ curl -s -X DELETE "https://api.atris.ai/api/integrations/slack" \
|
|
|
243
251
|
|
|
244
252
|
### "What did [person] say to me?"
|
|
245
253
|
1. Run bootstrap
|
|
246
|
-
2.
|
|
254
|
+
2. Find user: `GET /slack/users/search?q=NAME` (get user ID)
|
|
247
255
|
3. List DMs: `GET /slack/me/dms` (find DM channel with that user)
|
|
248
256
|
4. Read messages: `GET /slack/me/messages/{channel_id}`
|
|
249
257
|
5. Display conversation
|
|
@@ -317,4 +325,7 @@ curl -s "https://api.atris.ai/api/integrations/slack/me/search?q=project+update"
|
|
|
317
325
|
|
|
318
326
|
# List workspace users
|
|
319
327
|
curl -s "https://api.atris.ai/api/integrations/slack/users" -H "Authorization: Bearer $TOKEN"
|
|
328
|
+
|
|
329
|
+
# Search users by name
|
|
330
|
+
curl -s "https://api.atris.ai/api/integrations/slack/users/search?q=justin" -H "Authorization: Bearer $TOKEN"
|
|
320
331
|
```
|
package/bin/atris.js
CHANGED
|
@@ -56,6 +56,17 @@ if (!skipUpdateCheck && (!process.argv[2] || (process.argv[2] && !['version', 'u
|
|
|
56
56
|
|
|
57
57
|
const command = process.argv[2];
|
|
58
58
|
|
|
59
|
+
// Auto-sync skills on every command (fast — just file diffs, no network)
|
|
60
|
+
try {
|
|
61
|
+
const { syncSkills } = require('../commands/sync');
|
|
62
|
+
const skillsUpdated = syncSkills({ silent: true });
|
|
63
|
+
if (skillsUpdated > 0) {
|
|
64
|
+
console.log(`⬆️ ${skillsUpdated} skill${skillsUpdated > 1 ? 's' : ''} updated`);
|
|
65
|
+
}
|
|
66
|
+
} catch (e) {
|
|
67
|
+
// Non-critical
|
|
68
|
+
}
|
|
69
|
+
|
|
59
70
|
const TOKEN_REFRESH_BUFFER_SECONDS = 300; // Refresh ~5 minutes before expiry
|
|
60
71
|
|
|
61
72
|
function decodeJwtClaims(token) {
|
|
@@ -197,6 +208,8 @@ function showHelp() {
|
|
|
197
208
|
console.log(' login - Authenticate (use --token <t> for non-interactive)');
|
|
198
209
|
console.log(' logout - Remove credentials');
|
|
199
210
|
console.log(' whoami - Show auth status');
|
|
211
|
+
console.log(' switch - Switch account (atris switch <name>)');
|
|
212
|
+
console.log(' accounts - List saved accounts');
|
|
200
213
|
console.log('');
|
|
201
214
|
console.log('Integrations:');
|
|
202
215
|
console.log(' gmail - Email commands (inbox, read)');
|
|
@@ -210,6 +223,11 @@ function showHelp() {
|
|
|
210
223
|
console.log(' skill audit [name] - Validate skill against Anthropic guide');
|
|
211
224
|
console.log(' skill fix [name] - Auto-fix common compliance issues');
|
|
212
225
|
console.log('');
|
|
226
|
+
console.log('Plugin:');
|
|
227
|
+
console.log(' plugin build - Package skills as .plugin for Cowork');
|
|
228
|
+
console.log(' plugin publish - Sync skills to marketplace repo and push');
|
|
229
|
+
console.log(' plugin info - Preview what will be included');
|
|
230
|
+
console.log('');
|
|
213
231
|
console.log('Other:');
|
|
214
232
|
console.log(' version - Show Atris version');
|
|
215
233
|
console.log(' help - Show this help');
|
|
@@ -304,7 +322,7 @@ const { initAtris: initCmd } = require('../commands/init');
|
|
|
304
322
|
const { syncAtris: syncCmd } = require('../commands/sync');
|
|
305
323
|
const { logAtris: logCmd } = require('../commands/log');
|
|
306
324
|
const { logSyncAtris: logSyncCmd } = require('../commands/log-sync');
|
|
307
|
-
const { loginAtris: loginCmd, logoutAtris: logoutCmd, whoamiAtris: whoamiCmd } = require('../commands/auth');
|
|
325
|
+
const { loginAtris: loginCmd, logoutAtris: logoutCmd, whoamiAtris: whoamiCmd, switchAccount: switchCmd, listAccountsCmd: accountsCmd } = require('../commands/auth');
|
|
308
326
|
const { showVersion: versionCmd } = require('../commands/version');
|
|
309
327
|
const { planAtris: planCmd, doAtris: doCmd, reviewAtris: reviewCmd } = require('../commands/workflow');
|
|
310
328
|
const { visualizeAtris: visualizeCmd } = require('../commands/visualize');
|
|
@@ -316,11 +334,12 @@ const { analyticsAtris: analyticsCmd } = require('../commands/analytics');
|
|
|
316
334
|
const { cleanAtris: cleanCmd } = require('../commands/clean');
|
|
317
335
|
const { verifyAtris: verifyCmd } = require('../commands/verify');
|
|
318
336
|
const { skillCommand: skillCmd } = require('../commands/skill');
|
|
337
|
+
const { pluginCommand: pluginCmd } = require('../commands/plugin');
|
|
319
338
|
|
|
320
339
|
// Check if this is a known command or natural language input
|
|
321
340
|
const knownCommands = ['init', 'log', 'status', 'analytics', 'visualize', 'brainstorm', 'autopilot', 'plan', 'do', 'review',
|
|
322
|
-
'activate', 'agent', 'chat', 'login', 'logout', 'whoami', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
|
|
323
|
-
'clean', 'verify', 'search', 'skill',
|
|
341
|
+
'activate', 'agent', 'chat', 'login', 'logout', 'whoami', 'switch', 'accounts', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
|
|
342
|
+
'clean', 'verify', 'search', 'skill', 'plugin',
|
|
324
343
|
'gmail', 'calendar', 'twitter', 'slack', 'integrations'];
|
|
325
344
|
|
|
326
345
|
// Check if command is an atris.md spec file - triggers welcome visualization
|
|
@@ -650,6 +669,10 @@ if (command === 'init') {
|
|
|
650
669
|
logoutCmd();
|
|
651
670
|
} else if (command === 'whoami') {
|
|
652
671
|
whoamiCmd();
|
|
672
|
+
} else if (command === 'switch') {
|
|
673
|
+
switchCmd();
|
|
674
|
+
} else if (command === 'accounts') {
|
|
675
|
+
accountsCmd();
|
|
653
676
|
} else if (command === 'visualize') {
|
|
654
677
|
console.log('ℹ️ "atris visualize" is a legacy helper. Visualization is now built into "atris plan".');
|
|
655
678
|
console.log(' Prefer: atris plan');
|
|
@@ -802,6 +825,10 @@ if (command === 'init') {
|
|
|
802
825
|
const subcommand = process.argv[3];
|
|
803
826
|
const args = process.argv.slice(4);
|
|
804
827
|
skillCmd(subcommand, ...args);
|
|
828
|
+
} else if (command === 'plugin') {
|
|
829
|
+
const subcommand = process.argv[3] || 'build';
|
|
830
|
+
const args = process.argv.slice(4);
|
|
831
|
+
pluginCmd(subcommand, ...args);
|
|
805
832
|
} else {
|
|
806
833
|
console.log(`Unknown command: ${command}`);
|
|
807
834
|
console.log('Run "atris help" to see available commands');
|
|
@@ -2633,109 +2660,6 @@ async function atrisDevEntry(userInput = null) {
|
|
|
2633
2660
|
console.log('');
|
|
2634
2661
|
}
|
|
2635
2662
|
|
|
2636
|
-
function launchAtris() {
|
|
2637
|
-
const targetDir = path.join(process.cwd(), 'atris');
|
|
2638
|
-
const launcherFile = path.join(targetDir, 'team', 'launcher.md');
|
|
2639
|
-
|
|
2640
|
-
if (!fs.existsSync(launcherFile)) {
|
|
2641
|
-
console.log('✗ launcher.md not found. Run "atris init" first.');
|
|
2642
|
-
process.exit(1);
|
|
2643
|
-
}
|
|
2644
|
-
|
|
2645
|
-
// Read launcher.md
|
|
2646
|
-
const launcherSpec = fs.readFileSync(launcherFile, 'utf8');
|
|
2647
|
-
|
|
2648
|
-
// Reference TODO.md (agents read on-demand, legacy TASK_CONTEXTS.md supported)
|
|
2649
|
-
const todoFile = path.join(targetDir, 'TODO.md');
|
|
2650
|
-
const legacyTaskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
|
|
2651
|
-
|
|
2652
|
-
// Reference MAP.md (agents read on-demand)
|
|
2653
|
-
const mapFile = path.join(targetDir, 'MAP.md');
|
|
2654
|
-
const mapPath = fs.existsSync(mapFile) ? path.relative(process.cwd(), mapFile) : null;
|
|
2655
|
-
|
|
2656
|
-
// Reference journal (agents read on-demand)
|
|
2657
|
-
const { logFile, dateFormatted } = getLogPath();
|
|
2658
|
-
let journalPath = '';
|
|
2659
|
-
if (fs.existsSync(logFile)) {
|
|
2660
|
-
journalPath = path.relative(process.cwd(), logFile);
|
|
2661
|
-
}
|
|
2662
|
-
|
|
2663
|
-
console.log('');
|
|
2664
|
-
console.log('┌─────────────────────────────────────────────────────────────┐');
|
|
2665
|
-
console.log('│ Atris Launch — Launcher Agent Activated │');
|
|
2666
|
-
console.log('└─────────────────────────────────────────────────────────────┘');
|
|
2667
|
-
console.log('');
|
|
2668
|
-
console.log('📋 AGENT SPEC:');
|
|
2669
|
-
console.log('─────────────────────────────────────────────────────────────');
|
|
2670
|
-
console.log(launcherSpec);
|
|
2671
|
-
console.log('');
|
|
2672
|
-
console.log('─────────────────────────────────────────────────────────────');
|
|
2673
|
-
console.log('');
|
|
2674
|
-
const taskFilePath = fs.existsSync(todoFile)
|
|
2675
|
-
? todoFile
|
|
2676
|
-
: (fs.existsSync(legacyTaskContextsFile) ? legacyTaskContextsFile : null);
|
|
2677
|
-
const taskContextsPath = taskFilePath ? path.relative(process.cwd(), taskFilePath) : null;
|
|
2678
|
-
console.log('📝 TODO.md: ' + (taskContextsPath || 'Not found'));
|
|
2679
|
-
console.log(' Read for completed tasks context (usually small, or reference path if large).');
|
|
2680
|
-
console.log('');
|
|
2681
|
-
console.log('─────────────────────────────────────────────────────────────');
|
|
2682
|
-
console.log('');
|
|
2683
|
-
console.log('🗺️ MAP.md: ' + (mapPath || 'Not found'));
|
|
2684
|
-
console.log(' Read this file for file:line references when navigating the codebase.');
|
|
2685
|
-
console.log('');
|
|
2686
|
-
console.log('─────────────────────────────────────────────────────────────');
|
|
2687
|
-
console.log('');
|
|
2688
|
-
console.log('📅 JOURNAL: ' + (journalPath || 'Not found'));
|
|
2689
|
-
console.log(' Read for recent completions and context.');
|
|
2690
|
-
console.log('');
|
|
2691
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
2692
|
-
console.log('📋 INSTRUCTION PROMPT FOR YOUR CODING AGENT:');
|
|
2693
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
2694
|
-
console.log('');
|
|
2695
|
-
console.log('You are the Launcher. Ship it clean.');
|
|
2696
|
-
console.log('');
|
|
2697
|
-
console.log('⚠️ CRITICAL: Execute these steps NOW using terminal and file tools:');
|
|
2698
|
-
console.log('');
|
|
2699
|
-
// Detect if this is the atris package project or a user project
|
|
2700
|
-
const isAtrisPackage = fs.existsSync(path.join(process.cwd(), 'package.json')) &&
|
|
2701
|
-
fs.existsSync(path.join(process.cwd(), 'bin', 'atris.js')) &&
|
|
2702
|
-
fs.existsSync(path.join(process.cwd(), 'atris.md'));
|
|
2703
|
-
|
|
2704
|
-
console.log('Launch Workflow:');
|
|
2705
|
-
console.log(' 1. Document what was shipped (add Launch entry to journal Notes section)');
|
|
2706
|
-
console.log(' 2. Extract learnings (what worked? what would you do differently?)');
|
|
2707
|
-
console.log(' 3. Update MAP.md with new patterns/file locations');
|
|
2708
|
-
console.log(' 4. Update relevant docs (README, API docs, etc.)');
|
|
2709
|
-
console.log(' 5. Clean up (remove temp files, unused code, etc.)');
|
|
2710
|
-
if (isAtrisPackage) {
|
|
2711
|
-
console.log(' 6. [EXECUTE] Test locally (package development):');
|
|
2712
|
-
console.log(' - Run: npm link (link package for local testing)');
|
|
2713
|
-
console.log(' - Test: Create test project, run atris init to verify changes');
|
|
2714
|
-
console.log(' 7. [EXECUTE] Git commit + push:');
|
|
2715
|
-
console.log(' - Run: git add -A');
|
|
2716
|
-
console.log(' - Run: git commit -m "Descriptive message about what was shipped"');
|
|
2717
|
-
console.log(' - Run: git push origin master');
|
|
2718
|
-
console.log(' 8. [EXECUTE] Publish to npm (if ready for release):');
|
|
2719
|
-
console.log(' - Run: npm publish');
|
|
2720
|
-
console.log(' 9. Optional: Update changelog/blog (7 sentences max essay on what shipped)');
|
|
2721
|
-
console.log(' 10. Run: atris log sync (to sync journal to backend)');
|
|
2722
|
-
console.log(' 11. Celebrate! 🎉');
|
|
2723
|
-
} else {
|
|
2724
|
-
console.log(' 6. [EXECUTE] Git commit + push:');
|
|
2725
|
-
console.log(' - Run: git add -A');
|
|
2726
|
-
console.log(' - Run: git commit -m "Descriptive message about what was shipped"');
|
|
2727
|
-
console.log(' - Run: git push origin <your-branch>');
|
|
2728
|
-
console.log(' 7. Optional: Update changelog/blog (7 sentences max essay on what shipped)');
|
|
2729
|
-
console.log(' 8. Run: atris log sync (to sync journal to backend)');
|
|
2730
|
-
console.log(' 9. Celebrate! 🎉');
|
|
2731
|
-
}
|
|
2732
|
-
console.log('');
|
|
2733
|
-
console.log('DO NOT just describe these steps - actually execute the git commands!');
|
|
2734
|
-
console.log('');
|
|
2735
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
2736
|
-
console.log('');
|
|
2737
|
-
}
|
|
2738
|
-
|
|
2739
2663
|
function spawnClaudeCodeSession(url, token, body) {
|
|
2740
2664
|
return new Promise((resolve, reject) => {
|
|
2741
2665
|
const parsed = new URL(url);
|
package/commands/auth.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
const { loadCredentials, saveCredentials, deleteCredentials, getCredentialsPath, openBrowser, promptUser, displayAccountSummary } = require('../utils/auth');
|
|
1
|
+
const { loadCredentials, saveCredentials, deleteCredentials, getCredentialsPath, openBrowser, promptUser, displayAccountSummary, loadProfile, listProfiles, profileNameFromEmail } = require('../utils/auth');
|
|
2
2
|
const { getAppBaseUrl, apiRequestJson } = require('../utils/api');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
3
5
|
|
|
4
6
|
async function loginAtris(options = {}) {
|
|
5
7
|
// Support: atris login --token <token> --force
|
|
@@ -151,4 +153,97 @@ async function whoamiAtris() {
|
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
155
|
|
|
154
|
-
|
|
156
|
+
async function switchAccount() {
|
|
157
|
+
const args = process.argv.slice(3);
|
|
158
|
+
const targetName = args.filter(a => !a.startsWith('-'))[0];
|
|
159
|
+
|
|
160
|
+
const profiles = listProfiles();
|
|
161
|
+
if (profiles.length === 0) {
|
|
162
|
+
console.log('No saved profiles. Log in with different accounts to create profiles.');
|
|
163
|
+
console.log('Profiles are auto-saved on login.');
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const current = loadCredentials();
|
|
168
|
+
const currentName = profileNameFromEmail(current?.email);
|
|
169
|
+
|
|
170
|
+
if (!targetName) {
|
|
171
|
+
// Interactive: show list and let user pick
|
|
172
|
+
console.log('Switch account:\n');
|
|
173
|
+
profiles.forEach((name, i) => {
|
|
174
|
+
const profile = loadProfile(name);
|
|
175
|
+
const email = profile?.email || 'unknown';
|
|
176
|
+
const marker = name === currentName ? ' (active)' : '';
|
|
177
|
+
console.log(` ${i + 1}. ${name} — ${email}${marker}`);
|
|
178
|
+
});
|
|
179
|
+
console.log(` ${profiles.length + 1}. Cancel`);
|
|
180
|
+
|
|
181
|
+
const choice = await promptUser(`\nEnter choice (1-${profiles.length + 1}): `);
|
|
182
|
+
const idx = parseInt(choice, 10) - 1;
|
|
183
|
+
|
|
184
|
+
if (isNaN(idx) || idx < 0 || idx >= profiles.length) {
|
|
185
|
+
console.log('Cancelled.');
|
|
186
|
+
process.exit(0);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const chosen = profiles[idx];
|
|
190
|
+
return activateProfile(chosen, currentName);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Direct: atris switch <name>
|
|
194
|
+
// Fuzzy match: allow partial names
|
|
195
|
+
const exact = profiles.find(p => p === targetName);
|
|
196
|
+
const partial = !exact ? profiles.find(p => p.startsWith(targetName)) : null;
|
|
197
|
+
const match = exact || partial;
|
|
198
|
+
|
|
199
|
+
if (!match) {
|
|
200
|
+
console.error(`Profile "${targetName}" not found.`);
|
|
201
|
+
console.log(`Available: ${profiles.join(', ')}`);
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return activateProfile(match, currentName);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function activateProfile(name, currentName) {
|
|
209
|
+
if (name === currentName) {
|
|
210
|
+
console.log(`Already on "${name}".`);
|
|
211
|
+
process.exit(0);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const profile = loadProfile(name);
|
|
215
|
+
if (!profile || !profile.token) {
|
|
216
|
+
console.error(`Profile "${name}" is corrupted. Login again to fix it.`);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Copy profile to credentials.json
|
|
221
|
+
const credentialsPath = getCredentialsPath();
|
|
222
|
+
fs.writeFileSync(credentialsPath, JSON.stringify(profile, null, 2));
|
|
223
|
+
try { fs.chmodSync(credentialsPath, 0o600); } catch {}
|
|
224
|
+
|
|
225
|
+
console.log(`Switched to "${name}" (${profile.email || 'unknown'})`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function listAccountsCmd() {
|
|
229
|
+
const profiles = listProfiles();
|
|
230
|
+
if (profiles.length === 0) {
|
|
231
|
+
console.log('No saved profiles. Profiles are auto-saved on login.');
|
|
232
|
+
process.exit(0);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const current = loadCredentials();
|
|
236
|
+
const currentName = profileNameFromEmail(current?.email);
|
|
237
|
+
|
|
238
|
+
console.log('Accounts:\n');
|
|
239
|
+
profiles.forEach(name => {
|
|
240
|
+
const profile = loadProfile(name);
|
|
241
|
+
const email = profile?.email || 'unknown';
|
|
242
|
+
const marker = name === currentName ? ' *' : '';
|
|
243
|
+
console.log(` ${name} — ${email}${marker}`);
|
|
244
|
+
});
|
|
245
|
+
console.log('\n* = active');
|
|
246
|
+
console.log('\nSwitch: atris switch <name>');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
module.exports = { loginAtris, logoutAtris, whoamiAtris, switchAccount, listAccountsCmd };
|