atris 3.57.4 → 3.58.5

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 (174) hide show
  1. package/README.md +12 -1
  2. package/atris/skills/clawhub/member-runtime/SKILL.md +9 -9
  3. package/atris/skills/create-member/SKILL.md +16 -13
  4. package/atris/skills/design/SKILL.md +27 -4
  5. package/atris/skills/engines/SKILL.md +55 -12
  6. package/atris/skills/x-search/SKILL.md +8 -6
  7. package/atris/skills/youtube/SKILL.md +37 -8
  8. package/atris/team/brainstormer/MEMBER.md +2 -2
  9. package/atris/team/executor/MEMBER.md +3 -3
  10. package/atris/team/launcher/MEMBER.md +4 -4
  11. package/atris/team/navigator/MEMBER.md +3 -3
  12. package/atris/team/researcher/MEMBER.md +2 -2
  13. package/atris/team/validator/MEMBER.md +1 -1
  14. package/atris.md +14 -0
  15. package/ax +153 -23
  16. package/bin/atris.js +78 -30
  17. package/commands/aeo.js +4 -4
  18. package/commands/agents.js +1 -1
  19. package/commands/align.js +12 -12
  20. package/commands/analytics.js +3 -3
  21. package/commands/app.js +5 -5
  22. package/commands/ask.js +175 -0
  23. package/commands/auth.js +7 -7
  24. package/commands/autoland.js +29 -5
  25. package/commands/autopilot-front.js +6 -6
  26. package/commands/autopilot.js +90 -90
  27. package/commands/avail.js +5 -5
  28. package/commands/bench.js +63 -1
  29. package/commands/browse.js +2 -2
  30. package/commands/business.js +12 -12
  31. package/commands/card.js +2 -2
  32. package/commands/clean.js +13 -13
  33. package/commands/close.js +3 -3
  34. package/commands/compile.js +13 -13
  35. package/commands/computer.js +94 -27
  36. package/commands/console.js +5 -5
  37. package/commands/decide.js +1 -1
  38. package/commands/deck.js +9 -9
  39. package/commands/drive.js +4 -4
  40. package/commands/engine.js +173 -19
  41. package/commands/errors.js +3 -3
  42. package/commands/experiments.js +18 -1
  43. package/commands/feed.js +2 -2
  44. package/commands/feedback.js +4 -4
  45. package/commands/fleet.js +12 -12
  46. package/commands/game.js +1 -1
  47. package/commands/guide.js +49 -0
  48. package/commands/improve.js +19 -17
  49. package/commands/init.js +67 -19
  50. package/commands/interview.js +7 -7
  51. package/commands/land.js +40 -15
  52. package/commands/learn.js +12 -12
  53. package/commands/lesson.js +2 -2
  54. package/commands/log.js +1 -1
  55. package/commands/loops.js +4 -4
  56. package/commands/member.js +75 -50
  57. package/commands/mission.js +90 -82
  58. package/commands/one-lap.js +1 -1
  59. package/commands/pack.js +74 -8
  60. package/commands/plugin.js +6 -6
  61. package/commands/probe.js +9 -9
  62. package/commands/pull.js +35 -35
  63. package/commands/pulse.js +1 -1
  64. package/commands/push.js +21 -21
  65. package/commands/rainmaker.js +1 -1
  66. package/commands/recap.js +7 -7
  67. package/commands/reel.js +2 -2
  68. package/commands/release.js +3 -3
  69. package/commands/report.js +1 -1
  70. package/commands/review.js +5 -5
  71. package/commands/revisions.js +1 -1
  72. package/commands/run.js +1 -1
  73. package/commands/scout.js +3 -3
  74. package/commands/security-review.js +7 -7
  75. package/commands/serve.js +9 -9
  76. package/commands/setup.js +1 -1
  77. package/commands/sign.js +4 -4
  78. package/commands/signup.js +4 -4
  79. package/commands/site.js +8 -3
  80. package/commands/skill.js +6 -6
  81. package/commands/slop.js +18 -18
  82. package/commands/social.js +3 -3
  83. package/commands/soul.js +14 -14
  84. package/commands/spaceship.js +1 -1
  85. package/commands/status.js +2 -2
  86. package/commands/strings.js +13 -13
  87. package/commands/study.js +4 -4
  88. package/commands/sync.js +8 -8
  89. package/commands/task.js +50 -25
  90. package/commands/teach.js +2 -2
  91. package/commands/team.js +1 -1
  92. package/commands/theme.js +2 -2
  93. package/commands/tree.js +34 -0
  94. package/commands/truth.js +7 -7
  95. package/commands/verify.js +12 -12
  96. package/commands/visualize.js +2 -2
  97. package/commands/workflow.js +97 -40
  98. package/commands/worktree.js +1 -1
  99. package/commands/write.js +9 -9
  100. package/commands/x-search.js +89 -17
  101. package/commands/xp.js +6 -3
  102. package/commands/youtube.js +601 -48
  103. package/lib/accept-verify-gate.js +3 -3
  104. package/lib/activity-stream.js +1 -1
  105. package/lib/auto-accept-certified.js +28 -12
  106. package/lib/autoland.js +6 -6
  107. package/lib/bench/engines.js +59 -8
  108. package/lib/bench/report.js +1 -1
  109. package/lib/bench/runner.js +204 -5
  110. package/lib/bench/tree-render.js +76 -0
  111. package/lib/card.js +1 -1
  112. package/lib/chat-log-scan.js +1 -1
  113. package/lib/claude-boot-block.js +2 -0
  114. package/lib/deck-compose.js +1 -1
  115. package/lib/deck-history.js +2 -2
  116. package/lib/deck-layout.js +1 -1
  117. package/lib/deck-review.js +5 -5
  118. package/lib/deck-schema.js +1 -1
  119. package/lib/default-verifier.js +1 -1
  120. package/lib/developer-api.js +3 -0
  121. package/lib/engine-ask.js +45 -7
  122. package/lib/engine-registry.js +15 -2
  123. package/lib/file-ops.js +1 -1
  124. package/lib/fleet.js +71 -35
  125. package/lib/html-render.js +1 -1
  126. package/lib/intents.js +172 -0
  127. package/lib/journal.js +1 -1
  128. package/lib/known-commands.js +2 -2
  129. package/lib/land-green.js +364 -0
  130. package/lib/lesson-contradiction.js +2 -2
  131. package/lib/lesson-preflight.js +1 -1
  132. package/lib/manifest.js +5 -5
  133. package/lib/member-alive.js +9 -5
  134. package/lib/memory-view.js +3 -3
  135. package/lib/mission-protected-lane.js +1 -1
  136. package/lib/mission-root.js +2 -2
  137. package/lib/orb-context.js +10 -10
  138. package/lib/pack-capabilities.js +861 -3
  139. package/lib/permission-grants.js +2 -2
  140. package/lib/policy-lessons.js +6 -6
  141. package/lib/pulse.js +8 -8
  142. package/lib/receipt-evidence.js +1 -1
  143. package/lib/reel.js +1 -1
  144. package/lib/review-integrity.js +1 -1
  145. package/lib/runner-command.js +35 -5
  146. package/lib/scorecard.js +4 -4
  147. package/lib/security-scan.js +5 -5
  148. package/lib/self-drive.js +1 -1
  149. package/lib/site-publish.js +372 -0
  150. package/lib/site.js +1 -1
  151. package/lib/slides-deck.js +2 -2
  152. package/lib/state-detection.js +2 -2
  153. package/lib/sync-telemetry.js +2 -2
  154. package/lib/task-db.js +17 -10
  155. package/lib/task-explanation.js +1 -1
  156. package/lib/task-proof.js +2 -2
  157. package/lib/task-receipt.js +3 -3
  158. package/lib/theme.js +12 -12
  159. package/lib/todo-fallback.js +3 -3
  160. package/lib/todo-sections.js +1 -1
  161. package/lib/tree-hash.js +79 -0
  162. package/lib/usage.js +1 -1
  163. package/lib/wish-delegate.js +1 -1
  164. package/lib/workspace-safety.js +4 -4
  165. package/lib/workspace-scaffold.js +3 -3
  166. package/package.json +1 -1
  167. package/scripts/det/concierge-bakeoff.sh +26 -0
  168. package/scripts/det/ytquote-repair.js +0 -1
  169. package/scripts/det/ytrail-race.js +0 -5
  170. package/scripts/member-operate.mjs +52 -14
  171. package/scripts/outbound-artifact-gate.js +0 -3
  172. package/utils/auth.js +0 -3
  173. package/utils/config.js +0 -1
  174. package/scripts/det/checklist-score.js +0 -191
package/commands/push.js CHANGED
@@ -491,7 +491,7 @@ async function pushAtris() {
491
491
  // Push the whole business workspace by default. Brain-only sync must be
492
492
  // explicit with --only atris/ so root workspace files cannot be missed.
493
493
 
494
- // Resolve business always refresh from API
494
+ // Resolve business, always refresh from API
495
495
  let businessId, workspaceId, businessName, resolvedSlug;
496
496
  const businesses = loadBusinesses();
497
497
  const listResult = await apiRequestJson('/business/', { method: 'GET', token: creds.token });
@@ -516,7 +516,7 @@ async function pushAtris() {
516
516
 
517
517
  if (!workspaceId) { console.error(`Business "${slug}" has no workspace.`); process.exit(1); }
518
518
 
519
- // Telemetry helper emits one event with the elapsed wall-clock time.
519
+ // Telemetry helper, emits one event with the elapsed wall-clock time.
520
520
  // Awaited (not fire-and-forget) because process.exit kills in-flight requests.
521
521
  const emit = (outcome, extras = {}) =>
522
522
  emitSyncEvent(creds.token, businessId, workspaceId, 'push', outcome, elapsedMs(), extras);
@@ -590,12 +590,12 @@ async function pushAtris() {
590
590
  console.log(`Pushing to ${businessName}...`);
591
591
 
592
592
  // ───────────────────────────────────────────────────────────────────
593
- // FRESHNESS CHECK pull-before-push enforcement.
593
+ // FRESHNESS CHECK, pull-before-push enforcement.
594
594
  // ───────────────────────────────────────────────────────────────────
595
595
  // Compare cloud's current state to our local manifest. If cloud has any
596
596
  // file the manifest doesn't know about, OR a file with a different hash
597
597
  // than what we last pulled, the user is out of date and MUST pull first.
598
- // This prevents stale local state from clobbering fresh cloud changes
598
+ // This prevents stale local state from clobbering fresh cloud changes,
599
599
  // the "lagging version push" footgun. Use --force to bypass (e.g., for
600
600
  // genuine local-canonical pushes like align --hard).
601
601
  if (!force) {
@@ -624,7 +624,7 @@ async function pushAtris() {
624
624
  // files on the next push, undoing the deletion.
625
625
  //
626
626
  // CAVEAT: the warm runner's snapshot endpoint deliberately hides certain
627
- // basenames (CLAUDE.md, .* dotfiles, node_modules, __pycache__, .git)
627
+ // basenames (CLAUDE.md, .* dotfiles, node_modules, __pycache__, .git),
628
628
  // see ecs_warm_runner.py _snapshot_dir. They CAN exist on cloud but
629
629
  // never appear in cloudHashes. Skip them in the missing-side check or
630
630
  // every CLAUDE.md push will be flagged as drift forever.
@@ -646,7 +646,7 @@ async function pushAtris() {
646
646
  }
647
647
  console.log('fresh');
648
648
  } else {
649
- // Snapshot fetch failed fail closed. The whole point of the freshness
649
+ // Snapshot fetch failed, fail closed. The whole point of the freshness
650
650
  // check is to prevent accidental stale pushes; if we can't verify cloud
651
651
  // state, we don't push. Use --force to bypass when you know what you're
652
652
  // doing (e.g., the workspace is genuinely unhealthy and you have a clean
@@ -661,7 +661,7 @@ async function pushAtris() {
661
661
  }
662
662
  }
663
663
 
664
- // Compare local hashes to manifest NO server call needed
664
+ // Compare local hashes to manifest, NO server call needed
665
665
  // Files where local hash differs from manifest = changed locally
666
666
  if (parsed.changed && Array.isArray(onlyPrefixes) && onlyPrefixes.length === 0 && (!parsed.pathArgs || parsed.pathArgs.length === 0) && !parsed.onlyRaw) {
667
667
  console.log('\n Already up to date.\n');
@@ -699,7 +699,7 @@ async function pushAtris() {
699
699
  process.exit(1);
700
700
  }
701
701
 
702
- // Dry run show what would be pushed without pushing
702
+ // Dry run, show what would be pushed without pushing
703
703
  if (dryRun) {
704
704
  console.log('');
705
705
  for (const f of filesToPush) {
@@ -765,7 +765,7 @@ async function pushAtris() {
765
765
  // The /sync endpoint can silently drop files (role-based filters on the
766
766
  // business workspace route, path-rejection inside the warm runner, etc.)
767
767
  // and still return HTTP 200. If we don't cross-check per-file results,
768
- // the CLI prints "Pushed" for files that never actually landed and
768
+ // the CLI prints "Pushed" for files that never actually landed, and
769
769
  // the manifest records a hash that makes the next push skip them too,
770
770
  // losing them permanently. `failedToLand` collects those casualties so
771
771
  // we can warn the user AND keep them out of the manifest update.
@@ -818,7 +818,7 @@ async function pushAtris() {
818
818
  }
819
819
  }
820
820
  } else {
821
- // No results array in response old server. Best-effort: assume
821
+ // No results array in response, old server. Best-effort: assume
822
822
  // everything sent landed. This preserves existing behavior when
823
823
  // talking to a server that doesn't return per-file status.
824
824
  for (const f of sentFiles) {
@@ -835,7 +835,7 @@ async function pushAtris() {
835
835
  };
836
836
 
837
837
  if (filesToPush.length > 0) {
838
- // Push files to server (strip leading slash server requires workspace-relative paths)
838
+ // Push files to server (strip leading slash, server requires workspace-relative paths)
839
839
  const uploadBatches = buildPushUploadBatches(filesToPush);
840
840
  for (const batch of uploadBatches) {
841
841
  result = await syncFiles(batch);
@@ -848,7 +848,7 @@ async function pushAtris() {
848
848
  await emit('access_denied', { error_detail: detail });
849
849
  process.exit(1);
850
850
  }
851
- // Permission denied retry with only team/ and journal/ files
851
+ // Permission denied, retry with only team/ and journal/ files
852
852
  const allowed = filesToPush.filter(f => f.path.startsWith('/team/') || f.path.startsWith('/journal/'));
853
853
  skipped = filesToPush.filter(f => !f.path.startsWith('/team/') && !f.path.startsWith('/journal/'));
854
854
 
@@ -901,7 +901,7 @@ async function pushAtris() {
901
901
  }
902
902
  }
903
903
 
904
- // Delete loop throttled, 429-aware, tracks per-file success/failure.
904
+ // Delete loop, throttled, 429-aware, tracks per-file success/failure.
905
905
  // Earlier bug: bulk deletes hit rate limit (60/min default) at request 60,
906
906
  // then process.exit'd, leaving partial state and a manifest that thought
907
907
  // everything was deleted. New behavior:
@@ -924,7 +924,7 @@ async function pushAtris() {
924
924
  { method: 'DELETE', token: creds.token }
925
925
  );
926
926
  if (deleteResult.status === 429) {
927
- // Rate limit wait 20s, retry once
927
+ // Rate limit, wait 20s, retry once
928
928
  _rateLimitedDeletes++;
929
929
  await new Promise((r) => setTimeout(r, 20000));
930
930
  deleteResult = await apiRequestJson(
@@ -950,7 +950,7 @@ async function pushAtris() {
950
950
  if (deleteFailed.length > 10) console.log(` ... +${deleteFailed.length - 10} more`);
951
951
  }
952
952
 
953
- // Display results only for files the server confirmed as landed.
953
+ // Display results, only for files the server confirmed as landed.
954
954
  // A non-technical user seeing "+ atris/ideas/foo.md new file" naturally
955
955
  // assumes foo.md is on cloud. So we only print that line when the server
956
956
  // actually confirmed it via per-file status. Anything else goes into the
@@ -965,12 +965,12 @@ async function pushAtris() {
965
965
  for (const f of skipped) {
966
966
  console.log(` - ${f.path.replace(/^\//, '')} skipped (no permission)`);
967
967
  }
968
- // Only print confirmed deletes (not failed ones they were reported above)
968
+ // Only print confirmed deletes (not failed ones, they were reported above)
969
969
  for (const filePath of deletedConfirmed) {
970
970
  console.log(` x ${filePath.replace(/^\//, '')} deleted`);
971
971
  }
972
972
 
973
- // Loud failure block files the server silently dropped or rejected.
973
+ // Loud failure block, files the server silently dropped or rejected.
974
974
  // These did NOT land on cloud even though the HTTP call returned 200.
975
975
  if (failedToLand.length > 0) {
976
976
  console.log('');
@@ -978,7 +978,7 @@ async function pushAtris() {
978
978
  console.log(` dropped or rejected these files):`);
979
979
  const shown = failedToLand.slice(0, 15);
980
980
  for (const f of shown) {
981
- const detail = f.error ? ` ${f.error}` : ` (${f.status})`;
981
+ const detail = f.error ? `, ${f.error}` : ` (${f.status})`;
982
982
  console.log(` ✗ ${f.path.replace(/^\//, '')}${detail}`);
983
983
  }
984
984
  if (failedToLand.length > shown.length) {
@@ -1000,13 +1000,13 @@ async function pushAtris() {
1000
1000
  if (skipped.length > 0) parts.push(`${skipped.length} skipped`);
1001
1001
  console.log(` ${parts.join(', ')}.`);
1002
1002
 
1003
- // Update manifest mark pushed files with their new hash, drop ONLY confirmed deletes.
1003
+ // Update manifest, mark pushed files with their new hash, drop ONLY confirmed deletes.
1004
1004
  // Failed deletes stay in the manifest so the next push will retry them.
1005
1005
  //
1006
1006
  // CRITICAL: only record manifest entries for files the server confirmed as
1007
1007
  // landed (landedPaths). If we recorded the local hash for a file that the
1008
1008
  // server silently dropped, the next push would compare local==manifest and
1009
- // skip it the file would never land. Keeping it OUT of the manifest means
1009
+ // skip it, the file would never land. Keeping it OUT of the manifest means
1010
1010
  // the next push sees it as new/changed and retries automatically.
1011
1011
  const updatedFiles = { ...baseFiles };
1012
1012
  for (const f of filesToPush) {
@@ -1019,7 +1019,7 @@ async function pushAtris() {
1019
1019
  }
1020
1020
  saveManifest(resolvedSlug || slug, buildManifest(updatedFiles, null, { workspaceRoot: sourceDir }));
1021
1021
 
1022
- // Telemetry outcome reflects actual run quality, not just exit-code-zero.
1022
+ // Telemetry, outcome reflects actual run quality, not just exit-code-zero.
1023
1023
  // Partial delete failures or rate-limit retries mean the run was NOT a clean win;
1024
1024
  // labeling them success would poison the RL signal.
1025
1025
  const bytesChanged = filesToPush.reduce((acc, f) => acc + (f.content ? Buffer.byteLength(f.content, 'utf8') : 0), 0);
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * atris rainmaker passthrough to atrisos-backend/scripts/rainmaker.py
4
+ * atris rainmaker, passthrough to atrisos-backend/scripts/rainmaker.py
5
5
  *
6
6
  * atris rainmaker # ASCII status dashboard
7
7
  * atris rainmaker instinct # one morning opinion
package/commands/recap.js CHANGED
@@ -160,14 +160,14 @@ function buildRecapData(root = process.cwd(), { days = DEFAULT_DAYS } = {}) {
160
160
  function renderRecap(data) {
161
161
  if (data.empty) {
162
162
  return [
163
- `RECAP ${data.workspace}`,
163
+ `RECAP: ${data.workspace}`,
164
164
  '',
165
165
  'No task history yet.',
166
166
  'Run "atris init", then let Atris do one small job. Finished work will show up here with the checks that passed.',
167
167
  ].join('\n');
168
168
  }
169
169
  const lines = [];
170
- lines.push(`RECAP ${data.workspace} last ${data.days} day${data.days === 1 ? '' : 's'}`);
170
+ lines.push(`RECAP: ${data.workspace}, last ${data.days} day${data.days === 1 ? '' : 's'}`);
171
171
  lines.push('');
172
172
  lines.push('Plain English: what changed, how it was checked, and what still needs you.');
173
173
  const headline = [];
@@ -179,7 +179,7 @@ function renderRecap(data) {
179
179
 
180
180
  if (data.shipped.length) {
181
181
  lines.push('');
182
- lines.push(`DONE ${data.shipped.length}`);
182
+ lines.push(`DONE: ${data.shipped.length}`);
183
183
  for (const t of data.shipped.slice(0, 12)) {
184
184
  lines.push(` ${t.id} ${shortTitle(t.title)}`);
185
185
  const check = plainCheck(t.proof);
@@ -190,7 +190,7 @@ function renderRecap(data) {
190
190
 
191
191
  if (data.waiting.length) {
192
192
  lines.push('');
193
- lines.push(`NEEDS YOU ${data.waiting.length}`);
193
+ lines.push(`NEEDS YOU: ${data.waiting.length}`);
194
194
  for (const t of data.waiting.slice(0, 10)) {
195
195
  lines.push(` ${t.id} ${shortTitle(t.title)}`);
196
196
  const check = plainCheck(t.proof);
@@ -202,7 +202,7 @@ function renderRecap(data) {
202
202
 
203
203
  if (data.checking.length) {
204
204
  lines.push('');
205
- lines.push(`STILL BEING CHECKED ${data.checking.length}`);
205
+ lines.push(`STILL BEING CHECKED: ${data.checking.length}`);
206
206
  for (const t of data.checking.slice(0, 10)) {
207
207
  lines.push(` ${t.id} ${shortTitle(t.title)}`);
208
208
  }
@@ -211,7 +211,7 @@ function renderRecap(data) {
211
211
 
212
212
  if (data.inProgress.length) {
213
213
  lines.push('');
214
- lines.push(`STILL WORKING ${data.inProgress.length}`);
214
+ lines.push(`STILL WORKING: ${data.inProgress.length}`);
215
215
  for (const t of data.inProgress) {
216
216
  lines.push(` ${t.id} ${shortTitle(t.title)}${t.owner ? ` @${t.owner}` : ''}`);
217
217
  }
@@ -224,7 +224,7 @@ function renderRecap(data) {
224
224
  }
225
225
 
226
226
  function renderShare(data) {
227
- if (data.empty) return `Nothing to share yet on ${data.workspace} no finished tasks on record.`;
227
+ if (data.empty) return `Nothing to share yet on ${data.workspace}, no finished tasks on record.`;
228
228
  const lines = [];
229
229
  lines.push(`What got done on ${data.workspace} in the last ${data.days} day${data.days === 1 ? '' : 's'}:`);
230
230
  lines.push('');
package/commands/reel.js CHANGED
@@ -1,4 +1,4 @@
1
- // atris reel one line of text into a short, on-brand video (an animated card).
1
+ // atris reel, one line of text into a short, on-brand video (an animated card).
2
2
  //
3
3
  // atris reel "Ship faster" --theme brand --size square
4
4
  // atris reel "It just works." --kind quote --by "a founder" --seconds 3
@@ -72,7 +72,7 @@ async function renderFrames(chrome, ts, spec, dir, w, h, onDone) {
72
72
  async function run(argv) {
73
73
  const { flags, pos } = parseFlags(argv);
74
74
  if (pos[0] === 'help' || flags.help) {
75
- console.log(`\n atris reel one line of text into a short on-brand video\n
75
+ console.log(`\n atris reel, one line of text into a short on-brand video\n
76
76
  atris reel "Your headline" [--kind statement|quote|stat] [--theme <name>] [--size square|og|wide|story] [--seconds 2.6]
77
77
  flags: --sub --kicker --by --number --label --brand --version --out <file.mp4>\n`);
78
78
  return 0;
@@ -48,7 +48,7 @@ async function releaseAtris({ dryRun = false } = {}) {
48
48
  // 3. Build changelog
49
49
  const changelog = commitLines.map(l => `- ${l}`).join('\n');
50
50
 
51
- // 4. Determine bump type minor if any scorecard has reward >= 5, else patch
51
+ // 4. Determine bump type: minor if any scorecard has reward >= 5, else patch
52
52
  const bumpType = determineBumpType(cwd);
53
53
  console.log(`bump type: ${bumpType}`);
54
54
 
@@ -104,7 +104,7 @@ async function releaseAtris({ dryRun = false } = {}) {
104
104
  execFileSync('git', ['push', '--tags'], { cwd });
105
105
  console.log('pushed');
106
106
  } catch (err) {
107
- console.log('push failed you may need to push manually');
107
+ console.log('push failed, you may need to push manually');
108
108
  }
109
109
 
110
110
  // 11. Create GitHub release via gh
@@ -115,7 +115,7 @@ async function releaseAtris({ dryRun = false } = {}) {
115
115
  ], { cwd });
116
116
  console.log(`github release created: v${nextVersion}`);
117
117
  } catch (err) {
118
- console.log('gh release create failed install gh or create release manually');
118
+ console.log('gh release create failed, install gh or create release manually');
119
119
  }
120
120
 
121
121
  // 12. Draft launch post
@@ -39,7 +39,7 @@ function timestampMs(value) {
39
39
  }
40
40
 
41
41
  function inlineText(value, fallback = 'untitled') {
42
- const text = String(value || '').replace(/[—–]/g, '-').replace(/\s+/g, ' ').trim();
42
+ const text = String(value || '').replace(/[\u2014\u2013]/g, '-').replace(/\s+/g, ' ').trim();
43
43
  return text || fallback;
44
44
  }
45
45
 
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Atris Review Run code review with specialist agents
2
+ * Atris Review: run code review with specialist agents
3
3
  *
4
- * atris review Review staged changes
5
- * atris review <file> Review a specific file
6
- * atris review --diff HEAD~1 Review last commit
7
- * atris review --all Audit all Python services
4
+ * atris review : review staged changes
5
+ * atris review <file> : review a specific file
6
+ * atris review --diff HEAD~1 : review last commit
7
+ * atris review --all : audit all Python services
8
8
  */
9
9
 
10
10
  const fs = require('fs');
@@ -11,7 +11,7 @@ function plural(count, word) {
11
11
 
12
12
  function plainSubject(subject) {
13
13
  return String(subject || 'untitled landing')
14
- .replace(/—/g, ',')
14
+ .replace(/\u2014/g, ',')
15
15
  .replace(/\s+/g, ' ')
16
16
  .trim()
17
17
  .toLowerCase();
package/commands/run.js CHANGED
@@ -381,7 +381,7 @@ async function runAtris(options = {}) {
381
381
  process.exit(1);
382
382
  }
383
383
 
384
- // Check configured runner CLI is available. Dry runs preview context only
384
+ // Check configured runner CLI is available. Dry runs preview context only,
385
385
  // they must work on machines (and CI) without the runner installed.
386
386
  if (!dryRun) {
387
387
  try {
package/commands/scout.js CHANGED
@@ -1,4 +1,4 @@
1
- // atris scout dispatch cheap Haiku subagents for search/context questions.
1
+ // atris scout: dispatch cheap Haiku subagents for search/context questions.
2
2
  // Mirrors Claude Code's built-in subagent pattern: read-only, bounded report,
3
3
  // main session never pays for the exploration.
4
4
  //
@@ -44,7 +44,7 @@ const ENGINES = {
44
44
  try {
45
45
  const j = JSON.parse(out);
46
46
  if (j && typeof j.output === 'string') return j.ok === false ? `fast lane error: ${j.error || j.output}` : j.output.trim();
47
- } catch (_) { /* not JSON fall through to raw */ }
47
+ } catch (_) { /* not JSON, fall through to raw */ }
48
48
  return out.trim();
49
49
  },
50
50
  },
@@ -138,7 +138,7 @@ async function scoutCommand(argv) {
138
138
 
139
139
  function showHelp() {
140
140
  console.log(`
141
- atris scout cheap read-only subagents for search & context questions
141
+ atris scout: cheap read-only subagents for search & context questions
142
142
 
143
143
  usage:
144
144
  atris scout "<question>" ["<question>" ...] parallel scouts, one per question
@@ -1,4 +1,4 @@
1
- // atris security-review deterministic secrets / PII / privacy scan (no LLM).
1
+ // atris security-review, deterministic secrets / PII / privacy scan (no LLM).
2
2
  //
3
3
  // "Is this workspace safe to commit, publish, or hand to an autonomous loop?"
4
4
  // Answers it with facts: file:line + rule + severity. Exit 1 on a HIGH finding,
@@ -200,7 +200,7 @@ function securityReviewCommand(argv = []) {
200
200
  if (!opts.quiet) {
201
201
  console.log('\n ◉ atris security review');
202
202
  if (!findings.length) {
203
- console.log(`\n ✓ clean no active secrets, PII, or sensitive files in ${raw.scanned} tracked file${raw.scanned === 1 ? '' : 's'}`);
203
+ console.log(`\n ✓ clean, no active secrets, PII, or sensitive files in ${raw.scanned} tracked file${raw.scanned === 1 ? '' : 's'}`);
204
204
  if (result.suppressed) console.log(` ${result.suppressed} accepted finding${result.suppressed === 1 ? '' : 's'} suppressed by ${result.baseline.path}`);
205
205
  console.log('');
206
206
  return 0;
@@ -225,10 +225,10 @@ function securityReviewCommand(argv = []) {
225
225
  }
226
226
 
227
227
  // The landing: short, true, decision-ready. What you read after the overnight
228
- // loop the opposite of a finding dump.
228
+ // loop, the opposite of a finding dump.
229
229
  function renderLanding(landing, threshold) {
230
230
  const date = new Date().toISOString().slice(0, 10);
231
- const L = ['', ` ✈ security landing ${date}`, ''];
231
+ const L = ['', ` ✈ security landing, ${date}`, ''];
232
232
  if (landing.cleared) {
233
233
  L.push(` CLEARED TO SHIP no unresolved findings at the ${threshold.toUpperCase()} line`);
234
234
  } else {
@@ -248,7 +248,7 @@ function renderLanding(landing, threshold) {
248
248
  if (landing.open.length) {
249
249
  L.push(' needs you:');
250
250
  for (const f of landing.open.slice(0, 10)) {
251
- L.push(` ${f.sev.toUpperCase().padEnd(8)} ${f.file}${f.line ? ':' + f.line : ''} ${f.why}`);
251
+ L.push(` ${f.sev.toUpperCase().padEnd(8)} ${f.file}${f.line ? ':' + f.line : ''}, ${f.why}`);
252
252
  }
253
253
  if (landing.open.length > 10) L.push(` … and ${landing.open.length - 10} more`);
254
254
  } else {
@@ -268,7 +268,7 @@ function renderLanding(landing, threshold) {
268
268
  }
269
269
 
270
270
  function printRules() {
271
- console.log('\n atris security-review deterministic rules:\n');
271
+ console.log('\n atris security-review, deterministic rules:\n');
272
272
  for (const sev of SEVERITIES) {
273
273
  for (const r of RULES.filter((rule) => rule.sev === sev)) {
274
274
  console.log(` ${r.sev.toUpperCase().padEnd(8)} ${r.cat.padEnd(8)} ${r.id.padEnd(22)} ${r.why}`);
@@ -396,7 +396,7 @@ function installHook() {
396
396
 
397
397
  function printHelp() {
398
398
  console.log(`
399
- atris security-review deterministic secrets / PII / privacy scan (no LLM)
399
+ atris security-review, deterministic secrets / PII / privacy scan (no LLM)
400
400
 
401
401
  atris security-review scan git-tracked files (default)
402
402
  atris security-review <path> scan a file or dir
package/commands/serve.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Atris Serve make this directory a live AI Computer.
2
+ * Atris Serve, make this directory a live AI Computer.
3
3
  *
4
4
  * atris serve Start the bridge in current directory
5
5
  * atris serve --agent <agent_id> Bind to a specific agent
@@ -33,7 +33,7 @@ const { apiRequestJson, getApiBaseUrl } = require('../utils/api');
33
33
  const HEARTBEAT_INTERVAL_MS = 30000;
34
34
  const RECONNECT_DELAY_MS = 2000;
35
35
  const MAX_RECONNECT_DELAY_MS = 30000;
36
- // Bash commands are bounded to 10s long batches won't lock the CLI for hours
36
+ // Bash commands are bounded to 10s, long batches won't lock the CLI for hours
37
37
  const BASH_TIMEOUT_MS = 10000;
38
38
  // Hard size limits to prevent OOM on large payloads
39
39
  const MAX_WRITE_BYTES = 10 * 1024 * 1024; // 10 MB
@@ -138,7 +138,7 @@ async function applyOp(workingDir, op) {
138
138
 
139
139
  if (type === 'bash') {
140
140
  // Execute bash in the working directory with a timeout.
141
- // Note: This is a powerful op only the session owner can dispatch it,
141
+ // Note: This is a powerful op, only the session owner can dispatch it,
142
142
  // AND the session must have been created with allow_bash=true.
143
143
  try {
144
144
  const stdout = execSync(op.command, {
@@ -206,10 +206,10 @@ function streamSession(token, sessionId, workingDir) {
206
206
  return;
207
207
  }
208
208
 
209
- console.log(`● Bridge active listening for ops on session ${sessionId.slice(0, 8)}...`);
209
+ console.log(`● Bridge active, listening for ops on session ${sessionId.slice(0, 8)}...`);
210
210
 
211
211
  // Ping watchdog: the server pings every ~30s. Silence past 3 intervals
212
- // means the socket died without a FIN (server redeploy, NAT drop)
212
+ // means the socket died without a FIN (server redeploy, NAT drop),
213
213
  // kill it so the outer loop reconnects/re-registers instead of zombieing.
214
214
  let lastSeen = Date.now();
215
215
  const watchdog = setInterval(() => {
@@ -222,7 +222,7 @@ function streamSession(token, sessionId, workingDir) {
222
222
 
223
223
  // Registry check: a rolling deploy can leave this stream alive on an
224
224
  // old instance while new instances answer API calls from an empty
225
- // in-memory session store pings keep flowing so the silence watchdog
225
+ // in-memory session store, pings keep flowing so the silence watchdog
226
226
  // never fires, but dispatchers can't find us. Ask the registry
227
227
  // directly; if it forgot this session, reconnect and re-register.
228
228
  const registryCheck = setInterval(async () => {
@@ -311,7 +311,7 @@ async function serveAtris(options = {}) {
311
311
 
312
312
  console.log('');
313
313
  console.log('╭──────────────────────────────────────────╮');
314
- console.log('│ ATRIS SERVE Local AI Computer Bridge │');
314
+ console.log('│ ATRIS SERVE, Local AI Computer Bridge │');
315
315
  console.log('╰──────────────────────────────────────────╯');
316
316
  console.log('');
317
317
  console.log(` 📁 Directory: ${workingDir}`);
@@ -376,7 +376,7 @@ async function serveAtris(options = {}) {
376
376
  reconnectDelay = RECONNECT_DELAY_MS; // reset on clean disconnect
377
377
  } catch (err) {
378
378
  if (shuttingDown) break;
379
- // Server restarts wipe the in-memory session store a dead session id
379
+ // Server restarts wipe the in-memory session store, a dead session id
380
380
  // 404s forever, so re-register instead of retrying the corpse.
381
381
  if (/session not found|404/i.test(err.message || '')) {
382
382
  try {
@@ -391,7 +391,7 @@ async function serveAtris(options = {}) {
391
391
  });
392
392
  if (result.ok && result.data && result.data.session_id) {
393
393
  session = result.data;
394
- console.log(` ✓ Session expired server-side re-registered as ${session.session_id}`);
394
+ console.log(` ✓ Session expired server-side, re-registered as ${session.session_id}`);
395
395
  reconnectDelay = RECONNECT_DELAY_MS;
396
396
  continue;
397
397
  }
package/commands/setup.js CHANGED
@@ -117,7 +117,7 @@ async function setupAtris() {
117
117
  return;
118
118
  }
119
119
 
120
- // Resolve selection try number first, then slug match
120
+ // Resolve selection: try number first, then slug match
121
121
  let selected = null;
122
122
  const num = parseInt(answer, 10);
123
123
  if (!isNaN(num) && num >= 1 && num <= businesses.length) {
package/commands/sign.js CHANGED
@@ -3,7 +3,7 @@ const path = require('path');
3
3
 
4
4
  // atris sign: co-author trailer on every commit in an atris workspace.
5
5
  // Installs a prepare-commit-msg hook that appends "Co-authored-by: Atris"
6
- // when the repo has an atris/ folder same credit line Claude and Cursor use.
6
+ // when the repo has an atris/ folder, same credit line Claude and Cursor use.
7
7
  // Idempotent and non-destructive: appends a marked block, removes only its own block.
8
8
 
9
9
  const MARKER = '# atris co-author';
@@ -66,18 +66,18 @@ function signCommand(sub) {
66
66
  const { hookPath, removed } = removeSignHook();
67
67
  console.log(removed
68
68
  ? `\n ✓ atris co-author removed: ${rel(hookPath)}\n`
69
- : `\n nothing to remove atris co-author is not installed\n`);
69
+ : `\n nothing to remove, atris co-author is not installed\n`);
70
70
  return 0;
71
71
  }
72
72
  if (sub === 'status') {
73
73
  const { hookPath, installed } = signStatus();
74
74
  console.log(installed
75
75
  ? `\n ✓ atris co-author is on: ${rel(hookPath)}\n every commit gets: ${TRAILER}\n`
76
- : `\n atris co-author is off run 'atris sign' to turn it on\n`);
76
+ : `\n atris co-author is off, run 'atris sign' to turn it on\n`);
77
77
  return 0;
78
78
  }
79
79
  if (sub && sub !== 'on' && sub !== 'install') {
80
- console.log(`\n atris sign co-author trailer on every commit\n\n atris sign install the prepare-commit-msg hook\n atris sign off remove it\n atris sign status check whether it's installed\n\n While installed, any commit in a repo with an atris/ folder gets:\n ${TRAILER}\n`);
80
+ console.log(`\n atris sign: co-author trailer on every commit\n\n atris sign install the prepare-commit-msg hook\n atris sign off remove it\n atris sign status check whether it's installed\n\n While installed, any commit in a repo with an atris/ folder gets:\n ${TRAILER}\n`);
81
81
  return 2;
82
82
  }
83
83
  const { hookPath, already } = installSignHook();
@@ -7,7 +7,7 @@
7
7
  // seam: `npm i -g atris && atris signup x && atris play`.
8
8
  //
9
9
  // The account is born inert by design (0 credits, no executing agent, external
10
- // mail paid-gated) identity is free, capability is earned via AgentXP.
10
+ // mail paid-gated), identity is free, capability is earned via AgentXP.
11
11
  // Backend: backend/routers/agent_auth_router.py (POST /auth/agent/signup).
12
12
 
13
13
  const crypto = require('crypto');
@@ -19,7 +19,7 @@ const { argsWantHelp, isHelpToken } = require('../lib/noninteractive');
19
19
  // before spending a network round trip / a rate-limit slot.
20
20
  const HANDLE_RE = /^[a-z0-9]{3,30}$/;
21
21
 
22
- // Proof-of-work mirror the server (agent_auth_router.py). The signup endpoint
22
+ // Proof-of-work: mirror the server (agent_auth_router.py). The signup endpoint
23
23
  // requires a nonce whose sha256(prefix:handle:bucket:nonce) has DIFFICULTY_BITS
24
24
  // leading zero bits. Bucket = current 5-min window. Solved locally (~1s) so
25
25
  // signup stays a single call; this is the cost that makes mass-minting expensive.
@@ -79,7 +79,7 @@ async function signupCommand(args = []) {
79
79
  const { token, email, user_id: userId } = res.data;
80
80
  const identity = email || `${handle}@atrismail.com`;
81
81
  saveCredentials(token, null, identity, userId || null, 'atrisos');
82
- console.log(`\n✓ You're in ${identity}`);
82
+ console.log(`\n✓ You're in: ${identity}`);
83
83
  console.log(' Inert starter account (0 credits): identity is free, capability is earned.');
84
84
  console.log(' Saved to your active profile.');
85
85
  console.log('\nNext:');
@@ -88,7 +88,7 @@ async function signupCommand(args = []) {
88
88
  return 0;
89
89
  }
90
90
 
91
- // Friendly, actionable errors no stack traces for expected cases.
91
+ // Friendly, actionable errors, no stack traces for expected cases.
92
92
  if (res.status === 409) {
93
93
  console.error(`✗ "${handle}" is already taken or reserved. Try a different handle.`);
94
94
  return 1;
package/commands/site.js CHANGED
@@ -1,7 +1,8 @@
1
- // atris site turn a folder of markdown (docs, your wiki, memory) into a
1
+ // atris site, turn a folder of markdown (docs, your wiki, memory) into a
2
2
  // beautiful, navigable static site in the design system. Built on lib/site.js.
3
3
  //
4
4
  // atris site <dir|doc.md> [--out dist] [--theme atris|terminal|paper] [--title T] [--serve]
5
+ // atris site publish <dir> --slug <slug> [--profile strict|app] [--spa] [--no-claim] [--build] [--json]
5
6
 
6
7
  const { buildSite, serveSite } = require('../lib/site');
7
8
  const { hasFlag } = require('../lib/arg-parser');
@@ -9,6 +10,9 @@ const { hasFlag } = require('../lib/arg-parser');
9
10
  function flag(argv, name) { const i = argv.indexOf(name); return i !== -1 ? argv[i + 1] : null; }
10
11
 
11
12
  async function run(argv) {
13
+ if (argv[0] === 'publish') {
14
+ return require('../lib/site-publish').run(argv.slice(1));
15
+ }
12
16
  if (argv[0] === 'deploy') {
13
17
  return require('./site-deploy').run(argv.slice(1));
14
18
  }
@@ -20,14 +24,15 @@ async function run(argv) {
20
24
 
21
25
  atris site <dir|doc.md> [--out dist] [--theme atris|terminal|paper] [--title T]
22
26
  atris site atris/wiki --title "Atris Wiki" --serve
27
+ atris site publish <dir> --slug <slug> [--profile strict|app] [--spa] [--no-claim] [--build] [--json]
23
28
  atris site deploy <dir> --name <slug> [--spa] [--dry-run] --yes
24
29
  atris site deploy <dir> --fullstack --name <slug> [--dry-run] --yes
25
30
 
26
31
  Each .md becomes a page; an index links them all. Same anti-slop design system,
27
32
  semantic data-atris-block sections, ready for the web app. --serve previews it.
28
33
 
29
- deploy publishes web folders to <slug>.atris.ai. --fullstack deploys a node
30
- server with its own render service.
34
+ publish uploads any built web folder through the hosted-sites api. deploy is
35
+ the older site setup flow; --fullstack deploys a node server with render.
31
36
  `);
32
37
  return input === 'help' || hasFlag(argv, '--help') ? 0 : 2;
33
38
  }