atris 3.53.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 (119) 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/x-search/SKILL.md +24 -22
  7. package/atris/skills/youtube/SKILL.md +82 -33
  8. package/atris/team/_template/SOUL.md +40 -0
  9. package/atris/team/validator/MEMBER.md +1 -2
  10. package/bin/atris +21 -0
  11. package/bin/atris.js +563 -373
  12. package/commands/api-key.js +170 -0
  13. package/commands/auth.js +453 -43
  14. package/commands/autoland.js +3 -1
  15. package/commands/autopilot-front.js +58 -11
  16. package/commands/avail.js +5 -4
  17. package/commands/balance.js +55 -0
  18. package/commands/bench.js +29 -1
  19. package/commands/brain.js +63 -12
  20. package/commands/brainstorm.js +18 -0
  21. package/commands/business.js +149 -1
  22. package/commands/chat-scan.js +17 -4
  23. package/commands/close.js +5 -1
  24. package/commands/doctor.js +174 -0
  25. package/commands/dream.js +7 -0
  26. package/commands/drill.js +34 -2
  27. package/commands/engine.js +47 -35
  28. package/commands/errors.js +58 -8
  29. package/commands/experiments.js +14 -0
  30. package/commands/feedback.js +61 -2
  31. package/commands/fleet-report.js +27 -7
  32. package/commands/fleet.js +98 -11
  33. package/commands/founder.js +60 -21
  34. package/commands/human-missions.js +79 -4
  35. package/commands/improve.js +54 -4
  36. package/commands/init.js +68 -92
  37. package/commands/install.js +40 -0
  38. package/commands/integrations.js +399 -30
  39. package/commands/interview.js +8 -0
  40. package/commands/land.js +48 -17
  41. package/commands/learn.js +29 -4
  42. package/commands/log.js +51 -0
  43. package/commands/member.js +68 -10
  44. package/commands/mission.js +287 -50
  45. package/commands/now.js +15 -3
  46. package/commands/orb.js +97 -2
  47. package/commands/pack.js +83 -9
  48. package/commands/playbook.js +381 -0
  49. package/commands/plugin.js +16 -0
  50. package/commands/radar.js +31 -7
  51. package/commands/recap.js +128 -21
  52. package/commands/release.js +102 -2
  53. package/commands/review.js +5 -10
  54. package/commands/revisions.js +55 -0
  55. package/commands/run-front.js +12 -4
  56. package/commands/scout.js +12 -1
  57. package/commands/search.js +123 -28
  58. package/commands/sign.js +2 -2
  59. package/commands/signup.js +12 -3
  60. package/commands/site-deploy.js +17 -4
  61. package/commands/site.js +2 -2
  62. package/commands/skill-eval.js +289 -0
  63. package/commands/skill.js +56 -6
  64. package/commands/social.js +603 -0
  65. package/commands/spaceship.js +69 -6
  66. package/commands/stream.js +8 -1
  67. package/commands/study.js +32 -19
  68. package/commands/sync.js +28 -22
  69. package/commands/task.js +418 -60
  70. package/commands/teach.js +72 -63
  71. package/commands/terminal.js +25 -9
  72. package/commands/topup.js +88 -0
  73. package/commands/truth.js +52 -6
  74. package/commands/usage.js +91 -0
  75. package/commands/verify.js +3 -2
  76. package/commands/version.js +3 -1
  77. package/commands/voice.js +5 -1
  78. package/commands/who.js +69 -11
  79. package/commands/wiki.js +7 -4
  80. package/commands/wish.js +76 -5
  81. package/commands/workflow.js +327 -145
  82. package/commands/worktree.js +1 -0
  83. package/commands/write.js +5 -0
  84. package/commands/x-search.js +340 -0
  85. package/commands/xp.js +5 -1
  86. package/commands/youtube.js +551 -25
  87. package/lib/account-bound.js +47 -0
  88. package/lib/auto-accept-certified.js +8 -1
  89. package/lib/auto-lessons.js +186 -0
  90. package/lib/autoland.js +4 -2
  91. package/lib/chat-log-scan.js +7 -4
  92. package/lib/cli-json.js +77 -0
  93. package/lib/cli-scope.js +26 -0
  94. package/lib/codex-flight.js +1 -0
  95. package/lib/conductor-artifacts.js +1 -1
  96. package/lib/context-gatherer.js +11 -0
  97. package/lib/developer-api.js +116 -0
  98. package/lib/feedback-triage.js +274 -0
  99. package/lib/first-minute.js +398 -0
  100. package/lib/fleet.js +115 -34
  101. package/lib/functional-owner.js +22 -0
  102. package/lib/known-commands.js +31 -5
  103. package/lib/member-scaffold.js +197 -0
  104. package/lib/mission-root.js +4 -2
  105. package/lib/noninteractive.js +88 -0
  106. package/lib/policy-lessons.js +4 -1
  107. package/lib/revision-metric.js +279 -0
  108. package/lib/scratch-root.js +48 -0
  109. package/lib/skill-eval-gate.js +249 -0
  110. package/lib/task-db.js +4 -0
  111. package/lib/task-proof.js +69 -8
  112. package/lib/verifier-quality.js +69 -0
  113. package/lib/wish-audit.js +2 -2
  114. package/lib/wish-delegate.js +10 -1
  115. package/lib/workspace-scaffold.js +270 -0
  116. package/package.json +3 -2
  117. package/scripts/det/checklist-score.js +191 -0
  118. package/scripts/det/ytsearch +31 -0
  119. package/utils/update-check.js +15 -0
@@ -3,13 +3,16 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
- const LAYERS = [
6
+ const MEMORY_LAYERS = [
7
7
  { key: 'features', dir: path.join('atris', 'features') },
8
8
  { key: 'team', dir: path.join('atris', 'team') },
9
9
  { key: 'wiki', dir: path.join('atris', 'wiki') },
10
10
  { key: 'logs', dir: path.join('atris', 'logs') },
11
11
  ];
12
12
 
13
+ const SOURCE_DIRS = ['bin', 'commands', 'lib', 'utils', 'scripts'];
14
+ const SOURCE_EXTS = new Set(['.js', '.mjs', '.cjs', '.ts', '.sh', '.md']);
15
+
13
16
  const STOPWORDS = new Set([
14
17
  'a', 'an', 'and', 'are', 'as', 'at', 'for', 'from', 'in', 'is', 'it',
15
18
  'of', 'on', 'or', 'the', 'to', 'where', 'who',
@@ -38,9 +41,10 @@ const SYNONYM_MAP = {
38
41
  };
39
42
 
40
43
  function showSearchHelp() {
41
- console.log('Usage: atris search <keyword> [--raw]');
44
+ console.log('Usage: atris search <keyword> [--raw] [--memory-only]');
42
45
  console.log('Example: atris search auth');
43
- console.log(' --raw Print the full file:line dump instead of the compact answer');
46
+ console.log(' --raw Print the full file:line dump instead of the compact answer');
47
+ console.log(' --memory-only Search atris/features, team, wiki, logs only (skip source + MAP)');
44
48
  }
45
49
 
46
50
  function normalizeText(value) {
@@ -112,6 +116,37 @@ function listMarkdownFiles(rootDir) {
112
116
  return files;
113
117
  }
114
118
 
119
+ function listSourceFiles(root, dirs = SOURCE_DIRS) {
120
+ const files = [];
121
+
122
+ function walk(dir) {
123
+ let entries;
124
+ try {
125
+ entries = fs.readdirSync(dir, { withFileTypes: true });
126
+ } catch {
127
+ return;
128
+ }
129
+ entries
130
+ .sort((a, b) => a.name.localeCompare(b.name))
131
+ .forEach((entry) => {
132
+ if (entry.name === 'node_modules' || entry.name === '.git') return;
133
+ const fullPath = path.join(dir, entry.name);
134
+ if (entry.isDirectory()) {
135
+ walk(fullPath);
136
+ return;
137
+ }
138
+ if (!entry.isFile()) return;
139
+ if (SOURCE_EXTS.has(path.extname(entry.name).toLowerCase())) files.push(fullPath);
140
+ });
141
+ }
142
+
143
+ for (const rel of dirs) {
144
+ const abs = path.join(root, rel);
145
+ if (fs.existsSync(abs)) walk(abs);
146
+ }
147
+ return files;
148
+ }
149
+
115
150
  function recencyMs(relativePath, stat) {
116
151
  const match = relativePath.match(/(\d{4})-(\d{2})-(\d{2})/);
117
152
  if (match) {
@@ -121,17 +156,26 @@ function recencyMs(relativePath, stat) {
121
156
  return Number(stat && stat.mtimeMs) || 0;
122
157
  }
123
158
 
124
- function scanLayer(root, layer, terms) {
125
- const dir = path.join(root, layer.dir);
159
+ function emptyLayerResult(key, dir) {
160
+ return {
161
+ key,
162
+ dir,
163
+ exists: false,
164
+ fileHits: [],
165
+ lineHits: [],
166
+ };
167
+ }
168
+
169
+ function scanFiles(root, key, dirLabel, files, terms) {
126
170
  const result = {
127
- key: layer.key,
128
- dir: layer.dir.split(path.sep).join('/'),
129
- exists: fs.existsSync(dir),
171
+ key,
172
+ dir: dirLabel,
173
+ exists: files.length > 0,
130
174
  fileHits: [],
131
175
  lineHits: [],
132
176
  };
133
177
 
134
- for (const filePath of listMarkdownFiles(dir)) {
178
+ for (const filePath of files) {
135
179
  let stat;
136
180
  let content;
137
181
  try {
@@ -149,7 +193,7 @@ function scanLayer(root, layer, terms) {
149
193
  content.split(/\r?\n/).forEach((line, index) => {
150
194
  if (!hasTerm(line, terms)) return;
151
195
  const hit = {
152
- layer: layer.key,
196
+ layer: key,
153
197
  file: relativePath,
154
198
  line: index + 1,
155
199
  content: line.trim(),
@@ -162,7 +206,7 @@ function scanLayer(root, layer, terms) {
162
206
 
163
207
  if (pathMatched || fileLineHits.length > 0) {
164
208
  result.fileHits.push({
165
- layer: layer.key,
209
+ layer: key,
166
210
  file: relativePath,
167
211
  pathMatched,
168
212
  lineHits: fileLineHits,
@@ -174,6 +218,26 @@ function scanLayer(root, layer, terms) {
174
218
  return result;
175
219
  }
176
220
 
221
+ function scanLayer(root, layer, terms) {
222
+ const dir = path.join(root, layer.dir);
223
+ if (!fs.existsSync(dir)) {
224
+ return emptyLayerResult(layer.key, layer.dir.split(path.sep).join('/'));
225
+ }
226
+ return scanFiles(root, layer.key, layer.dir.split(path.sep).join('/'), listMarkdownFiles(dir), terms);
227
+ }
228
+
229
+ function scanMap(root, terms) {
230
+ const mapPath = path.join(root, 'atris', 'MAP.md');
231
+ if (!fs.existsSync(mapPath)) return emptyLayerResult('map', 'atris/MAP.md');
232
+ return scanFiles(root, 'map', 'atris/MAP.md', [mapPath], terms);
233
+ }
234
+
235
+ function scanSource(root, terms) {
236
+ const files = listSourceFiles(root);
237
+ if (!files.length) return emptyLayerResult('source', 'bin,commands,lib,utils,scripts');
238
+ return scanFiles(root, 'source', 'bin,commands,lib,utils,scripts', files, terms);
239
+ }
240
+
177
241
  function compareByRecency(a, b) {
178
242
  return (b.recencyMs || 0) - (a.recencyMs || 0)
179
243
  || a.file.localeCompare(b.file)
@@ -188,13 +252,21 @@ function sortedLineHits(layerResult) {
188
252
  return [...(layerResult.lineHits || [])].sort(compareByRecency);
189
253
  }
190
254
 
191
- function collectSearchResults(root, query) {
255
+ function layerKeys(results) {
256
+ return Object.keys(results.layers || {});
257
+ }
258
+
259
+ function collectSearchResults(root, query, options = {}) {
192
260
  const terms = queryTerms(query);
193
261
  const layers = {};
194
- for (const layer of LAYERS) {
262
+ for (const layer of MEMORY_LAYERS) {
195
263
  layers[layer.key] = scanLayer(root, layer, terms);
196
264
  }
197
- return { query, terms, layers };
265
+ if (!options.memoryOnly) {
266
+ layers.map = scanMap(root, terms);
267
+ layers.source = scanSource(root, terms);
268
+ }
269
+ return { query, terms, layers, memoryOnly: Boolean(options.memoryOnly) };
198
270
  }
199
271
 
200
272
  function featurePath(file) {
@@ -216,12 +288,12 @@ function ownerPath(root, file) {
216
288
  }
217
289
 
218
290
  function selectedFeature(results) {
219
- const hit = sortedFileHits(results.layers.features)[0];
291
+ const hit = sortedFileHits(results.layers.features || emptyLayerResult('features', 'atris/features'))[0];
220
292
  return hit ? featurePath(hit.file) : 'none';
221
293
  }
222
294
 
223
295
  function selectedOwner(root, results) {
224
- for (const hit of sortedFileHits(results.layers.team)) {
296
+ for (const hit of sortedFileHits(results.layers.team || emptyLayerResult('team', 'atris/team'))) {
225
297
  const member = ownerPath(root, hit.file);
226
298
  if (member) return member;
227
299
  }
@@ -229,10 +301,24 @@ function selectedOwner(root, results) {
229
301
  }
230
302
 
231
303
  function selectedWiki(results) {
232
- const hit = sortedFileHits(results.layers.wiki)[0];
304
+ const hit = sortedFileHits(results.layers.wiki || emptyLayerResult('wiki', 'atris/wiki'))[0];
233
305
  return hit ? hit.file : 'gap - not indexed yet';
234
306
  }
235
307
 
308
+ function selectedMap(results) {
309
+ if (!results.layers.map) return null;
310
+ const hit = sortedLineHits(results.layers.map)[0] || sortedFileHits(results.layers.map)[0];
311
+ if (!hit) return 'none';
312
+ return hit.line ? `${hit.file}:${hit.line}` : hit.file;
313
+ }
314
+
315
+ function selectedSource(results) {
316
+ if (!results.layers.source) return null;
317
+ const hit = sortedLineHits(results.layers.source)[0] || sortedFileHits(results.layers.source)[0];
318
+ if (!hit) return 'none';
319
+ return hit.line ? `${hit.file}:${hit.line}` : hit.file;
320
+ }
321
+
236
322
  function hitCount(layerResult) {
237
323
  return (layerResult.fileHits || []).reduce((total, fileHit) => {
238
324
  if (fileHit.lineHits.length > 0) return total + fileHit.lineHits.length;
@@ -241,7 +327,7 @@ function hitCount(layerResult) {
241
327
  }
242
328
 
243
329
  function totalHitCount(results) {
244
- return LAYERS.reduce((total, layer) => total + hitCount(results.layers[layer.key]), 0);
330
+ return layerKeys(results).reduce((total, key) => total + hitCount(results.layers[key]), 0);
245
331
  }
246
332
 
247
333
  function truncateLine(value, max = 140) {
@@ -256,8 +342,10 @@ function renderCompactSearch(root, results) {
256
342
  `Owner: ${selectedOwner(root, results)}`,
257
343
  `Wiki: ${selectedWiki(results)}`,
258
344
  ];
345
+ if (results.layers.map) lines.push(`Map: ${selectedMap(results)}`);
346
+ if (results.layers.source) lines.push(`Source: ${selectedSource(results)}`);
259
347
 
260
- const journalHits = sortedLineHits(results.layers.logs);
348
+ const journalHits = sortedLineHits(results.layers.logs || emptyLayerResult('logs', 'atris/logs'));
261
349
  if (totalHitCount(results) === 0) {
262
350
  lines.push('No matches found.');
263
351
  }
@@ -281,13 +369,13 @@ function renderCompactSearch(root, results) {
281
369
 
282
370
  function rawHits(results) {
283
371
  const hits = [];
284
- for (const layer of LAYERS) {
285
- for (const fileHit of sortedFileHits(results.layers[layer.key])) {
372
+ for (const key of layerKeys(results)) {
373
+ for (const fileHit of sortedFileHits(results.layers[key])) {
286
374
  if (fileHit.lineHits.length > 0) {
287
375
  hits.push(...fileHit.lineHits.sort((a, b) => (a.line || 0) - (b.line || 0)));
288
376
  } else if (fileHit.pathMatched) {
289
377
  hits.push({
290
- layer: layer.key,
378
+ layer: key,
291
379
  file: fileHit.file,
292
380
  line: 1,
293
381
  content: `(path match) ${fileHit.file}`,
@@ -301,9 +389,12 @@ function rawHits(results) {
301
389
  }
302
390
 
303
391
  function renderRawSearch(results) {
392
+ const scope = results.memoryOnly
393
+ ? 'atris/features, atris/team, atris/wiki, atris/logs'
394
+ : 'source, atris/MAP.md, atris/features, atris/team, atris/wiki, atris/logs';
304
395
  const hits = rawHits(results);
305
396
  const lines = [
306
- `Searching for "${results.query}" in atris/features, atris/team, atris/wiki, atris/logs...`,
397
+ `Searching for "${results.query}" in ${scope}...`,
307
398
  '',
308
399
  ];
309
400
 
@@ -326,12 +417,16 @@ function parseSearchArgs(args) {
326
417
  const argv = Array.isArray(args) ? args : [];
327
418
  const helpIndex = argv.findIndex(arg => arg === '--help' || arg === '-h');
328
419
  const raw = argv.includes('--raw');
329
- const query = argv.filter(arg => arg !== '--raw' && arg !== '--help' && arg !== '-h').join(' ').trim();
330
- return { helpIndex, raw, query, argCount: argv.length };
420
+ const memoryOnly = argv.includes('--memory-only');
421
+ const query = argv
422
+ .filter(arg => arg !== '--raw' && arg !== '--help' && arg !== '-h' && arg !== '--memory-only')
423
+ .join(' ')
424
+ .trim();
425
+ return { helpIndex, raw, memoryOnly, query, argCount: argv.length };
331
426
  }
332
427
 
333
428
  function searchCommand(args = [], options = {}) {
334
- const { helpIndex, raw, query, argCount } = parseSearchArgs(args);
429
+ const { helpIndex, raw, memoryOnly, query, argCount } = parseSearchArgs(args);
335
430
 
336
431
  if (helpIndex >= 0) {
337
432
  showSearchHelp();
@@ -344,9 +439,8 @@ function searchCommand(args = [], options = {}) {
344
439
  }
345
440
 
346
441
  const root = options.root || process.cwd();
347
- const results = collectSearchResults(root, query);
442
+ const results = collectSearchResults(root, query, { memoryOnly });
348
443
  if (totalHitCount(results) === 0 && !fs.existsSync(path.join(root, 'atris'))) {
349
- // Outside a workspace the empty scan rows are noise; lead with the way in.
350
444
  console.log('No atris folder here. Run atris init to set one up.');
351
445
  return 0;
352
446
  }
@@ -357,4 +451,5 @@ function searchCommand(args = [], options = {}) {
357
451
  module.exports = {
358
452
  searchCommand,
359
453
  showSearchHelp,
454
+ collectSearchResults,
360
455
  };
package/commands/sign.js CHANGED
@@ -74,11 +74,11 @@ function signCommand(sub) {
74
74
  console.log(installed
75
75
  ? `\n ✓ atris co-author is on: ${rel(hookPath)}\n every commit gets: ${TRAILER}\n`
76
76
  : `\n atris co-author is off — run 'atris sign' to turn it on\n`);
77
- return installed ? 0 : 1;
77
+ return 0;
78
78
  }
79
79
  if (sub && sub !== 'on' && sub !== 'install') {
80
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
- return 0;
81
+ return 2;
82
82
  }
83
83
  const { hookPath, already } = installSignHook();
84
84
  console.log(already
@@ -13,6 +13,7 @@
13
13
  const crypto = require('crypto');
14
14
  const { apiRequestJson, getApiBaseUrl } = require('../utils/api');
15
15
  const { saveCredentials } = require('../utils/auth');
16
+ const { argsWantHelp, isHelpToken } = require('../lib/noninteractive');
16
17
 
17
18
  // Mirror the server rule exactly (^[a-z0-9]{3,30}$) so we fail fast and friendly
18
19
  // before spending a network round trip / a rate-limit slot.
@@ -40,16 +41,24 @@ function solvePow(handle) {
40
41
  }
41
42
 
42
43
  function parseHandle(args = []) {
43
- const positional = args.find((a) => a && !a.startsWith('-'));
44
+ const positional = args.find((a) => a && !a.startsWith('-') && !isHelpToken(a));
44
45
  return (positional || '').trim().toLowerCase();
45
46
  }
46
47
 
48
+ function printSignupUsage() {
49
+ console.error('Usage: atris signup <handle>');
50
+ console.error(' handle: 3–30 characters, lowercase letters and digits only.');
51
+ }
52
+
47
53
  async function signupCommand(args = []) {
54
+ if (argsWantHelp(args)) {
55
+ printSignupUsage();
56
+ return 2;
57
+ }
48
58
  const handle = parseHandle(args);
49
59
 
50
60
  if (!handle) {
51
- console.error('Usage: atris signup <handle>');
52
- console.error(' handle: 3–30 characters, lowercase letters and digits only.');
61
+ printSignupUsage();
53
62
  return 1;
54
63
  }
55
64
  if (!HANDLE_RE.test(handle)) {
@@ -51,13 +51,14 @@ function printHelp() {
51
51
  console.log(`
52
52
  atris site deploy: publish a web folder at a free atris.ai subdomain
53
53
 
54
- atris site deploy <dir> --name <slug> [--spa] [--dry-run]
55
- atris site deploy <dir> --fullstack --name <slug> [--dry-run]
56
- atris site deploy dist --name my-site --api-base https://api.atris.ai
54
+ atris site deploy <dir> --name <slug> [--spa] [--dry-run] --yes
55
+ atris site deploy <dir> --fullstack --name <slug> [--dry-run] --yes
56
+ atris site deploy dist --name my-site --api-base https://api.atris.ai --yes
57
57
 
58
58
  names use lowercase letters, digits, and hyphens. files are capped at 2 mb
59
59
  each and 200 total. --spa enables single-page app routing. --fullstack
60
- deploys a node server with a package.json start script.
60
+ deploys a node server with a package.json start script. --yes is required
61
+ to publish.
61
62
  `);
62
63
  }
63
64
 
@@ -70,6 +71,7 @@ function parseArgs(argv) {
70
71
  fullstack: false,
71
72
  dryRun: false,
72
73
  help: false,
74
+ yes: false,
73
75
  };
74
76
 
75
77
  for (let i = 0; i < argv.length; i += 1) {
@@ -82,6 +84,8 @@ function parseArgs(argv) {
82
84
  options.fullstack = true;
83
85
  } else if (arg === '--dry-run') {
84
86
  options.dryRun = true;
87
+ } else if (arg === '--yes' || arg === '-y') {
88
+ options.yes = true;
85
89
  } else if (arg === '--name' || arg === '--api-base') {
86
90
  const value = argv[i + 1];
87
91
  if (!value || value.startsWith('-')) throw new Error(`${arg} needs a value`);
@@ -829,6 +833,10 @@ async function run(argv, deps = {}) {
829
833
  printFullstackPlan(options.name, packagePlan, log);
830
834
  return 0;
831
835
  }
836
+ if (!options.yes) {
837
+ errorLog(' Pass --yes to publish.');
838
+ return 2;
839
+ }
832
840
  return runFullstack(root, { ...options, apiBase }, packagePlan, deps);
833
841
  }
834
842
 
@@ -850,6 +858,11 @@ async function run(argv, deps = {}) {
850
858
  return 0;
851
859
  }
852
860
 
861
+ if (!options.yes) {
862
+ errorLog(' Pass --yes to publish.');
863
+ return 2;
864
+ }
865
+
853
866
  const readCredentials = deps.loadCredentials || loadCredentials;
854
867
  const credentials = readCredentials();
855
868
  if (!credentials || !credentials.token) {
package/commands/site.js CHANGED
@@ -20,8 +20,8 @@ async function run(argv) {
20
20
 
21
21
  atris site <dir|doc.md> [--out dist] [--theme atris|terminal|paper] [--title T]
22
22
  atris site atris/wiki --title "Atris Wiki" --serve
23
- atris site deploy <dir> --name <slug> [--spa] [--dry-run]
24
- atris site deploy <dir> --fullstack --name <slug> [--dry-run]
23
+ atris site deploy <dir> --name <slug> [--spa] [--dry-run] --yes
24
+ atris site deploy <dir> --fullstack --name <slug> [--dry-run] --yes
25
25
 
26
26
  Each .md becomes a page; an index links them all. Same anti-slop design system,
27
27
  semantic data-atris-block sections, ready for the web app. --serve previews it.