atris 3.35.0 → 3.37.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.
Files changed (133) hide show
  1. package/AGENTS.md +37 -0
  2. package/README.md +5 -3
  3. package/atris/GETTING_STARTED.md +1 -1
  4. package/atris/atris.md +3 -0
  5. package/atris/policies/day-loop-voice.md +102 -0
  6. package/atris/policies/outbound-artifact-gate.md +2 -0
  7. package/atris/skills/design/SKILL.md +56 -32
  8. package/atris/skills/endgame/SKILL.md +12 -6
  9. package/atris/skills/engines/SKILL.md +22 -4
  10. package/atris/skills/fable-method/SKILL.md +66 -0
  11. package/atris/skills/improve/SKILL.md +65 -45
  12. package/atris/skills/youtube/SKILL.md +10 -1
  13. package/atris.md +2 -0
  14. package/ax +147 -19
  15. package/bin/atris.js +576 -267
  16. package/commands/activate.js +194 -88
  17. package/commands/agents.js +166 -0
  18. package/commands/autoland.js +459 -107
  19. package/commands/autopilot-front.js +20 -2
  20. package/commands/autopilot.js +118 -2
  21. package/commands/avail.js +407 -0
  22. package/commands/bench.js +188 -0
  23. package/commands/brain.js +3 -0
  24. package/commands/brief.js +651 -0
  25. package/commands/business-sync.js +192 -6
  26. package/commands/clean.js +50 -24
  27. package/commands/close.js +1083 -0
  28. package/commands/cloud.js +245 -0
  29. package/commands/compile.js +292 -1
  30. package/commands/computer.js +150 -3
  31. package/commands/dream.js +365 -0
  32. package/commands/drill.js +371 -0
  33. package/commands/engine.js +993 -32
  34. package/commands/experiments.js +28 -0
  35. package/commands/feedback.js +34 -12
  36. package/commands/fleet-report.js +206 -0
  37. package/commands/gm.js +23 -0
  38. package/commands/goal.js +247 -0
  39. package/commands/improve.js +642 -26
  40. package/commands/init.js +72 -44
  41. package/commands/interview.js +67 -1
  42. package/commands/land.js +152 -52
  43. package/commands/lifecycle.js +39 -3
  44. package/commands/log.js +84 -1
  45. package/commands/loops.js +220 -16
  46. package/commands/meet.js +220 -0
  47. package/commands/member.js +511 -34
  48. package/commands/mission.js +3144 -338
  49. package/commands/next.js +137 -0
  50. package/commands/now.js +220 -25
  51. package/commands/one-lap.js +776 -0
  52. package/commands/orb.js +314 -0
  53. package/commands/pack-craft.js +179 -0
  54. package/commands/pack.js +823 -0
  55. package/commands/play.js +3 -2
  56. package/commands/probe.js +30 -3
  57. package/commands/pulse.js +241 -46
  58. package/commands/push.js +260 -82
  59. package/commands/rainmaker.js +49 -0
  60. package/commands/report.js +415 -0
  61. package/commands/scout.js +147 -0
  62. package/commands/search.js +363 -0
  63. package/commands/skill.js +47 -3
  64. package/commands/slop.js +50 -2
  65. package/commands/soul.js +1 -1
  66. package/commands/stream.js +861 -0
  67. package/commands/study.js +693 -0
  68. package/commands/sync.js +67 -54
  69. package/commands/task.js +1346 -117
  70. package/commands/team.js +73 -0
  71. package/commands/verify.js +96 -0
  72. package/commands/watch.js +303 -0
  73. package/commands/wish.js +500 -0
  74. package/commands/workflow.js +11 -5
  75. package/commands/worktree.js +234 -13
  76. package/commands/xp.js +29 -11
  77. package/lib/auto-accept-certified.js +331 -34
  78. package/lib/autoland.js +319 -54
  79. package/lib/ax-auto-lane.js +96 -0
  80. package/lib/bench/context.js +147 -0
  81. package/lib/bench/engines.js +141 -0
  82. package/lib/bench/report.js +140 -0
  83. package/lib/bench/runner.js +512 -0
  84. package/lib/brief-ledger.js +350 -0
  85. package/lib/cloud-mission.js +259 -0
  86. package/lib/codex-flight.js +154 -0
  87. package/lib/default-runner.js +45 -0
  88. package/lib/default-verifier.js +70 -0
  89. package/lib/engine-registry.js +232 -0
  90. package/lib/experiments/daily.js +640 -0
  91. package/lib/fleet.js +2219 -67
  92. package/lib/improve-vitals-html.js +171 -0
  93. package/lib/known-commands.js +58 -0
  94. package/lib/loop-doctor.js +416 -0
  95. package/lib/member-switches.js +144 -0
  96. package/lib/mission-room.js +1 -0
  97. package/lib/mission-root.js +52 -0
  98. package/lib/next-moves.js +327 -10
  99. package/lib/one-lap-validator.js +60 -0
  100. package/lib/orb-context.js +477 -0
  101. package/lib/orb-scorecard.js +224 -0
  102. package/lib/policy-lessons.js +52 -1
  103. package/lib/pulse.js +277 -3
  104. package/lib/receipt-block.js +168 -0
  105. package/lib/receipt-evidence.js +65 -4
  106. package/lib/router-brain.js +352 -0
  107. package/lib/runner-command.js +10 -0
  108. package/lib/self-drive.js +258 -0
  109. package/lib/short-name.js +103 -0
  110. package/lib/spawn-env.js +18 -0
  111. package/lib/state-detection.js +56 -1
  112. package/lib/sync-status.js +59 -0
  113. package/lib/task-db.js +108 -29
  114. package/lib/task-proof.js +23 -1
  115. package/lib/team-presence.js +260 -0
  116. package/lib/tool-result-encode.js +7 -0
  117. package/lib/trust-tiers.js +90 -0
  118. package/lib/usage.js +107 -0
  119. package/lib/voice-gate.js +163 -0
  120. package/lib/wish-audit.js +1368 -0
  121. package/lib/wish-delegate.js +1840 -0
  122. package/lib/wish-design.js +110 -0
  123. package/lib/wish-stats.js +183 -0
  124. package/lib/wish-store.js +354 -0
  125. package/lib/zip.js +221 -0
  126. package/package.json +3 -1
  127. package/templates/loops/atris/loops/LOOPS.md +55 -0
  128. package/templates/loops/atris/loops/TICK.md +24 -0
  129. package/templates/loops/atris/loops/feedback.md +22 -0
  130. package/templates/loops/atris/loops/quality.md +22 -0
  131. package/templates/loops/atris/wiki/systems/loops.md +41 -0
  132. package/utils/api.js +5 -1
  133. package/utils/auth.js +57 -21
package/commands/sync.js CHANGED
@@ -89,12 +89,15 @@ function syncPackageSkills(targetAtrisDir, opts = {}) {
89
89
  const userSkillsDir = path.join(targetAtrisDir, 'skills');
90
90
  const claudeSkillsBaseDir = path.join(path.dirname(targetAtrisDir), '.claude', 'skills');
91
91
  const verbose = opts.verbose !== false;
92
+ const dryRun = opts.dryRun === true;
92
93
  let updated = 0;
93
94
 
94
95
  if (!fs.existsSync(packageSkillsDir)) return 0;
95
96
 
96
- ensureRealDirectory(userSkillsDir);
97
- ensureRealDirectory(claudeSkillsBaseDir);
97
+ if (!dryRun) {
98
+ ensureRealDirectory(userSkillsDir);
99
+ ensureRealDirectory(claudeSkillsBaseDir);
100
+ }
98
101
 
99
102
  const skillFolders = fs.readdirSync(packageSkillsDir).filter(f => {
100
103
  try { return fs.statSync(path.join(packageSkillsDir, f)).isDirectory(); }
@@ -107,7 +110,7 @@ function syncPackageSkills(targetAtrisDir, opts = {}) {
107
110
  const symlinkPath = path.join(claudeSkillsBaseDir, skill);
108
111
 
109
112
  const syncRecursive = (src, dest, skillName, basePath = '') => {
110
- ensureRealDirectory(dest);
113
+ if (!dryRun) ensureRealDirectory(dest);
111
114
  for (const entry of fs.readdirSync(src)) {
112
115
  const srcPath = path.join(src, entry);
113
116
  const destPath = path.join(dest, entry);
@@ -118,9 +121,11 @@ function syncPackageSkills(targetAtrisDir, opts = {}) {
118
121
  const srcContent = fs.readFileSync(srcPath, 'utf8');
119
122
  const destContent = fs.existsSync(destPath) ? fs.readFileSync(destPath, 'utf8') : '';
120
123
  if (srcContent !== destContent) {
121
- fs.writeFileSync(destPath, srcContent);
122
- if (entry.endsWith('.sh')) fs.chmodSync(destPath, 0o755);
123
- if (verbose) console.log(`✓ Updated atris/skills/${skillName}/${relPath}`);
124
+ if (!dryRun) {
125
+ fs.writeFileSync(destPath, srcContent);
126
+ if (entry.endsWith('.sh')) fs.chmodSync(destPath, 0o755);
127
+ }
128
+ if (verbose) console.log(`${dryRun ? 'Would update' : '✓ Updated'} atris/skills/${skillName}/${relPath}`);
124
129
  updated++;
125
130
  }
126
131
  }
@@ -131,16 +136,20 @@ function syncPackageSkills(targetAtrisDir, opts = {}) {
131
136
 
132
137
  if (!fs.existsSync(symlinkPath)) {
133
138
  const relativePath = path.relative(claudeSkillsBaseDir, destSkillDir);
134
- try {
135
- fs.symlinkSync(relativePath, symlinkPath);
136
- if (verbose) console.log(`✓ Linked .claude/skills/${skill}`);
137
- } catch (e) {
138
- ensureRealDirectory(symlinkPath);
139
- const skillFile = path.join(destSkillDir, 'SKILL.md');
140
- if (fs.existsSync(skillFile)) {
141
- fs.copyFileSync(skillFile, path.join(symlinkPath, 'SKILL.md'));
139
+ if (dryRun) {
140
+ if (verbose) console.log(`Would link .claude/skills/${skill}`);
141
+ } else {
142
+ try {
143
+ fs.symlinkSync(relativePath, symlinkPath);
144
+ if (verbose) console.log(`✓ Linked .claude/skills/${skill}`);
145
+ } catch (e) {
146
+ ensureRealDirectory(symlinkPath);
147
+ const skillFile = path.join(destSkillDir, 'SKILL.md');
148
+ if (fs.existsSync(skillFile)) {
149
+ fs.copyFileSync(skillFile, path.join(symlinkPath, 'SKILL.md'));
150
+ }
151
+ if (verbose) console.log(`✓ Copied .claude/skills/${skill} (symlink failed)`);
142
152
  }
143
- if (verbose) console.log(`✓ Copied .claude/skills/${skill} (symlink failed)`);
144
153
  }
145
154
  }
146
155
  }
@@ -242,7 +251,7 @@ function renderBusinessAgentAdapter(bizMeta = {}, targetRoot = '.') {
242
251
  '- Check `atris/MAP.md` before broad code or file search.',
243
252
  '- Use `atris task` for ownership, notes, proof, and review state.',
244
253
  '- Use `atris mission` when work should survive the current chat.',
245
- '- Put completed agent work in Review with `atris task ready <id> --proof "<receipt>".`',
254
+ '- Put completed agent work in Review with `atris task ready <id> --proof "<receipt>" --result "<day-one PM sentence>".`',
246
255
  '- Do not run `atris task accept` or claim XP unless a human approved the proof.',
247
256
  '- Do not mix another business into this workspace.',
248
257
  '- No external sends, spend, or launches without operator approval.',
@@ -355,16 +364,13 @@ function syncWorkspaceTemplate(targetRoot, bizMeta, options = {}) {
355
364
  // customer workspace. Business-starter template ships skill infra (README,
356
365
  // folders) but skill files live in atris-cli/atris/skills/ — single source
357
366
  // of truth. Any new skill (e.g. AEO) auto-propagates to every customer.
358
- let skillsUpdated = 0;
359
- if (!dryRun) {
360
- skillsUpdated = syncPackageSkills(targetAtrisDir, { verbose: false });
361
- }
367
+ const skillsUpdated = syncPackageSkills(targetAtrisDir, { verbose: false, dryRun });
362
368
 
363
369
  console.log(` Added: ${added}`);
364
370
  console.log(` Updated: ${updated} ${force ? '' : '(--force to enable)'}`);
365
371
  console.log(` Preserved: ${preserved} (existing customizations kept)`);
366
372
  console.log(` Skipped: ${skipped} (already match template)`);
367
- console.log(` Skills: ${skillsUpdated} updated from atris-cli/atris/skills/`);
373
+ console.log(` Skills: ${skillsUpdated} ${dryRun ? 'would update' : 'updated'} from atris-cli/atris/skills/`);
368
374
  console.log('');
369
375
 
370
376
  if (addedList.length > 0) {
@@ -421,7 +427,9 @@ function syncBusinessCanonical(targetRoot, bizMeta, options = {}) {
421
427
  });
422
428
  }
423
429
 
424
- function syncAtris() {
430
+ function syncAtris(options = {}) {
431
+ const dryRun = options.dryRun != null ? options.dryRun : process.argv.includes('--dry-run');
432
+ const force = options.force != null ? options.force : process.argv.includes('--force');
425
433
  // Business mode detection: if .atris/business.json exists, use canonical templates
426
434
  const bizFile = path.join(process.cwd(), '.atris', 'business.json');
427
435
  if (fs.existsSync(bizFile)) {
@@ -429,6 +437,8 @@ function syncAtris() {
429
437
  const bizMeta = JSON.parse(fs.readFileSync(bizFile, 'utf8'));
430
438
  return syncWorkspaceTemplate(process.cwd(), bizMeta, {
431
439
  templateName: bizMeta.workspace_template || 'business',
440
+ dryRun,
441
+ force,
432
442
  });
433
443
  } catch (e) {
434
444
  console.error(`✗ Failed to read .atris/business.json: ${e.message}`);
@@ -452,7 +462,7 @@ function syncAtris() {
452
462
  console.log('📦 Migrating agent_team/ → team/ (v2.1.0 update)');
453
463
 
454
464
  // Create team/ if it doesn't exist
455
- if (!fs.existsSync(teamDir)) {
465
+ if (!dryRun && !fs.existsSync(teamDir)) {
456
466
  fs.mkdirSync(teamDir, { recursive: true });
457
467
  }
458
468
 
@@ -465,27 +475,27 @@ function syncAtris() {
465
475
  // Only copy if destination doesn't exist (preserve any customizations)
466
476
  if (!fs.existsSync(destPath)) {
467
477
  if (fs.statSync(srcPath).isFile()) {
468
- fs.copyFileSync(srcPath, destPath);
469
- console.log(` ✓ Migrated ${file}`);
478
+ if (!dryRun) fs.copyFileSync(srcPath, destPath);
479
+ console.log(` ${dryRun ? 'Would migrate' : '✓ Migrated'} ${file}`);
470
480
  }
471
481
  }
472
482
  }
473
483
 
474
484
  // Remove old agent_team/ folder
475
- fs.rmSync(legacyAgentTeamDir, { recursive: true, force: true });
476
- console.log(' ✓ Removed old agent_team/ folder');
485
+ if (!dryRun) fs.rmSync(legacyAgentTeamDir, { recursive: true, force: true });
486
+ console.log(` ${dryRun ? 'Would remove' : '✓ Removed'} old agent_team/ folder`);
477
487
  console.log('');
478
488
  }
479
489
 
480
490
  if (!fs.existsSync(teamDir)) {
481
- fs.mkdirSync(teamDir, { recursive: true });
491
+ if (!dryRun) fs.mkdirSync(teamDir, { recursive: true });
482
492
  }
483
493
 
484
494
  // Ensure policies folder exists
485
495
  const policiesDir = path.join(targetDir, 'policies');
486
496
  if (!fs.existsSync(policiesDir)) {
487
- fs.mkdirSync(policiesDir, { recursive: true });
488
- console.log('✓ Created atris/policies/ folder');
497
+ if (!dryRun) fs.mkdirSync(policiesDir, { recursive: true });
498
+ console.log(`${dryRun ? 'Would create' : '✓ Created'} atris/policies/ folder`);
489
499
  }
490
500
 
491
501
  const filesToSync = [
@@ -522,9 +532,11 @@ function syncAtris() {
522
532
  return;
523
533
  }
524
534
 
525
- fs.mkdirSync(path.dirname(targetFile), { recursive: true });
526
- fs.copyFileSync(sourceFile, targetFile);
527
- console.log(`✓ Updated ${target}`);
535
+ if (!dryRun) {
536
+ fs.mkdirSync(path.dirname(targetFile), { recursive: true });
537
+ fs.copyFileSync(sourceFile, targetFile);
538
+ }
539
+ console.log(`${dryRun ? 'Would update' : '✓ Updated'} ${target}`);
528
540
  updated++;
529
541
  });
530
542
 
@@ -532,12 +544,13 @@ function syncAtris() {
532
544
  const todoFile = path.join(targetDir, 'TODO.md');
533
545
  const legacyTaskFile = path.join(targetDir, 'TASK_CONTEXTS.md');
534
546
  if (!fs.existsSync(todoFile) && fs.existsSync(legacyTaskFile)) {
535
- fs.renameSync(legacyTaskFile, todoFile);
536
- console.log('✓ Migrated TASK_CONTEXTS.md to TODO.md');
547
+ if (!dryRun) fs.renameSync(legacyTaskFile, todoFile);
548
+ console.log(`${dryRun ? 'Would migrate' : '✓ Migrated'} TASK_CONTEXTS.md to TODO.md`);
549
+ updated++;
537
550
  }
538
551
 
539
552
  // Sync all skills from package to user's project via shared helper.
540
- updated += syncPackageSkills(targetDir, { verbose: true });
553
+ updated += syncPackageSkills(targetDir, { verbose: true, dryRun });
541
554
 
542
555
  // Update .claude/skills/atris/SKILL.md (legacy - now handled above, keeping for compatibility)
543
556
  const claudeSkillsDir = path.join(process.cwd(), '.claude', 'skills', 'atris');
@@ -572,13 +585,13 @@ Key behaviors:
572
585
  - Use \`atris task\` for claims, proof, ready, and accept
573
586
  - Treat TODO.md as a rendered view; regenerate it instead of hand-editing tasks`;
574
587
 
575
- if (!fs.existsSync(claudeSkillsDir)) {
588
+ if (!dryRun && !fs.existsSync(claudeSkillsDir)) {
576
589
  fs.mkdirSync(claudeSkillsDir, { recursive: true });
577
590
  }
578
591
  const currentSkill = fs.existsSync(claudeSkillFile) ? fs.readFileSync(claudeSkillFile, 'utf8') : '';
579
592
  if (currentSkill !== skillContent) {
580
- fs.writeFileSync(claudeSkillFile, skillContent);
581
- console.log('✓ Updated .claude/skills/atris/SKILL.md');
593
+ if (!dryRun) fs.writeFileSync(claudeSkillFile, skillContent);
594
+ console.log(`${dryRun ? 'Would update' : '✓ Updated'} .claude/skills/atris/SKILL.md`);
582
595
  updated++;
583
596
  }
584
597
 
@@ -599,8 +612,8 @@ Key behaviors:
599
612
  ]
600
613
  }
601
614
  };
602
- fs.writeFileSync(claudeSettingsFile, JSON.stringify(claudeSettings, null, 2));
603
- console.log('✓ Created .claude/settings.json (SessionStart hook)');
615
+ if (!dryRun) fs.writeFileSync(claudeSettingsFile, JSON.stringify(claudeSettings, null, 2));
616
+ console.log(`${dryRun ? 'Would create' : '✓ Created'} .claude/settings.json (SessionStart hook)`);
604
617
  updated++;
605
618
  }
606
619
 
@@ -618,6 +631,12 @@ After displaying the boot output, respond to the user naturally.
618
631
 
619
632
  `;
620
633
 
634
+ const applyClaudeUpdate = (content, dryAction, completedAction) => {
635
+ if (!dryRun) fs.writeFileSync(rootClaudeMd, content);
636
+ console.log(dryRun ? `Would ${dryAction}` : `✓ ${completedAction}`);
637
+ updated++;
638
+ };
639
+
621
640
  if (fs.existsSync(rootClaudeMd)) {
622
641
  let content = fs.readFileSync(rootClaudeMd, 'utf8');
623
642
  const startMarker = '<!-- ATRIS:START';
@@ -630,9 +649,7 @@ After displaying the boot output, respond to the user naturally.
630
649
  if (endRaw === -1) {
631
650
  // End marker missing — replace from start marker to end of file with fresh block
632
651
  content = atrisBlock + content.slice(0, startIdx);
633
- fs.writeFileSync(rootClaudeMd, content);
634
- console.log('✓ Repaired Atris block in CLAUDE.md (missing end marker)');
635
- updated++;
652
+ applyClaudeUpdate(content, 'repair Atris block in CLAUDE.md (missing end marker)', 'Repaired Atris block in CLAUDE.md (missing end marker)');
636
653
  } else {
637
654
  const endIdx = endRaw + endMarker.length;
638
655
  const existingBlock = content.slice(startIdx, endIdx);
@@ -640,25 +657,21 @@ After displaying the boot output, respond to the user naturally.
640
657
  if (!existingBlock.includes('Atris boot sequence')) {
641
658
  // Replace existing Atris block with new version
642
659
  content = atrisBlock + content.slice(0, startIdx) + content.slice(endIdx).replace(/^\n+/, '');
643
- fs.writeFileSync(rootClaudeMd, content);
644
- console.log('✓ Updated Atris block in CLAUDE.md');
645
- updated++;
660
+ applyClaudeUpdate(content, 'update Atris block in CLAUDE.md', 'Updated Atris block in CLAUDE.md');
646
661
  }
647
662
  }
648
663
  } else {
649
664
  // Prepend Atris block
650
- fs.writeFileSync(rootClaudeMd, atrisBlock + content);
651
- console.log('✓ Prepended Atris block to CLAUDE.md');
652
- updated++;
665
+ applyClaudeUpdate(atrisBlock + content, 'prepend Atris block to CLAUDE.md', 'Prepended Atris block to CLAUDE.md');
653
666
  }
654
667
  } else {
655
668
  // Create new CLAUDE.md with just Atris block
656
- fs.writeFileSync(rootClaudeMd, atrisBlock.trim() + '\n');
657
- console.log('✓ Created CLAUDE.md with Atris block');
658
- updated++;
669
+ applyClaudeUpdate(atrisBlock.trim() + '\n', 'create CLAUDE.md with Atris block', 'Created CLAUDE.md with Atris block');
659
670
  }
660
671
 
661
- if (updated === 0) {
672
+ if (dryRun) {
673
+ console.log(`\nDry run: ${updated} file(s) would update, ${skipped} unchanged; no changes made.`);
674
+ } else if (updated === 0) {
662
675
  ensureWikiScaffold(process.cwd());
663
676
  console.log('✓ Already up to date');
664
677
  } else {