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/now.js CHANGED
@@ -270,7 +270,13 @@ function taskReceiptTitle(row) {
270
270
  // one per receipt, landed result + human proof state. Shares the collector
271
271
  // with the count so the number and the visible list always match.
272
272
  function todayTaskReceiptLines(root = process.cwd(), date = new Date(), limit = 6) {
273
- return collectTaskReceiptsToday(root, date)
273
+ return formatTaskReceiptLines(collectTaskReceiptsToday(root, date), limit);
274
+ }
275
+
276
+ // Format an already-collected receipt list, so a caller that needs both the
277
+ // count and the lines (renderDefaultNow) parses the receipt files only once.
278
+ function formatTaskReceiptLines(receipts, limit = 6) {
279
+ return receipts
274
280
  .slice(-limit)
275
281
  .reverse()
276
282
  .map((r) => {
@@ -411,7 +417,8 @@ function renderDefaultNow(root = process.cwd()) {
411
417
  const journalPath = currentJournalPath(root);
412
418
  const openTodoCount = countOpenWorkItems(root, todoPath);
413
419
  const inboxCount = countMatches(journalPath, /^-\s+\*\*I\d+:/gm);
414
- const taskReceiptCount = countTaskReceiptsToday(root);
420
+ const taskReceipts = collectTaskReceiptsToday(root);
421
+ const taskReceiptCount = taskReceipts.length;
415
422
  const missionReceiptCount = countMissionReceiptsToday(root);
416
423
  const completedCount = taskReceiptCount + missionReceiptCount || countJournalCompletedReceipts(journalPath);
417
424
  const generated = formatLocalTimestamp();
@@ -419,7 +426,7 @@ function renderDefaultNow(root = process.cwd()) {
419
426
  const whatMattersNow = moveLine
420
427
  ? `${moveLine}\n\n- Run the named next action and leave proof before choosing another.`
421
428
  : '- Decide the next useful move before opening more context.';
422
- const receiptLines = todayTaskReceiptLines(root);
429
+ const receiptLines = formatTaskReceiptLines(taskReceipts);
423
430
  const missionReceiptLines = todayMissionReceiptLines(root);
424
431
  const commitLines = landedCommitLines(root);
425
432
  const hiddenReceiptCount = Math.max(0, completedCount - missionReceiptLines.length - receiptLines.length);
@@ -572,7 +579,7 @@ function refreshNowFile(root = process.cwd(), options = {}) {
572
579
  function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
573
580
  const help = args.includes('--help') || args.includes('-h') || args[0] === 'help';
574
581
  if (help) {
575
- console.log('Usage: atris now [--init|--refresh|--all|--path]');
582
+ console.log('Usage: atris now [--init|--refresh|--all|--path] [--json]');
576
583
  console.log('');
577
584
  console.log('Show the current operating truth for this workspace.');
578
585
  console.log('');
@@ -581,6 +588,7 @@ function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
581
588
  console.log(' atris now --refresh Regenerate a small local now.md');
582
589
  console.log(' atris now --all Refresh this parent and every child Atris workspace');
583
590
  console.log(' atris now --path Print the file path only');
591
+ console.log(' atris now --json Emit path and content as JSON');
584
592
  return;
585
593
  }
586
594
 
@@ -588,6 +596,7 @@ function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
588
596
  const refresh = args.includes('--refresh');
589
597
  const all = args.includes('--all');
590
598
  const pathOnly = args.includes('--path');
599
+ const asJson = args.includes('--json');
591
600
 
592
601
  let result;
593
602
  if (all) {
@@ -596,7 +605,7 @@ function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
596
605
  refreshNowFile(workspace.root);
597
606
  }
598
607
  result = refreshNowFile(root);
599
- if (!pathOnly) {
608
+ if (!pathOnly && !asJson) {
600
609
  console.log(`Refreshed ${workspaces.length} child workspace${workspaces.length === 1 ? '' : 's'}.`);
601
610
  console.log('');
602
611
  }
@@ -614,12 +623,22 @@ function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
614
623
  return;
615
624
  }
616
625
 
626
+ const content = fs.readFileSync(result.path, 'utf8').trimEnd();
627
+ if (asJson) {
628
+ console.log(JSON.stringify({
629
+ ok: true,
630
+ path: rel,
631
+ created: Boolean(result.created),
632
+ content,
633
+ }, null, 2));
634
+ return;
635
+ }
636
+
617
637
  if (result.created) {
618
638
  console.log(`Created ${rel}`);
619
639
  console.log('');
620
640
  }
621
641
 
622
- const content = fs.readFileSync(result.path, 'utf8').trimEnd();
623
642
  console.log(content);
624
643
  }
625
644
 
package/commands/orb.js CHANGED
@@ -86,10 +86,68 @@ function renderHint() {
86
86
  }
87
87
 
88
88
  function showHelp() {
89
- console.log('Usage: atris orb [--once] [--engine claude|codex|fast]');
89
+ console.log('Usage: atris orb [--once] [--json] [--engine claude|codex|fast]');
90
+ console.log(' atris orb <n> | atris orb --pick <n>');
90
91
  console.log(' atris orb scorecard [--days N]');
91
92
  console.log('');
92
93
  console.log('Choose a next move while background engine jobs keep working.');
94
+ console.log('--json emits {moves:[{n,label,command}]} and exits.');
95
+ console.log('Supported flags: --once, --json, --pick, --engine, --help, -h');
96
+ }
97
+
98
+ function suggestionCommand(suggestion, n) {
99
+ const kind = String(suggestion && suggestion.kind || '');
100
+ const label = String(suggestion && suggestion.label || '');
101
+ if (kind === 'digest') return 'atris stream --once';
102
+ if (kind === 'log') return 'atris log';
103
+ if (kind === 'review' && /^accept\b/i.test(label)) return 'atris task reviews';
104
+ return `atris orb --pick ${n}`;
105
+ }
106
+
107
+ function movesPayload(suggestions) {
108
+ return {
109
+ moves: suggestions.map((suggestion, index) => {
110
+ const n = index + 1;
111
+ return {
112
+ n,
113
+ label: suggestion.label || '',
114
+ command: suggestionCommand(suggestion, n),
115
+ };
116
+ }),
117
+ };
118
+ }
119
+
120
+ function parsePick(args = []) {
121
+ const equals = args.find((arg) => arg.startsWith('--pick='));
122
+ if (equals) {
123
+ const value = Number(equals.slice('--pick='.length));
124
+ return Number.isInteger(value) && value >= 1 ? value : null;
125
+ }
126
+ const idx = args.indexOf('--pick');
127
+ if (idx !== -1) {
128
+ const value = Number(args[idx + 1]);
129
+ return Number.isInteger(value) && value >= 1 ? value : null;
130
+ }
131
+ const positional = args.find((arg) => /^[1-9]$/.test(String(arg)));
132
+ if (positional) return Number(positional);
133
+ return null;
134
+ }
135
+
136
+ function unknownOrbFlags(args = []) {
137
+ const known = new Set(['--once', '--json', '--help', '-h', '--engine', '--pick']);
138
+ const unknown = [];
139
+ for (let i = 0; i < args.length; i += 1) {
140
+ const arg = String(args[i] || '');
141
+ if (!arg.startsWith('-')) continue;
142
+ if (arg.startsWith('--engine=')) continue;
143
+ if (arg.startsWith('--pick=')) continue;
144
+ if (arg === '--engine' || arg === '--pick') {
145
+ i += 1;
146
+ continue;
147
+ }
148
+ if (!known.has(arg)) unknown.push(arg);
149
+ }
150
+ return unknown;
93
151
  }
94
152
 
95
153
  function tailLines(text, count = 60) {
@@ -115,20 +173,57 @@ async function runOrb(args = []) {
115
173
  return 0;
116
174
  }
117
175
 
176
+ const unknown = unknownOrbFlags(args);
177
+ if (unknown.length) {
178
+ console.error(`unknown orb option: ${unknown.join(', ')}`);
179
+ console.error('supported flags: --once, --json, --pick, --engine, --help, -h');
180
+ return 2;
181
+ }
182
+
118
183
  const engine = parseEngine(args);
119
184
  if (!VALID_ENGINES.has(engine)) {
120
185
  console.error(`Unknown orb engine: ${engine || '(empty)'}. Use claude, codex, or fast.`);
121
186
  return 2;
122
187
  }
123
188
 
189
+ const asJson = args.includes('--json');
190
+ const pick = parsePick(args);
124
191
  const engineCommand = engineInvocation(engine, '').command;
125
- if (!findExecutableOnPath(engineCommand)) {
192
+ if (!asJson && !findExecutableOnPath(engineCommand)) {
126
193
  console.error(`orb warning: engine binary "${engineCommand}" is missing from PATH; picks will fail until it is installed.`);
127
194
  }
128
195
 
129
196
  const root = workspaceRoot();
130
197
  let context = await collectOrbContext(root, { threads: [] });
198
+
199
+ if (asJson) {
200
+ const suggestions = (context.suggestions || []).slice(0, MAX_VISIBLE_SUGGESTIONS);
201
+ console.log(JSON.stringify(movesPayload(suggestions), null, 2));
202
+ return 0;
203
+ }
204
+
131
205
  let visibleSuggestions = renderContext(context);
206
+
207
+ if (pick != null) {
208
+ const choice = visibleSuggestions[pick - 1];
209
+ if (!choice) {
210
+ console.error(`orb: no move ${pick}. Pick 1-${visibleSuggestions.length || 0}.`);
211
+ return 2;
212
+ }
213
+ // One-shot pick: record the choice and print the runnable command, then exit.
214
+ // Interactive mode still launches background engines; agents use --json + --pick.
215
+ const appended = await appendOrbChoice(root, cleanLabel(choice.label));
216
+ if (!appended.ok) {
217
+ console.error(`Could not update now.md: ${appended.error}`);
218
+ return 1;
219
+ }
220
+ const command = suggestionCommand(choice, pick);
221
+ console.log(`picked ${pick}: ${choice.label}`);
222
+ console.log(`next: ${command}`);
223
+ if (choice.prompt) console.log(`prompt: ${choice.prompt}`);
224
+ return 0;
225
+ }
226
+
132
227
  const once = args.includes('--once') || !process.stdin.isTTY || !process.stdout.isTTY;
133
228
  if (once) return 0;
134
229
 
package/commands/pack.js CHANGED
@@ -745,6 +745,74 @@ async function postPackToRegistry(manifest, zipBuffer, deps = {}) {
745
745
  return parsed.data;
746
746
  }
747
747
 
748
+ function paidPackBarrierFromResponse(response, slug) {
749
+ const status = Number(response && response.status) || 0;
750
+ if (status === 404) return null;
751
+
752
+ const rawBody = response && response.body;
753
+ const body = !rawBody
754
+ ? Buffer.alloc(0)
755
+ : (Buffer.isBuffer(rawBody) ? rawBody : Buffer.from(String(rawBody)));
756
+ const empty = body.length === 0;
757
+ const parsed = parseJsonBody(body);
758
+ const data = parsed.data && typeof parsed.data === 'object' && !Array.isArray(parsed.data)
759
+ ? parsed.data
760
+ : {};
761
+ const priceRaw = data.price_cents ?? data.priceCents;
762
+ const priceCents = Number(priceRaw);
763
+ const hasPrice = Number.isFinite(priceCents) && priceCents > 0;
764
+ const nameSource = [data.name, data.title, data.slug, slug]
765
+ .find((value) => typeof value === 'string' && value.trim());
766
+ const details = {
767
+ name: String(nameSource || slug || 'this pack').trim().toLowerCase(),
768
+ priceCents: hasPrice ? Math.round(priceCents) : null,
769
+ };
770
+
771
+ const entitled = data.entitled === true
772
+ || data.has_access === true
773
+ || data.hasAccess === true
774
+ || data.is_owner === true
775
+ || data.isOwner === true;
776
+ const unentitled = data.entitled === false
777
+ || data.has_access === false
778
+ || data.hasAccess === false
779
+ || data.entitlement === false
780
+ || data.entitlement === null;
781
+ const message = String(data.error || data.message || data.detail || '');
782
+ const looksLikeZip = body.length >= 4 && body[0] === 0x50 && body[1] === 0x4b;
783
+ const paid = status === 402
784
+ || data.paid === true
785
+ || data.is_paid === true
786
+ || data.isPaid === true
787
+ || data.purchase_required === true
788
+ || typeof data.checkout_hint === 'string'
789
+ || /purchase required/i.test(message)
790
+ || (hasPrice && (unentitled || !looksLikeZip));
791
+
792
+ if (entitled && status >= 200 && status < 300 && looksLikeZip) return null;
793
+ if (status === 402 || status === 403) return details;
794
+ if (status >= 200 && status < 300 && empty) return details;
795
+ if (paid && !entitled && !looksLikeZip) return details;
796
+ return null;
797
+ }
798
+
799
+ function printPaidPackBarrier(slug, details = {}, deps = {}) {
800
+ const name = String(details.name || slug || 'this pack').trim().toLowerCase() || 'this pack';
801
+ const cents = Number(details.priceCents);
802
+ const price = Number.isFinite(cents) && cents > 0 ? ` (${formatSalesDollars(cents)})` : '';
803
+ console.error(`this pack is paid: ${name}${price}.`);
804
+ console.error('buy it on its page, then run this again.');
805
+ console.error(registryUrl(`/packs/${encodeURIComponent(slug)}`, deps));
806
+ console.error('already bought it? run atris login');
807
+ }
808
+
809
+ function throwPaidPackBarrier(barrier, status) {
810
+ const error = new Error('this pack is paid');
811
+ error.status = status;
812
+ error.paidPack = barrier;
813
+ throw error;
814
+ }
815
+
748
816
  async function fetchRegistryZip(slug, deps = {}) {
749
817
  const url = registryUrl(`/api/pack/registry/${encodeURIComponent(slug)}`, deps);
750
818
  let result;
@@ -756,8 +824,10 @@ async function fetchRegistryZip(slug, deps = {}) {
756
824
  throw new Error('could not reach pack registry. check your connection and try again.');
757
825
  }
758
826
  const { response, authenticated } = result;
827
+ if (response.status === 404) throw registryNotFoundError(slug, authenticated);
828
+ const barrier = paidPackBarrierFromResponse(response, slug);
829
+ if (barrier) throwPaidPackBarrier(barrier, response.status);
759
830
  if (response.status < 200 || response.status >= 300) {
760
- if (response.status === 404) throw registryNotFoundError(slug, authenticated);
761
831
  const error = new Error(responseErrorText(response, `registry lookup failed for ${slug} with status ${response.status}`));
762
832
  error.status = response.status;
763
833
  throw error;
@@ -772,9 +842,8 @@ async function fetchRegistryZipForUser(slug, deps = {}) {
772
842
  try {
773
843
  return await fetchRegistryZip(slug, deps);
774
844
  } catch (error) {
775
- if (error.status !== 402) throw error;
776
- console.error('this pack is paid. buy it on its page, then run this again.');
777
- console.error(registryUrl(`/packs/${encodeURIComponent(slug)}`, deps));
845
+ if (!error || !error.paidPack) throw error;
846
+ printPaidPackBarrier(slug, error.paidPack, deps);
778
847
  return null;
779
848
  }
780
849
  }
@@ -2734,17 +2803,22 @@ async function updatePack(rawArgs, cwd = process.cwd(), options = {}) {
2734
2803
  const headers = credentials && credentials.token ? { Authorization: `Bearer ${credentials.token}` } : {};
2735
2804
  response = await request(url, { method: 'GET', headers });
2736
2805
  }
2737
- if (response.status < 200 || response.status >= 300) {
2738
- if (origin.type === 'registry' && response.status === 404) {
2806
+ if (origin.type === 'registry') {
2807
+ if (response.status === 404) {
2739
2808
  throw registryNotFoundError(origin.slug, authenticated);
2740
2809
  }
2741
- if (origin.type === 'registry' && response.status === 402) {
2742
- console.error('this pack is paid. buy it on its page, then run this again.');
2743
- console.error(registryUrl(`/packs/${encodeURIComponent(origin.slug)}`, deps));
2810
+ const barrier = paidPackBarrierFromResponse(response, origin.slug);
2811
+ if (barrier) {
2812
+ printPaidPackBarrier(origin.slug, barrier, deps);
2744
2813
  return 1;
2745
2814
  }
2815
+ }
2816
+ if (response.status < 200 || response.status >= 300) {
2746
2817
  throw new Error(`download failed with status ${response.status}`);
2747
2818
  }
2819
+ if (!response.body || response.body.length === 0) {
2820
+ throw new Error('download returned an empty zip');
2821
+ }
2748
2822
 
2749
2823
  const entries = readZipBuffer(response.body);
2750
2824
  return stagePackUpdate({