atris 2.0.21 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/atris.js CHANGED
@@ -164,6 +164,7 @@ function showHelp() {
164
164
  console.log('Setup:');
165
165
  console.log(' init - Initialize Atris in current project');
166
166
  console.log(' update - Update local files to latest version');
167
+ console.log(' upgrade - Install latest Atris from npm');
167
168
  console.log('');
168
169
  console.log('Core workflow:');
169
170
  console.log(' plan - Create build spec with visualization');
@@ -191,10 +192,17 @@ function showHelp() {
191
192
  console.log('Cloud & agents:');
192
193
  console.log(' agent - Select which Atris agent to use');
193
194
  console.log(' chat - Chat with the selected Atris agent');
194
- console.log(' login - Authenticate with Atris cloud (optional)');
195
+ console.log(' login - Authenticate (use --token <t> for non-interactive)');
195
196
  console.log(' logout - Remove credentials');
196
197
  console.log(' whoami - Show auth status');
197
198
  console.log('');
199
+ console.log('Integrations:');
200
+ console.log(' gmail - Email commands (inbox, read)');
201
+ console.log(' calendar - Calendar commands (today, week)');
202
+ console.log(' twitter - Twitter commands (post)');
203
+ console.log(' slack - Slack commands (channels)');
204
+ console.log(' integrations - Show integration status');
205
+ console.log('');
198
206
  console.log('Other:');
199
207
  console.log(' version - Show Atris version');
200
208
  console.log(' help - Show this help');
@@ -303,8 +311,9 @@ const { verifyAtris: verifyCmd } = require('../commands/verify');
303
311
 
304
312
  // Check if this is a known command or natural language input
305
313
  const knownCommands = ['init', 'log', 'status', 'analytics', 'visualize', 'brainstorm', 'autopilot', 'plan', 'do', 'review',
306
- 'activate', 'agent', 'chat', 'login', 'logout', 'whoami', 'update', 'version', 'help', 'next', 'atris',
307
- 'clean', 'verify', 'search'];
314
+ 'activate', 'agent', 'chat', 'login', 'logout', 'whoami', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
315
+ 'clean', 'verify', 'search',
316
+ 'gmail', 'calendar', 'twitter', 'slack', 'integrations'];
308
317
 
309
318
  // Check if command is an atris.md spec file - triggers welcome visualization
310
319
  function isSpecFile(cmd) {
@@ -616,6 +625,8 @@ if (command === 'init') {
616
625
  activateCmd();
617
626
  } else if (command === 'update') {
618
627
  syncCmd();
628
+ } else if (command === 'upgrade') {
629
+ upgradeAtris();
619
630
  } else if (command === 'chat') {
620
631
  chatAtris()
621
632
  .then(() => process.exit(0))
@@ -746,6 +757,39 @@ if (command === 'init') {
746
757
  } else if (command === 'search') {
747
758
  const keyword = process.argv.slice(3).join(' ');
748
759
  searchJournal(keyword);
760
+ } else if (command === 'gmail') {
761
+ const { gmailCommand } = require('../commands/integrations');
762
+ const subcommand = process.argv[3];
763
+ const args = process.argv.slice(4);
764
+ gmailCommand(subcommand, ...args)
765
+ .then(() => process.exit(0))
766
+ .catch((err) => { console.error(err.message); process.exit(1); });
767
+ } else if (command === 'calendar') {
768
+ const { calendarCommand } = require('../commands/integrations');
769
+ const subcommand = process.argv[3];
770
+ const args = process.argv.slice(4);
771
+ calendarCommand(subcommand, ...args)
772
+ .then(() => process.exit(0))
773
+ .catch((err) => { console.error(err.message); process.exit(1); });
774
+ } else if (command === 'twitter') {
775
+ const { twitterCommand } = require('../commands/integrations');
776
+ const subcommand = process.argv[3];
777
+ const args = process.argv.slice(4);
778
+ twitterCommand(subcommand, ...args)
779
+ .then(() => process.exit(0))
780
+ .catch((err) => { console.error(err.message); process.exit(1); });
781
+ } else if (command === 'slack') {
782
+ const { slackCommand } = require('../commands/integrations');
783
+ const subcommand = process.argv[3];
784
+ const args = process.argv.slice(4);
785
+ slackCommand(subcommand, ...args)
786
+ .then(() => process.exit(0))
787
+ .catch((err) => { console.error(err.message); process.exit(1); });
788
+ } else if (command === 'integrations') {
789
+ const { integrationsStatus } = require('../commands/integrations');
790
+ integrationsStatus()
791
+ .then(() => process.exit(0))
792
+ .catch((err) => { console.error(err.message); process.exit(1); });
749
793
  } else {
750
794
  console.log(`Unknown command: ${command}`);
751
795
  console.log('Run "atris help" to see available commands');
@@ -754,7 +798,7 @@ if (command === 'init') {
754
798
 
755
799
  function initAtris() {
756
800
  const targetDir = path.join(process.cwd(), 'atris');
757
- const agentTeamDir = path.join(targetDir, 'agent_team');
801
+ const teamDir = path.join(targetDir, 'team');
758
802
  const sourceFile = path.join(__dirname, '..', 'atris.md');
759
803
  const targetFile = path.join(targetDir, 'atris.md');
760
804
 
@@ -766,10 +810,10 @@ function initAtris() {
766
810
  console.log('✓ atris/ folder already exists');
767
811
  }
768
812
 
769
- // Create agent_team/ subfolder
770
- if (!fs.existsSync(agentTeamDir)) {
771
- fs.mkdirSync(agentTeamDir, { recursive: true });
772
- console.log('✓ Created atris/agent_team/ folder');
813
+ // Create team/ subfolder
814
+ if (!fs.existsSync(teamDir)) {
815
+ fs.mkdirSync(teamDir, { recursive: true });
816
+ console.log('✓ Created atris/team/ folder');
773
817
  }
774
818
 
775
819
  // Create policies/ subfolder
@@ -784,10 +828,10 @@ function initAtris() {
784
828
  const personaFile = path.join(targetDir, 'PERSONA.md');
785
829
  const mapFile = path.join(targetDir, 'MAP.md');
786
830
  const taskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
787
- const navigatorFile = path.join(agentTeamDir, 'navigator.md');
788
- const executorFile = path.join(agentTeamDir, 'executor.md');
789
- const validatorFile = path.join(agentTeamDir, 'validator.md');
790
- const launcherFile = path.join(agentTeamDir, 'launcher.md');
831
+ const navigatorFile = path.join(teamDir, 'navigator.md');
832
+ const executorFile = path.join(teamDir, 'executor.md');
833
+ const validatorFile = path.join(teamDir, 'validator.md');
834
+ const launcherFile = path.join(teamDir, 'launcher.md');
791
835
 
792
836
  const gettingStartedSource = path.join(__dirname, '..', 'GETTING_STARTED.md');
793
837
  const personaSource = path.join(__dirname, '..', 'PERSONA.md');
@@ -815,29 +859,29 @@ function initAtris() {
815
859
  }
816
860
 
817
861
  // Copy agent templates from package
818
- const navigatorSource = path.join(__dirname, '..', 'atris', 'agent_team', 'navigator.md');
819
- const executorSource = path.join(__dirname, '..', 'atris', 'agent_team', 'executor.md');
820
- const validatorSource = path.join(__dirname, '..', 'atris', 'agent_team', 'validator.md');
821
- const launcherSource = path.join(__dirname, '..', 'atris', 'agent_team', 'launcher.md');
862
+ const navigatorSource = path.join(__dirname, '..', 'atris', 'team', 'navigator.md');
863
+ const executorSource = path.join(__dirname, '..', 'atris', 'team', 'executor.md');
864
+ const validatorSource = path.join(__dirname, '..', 'atris', 'team', 'validator.md');
865
+ const launcherSource = path.join(__dirname, '..', 'atris', 'team', 'launcher.md');
822
866
 
823
867
  if (!fs.existsSync(navigatorFile) && fs.existsSync(navigatorSource)) {
824
868
  fs.copyFileSync(navigatorSource, navigatorFile);
825
- console.log('✓ Created agent_team/navigator.md');
869
+ console.log('✓ Created team/navigator.md');
826
870
  }
827
871
 
828
872
  if (!fs.existsSync(executorFile) && fs.existsSync(executorSource)) {
829
873
  fs.copyFileSync(executorSource, executorFile);
830
- console.log('✓ Created agent_team/executor.md');
874
+ console.log('✓ Created team/executor.md');
831
875
  }
832
876
 
833
877
  if (!fs.existsSync(validatorFile) && fs.existsSync(validatorSource)) {
834
878
  fs.copyFileSync(validatorSource, validatorFile);
835
- console.log('✓ Created agent_team/validator.md');
879
+ console.log('✓ Created team/validator.md');
836
880
  }
837
881
 
838
882
  if (!fs.existsSync(launcherFile) && fs.existsSync(launcherSource)) {
839
883
  fs.copyFileSync(launcherSource, launcherFile);
840
- console.log('✓ Created agent_team/launcher.md');
884
+ console.log('✓ Created team/launcher.md');
841
885
  }
842
886
 
843
887
  // Copy policies from package
@@ -859,7 +903,7 @@ function initAtris() {
859
903
  console.log(' ├── atris.md (AI agent instructions)');
860
904
  console.log(' ├── MAP.md (placeholder)');
861
905
  console.log(' ├── TASK_CONTEXTS.md (placeholder)');
862
- console.log(' ├── agent_team/');
906
+ console.log(' ├── team/');
863
907
  console.log(' │ ├── navigator.md');
864
908
  console.log(' │ ├── executor.md');
865
909
  console.log(' │ ├── validator.md');
@@ -878,7 +922,7 @@ function initAtris() {
878
922
 
879
923
  function syncAtris() {
880
924
  const targetDir = path.join(process.cwd(), 'atris');
881
- const agentTeamDir = path.join(targetDir, 'agent_team');
925
+ const teamDir = path.join(targetDir, 'team');
882
926
 
883
927
  // Check if atris/ folder exists
884
928
  if (!fs.existsSync(targetDir)) {
@@ -886,9 +930,9 @@ function syncAtris() {
886
930
  process.exit(1);
887
931
  }
888
932
 
889
- // Ensure agent_team folder exists
890
- if (!fs.existsSync(agentTeamDir)) {
891
- fs.mkdirSync(agentTeamDir, { recursive: true });
933
+ // Ensure team folder exists
934
+ if (!fs.existsSync(teamDir)) {
935
+ fs.mkdirSync(teamDir, { recursive: true });
892
936
  }
893
937
 
894
938
  // Ensure policies folder exists
@@ -904,10 +948,10 @@ function syncAtris() {
904
948
  { source: 'atrisDev.md', target: 'atrisDev.md' },
905
949
  { source: 'PERSONA.md', target: 'PERSONA.md' },
906
950
  { source: 'GETTING_STARTED.md', target: 'GETTING_STARTED.md' },
907
- { source: 'atris/agent_team/navigator.md', target: 'agent_team/navigator.md' },
908
- { source: 'atris/agent_team/executor.md', target: 'agent_team/executor.md' },
909
- { source: 'atris/agent_team/validator.md', target: 'agent_team/validator.md' },
910
- { source: 'atris/agent_team/launcher.md', target: 'agent_team/launcher.md' },
951
+ { source: 'atris/team/navigator.md', target: 'team/navigator.md' },
952
+ { source: 'atris/team/executor.md', target: 'team/executor.md' },
953
+ { source: 'atris/team/validator.md', target: 'team/validator.md' },
954
+ { source: 'atris/team/launcher.md', target: 'team/launcher.md' },
911
955
  { source: 'atris/policies/ANTISLOP.md', target: 'policies/ANTISLOP.md' }
912
956
  ];
913
957
 
@@ -944,6 +988,55 @@ function syncAtris() {
944
988
  }
945
989
  }
946
990
 
991
+ async function upgradeAtris() {
992
+ console.log('');
993
+ console.log('┌─────────────────────────────────────────────────────────────┐');
994
+ console.log('│ Atris Upgrade │');
995
+ console.log('└─────────────────────────────────────────────────────────────┘');
996
+ console.log('');
997
+ console.log(`Current version: ${CLI_VERSION}`);
998
+ console.log('');
999
+ console.log('Checking for updates...');
1000
+
1001
+ // Force check npm for latest version
1002
+ const updateInfo = await checkForUpdates(true);
1003
+
1004
+ if (!updateInfo || !updateInfo.needsUpdate) {
1005
+ console.log('');
1006
+ console.log('✓ You are on the latest version!');
1007
+ console.log('');
1008
+ return;
1009
+ }
1010
+
1011
+ console.log('');
1012
+ console.log(`📦 Update available: ${updateInfo.installed} → ${updateInfo.latest}`);
1013
+ console.log('');
1014
+ console.log('Installing update...');
1015
+ console.log('');
1016
+
1017
+ // Run npm update -g atris
1018
+ const result = spawnSync('npm', ['update', '-g', 'atris'], {
1019
+ stdio: 'inherit',
1020
+ shell: true
1021
+ });
1022
+
1023
+ if (result.status === 0) {
1024
+ console.log('');
1025
+ console.log('✓ Atris upgraded successfully!');
1026
+ console.log('');
1027
+ console.log('Run `atris update` in your projects to sync local files.');
1028
+ console.log('');
1029
+ } else {
1030
+ console.log('');
1031
+ console.log('✗ Upgrade failed. Try running manually:');
1032
+ console.log(' npm update -g atris');
1033
+ console.log('');
1034
+ console.log('If you see permission errors, try:');
1035
+ console.log(' sudo npm update -g atris');
1036
+ console.log('');
1037
+ }
1038
+ }
1039
+
947
1040
  // ============================================
948
1041
  // Log System
949
1042
  // ============================================
@@ -1884,7 +1977,7 @@ async function loginAtris() {
1884
1977
  process.exit(0);
1885
1978
  } else if (choice === '2') {
1886
1979
  console.log('\n📋 Manual Token Entry');
1887
- console.log('Get your token from: https://app.atris.ai/settings/api\n');
1980
+ console.log('Get your token from: https://atris.ai/auth/cli\n');
1888
1981
 
1889
1982
  const tokenInput = await promptUser('Paste your API token: ');
1890
1983
 
@@ -2209,7 +2302,7 @@ async function agentAtris() {
2209
2302
  const agents = result.data?.my_agents || [];
2210
2303
 
2211
2304
  if (agents.length === 0) {
2212
- console.log('No agents found. Create one at https://app.atris.ai');
2305
+ console.log('No agents found. Create one at https://atris.ai');
2213
2306
  process.exit(0);
2214
2307
  }
2215
2308
 
@@ -2528,7 +2621,7 @@ async function atrisDevEntry(userInput = null) {
2528
2621
 
2529
2622
  function launchAtris() {
2530
2623
  const targetDir = path.join(process.cwd(), 'atris');
2531
- const launcherFile = path.join(targetDir, 'agent_team', 'launcher.md');
2624
+ const launcherFile = path.join(targetDir, 'team', 'launcher.md');
2532
2625
 
2533
2626
  if (!fs.existsSync(launcherFile)) {
2534
2627
  console.log('✗ launcher.md not found. Run "atris init" first.');
package/commands/auth.js CHANGED
@@ -1,13 +1,33 @@
1
1
  const { loadCredentials, saveCredentials, deleteCredentials, getCredentialsPath, openBrowser, promptUser, displayAccountSummary } = require('../utils/auth');
2
2
  const { getAppBaseUrl, apiRequestJson } = require('../utils/api');
3
3
 
4
- async function loginAtris() {
5
-
4
+ async function loginAtris(options = {}) {
5
+ // Support: atris login --token <token> --force
6
+ const args = process.argv.slice(3);
7
+ const forceFlag = args.includes('--force') || args.includes('-f') || options.force;
8
+ const tokenIndex = args.indexOf('--token');
9
+ const directToken = tokenIndex !== -1 ? args[tokenIndex + 1] : options.token;
10
+
6
11
  try {
7
12
  console.log('🔐 Login to AtrisOS\n');
8
13
 
9
14
  const existing = loadCredentials();
10
- if (existing) {
15
+
16
+ // Direct token mode (non-interactive)
17
+ if (directToken) {
18
+ const trimmed = directToken.trim();
19
+ saveCredentials(trimmed, null, existing?.email || null, existing?.user_id || null, existing?.provider || 'manual');
20
+ console.log('Token saved. Validating…\n');
21
+ const summary = await displayAccountSummary(apiRequestJson);
22
+ if (summary.error) {
23
+ console.log('\n⚠️ Token saved, but validation failed.');
24
+ process.exit(1);
25
+ }
26
+ console.log('\n✓ Logged in successfully.');
27
+ process.exit(0);
28
+ }
29
+
30
+ if (existing && !forceFlag) {
11
31
  const label = existing.email || existing.user_id || 'unknown user';
12
32
  console.log(`Already logged in as: ${label}`);
13
33
  const confirm = await promptUser('Do you want to login again? (y/N): ');
@@ -70,7 +90,7 @@ async function loginAtris() {
70
90
  process.exit(0);
71
91
  } else if (choice === '2') {
72
92
  console.log('\n📋 Manual Token Entry');
73
- console.log('Get your token from: https://app.atris.ai/settings/api\n');
93
+ console.log('Get your token from: https://atris.ai/auth/cli\n');
74
94
 
75
95
  const tokenInput = await promptUser('Paste your API token: ');
76
96
 
@@ -354,10 +354,10 @@ function generateWorkflowFile(workflowFile, metadata) {
354
354
  const targetDir = path.join(process.cwd(), 'atris');
355
355
 
356
356
  // Load all context needed for agents
357
- const navigatorFile = path.join(targetDir, 'agent_team', 'navigator.md');
358
- const executorFile = path.join(targetDir, 'agent_team', 'executor.md');
359
- const validatorFile = path.join(targetDir, 'agent_team', 'validator.md');
360
- const launcherFile = path.join(targetDir, 'agent_team', 'launcher.md');
357
+ const navigatorFile = path.join(targetDir, 'team', 'navigator.md');
358
+ const executorFile = path.join(targetDir, 'team', 'executor.md');
359
+ const validatorFile = path.join(targetDir, 'team', 'validator.md');
360
+ const launcherFile = path.join(targetDir, 'team', 'launcher.md');
361
361
  const personaFile = path.join(targetDir, 'PERSONA.md');
362
362
  const mapFile = path.join(targetDir, 'MAP.md');
363
363
  const todoFile = path.join(targetDir, 'TODO.md');
@@ -471,10 +471,10 @@ async function autopilotAtris(initialIdea = null) {
471
471
  throw new Error('atris/ folder not found. Run "atris init" first.');
472
472
  }
473
473
 
474
- const navigatorFile = path.join(targetDir, 'agent_team', 'navigator.md');
475
- const executorFile = path.join(targetDir, 'agent_team', 'executor.md');
476
- const validatorFile = path.join(targetDir, 'agent_team', 'validator.md');
477
- const launcherFile = path.join(targetDir, 'agent_team', 'launcher.md');
474
+ const navigatorFile = path.join(targetDir, 'team', 'navigator.md');
475
+ const executorFile = path.join(targetDir, 'team', 'executor.md');
476
+ const validatorFile = path.join(targetDir, 'team', 'validator.md');
477
+ const launcherFile = path.join(targetDir, 'team', 'launcher.md');
478
478
 
479
479
  const missingSpecs = [];
480
480
  if (!fs.existsSync(navigatorFile)) missingSpecs.push('navigator.md');
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';
@@ -150,7 +153,7 @@ function detectProjectContext(projectRoot = process.cwd()) {
150
153
 
151
154
  /**
152
155
  * Inject project-specific patterns into agent specs
153
- * @param {string} agentTeamDir - Directory containing agent_team specs
156
+ * @param {string} agentTeamDir - Directory containing team specs
154
157
  * @param {Object} profile - Project profile from detectProjectContext()
155
158
  */
156
159
  function injectProjectPatterns(agentTeamDir, profile) {
@@ -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 agentTeamDir = path.join(targetDir, 'agent_team');
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,20 +244,51 @@ function initAtris() {
240
244
  console.log('✓ atris/ folder already exists');
241
245
  }
242
246
 
243
- if (!fs.existsSync(agentTeamDir)) {
244
- fs.mkdirSync(agentTeamDir, { recursive: true });
245
- console.log('✓ Created atris/agent_team/ folder');
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 });
280
+ console.log('✓ Created atris/team/ folder');
246
281
  }
247
282
 
248
283
  const gettingStartedFile = path.join(targetDir, 'GETTING_STARTED.md');
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(agentTeamDir, 'navigator.md');
253
- const executorFile = path.join(agentTeamDir, 'executor.md');
254
- const validatorFile = path.join(agentTeamDir, 'validator.md');
255
- const launcherFile = path.join(agentTeamDir, 'launcher.md');
256
- const brainstormerFile = path.join(agentTeamDir, 'brainstormer.md');
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');
@@ -427,35 +462,35 @@ function initAtris() {
427
462
  });
428
463
 
429
464
 
430
- const navigatorSource = path.join(__dirname, '..', 'atris', 'agent_team', 'navigator.md');
431
- const executorSource = path.join(__dirname, '..', 'atris', 'agent_team', 'executor.md');
432
- const validatorSource = path.join(__dirname, '..', 'atris', 'agent_team', 'validator.md');
433
- const launcherSource = path.join(__dirname, '..', 'atris', 'agent_team', 'launcher.md');
434
- const brainstormerSource = path.join(__dirname, '..', 'atris', 'agent_team', 'brainstormer.md');
465
+ const navigatorSource = path.join(__dirname, '..', 'atris', 'team', 'navigator.md');
466
+ const executorSource = path.join(__dirname, '..', 'atris', 'team', 'executor.md');
467
+ const validatorSource = path.join(__dirname, '..', 'atris', 'team', 'validator.md');
468
+ const launcherSource = path.join(__dirname, '..', 'atris', 'team', 'launcher.md');
469
+ const brainstormerSource = path.join(__dirname, '..', 'atris', 'team', 'brainstormer.md');
435
470
 
436
471
  if (!fs.existsSync(navigatorFile) && fs.existsSync(navigatorSource)) {
437
472
  fs.copyFileSync(navigatorSource, navigatorFile);
438
- console.log('✓ Created agent_team/navigator.md');
473
+ console.log('✓ Created team/navigator.md');
439
474
  }
440
475
 
441
476
  if (!fs.existsSync(executorFile) && fs.existsSync(executorSource)) {
442
477
  fs.copyFileSync(executorSource, executorFile);
443
- console.log('✓ Created agent_team/executor.md');
478
+ console.log('✓ Created team/executor.md');
444
479
  }
445
480
 
446
481
  if (!fs.existsSync(validatorFile) && fs.existsSync(validatorSource)) {
447
482
  fs.copyFileSync(validatorSource, validatorFile);
448
- console.log('✓ Created agent_team/validator.md');
483
+ console.log('✓ Created team/validator.md');
449
484
  }
450
485
 
451
486
  if (!fs.existsSync(launcherFile) && fs.existsSync(launcherSource)) {
452
487
  fs.copyFileSync(launcherSource, launcherFile);
453
- console.log('✓ Created agent_team/launcher.md');
488
+ console.log('✓ Created team/launcher.md');
454
489
  }
455
490
 
456
491
  if (!fs.existsSync(brainstormerFile) && fs.existsSync(brainstormerSource)) {
457
492
  fs.copyFileSync(brainstormerSource, brainstormerFile);
458
- console.log('✓ Created agent_team/brainstormer.md');
493
+ console.log('✓ Created team/brainstormer.md');
459
494
  }
460
495
 
461
496
  // Detect project context and generate profile
@@ -465,8 +500,8 @@ function initAtris() {
465
500
  console.log(`✓ Generated .project-profile.json (detected: ${profile.type}${profile.framework !== 'none' ? '/' + profile.framework : ''})`);
466
501
 
467
502
  // Inject project patterns into agent specs
468
- injectProjectPatterns(agentTeamDir, profile);
469
- console.log('✓ Injected project patterns into agent_team specs');
503
+ injectProjectPatterns(teamDir, profile);
504
+ console.log('✓ Injected project patterns into team specs');
470
505
 
471
506
  // Create agent instruction files for different tools
472
507
  const agentInstructions = `# AGENTS.md — Universal Agent Instructions
@@ -560,6 +595,62 @@ Rules: 3-4 sentences max, ASCII visuals, check MAP.md first.`;
560
595
  console.log('✓ Created .claude/commands/atris.md (for Claude Code)');
561
596
  }
562
597
 
598
+ // .claude/commands/atris-autopilot.md for autonomous loops
599
+ const autopilotCommandFile = path.join(claudeCommandsDir, 'atris-autopilot.md');
600
+ if (!fs.existsSync(autopilotCommandFile)) {
601
+ fs.mkdirSync(claudeCommandsDir, { recursive: true });
602
+ const autopilotCommand = `---
603
+ description: PRD-driven autonomous execution - give it a task, it loops until done
604
+ arguments:
605
+ - name: task
606
+ description: What to build (e.g., "Add dark mode toggle")
607
+ required: true
608
+ - name: max-iterations
609
+ description: Max loops before stopping (default 10)
610
+ required: false
611
+ ---
612
+
613
+ # Atris Autopilot
614
+
615
+ Autonomous mode. Loop until task complete or max iterations.
616
+
617
+ ## Setup State
618
+
619
+ \`\`\`bash
620
+ mkdir -p .claude
621
+ cat > .claude/atris-autopilot.state.md << 'STATEEOF'
622
+ ---
623
+ iteration: 1
624
+ max_iterations: \${2:-10}
625
+ completion_promise: <promise>COMPLETE</promise>
626
+ ---
627
+
628
+ $1
629
+ STATEEOF
630
+ \`\`\`
631
+
632
+ ## Task: $1
633
+
634
+ ## Process (each iteration)
635
+
636
+ 1. **PLAN** — Read MAP.md, identify ONE thing to do
637
+ 2. **DO** — Implement it, commit
638
+ 3. **REVIEW** — Check acceptance criteria
639
+
640
+ ## Rules
641
+
642
+ - ONE thing per iteration
643
+ - Check MAP.md before touching code
644
+ - Search before assuming not implemented
645
+ - When done: \`<promise>COMPLETE</promise>\`
646
+
647
+ ## Start
648
+
649
+ Read atris/MAP.md. Begin iteration 1.`;
650
+ fs.writeFileSync(autopilotCommandFile, autopilotCommand);
651
+ console.log('✓ Created .claude/commands/atris-autopilot.md (autonomous loops)');
652
+ }
653
+
563
654
  // Copy skills from package to atris/skills/ and symlink to .claude/skills/
564
655
  const skillsSourceDir = path.join(__dirname, '..', 'atris', 'skills');
565
656
  const skillsTargetDir = path.join(targetDir, 'skills');
@@ -582,12 +673,21 @@ Rules: 3-4 sentences max, ASCII visuals, check MAP.md first.`;
582
673
  const destSkillDir = path.join(skillsTargetDir, skill);
583
674
 
584
675
  if (!fs.existsSync(destSkillDir)) {
585
- fs.mkdirSync(destSkillDir, { recursive: true });
586
- // Copy all files in skill folder
587
- const files = fs.readdirSync(srcSkillDir);
588
- for (const file of files) {
589
- fs.copyFileSync(path.join(srcSkillDir, file), path.join(destSkillDir, file));
590
- }
676
+ // Recursive copy function for skills (handles subdirs like hooks/)
677
+ const copyRecursive = (src, dest) => {
678
+ fs.mkdirSync(dest, { recursive: true });
679
+ const entries = fs.readdirSync(src);
680
+ for (const entry of entries) {
681
+ const srcPath = path.join(src, entry);
682
+ const destPath = path.join(dest, entry);
683
+ if (fs.statSync(srcPath).isDirectory()) {
684
+ copyRecursive(srcPath, destPath);
685
+ } else {
686
+ fs.copyFileSync(srcPath, destPath);
687
+ }
688
+ }
689
+ };
690
+ copyRecursive(srcSkillDir, destSkillDir);
591
691
  console.log(`✓ Copied skill: ${skill}`);
592
692
  }
593
693
  }
@@ -659,6 +759,16 @@ Rules: 3-4 sentences max, ASCII visuals, check MAP.md first.`;
659
759
  }
660
760
  ]
661
761
  }
762
+ ],
763
+ Stop: [
764
+ {
765
+ hooks: [
766
+ {
767
+ type: "command",
768
+ command: "atris/skills/autopilot/hooks/stop-hook.sh"
769
+ }
770
+ ]
771
+ }
662
772
  ]
663
773
  }
664
774
  };