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.
- package/AGENT.md +35 -0
- package/AGENTS.md +46 -0
- package/GETTING_STARTED.md +2 -2
- package/PERSONA.md +5 -1
- package/README.md +16 -8
- package/atris/AGENTS.md +25 -0
- package/atris/GEMINI.md +8 -0
- package/atris/GETTING_STARTED.md +2 -2
- package/atris/atris.md +4 -3
- package/atris/features/README.md +41 -15
- package/atris/policies/LESSONS.md +1 -0
- package/atris/skills/README.md +45 -14
- package/atris/skills/atris/SKILL.md +7 -0
- package/atris/skills/autopilot/SKILL.md +9 -4
- package/atris/skills/autopilot/atris-autopilot.md +71 -0
- package/atris/skills/autopilot/hooks/stop-hook.sh +79 -0
- package/atris/skills/backend/SKILL.md +6 -1
- package/atris/skills/calendar/SKILL.md +301 -0
- package/atris/skills/clawhub/atris/SKILL.md +121 -0
- package/atris/skills/copy-editor/SKILL.md +470 -0
- package/atris/skills/design/SKILL.md +5 -1
- package/atris/skills/drive/SKILL.md +333 -0
- package/atris/skills/email-agent/SKILL.md +376 -0
- package/atris/skills/memory/SKILL.md +8 -0
- package/atris/skills/meta/SKILL.md +4 -0
- package/atris/skills/skill-improver/SKILL.md +147 -0
- package/atris/skills/writing/SKILL.md +3 -0
- package/atris/team/brainstormer.md +1 -0
- package/atris/team/executor.md +27 -0
- package/atris/team/launcher.md +1 -0
- package/atris/team/navigator.md +44 -5
- package/atris/team/validator.md +44 -3
- package/atris.md +37 -1
- package/bin/atris.js +58 -5
- package/commands/auth.js +24 -4
- package/commands/init.js +140 -17
- package/commands/integrations.js +330 -0
- package/commands/skill.js +496 -0
- package/commands/status.js +9 -1
- package/commands/sync.js +64 -19
- package/commands/workflow.js +7 -0
- package/package.json +4 -2
- package/utils/auth.js +33 -0
- package/commands/stubs.txt +0 -10
package/commands/init.js
CHANGED
|
@@ -46,10 +46,13 @@ function detectProjectContext(projectRoot = process.cwd()) {
|
|
|
46
46
|
try {
|
|
47
47
|
const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
|
|
48
48
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
49
|
+
// Check meta-frameworks first (they include base frameworks as deps)
|
|
50
|
+
if (deps.next) return 'next';
|
|
51
|
+
if (deps.nuxt) return 'nuxt';
|
|
52
|
+
if (deps.angular || deps['@angular/core']) return 'angular';
|
|
53
|
+
// Then base frameworks
|
|
49
54
|
if (deps.react || deps['react-dom']) return 'react';
|
|
50
55
|
if (deps.vue) return 'vue';
|
|
51
|
-
if (deps.angular || deps['@angular/core']) return 'angular';
|
|
52
|
-
if (deps.next) return 'next';
|
|
53
56
|
if (deps['express']) return 'express';
|
|
54
57
|
if (deps['fastify']) return 'fastify';
|
|
55
58
|
return 'nodejs';
|
|
@@ -229,7 +232,8 @@ ${profile.hasCode ? `**Validation:** Run \`${profile.testCommand}\` to verify ch
|
|
|
229
232
|
|
|
230
233
|
function initAtris() {
|
|
231
234
|
const targetDir = path.join(process.cwd(), 'atris');
|
|
232
|
-
const
|
|
235
|
+
const teamDir = path.join(targetDir, 'team');
|
|
236
|
+
const legacyAgentTeamDir = path.join(targetDir, 'agent_team');
|
|
233
237
|
const sourceFile = path.join(__dirname, '..', 'atris.md');
|
|
234
238
|
const targetFile = path.join(targetDir, 'atris.md');
|
|
235
239
|
|
|
@@ -240,8 +244,39 @@ function initAtris() {
|
|
|
240
244
|
console.log('✓ atris/ folder already exists');
|
|
241
245
|
}
|
|
242
246
|
|
|
243
|
-
|
|
244
|
-
|
|
247
|
+
// MIGRATION: agent_team/ → team/ (v2.0.x → v2.1.0)
|
|
248
|
+
if (fs.existsSync(legacyAgentTeamDir)) {
|
|
249
|
+
console.log('');
|
|
250
|
+
console.log('📦 Migrating agent_team/ → team/ (v2.1.0 update)');
|
|
251
|
+
|
|
252
|
+
// Create team/ if it doesn't exist
|
|
253
|
+
if (!fs.existsSync(teamDir)) {
|
|
254
|
+
fs.mkdirSync(teamDir, { recursive: true });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Copy any custom files from agent_team/ to team/
|
|
258
|
+
const legacyFiles = fs.readdirSync(legacyAgentTeamDir);
|
|
259
|
+
for (const file of legacyFiles) {
|
|
260
|
+
const srcPath = path.join(legacyAgentTeamDir, file);
|
|
261
|
+
const destPath = path.join(teamDir, file);
|
|
262
|
+
|
|
263
|
+
// Only copy if destination doesn't exist (preserve any customizations)
|
|
264
|
+
if (!fs.existsSync(destPath)) {
|
|
265
|
+
if (fs.statSync(srcPath).isFile()) {
|
|
266
|
+
fs.copyFileSync(srcPath, destPath);
|
|
267
|
+
console.log(` ✓ Migrated ${file}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Remove old agent_team/ folder
|
|
273
|
+
fs.rmSync(legacyAgentTeamDir, { recursive: true, force: true });
|
|
274
|
+
console.log(' ✓ Removed old agent_team/ folder');
|
|
275
|
+
console.log('');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
if (!fs.existsSync(teamDir)) {
|
|
279
|
+
fs.mkdirSync(teamDir, { recursive: true });
|
|
245
280
|
console.log('✓ Created atris/team/ folder');
|
|
246
281
|
}
|
|
247
282
|
|
|
@@ -249,11 +284,11 @@ function initAtris() {
|
|
|
249
284
|
const personaFile = path.join(targetDir, 'PERSONA.md');
|
|
250
285
|
const mapFile = path.join(targetDir, 'MAP.md');
|
|
251
286
|
const todoFile = path.join(targetDir, 'TODO.md');
|
|
252
|
-
const navigatorFile = path.join(
|
|
253
|
-
const executorFile = path.join(
|
|
254
|
-
const validatorFile = path.join(
|
|
255
|
-
const launcherFile = path.join(
|
|
256
|
-
const brainstormerFile = path.join(
|
|
287
|
+
const navigatorFile = path.join(teamDir, 'navigator.md');
|
|
288
|
+
const executorFile = path.join(teamDir, 'executor.md');
|
|
289
|
+
const validatorFile = path.join(teamDir, 'validator.md');
|
|
290
|
+
const launcherFile = path.join(teamDir, 'launcher.md');
|
|
291
|
+
const brainstormerFile = path.join(teamDir, 'brainstormer.md');
|
|
257
292
|
|
|
258
293
|
const gettingStartedSource = path.join(__dirname, '..', 'GETTING_STARTED.md');
|
|
259
294
|
const personaSource = path.join(__dirname, '..', 'PERSONA.md');
|
|
@@ -338,6 +373,19 @@ function initAtris() {
|
|
|
338
373
|
console.log('✓ Created TODO.md placeholder');
|
|
339
374
|
}
|
|
340
375
|
|
|
376
|
+
// Create lessons.md (feedback loop for learning across features)
|
|
377
|
+
const lessonsFile = path.join(targetDir, 'lessons.md');
|
|
378
|
+
if (!fs.existsSync(lessonsFile)) {
|
|
379
|
+
fs.writeFileSync(lessonsFile, `# lessons.md — What We Learned
|
|
380
|
+
|
|
381
|
+
> Append-only. One line per lesson. Harvested by validator after every feature.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
`);
|
|
386
|
+
console.log('✓ Created lessons.md');
|
|
387
|
+
}
|
|
388
|
+
|
|
341
389
|
// Create logs directory and today's journal with bootstrap tasks
|
|
342
390
|
const logsDir = path.join(targetDir, 'logs');
|
|
343
391
|
const yearDir = path.join(logsDir, new Date().getFullYear().toString());
|
|
@@ -465,7 +513,7 @@ function initAtris() {
|
|
|
465
513
|
console.log(`✓ Generated .project-profile.json (detected: ${profile.type}${profile.framework !== 'none' ? '/' + profile.framework : ''})`);
|
|
466
514
|
|
|
467
515
|
// Inject project patterns into agent specs
|
|
468
|
-
injectProjectPatterns(
|
|
516
|
+
injectProjectPatterns(teamDir, profile);
|
|
469
517
|
console.log('✓ Injected project patterns into team specs');
|
|
470
518
|
|
|
471
519
|
// Create agent instruction files for different tools
|
|
@@ -560,6 +608,62 @@ Rules: 3-4 sentences max, ASCII visuals, check MAP.md first.`;
|
|
|
560
608
|
console.log('✓ Created .claude/commands/atris.md (for Claude Code)');
|
|
561
609
|
}
|
|
562
610
|
|
|
611
|
+
// .claude/commands/atris-autopilot.md for autonomous loops
|
|
612
|
+
const autopilotCommandFile = path.join(claudeCommandsDir, 'atris-autopilot.md');
|
|
613
|
+
if (!fs.existsSync(autopilotCommandFile)) {
|
|
614
|
+
fs.mkdirSync(claudeCommandsDir, { recursive: true });
|
|
615
|
+
const autopilotCommand = `---
|
|
616
|
+
description: PRD-driven autonomous execution - give it a task, it loops until done
|
|
617
|
+
arguments:
|
|
618
|
+
- name: task
|
|
619
|
+
description: What to build (e.g., "Add dark mode toggle")
|
|
620
|
+
required: true
|
|
621
|
+
- name: max-iterations
|
|
622
|
+
description: Max loops before stopping (default 10)
|
|
623
|
+
required: false
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
# Atris Autopilot
|
|
627
|
+
|
|
628
|
+
Autonomous mode. Loop until task complete or max iterations.
|
|
629
|
+
|
|
630
|
+
## Setup State
|
|
631
|
+
|
|
632
|
+
\`\`\`bash
|
|
633
|
+
mkdir -p .claude
|
|
634
|
+
cat > .claude/atris-autopilot.state.md << 'STATEEOF'
|
|
635
|
+
---
|
|
636
|
+
iteration: 1
|
|
637
|
+
max_iterations: \${2:-10}
|
|
638
|
+
completion_promise: <promise>COMPLETE</promise>
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
$1
|
|
642
|
+
STATEEOF
|
|
643
|
+
\`\`\`
|
|
644
|
+
|
|
645
|
+
## Task: $1
|
|
646
|
+
|
|
647
|
+
## Process (each iteration)
|
|
648
|
+
|
|
649
|
+
1. **PLAN** — Read MAP.md, identify ONE thing to do
|
|
650
|
+
2. **DO** — Implement it, commit
|
|
651
|
+
3. **REVIEW** — Check acceptance criteria
|
|
652
|
+
|
|
653
|
+
## Rules
|
|
654
|
+
|
|
655
|
+
- ONE thing per iteration
|
|
656
|
+
- Check MAP.md before touching code
|
|
657
|
+
- Search before assuming not implemented
|
|
658
|
+
- When done: \`<promise>COMPLETE</promise>\`
|
|
659
|
+
|
|
660
|
+
## Start
|
|
661
|
+
|
|
662
|
+
Read atris/MAP.md. Begin iteration 1.`;
|
|
663
|
+
fs.writeFileSync(autopilotCommandFile, autopilotCommand);
|
|
664
|
+
console.log('✓ Created .claude/commands/atris-autopilot.md (autonomous loops)');
|
|
665
|
+
}
|
|
666
|
+
|
|
563
667
|
// Copy skills from package to atris/skills/ and symlink to .claude/skills/
|
|
564
668
|
const skillsSourceDir = path.join(__dirname, '..', 'atris', 'skills');
|
|
565
669
|
const skillsTargetDir = path.join(targetDir, 'skills');
|
|
@@ -582,12 +686,21 @@ Rules: 3-4 sentences max, ASCII visuals, check MAP.md first.`;
|
|
|
582
686
|
const destSkillDir = path.join(skillsTargetDir, skill);
|
|
583
687
|
|
|
584
688
|
if (!fs.existsSync(destSkillDir)) {
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
689
|
+
// Recursive copy function for skills (handles subdirs like hooks/)
|
|
690
|
+
const copyRecursive = (src, dest) => {
|
|
691
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
692
|
+
const entries = fs.readdirSync(src);
|
|
693
|
+
for (const entry of entries) {
|
|
694
|
+
const srcPath = path.join(src, entry);
|
|
695
|
+
const destPath = path.join(dest, entry);
|
|
696
|
+
if (fs.statSync(srcPath).isDirectory()) {
|
|
697
|
+
copyRecursive(srcPath, destPath);
|
|
698
|
+
} else {
|
|
699
|
+
fs.copyFileSync(srcPath, destPath);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
copyRecursive(srcSkillDir, destSkillDir);
|
|
591
704
|
console.log(`✓ Copied skill: ${skill}`);
|
|
592
705
|
}
|
|
593
706
|
}
|
|
@@ -659,6 +772,16 @@ Rules: 3-4 sentences max, ASCII visuals, check MAP.md first.`;
|
|
|
659
772
|
}
|
|
660
773
|
]
|
|
661
774
|
}
|
|
775
|
+
],
|
|
776
|
+
Stop: [
|
|
777
|
+
{
|
|
778
|
+
hooks: [
|
|
779
|
+
{
|
|
780
|
+
type: "command",
|
|
781
|
+
command: "atris/skills/autopilot/hooks/stop-hook.sh"
|
|
782
|
+
}
|
|
783
|
+
]
|
|
784
|
+
}
|
|
662
785
|
]
|
|
663
786
|
}
|
|
664
787
|
};
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration commands for Atris CLI
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* atris gmail inbox - List recent emails
|
|
6
|
+
* atris gmail read <id> - Read specific email
|
|
7
|
+
* atris calendar today - Show today's events
|
|
8
|
+
* atris calendar week - Show this week's events
|
|
9
|
+
* atris twitter post - Post a tweet (interactive)
|
|
10
|
+
* atris slack channels - List Slack channels
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { loadCredentials } = require('../utils/auth');
|
|
14
|
+
const { apiRequestJson } = require('../utils/api');
|
|
15
|
+
|
|
16
|
+
async function getAuthToken() {
|
|
17
|
+
const creds = loadCredentials();
|
|
18
|
+
if (!creds || !creds.token) {
|
|
19
|
+
console.error('Not logged in. Run: atris login');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
return creds.token;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// GMAIL
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
async function gmailInbox(options = {}) {
|
|
30
|
+
const token = await getAuthToken();
|
|
31
|
+
const limit = options.limit || 10;
|
|
32
|
+
|
|
33
|
+
console.log('📬 Fetching inbox...\n');
|
|
34
|
+
|
|
35
|
+
const result = await apiRequestJson(`/integrations/gmail/messages?max_results=${limit}`, {
|
|
36
|
+
method: 'GET',
|
|
37
|
+
token,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!result.ok) {
|
|
41
|
+
if (result.status === 401) {
|
|
42
|
+
console.error('Gmail not connected. Connect at: https://atris.ai/dashboard/settings');
|
|
43
|
+
} else {
|
|
44
|
+
console.error(`Error: ${result.error || 'Failed to fetch inbox'}`);
|
|
45
|
+
}
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const messages = result.data?.messages || result.data || [];
|
|
50
|
+
|
|
51
|
+
if (messages.length === 0) {
|
|
52
|
+
console.log('No messages found.');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.log(`Found ${messages.length} messages:\n`);
|
|
57
|
+
console.log('─'.repeat(60));
|
|
58
|
+
|
|
59
|
+
for (const msg of messages) {
|
|
60
|
+
const from = msg.from || msg.sender || 'Unknown';
|
|
61
|
+
const subject = msg.subject || '(no subject)';
|
|
62
|
+
const date = msg.date || msg.received_at || '';
|
|
63
|
+
const id = msg.id || msg.message_id || '';
|
|
64
|
+
|
|
65
|
+
console.log(`From: ${from}`);
|
|
66
|
+
console.log(`Subj: ${subject}`);
|
|
67
|
+
console.log(`Date: ${date}`);
|
|
68
|
+
console.log(`ID: ${id}`);
|
|
69
|
+
console.log('─'.repeat(60));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function gmailRead(messageId) {
|
|
74
|
+
if (!messageId) {
|
|
75
|
+
console.error('Usage: atris gmail read <message_id>');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const token = await getAuthToken();
|
|
80
|
+
|
|
81
|
+
console.log('📧 Fetching message...\n');
|
|
82
|
+
|
|
83
|
+
const result = await apiRequestJson(`/integrations/gmail/messages/${messageId}`, {
|
|
84
|
+
method: 'GET',
|
|
85
|
+
token,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (!result.ok) {
|
|
89
|
+
console.error(`Error: ${result.error || 'Failed to fetch message'}`);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const msg = result.data;
|
|
94
|
+
|
|
95
|
+
console.log('─'.repeat(60));
|
|
96
|
+
console.log(`From: ${msg.from || 'Unknown'}`);
|
|
97
|
+
console.log(`To: ${msg.to || 'Unknown'}`);
|
|
98
|
+
console.log(`Subject: ${msg.subject || '(no subject)'}`);
|
|
99
|
+
console.log(`Date: ${msg.date || ''}`);
|
|
100
|
+
console.log('─'.repeat(60));
|
|
101
|
+
console.log('');
|
|
102
|
+
console.log(msg.body || msg.snippet || '(no body)');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function gmailCommand(subcommand, ...args) {
|
|
106
|
+
switch (subcommand) {
|
|
107
|
+
case 'inbox':
|
|
108
|
+
case 'list':
|
|
109
|
+
await gmailInbox();
|
|
110
|
+
break;
|
|
111
|
+
case 'read':
|
|
112
|
+
await gmailRead(args[0]);
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
console.log('Gmail commands:');
|
|
116
|
+
console.log(' atris gmail inbox - List recent emails');
|
|
117
|
+
console.log(' atris gmail read <id> - Read specific email');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ============================================================================
|
|
122
|
+
// CALENDAR
|
|
123
|
+
// ============================================================================
|
|
124
|
+
|
|
125
|
+
async function calendarToday() {
|
|
126
|
+
const token = await getAuthToken();
|
|
127
|
+
|
|
128
|
+
console.log('📅 Today\'s events:\n');
|
|
129
|
+
|
|
130
|
+
const result = await apiRequestJson('/integrations/google-calendar/events/today', {
|
|
131
|
+
method: 'GET',
|
|
132
|
+
token,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (!result.ok) {
|
|
136
|
+
if (result.status === 401) {
|
|
137
|
+
console.error('Calendar not connected. Connect at: https://atris.ai/dashboard/settings');
|
|
138
|
+
} else {
|
|
139
|
+
console.error(`Error: ${result.error || 'Failed to fetch events'}`);
|
|
140
|
+
}
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const events = result.data?.events || result.data || [];
|
|
145
|
+
|
|
146
|
+
if (events.length === 0) {
|
|
147
|
+
console.log('No events today. 🎉');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.log('─'.repeat(50));
|
|
152
|
+
|
|
153
|
+
for (const event of events) {
|
|
154
|
+
const start = event.start?.dateTime || event.start?.date || '';
|
|
155
|
+
const time = start ? new Date(start).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' }) : 'All day';
|
|
156
|
+
const title = event.summary || '(no title)';
|
|
157
|
+
|
|
158
|
+
console.log(`${time} ${title}`);
|
|
159
|
+
if (event.location) {
|
|
160
|
+
console.log(` 📍 ${event.location}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log('─'.repeat(50));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function calendarCommand(subcommand, ...args) {
|
|
168
|
+
switch (subcommand) {
|
|
169
|
+
case 'today':
|
|
170
|
+
await calendarToday();
|
|
171
|
+
break;
|
|
172
|
+
case 'week':
|
|
173
|
+
console.log('Week view coming soon...');
|
|
174
|
+
break;
|
|
175
|
+
default:
|
|
176
|
+
console.log('Calendar commands:');
|
|
177
|
+
console.log(' atris calendar today - Show today\'s events');
|
|
178
|
+
console.log(' atris calendar week - Show this week\'s events');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ============================================================================
|
|
183
|
+
// TWITTER
|
|
184
|
+
// ============================================================================
|
|
185
|
+
|
|
186
|
+
async function twitterPost(text) {
|
|
187
|
+
const token = await getAuthToken();
|
|
188
|
+
|
|
189
|
+
if (!text) {
|
|
190
|
+
const readline = require('readline');
|
|
191
|
+
const rl = readline.createInterface({
|
|
192
|
+
input: process.stdin,
|
|
193
|
+
output: process.stdout
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
text = await new Promise((resolve) => {
|
|
197
|
+
rl.question('Tweet: ', (answer) => {
|
|
198
|
+
rl.close();
|
|
199
|
+
resolve(answer.trim());
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (!text) {
|
|
205
|
+
console.error('Tweet text is required');
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.log('\n🐦 Posting tweet...');
|
|
210
|
+
|
|
211
|
+
const result = await apiRequestJson('/integrations/twitter/tweet', {
|
|
212
|
+
method: 'POST',
|
|
213
|
+
token,
|
|
214
|
+
body: { text },
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
if (!result.ok) {
|
|
218
|
+
if (result.status === 401) {
|
|
219
|
+
console.error('Twitter not connected. Connect at: https://atris.ai/dashboard/settings');
|
|
220
|
+
} else {
|
|
221
|
+
console.error(`Error: ${result.error || 'Failed to post tweet'}`);
|
|
222
|
+
}
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
console.log('✓ Tweet posted!');
|
|
227
|
+
if (result.data?.id) {
|
|
228
|
+
console.log(` https://twitter.com/i/status/${result.data.id}`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function twitterCommand(subcommand, ...args) {
|
|
233
|
+
switch (subcommand) {
|
|
234
|
+
case 'post':
|
|
235
|
+
case 'tweet':
|
|
236
|
+
await twitterPost(args.join(' '));
|
|
237
|
+
break;
|
|
238
|
+
default:
|
|
239
|
+
console.log('Twitter commands:');
|
|
240
|
+
console.log(' atris twitter post [text] - Post a tweet');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ============================================================================
|
|
245
|
+
// SLACK
|
|
246
|
+
// ============================================================================
|
|
247
|
+
|
|
248
|
+
async function slackChannels() {
|
|
249
|
+
const token = await getAuthToken();
|
|
250
|
+
|
|
251
|
+
console.log('💬 Fetching Slack channels...\n');
|
|
252
|
+
|
|
253
|
+
const result = await apiRequestJson('/integrations/slack/channels', {
|
|
254
|
+
method: 'GET',
|
|
255
|
+
token,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
if (!result.ok) {
|
|
259
|
+
if (result.status === 401) {
|
|
260
|
+
console.error('Slack not connected. Connect at: https://atris.ai/dashboard/settings');
|
|
261
|
+
} else {
|
|
262
|
+
console.error(`Error: ${result.error || 'Failed to fetch channels'}`);
|
|
263
|
+
}
|
|
264
|
+
process.exit(1);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const channels = result.data?.channels || result.data || [];
|
|
268
|
+
|
|
269
|
+
if (channels.length === 0) {
|
|
270
|
+
console.log('No channels found.');
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
console.log('Channels:');
|
|
275
|
+
for (const ch of channels) {
|
|
276
|
+
const name = ch.name || ch.id;
|
|
277
|
+
const priv = ch.is_private ? '🔒' : '#';
|
|
278
|
+
console.log(` ${priv} ${name}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function slackCommand(subcommand, ...args) {
|
|
283
|
+
switch (subcommand) {
|
|
284
|
+
case 'channels':
|
|
285
|
+
case 'list':
|
|
286
|
+
await slackChannels();
|
|
287
|
+
break;
|
|
288
|
+
default:
|
|
289
|
+
console.log('Slack commands:');
|
|
290
|
+
console.log(' atris slack channels - List Slack channels');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ============================================================================
|
|
295
|
+
// STATUS
|
|
296
|
+
// ============================================================================
|
|
297
|
+
|
|
298
|
+
async function integrationsStatus() {
|
|
299
|
+
const token = await getAuthToken();
|
|
300
|
+
|
|
301
|
+
console.log('🔌 Integration Status:\n');
|
|
302
|
+
|
|
303
|
+
const integrations = ['gmail', 'google-calendar', 'slack', 'twitter', 'github'];
|
|
304
|
+
|
|
305
|
+
for (const name of integrations) {
|
|
306
|
+
try {
|
|
307
|
+
const result = await apiRequestJson(`/integrations/${name}/status`, {
|
|
308
|
+
method: 'GET',
|
|
309
|
+
token,
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const connected = result.ok && result.data?.connected;
|
|
313
|
+
const icon = connected ? '✅' : '❌';
|
|
314
|
+
const displayName = name.replace('google-', '').replace('-', ' ');
|
|
315
|
+
console.log(` ${icon} ${displayName}`);
|
|
316
|
+
} catch {
|
|
317
|
+
console.log(` ❓ ${name}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
console.log('\nConnect integrations at: https://atris.ai/dashboard/settings');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
module.exports = {
|
|
325
|
+
gmailCommand,
|
|
326
|
+
calendarCommand,
|
|
327
|
+
twitterCommand,
|
|
328
|
+
slackCommand,
|
|
329
|
+
integrationsStatus,
|
|
330
|
+
};
|