atris 3.52.0 → 3.55.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 (131) hide show
  1. package/FOR_AGENTS.md +7 -0
  2. package/README.md +1 -1
  3. package/atris/policies/ANTISLOP.md +2 -1
  4. package/atris/skills/copy-editor/SKILL.md +2 -1
  5. package/atris/skills/design/SKILL.md +1 -1
  6. package/atris/skills/engines/SKILL.md +8 -7
  7. package/atris/skills/x-search/SKILL.md +24 -22
  8. package/atris/skills/youtube/SKILL.md +89 -40
  9. package/atris/team/_template/SOUL.md +40 -0
  10. package/atris/team/validator/MEMBER.md +1 -2
  11. package/ax +592 -91
  12. package/bin/atris +21 -0
  13. package/bin/atris.js +580 -378
  14. package/commands/api-key.js +170 -0
  15. package/commands/auth.js +453 -43
  16. package/commands/autoland.js +3 -1
  17. package/commands/autopilot-front.js +58 -11
  18. package/commands/avail.js +5 -4
  19. package/commands/balance.js +55 -0
  20. package/commands/bench.js +29 -1
  21. package/commands/brain.js +63 -12
  22. package/commands/brainstorm.js +18 -0
  23. package/commands/business.js +149 -1
  24. package/commands/chat-scan.js +17 -4
  25. package/commands/close.js +5 -1
  26. package/commands/computer.js +2 -2
  27. package/commands/doctor.js +174 -0
  28. package/commands/dream.js +7 -0
  29. package/commands/drill.js +34 -2
  30. package/commands/engine.js +47 -35
  31. package/commands/errors.js +58 -8
  32. package/commands/experiments.js +14 -0
  33. package/commands/feedback.js +61 -2
  34. package/commands/fleet-report.js +27 -7
  35. package/commands/fleet.js +98 -11
  36. package/commands/founder.js +60 -21
  37. package/commands/human-missions.js +79 -4
  38. package/commands/improve.js +54 -4
  39. package/commands/init.js +68 -92
  40. package/commands/install.js +40 -0
  41. package/commands/integrations.js +399 -30
  42. package/commands/interview.js +8 -0
  43. package/commands/land.js +48 -17
  44. package/commands/learn.js +29 -4
  45. package/commands/log.js +51 -0
  46. package/commands/member.js +68 -10
  47. package/commands/mission.js +339 -74
  48. package/commands/now.js +25 -6
  49. package/commands/orb.js +97 -2
  50. package/commands/pack.js +83 -9
  51. package/commands/playbook.js +381 -0
  52. package/commands/plugin.js +16 -0
  53. package/commands/radar.js +96 -40
  54. package/commands/recap.js +128 -21
  55. package/commands/release.js +102 -2
  56. package/commands/review.js +5 -10
  57. package/commands/revisions.js +55 -0
  58. package/commands/run-front.js +12 -4
  59. package/commands/scout.js +12 -1
  60. package/commands/search.js +123 -28
  61. package/commands/sign.js +2 -2
  62. package/commands/signup.js +12 -3
  63. package/commands/site-deploy.js +17 -4
  64. package/commands/site.js +2 -2
  65. package/commands/skill-eval.js +289 -0
  66. package/commands/skill.js +56 -6
  67. package/commands/social.js +603 -0
  68. package/commands/spaceship.js +69 -6
  69. package/commands/stream.js +8 -1
  70. package/commands/study.js +32 -19
  71. package/commands/sync.js +28 -22
  72. package/commands/task.js +418 -60
  73. package/commands/teach.js +72 -63
  74. package/commands/terminal.js +25 -9
  75. package/commands/topup.js +88 -0
  76. package/commands/truth.js +52 -6
  77. package/commands/usage.js +91 -0
  78. package/commands/verify.js +3 -2
  79. package/commands/version.js +3 -1
  80. package/commands/voice.js +5 -1
  81. package/commands/who.js +69 -11
  82. package/commands/wiki.js +7 -4
  83. package/commands/wish.js +76 -5
  84. package/commands/workflow.js +327 -145
  85. package/commands/worktree.js +1 -0
  86. package/commands/write.js +5 -0
  87. package/commands/x-search.js +340 -0
  88. package/commands/xp.js +5 -1
  89. package/commands/youtube.js +551 -33
  90. package/lib/account-bound.js +47 -0
  91. package/lib/auto-accept-certified.js +10 -2
  92. package/lib/auto-lessons.js +186 -0
  93. package/lib/autoland.js +4 -2
  94. package/lib/chat-log-scan.js +7 -4
  95. package/lib/cli-json.js +77 -0
  96. package/lib/cli-scope.js +26 -0
  97. package/lib/codex-flight.js +1 -0
  98. package/lib/conductor-artifacts.js +1 -1
  99. package/lib/context-gatherer.js +11 -0
  100. package/lib/developer-api.js +116 -0
  101. package/lib/engine-ask.js +11 -3
  102. package/lib/engine-registry.js +32 -7
  103. package/lib/feedback-triage.js +274 -0
  104. package/lib/first-minute.js +398 -0
  105. package/lib/fleet.js +115 -34
  106. package/lib/functional-owner.js +22 -0
  107. package/lib/known-commands.js +31 -5
  108. package/lib/member-scaffold.js +197 -0
  109. package/lib/mission-ledger-compact.js +127 -0
  110. package/lib/mission-root.js +4 -2
  111. package/lib/mission-runtime-loop.js +30 -2
  112. package/lib/next-moves.js +34 -34
  113. package/lib/noninteractive.js +88 -0
  114. package/lib/permission-grants.js +10 -1
  115. package/lib/policy-lessons.js +4 -1
  116. package/lib/revision-metric.js +279 -0
  117. package/lib/runner-command.js +5 -4
  118. package/lib/scratch-root.js +48 -0
  119. package/lib/skill-eval-gate.js +249 -0
  120. package/lib/task-db.js +22 -2
  121. package/lib/task-proof.js +75 -9
  122. package/lib/task-receipt.js +5 -1
  123. package/lib/verifier-quality.js +69 -0
  124. package/lib/wish-audit.js +2 -2
  125. package/lib/wish-delegate.js +10 -1
  126. package/lib/workspace-scaffold.js +270 -0
  127. package/package.json +4 -2
  128. package/scripts/det/checklist-score.js +191 -0
  129. package/scripts/det/ytsearch +31 -0
  130. package/scripts/outbound-artifact-gate.js +227 -0
  131. package/utils/update-check.js +15 -0
package/commands/avail.js CHANGED
@@ -10,6 +10,7 @@
10
10
 
11
11
  const { ensureValidCredentials } = require('../utils/auth');
12
12
  const { apiRequestJson, getAppBaseUrl, httpRequest } = require('../utils/api');
13
+ const { argsWantHelp } = require('../lib/noninteractive');
13
14
 
14
15
  const DAY_INDEX = { mon: 0, tue: 1, wed: 2, thu: 3, fri: 4, sat: 5, sun: 6 };
15
16
  const DAY_LABEL = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
@@ -381,14 +382,14 @@ Examples:
381
382
  }
382
383
 
383
384
  async function availCommand(argv = []) {
385
+ if (argsWantHelp(argv)) {
386
+ printHelp();
387
+ return 2;
388
+ }
384
389
  if (!argv.length || argv[0]?.startsWith('-') || argv[0] === 'show' || argv[0] === 'get') {
385
390
  return showAvail({ json: argv.includes('--json') });
386
391
  }
387
392
  const sub = argv[0].toLowerCase();
388
- if (sub === '--help' || sub === '-h' || sub === 'help') {
389
- printHelp();
390
- return 0;
391
- }
392
393
  if (sub === 'set' || sub === 'update') return setAvail(argv.slice(1));
393
394
  if (sub === 'slots') return slotsAvail(argv.slice(1));
394
395
  console.error(`Unknown subcommand: ${sub}`);
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ const { apiRequestJson } = require('../utils/api');
4
+ const { loadCredentials } = require('../utils/auth');
5
+ const {
6
+ NOT_LOGGED_IN,
7
+ wantsHelp,
8
+ wantsJson,
9
+ tokenFrom,
10
+ oneLine,
11
+ printResult,
12
+ formatDollars,
13
+ normalizeBalance,
14
+ } = require('../lib/developer-api');
15
+
16
+ function showBalanceHelp() {
17
+ console.log('usage: atris balance [--json]');
18
+ console.log('show your credit balance in dollars, with credits on the next line.');
19
+ }
20
+
21
+ async function balanceCommand(args = [], deps = {}) {
22
+ if (wantsHelp(args)) {
23
+ showBalanceHelp();
24
+ return 0;
25
+ }
26
+
27
+ const json = wantsJson(args);
28
+ const io = { log: deps.log || console.log, err: deps.err || console.error };
29
+ const load = deps.loadCredentials || loadCredentials;
30
+ const request = deps.apiRequestJson || apiRequestJson;
31
+ const token = tokenFrom(load());
32
+ if (!token) {
33
+ printResult({ json, ok: false, error: NOT_LOGGED_IN, payload: { ok: false, error: NOT_LOGGED_IN } }, io);
34
+ return 1;
35
+ }
36
+
37
+ try {
38
+ const result = await request('/credits/balance', { method: 'GET', token });
39
+ if (!result || !result.ok) {
40
+ const error = oneLine(result && result.error);
41
+ printResult({ json, ok: false, error, payload: { ok: false, error, status: result && result.status } }, io);
42
+ return 1;
43
+ }
44
+ const balance = normalizeBalance(result.data || {});
45
+ const text = `${formatDollars(balance.dollars)}\n${balance.credits} credits`;
46
+ printResult({ json, ok: true, text, payload: { ok: true, ...balance } }, io);
47
+ return 0;
48
+ } catch (error) {
49
+ const message = oneLine(error && error.message);
50
+ printResult({ json, ok: false, error: message, payload: { ok: false, error: message } }, io);
51
+ return 1;
52
+ }
53
+ }
54
+
55
+ module.exports = { balanceCommand, showBalanceHelp };
package/commands/bench.js CHANGED
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
3
6
  const {
4
7
  BenchInfraError,
5
8
  packMetadata,
@@ -10,6 +13,18 @@ const {
10
13
  const { buildBenchReport, renderBenchReportText } = require('../lib/bench/report');
11
14
  const { hasFlag } = require('../lib/arg-parser');
12
15
 
16
+ function isAtrisCliRepo(cwd = process.cwd()) {
17
+ const root = path.resolve(cwd);
18
+ try {
19
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
20
+ if (pkg.name !== 'atris') return false;
21
+ } catch {
22
+ return false;
23
+ }
24
+ return fs.existsSync(path.join(root, 'bin', 'atris.js'))
25
+ && fs.existsSync(path.join(root, 'atris', 'benchmarks'));
26
+ }
27
+
13
28
  // Preserve the existing rule that any inline value wins over a split value.
14
29
  function readInlineFirstFlag(args, name) {
15
30
  const prefix = `${name}=`;
@@ -41,6 +56,9 @@ function printHelp() {
41
56
  console.log('Usage: atris bench tasks [--pack <id>] [--json]');
42
57
  console.log('Usage: atris bench packs [--json]');
43
58
  console.log('Usage: atris bench report [--pack agents-v1] [--json]');
59
+ console.log('');
60
+ console.log('Bench runs product gates and only belongs in the atris CLI repo.');
61
+ console.log('Pass --here to allow running outside that repo.');
44
62
  }
45
63
 
46
64
  function printRunText(record) {
@@ -167,11 +185,20 @@ function reportCommand(args) {
167
185
 
168
186
  async function benchCommand(args = []) {
169
187
  const subcommand = args[0] || 'run';
170
- const rest = args.slice(1);
188
+ const rest = args.slice(1).filter((arg) => arg !== '--here');
171
189
  if (subcommand === '--help' || subcommand === '-h' || subcommand === 'help') {
172
190
  printHelp();
173
191
  return 0;
174
192
  }
193
+ if (!hasFlag(args, '--here') && !isAtrisCliRepo(process.cwd())) {
194
+ const message = 'refuse outside the atris cli repo; pass --here to run here';
195
+ if (hasFlag(args, '--json') || hasFlag(rest, '--json')) {
196
+ console.log(JSON.stringify({ schema: 'atris.bench.error.v1', error: message }));
197
+ } else {
198
+ console.error(`atris bench: ${message}`);
199
+ }
200
+ return 2;
201
+ }
175
202
  if (subcommand === 'run') return runCommand(rest);
176
203
  if (subcommand === 'tasks') return tasksCommand(rest);
177
204
  if (subcommand === 'packs') return packsCommand(rest);
@@ -183,4 +210,5 @@ async function benchCommand(args = []) {
183
210
 
184
211
  module.exports = {
185
212
  benchCommand,
213
+ isAtrisCliRepo,
186
214
  };
package/commands/brain.js CHANGED
@@ -5,6 +5,7 @@ const { spawnSync } = require('child_process');
5
5
  const { refreshNowFile } = require('./now');
6
6
  const escapeRegExp = require('../lib/escape-regexp');
7
7
  const { hasRenderedSections, isOpenSection, isDoneSection } = require('../lib/todo-sections');
8
+ const { isNonInteractive } = require('../lib/noninteractive');
8
9
  const {
9
10
  renderVoiceCardBlock,
10
11
  upsertCursorVoiceCard,
@@ -70,6 +71,7 @@ function parseArgs(args) {
70
71
  root: process.cwd(),
71
72
  verify: false,
72
73
  json: false,
74
+ yes: false,
73
75
  rating: null,
74
76
  recommendation: null,
75
77
  note: '',
@@ -88,6 +90,8 @@ function parseArgs(args) {
88
90
  options.verify = true;
89
91
  } else if (arg === '--json') {
90
92
  options.json = true;
93
+ } else if (arg === '--yes' || arg === '-y') {
94
+ options.yes = true;
91
95
  } else if (arg === '--rating' && args[i + 1]) {
92
96
  options.rating = args[++i];
93
97
  } else if (arg.startsWith('--rating=')) {
@@ -1436,7 +1440,7 @@ function generatedBootBlock(state, voiceCard = voiceCardForRoot(state.root)) {
1436
1440
  This workspace has a compiled agent brain.
1437
1441
 
1438
1442
  On session start, activate it first:
1439
- \`atris brain activate --root . --verify\`
1443
+ \`atris brain activate --root . --yes --verify\`
1440
1444
 
1441
1445
  Load these first:
1442
1446
  ${renderBulletedLoadOrder(state)}
@@ -1450,7 +1454,7 @@ Keep this voice beside every reply:
1450
1454
  ${renderVoiceCardBlock(voiceCard)}
1451
1455
 
1452
1456
  Re-run after meaningful work:
1453
- \`atris brain compile --root .\`
1457
+ \`atris brain compile --root . --yes\`
1454
1458
  ${GENERATED_END}
1455
1459
  `;
1456
1460
  }
@@ -1471,6 +1475,40 @@ function upsertGeneratedBlock(filePath, title, block) {
1471
1475
  fs.writeFileSync(filePath, `${current.trimEnd()}\n\n${block}`, 'utf8');
1472
1476
  }
1473
1477
 
1478
+ function plannedBrainWrites(state) {
1479
+ const files = [
1480
+ 'atris/brain/STATUS.md',
1481
+ 'atris/brain/self_improvement_ledger.md',
1482
+ 'atris/brain/state.json',
1483
+ 'AGENTS.md',
1484
+ 'CLAUDE.md',
1485
+ 'GEMINI.md',
1486
+ '.cursor/rules/atris-voice.mdc',
1487
+ ];
1488
+ if (fs.existsSync(path.join(state.root, 'atris', 'wiki', 'STATUS.md'))) {
1489
+ files.push('atris/wiki/STATUS.md');
1490
+ }
1491
+ return files;
1492
+ }
1493
+
1494
+ function refuseBrainWriteWithoutYes(state, options = {}) {
1495
+ const files = plannedBrainWrites(state);
1496
+ if (options.json) {
1497
+ console.log(JSON.stringify({
1498
+ ok: true,
1499
+ action: 'brain_plan',
1500
+ files,
1501
+ next_command: 'atris brain compile --yes',
1502
+ }, null, 2));
1503
+ } else {
1504
+ console.log('Plan only. Would write:');
1505
+ for (const file of files) console.log(` ${file}`);
1506
+ console.log('Re-run with --yes to write these files.');
1507
+ }
1508
+ if (isNonInteractive()) process.exit(2);
1509
+ return { planned: true, files };
1510
+ }
1511
+
1474
1512
  function writeBrain(state) {
1475
1513
  const brainDir = path.join(state.root, 'atris', 'brain');
1476
1514
  fs.mkdirSync(brainDir, { recursive: true });
@@ -1529,9 +1567,9 @@ function verifyBrain(root) {
1529
1567
 
1530
1568
  function brainUsageLines() {
1531
1569
  return [
1532
- 'Usage: atris brain compile [--root <workspace>] [--verify] [--json]',
1533
- ' atris brain activate [--member <slug>] [--root <workspace>] [--verify] [--json]',
1534
- ' atris brain gallery [--root <workspace>] [--verify] [--json]',
1570
+ 'Usage: atris brain compile [--root <workspace>] [--yes] [--verify] [--json]',
1571
+ ' atris brain activate [--member <slug>] [--root <workspace>] [--yes] [--verify] [--json]',
1572
+ ' atris brain gallery [--root <workspace>] [--yes] [--verify] [--json]',
1535
1573
  ' atris brain go|hold [note] [--recommendation <text>] [--root <workspace>] [--verify]',
1536
1574
  ' atris brain approval go|edit|hold [note] [--recommendation <text>] [--root <workspace>] [--verify]',
1537
1575
  ' atris brain scorecard [--root <workspace>] [--verify] [--json]',
@@ -1618,6 +1656,10 @@ function brainCommand(args = process.argv.slice(3)) {
1618
1656
 
1619
1657
  if (subcommand === 'activate') {
1620
1658
  const state = prepareBrainState(options.root);
1659
+ if (!options.yes) {
1660
+ refuseBrainWriteWithoutYes(state, options);
1661
+ return 0;
1662
+ }
1621
1663
  writeBrain(state);
1622
1664
  if (options.verify) verifyBrain(options.root);
1623
1665
  const card = renderActivationCard(state, options);
@@ -1636,7 +1678,7 @@ function brainCommand(args = process.argv.slice(3)) {
1636
1678
  }
1637
1679
  }
1638
1680
  console.log(JSON.stringify({ ok: true, state, card }, null, 2));
1639
- return;
1681
+ return 0;
1640
1682
  }
1641
1683
  console.log('');
1642
1684
  console.log(card);
@@ -1645,7 +1687,7 @@ function brainCommand(args = process.argv.slice(3)) {
1645
1687
  verifyActivationCard(card);
1646
1688
  } catch (error) {
1647
1689
  printVerifyFailure(error);
1648
- return;
1690
+ return 1;
1649
1691
  }
1650
1692
  const operator = (card.match(/^OPERATOR:\s*(.+)$/m) || [null, 'unknown'])[1];
1651
1693
  if (operator === 'unknown') {
@@ -1655,10 +1697,14 @@ function brainCommand(args = process.argv.slice(3)) {
1655
1697
  }
1656
1698
  }
1657
1699
  console.log('');
1658
- return;
1700
+ return 0;
1659
1701
  }
1660
1702
  if (subcommand === 'gallery') {
1661
1703
  const state = prepareBrainState(options.root);
1704
+ if (!options.yes) {
1705
+ refuseBrainWriteWithoutYes(state, options);
1706
+ return 0;
1707
+ }
1662
1708
  writeBrain(state);
1663
1709
  if (options.verify) verifyBrain(options.root);
1664
1710
  const gallery = renderActivationGallery(state);
@@ -1676,18 +1722,18 @@ function brainCommand(args = process.argv.slice(3)) {
1676
1722
  process.exit(1);
1677
1723
  }
1678
1724
  printVerifyFailure(error);
1679
- return;
1725
+ return 1;
1680
1726
  }
1681
1727
  }
1682
1728
  if (options.json) {
1683
1729
  console.log(JSON.stringify({ ok: true, members: listMemberSlugs(options.root), gallery }, null, 2));
1684
- return;
1730
+ return 0;
1685
1731
  }
1686
1732
  console.log('');
1687
1733
  console.log(gallery);
1688
1734
  if (options.verify) console.log('\nVERIFY: brain artifacts and member readiness present');
1689
1735
  console.log('');
1690
- return;
1736
+ return 0;
1691
1737
  }
1692
1738
  if (subcommand !== 'compile' && subcommand !== 'status') {
1693
1739
  if (options.json) {
@@ -1703,12 +1749,16 @@ function brainCommand(args = process.argv.slice(3)) {
1703
1749
  }
1704
1750
 
1705
1751
  const state = prepareBrainState(options.root);
1752
+ if (!options.yes) {
1753
+ refuseBrainWriteWithoutYes(state, options);
1754
+ return 0;
1755
+ }
1706
1756
  const written = writeBrain(state);
1707
1757
  if (options.verify) verifyBrain(options.root);
1708
1758
 
1709
1759
  if (options.json) {
1710
1760
  console.log(JSON.stringify({ ok: true, state, written }, null, 2));
1711
- return;
1761
+ return 0;
1712
1762
  }
1713
1763
 
1714
1764
  console.log('');
@@ -1720,6 +1770,7 @@ function brainCommand(args = process.argv.slice(3)) {
1720
1770
  console.log(` Next: ${nextMove(state)}`);
1721
1771
  if (options.verify) console.log(' Verify: passed');
1722
1772
  console.log('');
1773
+ return 0;
1723
1774
  }
1724
1775
 
1725
1776
  module.exports = {
@@ -14,6 +14,7 @@ const {
14
14
  const { loadConfig } = require('../utils/config');
15
15
  const { loadCredentials, ensureValidCredentials } = require('../utils/auth');
16
16
  const { apiRequestJson } = require('../utils/api');
17
+ const { isNonInteractive } = require('../lib/noninteractive');
17
18
  const { planAtris, doAtris, reviewAtris } = require('./workflow');
18
19
 
19
20
  const pkg = require('../package.json');
@@ -27,10 +28,12 @@ async function brainstormAtris() {
27
28
  console.log('Description:');
28
29
  console.log(' Guided prompt generator for exploration before planning.');
29
30
  console.log(' Default is local-first; pass --cloud to include AtrisOS journal context.');
31
+ console.log(' Headless agents: pass the idea on the command line (never prompts).');
30
32
  console.log('');
31
33
  console.log('Options:');
32
34
  console.log(' --cloud Include AtrisOS journal context (optional).');
33
35
  console.log(' --no-cloud Force local-only mode (skip AtrisOS).');
36
+ console.log(' --yes Non-interactive: capture idea args and exit.');
34
37
  console.log('');
35
38
  return;
36
39
  }
@@ -49,6 +52,21 @@ async function brainstormAtris() {
49
52
  const useCloudJournal = args.includes('--cloud') && !args.includes('--no-cloud');
50
53
  const topicFromArgs = args.filter((arg) => !arg.startsWith('-')).join(' ').trim() || null;
51
54
 
55
+ // Headless agents must never hang on "Describe the desired outcome".
56
+ if (isNonInteractive(args)) {
57
+ if (topicFromArgs) {
58
+ const newId = addInboxIdea(logFile, topicFromArgs);
59
+ console.log(`captured I${newId}: ${topicFromArgs}`);
60
+ console.log(`journal: ${path.relative(process.cwd(), logFile) || logFile}`);
61
+ console.log('Next: atris plan');
62
+ return;
63
+ }
64
+ console.log('brainstorm is interactive without an idea on the command line.');
65
+ console.log(`journal: ${path.relative(process.cwd(), logFile) || logFile}`);
66
+ console.log('Next: atris brainstorm "<idea>"');
67
+ return;
68
+ }
69
+
52
70
  console.log('');
53
71
  console.log('┌─────────────────────────────────────────────────────────────┐');
54
72
  console.log('│ Atris Brainstorm — structured prompt generator │');
@@ -35,6 +35,7 @@ function isHelpToken(arg) {
35
35
  }
36
36
 
37
37
  const BUSINESS_CREATE_USAGE = 'Usage: atris business create <name> [--description "..."] [--workspace] [--here|--root <dir>]';
38
+ const BUSINESS_SHIP_USAGE = 'Usage: atris business ship "<paragraph>" [--name <name>] [--email <email>] --yes';
38
39
 
39
40
  function isAccidentalHelpBusiness(value) {
40
41
  const raw = String(value || '').trim();
@@ -70,6 +71,75 @@ function validateBusinessCreateName(name) {
70
71
  return { ok: true };
71
72
  }
72
73
 
74
+ function compactShipText(value) {
75
+ return String(value || '').replace(/\s+/g, ' ').trim();
76
+ }
77
+
78
+ function parseBusinessShipArgs(args = []) {
79
+ const positional = [];
80
+ let name = '';
81
+ let email = '';
82
+
83
+ for (let i = 0; i < args.length; i++) {
84
+ const arg = args[i];
85
+ const next = args[i + 1];
86
+
87
+ if (arg === '--name') {
88
+ if (!next || String(next).startsWith('-')) {
89
+ return { ok: false, usage: BUSINESS_SHIP_USAGE, detail: 'Missing value for --name.' };
90
+ }
91
+ name = next;
92
+ i++;
93
+ } else if (typeof arg === 'string' && arg.startsWith('--name=')) {
94
+ name = arg.slice('--name='.length);
95
+ } else if (arg === '--email') {
96
+ if (!next || String(next).startsWith('-')) {
97
+ return { ok: false, usage: BUSINESS_SHIP_USAGE, detail: 'Missing value for --email.' };
98
+ }
99
+ email = next;
100
+ i++;
101
+ } else if (typeof arg === 'string' && arg.startsWith('--email=')) {
102
+ email = arg.slice('--email='.length);
103
+ } else if (arg === '--yes' || arg === '-y') {
104
+ // proceed flag; checked by shipBusiness
105
+ } else if (typeof arg === 'string' && arg.startsWith('-')) {
106
+ return { ok: false, usage: BUSINESS_SHIP_USAGE, detail: `Unknown option: ${arg}` };
107
+ } else {
108
+ positional.push(arg);
109
+ }
110
+ }
111
+
112
+ const description = compactShipText(positional.join(' '));
113
+ if (!description) {
114
+ return { ok: false, usage: BUSINESS_SHIP_USAGE };
115
+ }
116
+
117
+ return {
118
+ ok: true,
119
+ description,
120
+ name: compactShipText(name),
121
+ email: compactShipText(email),
122
+ };
123
+ }
124
+
125
+ function shipTeamNames(team) {
126
+ const rows = Array.isArray(team)
127
+ ? team
128
+ : (Array.isArray(team?.members) ? team.members : []);
129
+ return rows.map((member) => {
130
+ if (typeof member === 'string') return compactShipText(member);
131
+ if (!member || typeof member !== 'object') return '';
132
+ return compactShipText(member.display_name || member.name || member.slug || '');
133
+ }).filter(Boolean);
134
+ }
135
+
136
+ function shipTaskTitle(task) {
137
+ if (!task) return '';
138
+ if (typeof task === 'string') return compactShipText(task);
139
+ if (typeof task !== 'object') return '';
140
+ return compactShipText(task.title || task.name || '');
141
+ }
142
+
73
143
  function loadBusinesses() {
74
144
  const p = getBusinessConfigPath();
75
145
  if (!fs.existsSync(p)) return {};
@@ -2789,6 +2859,73 @@ async function initBusinessWorkspace(name, ...flags) {
2789
2859
  return createBusinessInternal(name, flags, 'canonical');
2790
2860
  }
2791
2861
 
2862
+ function printBusinessShipHelp() {
2863
+ console.log(BUSINESS_SHIP_USAGE);
2864
+ console.log('');
2865
+ console.log('Create a live business from one paragraph.');
2866
+ console.log(' --name <name> Optional business name');
2867
+ console.log(' --email <email> Optional customer email');
2868
+ console.log(' --yes Required to POST /business/ship');
2869
+ }
2870
+
2871
+ async function shipBusiness(...args) {
2872
+ if (args.some(isHelpToken)) {
2873
+ printBusinessShipHelp();
2874
+ return;
2875
+ }
2876
+
2877
+ if (!args.includes('--yes') && !args.includes('-y')) {
2878
+ console.error(BUSINESS_SHIP_USAGE);
2879
+ console.error('Pass --yes to create a live business.');
2880
+ process.exit(2);
2881
+ }
2882
+
2883
+ const parsed = parseBusinessShipArgs(args);
2884
+ if (!parsed.ok) {
2885
+ console.error(parsed.usage);
2886
+ if (parsed.detail) console.error(`\n ${parsed.detail}`);
2887
+ process.exit(1);
2888
+ }
2889
+
2890
+ const creds = loadCredentials();
2891
+ if (!creds || !creds.token) {
2892
+ console.error('Not logged in. Run: atris login');
2893
+ process.exit(1);
2894
+ }
2895
+
2896
+ const body = { description: parsed.description };
2897
+ if (parsed.name) body.name = parsed.name;
2898
+ if (parsed.email) body.customer_email = parsed.email;
2899
+
2900
+ console.log(`Shipping business: ${parsed.name || parsed.description}...`);
2901
+
2902
+ const result = await apiRequestJson('/business/ship', {
2903
+ method: 'POST',
2904
+ token: creds.token,
2905
+ body,
2906
+ });
2907
+
2908
+ if (!result.ok) {
2909
+ console.error(`Failed: ${result.errorMessage || result.error || result.status}`);
2910
+ process.exit(1);
2911
+ }
2912
+
2913
+ const payload = result.data || {};
2914
+ const slug = payload.slug || '';
2915
+ const label = payload.name || parsed.name || slug || 'business';
2916
+ const pageUrl = payload.public_page_url || '';
2917
+ const names = shipTeamNames(payload.team);
2918
+ const firstTask = shipTaskTitle(payload.seeded_task);
2919
+
2920
+ console.log('');
2921
+ console.log(` ${label}${slug ? ` (${slug})` : ''}`);
2922
+ if (pageUrl) console.log(` Public page: ${pageUrl}`);
2923
+ if (names.length > 0) console.log(` Team: ${names.join(', ')}`);
2924
+ if (firstTask) console.log(` First task: ${firstTask}`);
2925
+ console.log('');
2926
+ return payload;
2927
+ }
2928
+
2792
2929
 
2793
2930
  async function businessStatus(slug) {
2794
2931
  const creds = loadCredentials();
@@ -3364,6 +3501,7 @@ function printBusinessHelp() {
3364
3501
  console.log(' init <name> RECOMMENDED: create a business environment (cloud + local)');
3365
3502
  console.log(' workspace <name> Alias for init');
3366
3503
  console.log(' create <name> Cloud-only business record; add --workspace to also scaffold local');
3504
+ console.log(' ship "<paragraph>" One paragraph, live business (optional --name, --email; --yes required)');
3367
3505
  console.log(' simulate <idea> Create a business, four-role team, missions, and endgame loop');
3368
3506
  console.log(' add <slug> Register an existing cloud business');
3369
3507
  console.log(' list Show registered businesses');
@@ -3401,7 +3539,11 @@ async function businessCommand(subcommand, ...args) {
3401
3539
  printBusinessSimulateHelp();
3402
3540
  return;
3403
3541
  }
3404
- const nameTakingSubcommands = new Set(['create', 'new', 'init', 'workspace']);
3542
+ if (subcommand === 'ship' && args.some(isHelpToken)) {
3543
+ printBusinessShipHelp();
3544
+ return;
3545
+ }
3546
+ const nameTakingSubcommands = new Set(['create', 'new', 'init', 'workspace', 'ship']);
3405
3547
  if (nameTakingSubcommands.has(subcommand) && args.some(isHelpToken)) {
3406
3548
  printBusinessHelp();
3407
3549
  return;
@@ -3419,6 +3561,9 @@ async function businessCommand(subcommand, ...args) {
3419
3561
  case 'new':
3420
3562
  await createBusiness(args[0], ...args.slice(1));
3421
3563
  break;
3564
+ case 'ship':
3565
+ await shipBusiness(...args);
3566
+ break;
3422
3567
  case 'init':
3423
3568
  case 'workspace':
3424
3569
  await initBusinessWorkspace(args[0], ...args.slice(1));
@@ -3523,6 +3668,9 @@ module.exports = {
3523
3668
  businessMatchesSlug,
3524
3669
  isAccidentalHelpBusiness,
3525
3670
  validateBusinessCreateName,
3671
+ parseBusinessShipArgs,
3672
+ shipTeamNames,
3673
+ shipTaskTitle,
3526
3674
  analyzeBusinessDoctor,
3527
3675
  createCanonicalBusinessWorkspace,
3528
3676
  onboardBusiness,
@@ -2,6 +2,8 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { hasFlag } = require('../lib/arg-parser');
4
4
  const { scanChatLogs, writeLatestScan } = require('../lib/chat-log-scan');
5
+ const { parseScopeFlag } = require('../lib/cli-scope');
6
+ const { requireAccountBound, refuseAccountGlobal } = require('../lib/account-bound');
5
7
 
6
8
  // Preserve the existing rule that the next token is a value, even if it is a flag.
7
9
  function readFollowingFlag(args, name, fallback) {
@@ -57,7 +59,7 @@ function printReport(report) {
57
59
  function chatScanCommand(argv = []) {
58
60
  const args = argv.filter((a) => a !== '--');
59
61
  if (hasFlag(args, '--help') || hasFlag(args, '-h') || args[0] === 'help') {
60
- console.log(`Usage: atris chat scan [--json] [--hours <n>] [--limit <n>] [--no-write]
62
+ console.log(`Usage: atris chat scan [--json] [--hours <n>] [--limit <n>] [--no-write] [--global]
61
63
 
62
64
  Read recent ax chat logs and Cursor agent transcripts; surface friction/errors.
63
65
 
@@ -65,10 +67,20 @@ Options:
65
67
  --hours <n> Lookback window (default 720 = 30 days)
66
68
  --limit <n> Max sessions per source (default 12)
67
69
  --no-write Skip writing .atris/state/chat_scan.latest.json
68
- --json Machine-readable output`);
70
+ --json Machine-readable output
71
+ --global Include ~/.atris/runs (unbound folders need --account)`);
69
72
  return { ok: true };
70
73
  }
71
74
 
75
+ const scope = parseScopeFlag(args);
76
+ if (scope.global) {
77
+ const gate = requireAccountBound(scope.args);
78
+ if (!gate.ok) {
79
+ refuseAccountGlobal();
80
+ process.exit(2);
81
+ }
82
+ }
83
+
72
84
  const root = process.cwd();
73
85
  if (!fs.existsSync(path.join(root, 'atris'))) {
74
86
  console.error('✗ atris/ folder not found. Run from an Atris workspace.');
@@ -77,8 +89,9 @@ Options:
77
89
 
78
90
  const report = scanChatLogs({
79
91
  cwd: root,
80
- hours: readFollowingFlag(args, '--hours', 720),
81
- limit: readFollowingFlag(args, '--limit', 12),
92
+ hours: readFollowingFlag(scope.args, '--hours', 720),
93
+ limit: readFollowingFlag(scope.args, '--limit', 12),
94
+ global: scope.global,
82
95
  });
83
96
 
84
97
  let latestPath = null;
package/commands/close.js CHANGED
@@ -1239,7 +1239,11 @@ function run(args = [], context = {}) {
1239
1239
  const subcommand = args[0];
1240
1240
  const rest = args.slice(1);
1241
1241
  try {
1242
- if (!subcommand || subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
1242
+ if (!subcommand) {
1243
+ printHelp();
1244
+ return 2;
1245
+ }
1246
+ if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
1243
1247
  printHelp();
1244
1248
  return 0;
1245
1249
  }
@@ -2141,7 +2141,7 @@ async function runBusinessPromptViaRunnerProxy(token, ctx, prompt, options = {})
2141
2141
  const payload = {
2142
2142
  prompt,
2143
2143
  permission_mode: 'bypassPermissions',
2144
- max_turns: Math.min(Math.max(Number(options.maxTurns || 12), 1), 25),
2144
+ max_turns: Math.min(Math.max(Number(options.maxTurns || 40), 1), 100),
2145
2145
  reset_context: Boolean(options.resetContext),
2146
2146
  };
2147
2147
  if (options.worker) payload.worker = options.worker;
@@ -3399,7 +3399,7 @@ async function sendBusinessChat(token, ctx, message, sessionId, resetContext = f
3399
3399
  const fallback = await runBusinessPromptViaRunnerProxy(token, ctx, message, {
3400
3400
  ...options,
3401
3401
  resetContext,
3402
- maxTurns: 25,
3402
+ maxTurns: 60,
3403
3403
  });
3404
3404
  if (!fallback.ok) {
3405
3405
  if (typeof options.onFailure === 'function') {