atris 3.80.0 → 3.82.1

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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +10 -0
  3. package/atris/atris.md +14 -0
  4. package/atris/features/README.md +15 -0
  5. package/atris/policies/README.md +24 -0
  6. package/atris/skills/README.md +61 -45
  7. package/atris/skills/design/SKILL.md +6 -5
  8. package/atris/skills/taste/SKILL.md +39 -0
  9. package/atris/skills/taste/notes.md +29 -0
  10. package/atris/skills/taste/principles.md +23 -0
  11. package/atris/skills/wiki/SKILL.md +14 -2
  12. package/atris/skills/wiki/index-lines.md +30 -0
  13. package/atris/skills/wiki/measure.md +20 -0
  14. package/atris/skills/wiki/note-shape.md +16 -0
  15. package/atris/skills/wiki/upkeep.md +16 -0
  16. package/atris/team/README.md +38 -0
  17. package/atris.md +14 -0
  18. package/bin/atris.js +1 -1
  19. package/commands/activate.js +1 -1
  20. package/commands/auth.js +2 -2
  21. package/commands/autoland.js +25 -26
  22. package/commands/autopilot-front.js +46 -1
  23. package/commands/autopilot.js +55 -25
  24. package/commands/clean.js +22 -11
  25. package/commands/computer.js +166 -2413
  26. package/commands/doc-health.js +48 -1
  27. package/commands/improve.js +16 -3
  28. package/commands/init.js +4 -0
  29. package/commands/integrations.js +69 -1616
  30. package/commands/mission.js +109 -13
  31. package/commands/release.js +41 -10
  32. package/commands/security-review.js +5 -1
  33. package/commands/site-deploy.js +5 -1
  34. package/commands/status.js +1 -1
  35. package/commands/sync.js +12 -8
  36. package/commands/task.js +144 -118
  37. package/commands/unknowns.js +9 -5
  38. package/commands/visualize.js +1 -12
  39. package/commands/workflow.js +4 -3
  40. package/commands/worktree.js +68 -1602
  41. package/commands/xp.js +23 -2113
  42. package/commands/youtube.js +192 -3110
  43. package/lib/business-core.js +3 -4
  44. package/lib/campaign-run-lock.js +12 -1
  45. package/lib/computer-core.js +2447 -0
  46. package/lib/engine-ask.js +1 -1
  47. package/lib/engine-receipt-sweep.js +64 -0
  48. package/lib/file-lock.js +175 -0
  49. package/lib/fleet.js +1 -1
  50. package/lib/integrations-core.js +1695 -0
  51. package/lib/json-file.js +22 -1
  52. package/lib/loop-doctor.js +36 -2
  53. package/lib/map-refs.js +68 -8
  54. package/lib/member-engine.js +1 -1
  55. package/lib/member-map-fold.js +49 -11
  56. package/lib/member-map-inbox.js +2 -1
  57. package/lib/mission-core.js +9 -2
  58. package/lib/recall-exam.js +171 -0
  59. package/lib/roster-markdown.js +2 -2
  60. package/lib/runner-command.js +5 -5
  61. package/lib/security-audit-classes.js +20 -2
  62. package/lib/security-audit.js +31 -1
  63. package/lib/task-db.js +105 -37
  64. package/lib/taste-notes.js +35 -0
  65. package/lib/team-lineup.js +3 -3
  66. package/lib/todo.js +2 -0
  67. package/lib/wiki.js +11 -3
  68. package/lib/worktree-core.js +1517 -0
  69. package/lib/xp-core.js +2172 -0
  70. package/lib/youtube-core.js +3178 -0
  71. package/lib/zen-apply.js +3 -1
  72. package/package.json +10 -3
  73. package/utils/auth.js +18 -4
@@ -5,6 +5,7 @@ const path = require('path');
5
5
  const { resolveWorkspaceRoot } = require('../lib/mission-root');
6
6
  const { readText } = require('./brain');
7
7
  const { checkMapDocs, fixMapDocs } = require('../lib/map-refs');
8
+ const { runRecallExam } = require('../lib/recall-exam');
8
9
 
9
10
  const BOOT_FILES = [
10
11
  'CLAUDE.md', 'AGENTS.md', 'atris/atris.md', 'atris/MAP.md',
@@ -357,12 +358,58 @@ function renderDocHealth(payload) {
357
358
  return lines.join('\n');
358
359
  }
359
360
 
361
+ function flagValue(args, name) {
362
+ const index = args.indexOf(name);
363
+ if (index >= 0 && args[index + 1] && !args[index + 1].startsWith('--')) return args[index + 1];
364
+ return args.find(arg => arg.startsWith(`${name}=`))?.slice(name.length + 1);
365
+ }
366
+
367
+ function renderRecall(report, summaryOnly) {
368
+ const pct = value => (value === null ? 'n/a' : `${Math.round(value * 100)}%`);
369
+ const lines = [
370
+ `recall exam: ${report.found}/${report.questions} found (${pct(report.found_share)})`,
371
+ `boot read first: ${report.boot_tokens} tokens`,
372
+ `files opened after boot: median ${report.files_opened.median}, mean ${report.files_opened.mean === null ? 'n/a' : report.files_opened.mean.toFixed(2)}`,
373
+ `wrong opens (opened but not on the path to the answer): total ${report.wrong_opens.total}, mean ${report.wrong_opens.mean === null ? 'n/a' : report.wrong_opens.mean.toFixed(2)}`,
374
+ `tokens opened after boot: median ${report.tokens_opened.median}, mean ${report.tokens_opened.mean}, median when found ${report.tokens_opened_when_found.median}`,
375
+ ...table(['area', 'found', 'total'], Object.entries(report.by_area).sort().map(([area, row]) => [area, row.found, row.total])),
376
+ ];
377
+ if (report.invalid_lines.length) lines.push(`invalid question lines skipped: ${report.invalid_lines.join(', ')}`);
378
+ if (!summaryOnly) {
379
+ lines.push('', ...table(['id', 'found', 'opened', 'wrong', 'tokens', 'answer in'],
380
+ report.results.map(row => [row.id || '-', row.found ? 'yes' : 'no', row.files_opened, row.wrong_opens, row.tokens_opened, row.found_in || '-'])));
381
+ }
382
+ return lines.join('\n');
383
+ }
384
+
385
+ function recallCommand(args, options, file) {
386
+ const root = resolveWorkspaceRoot(options.cwd || process.cwd());
387
+ const questionsFile = path.resolve(options.cwd || process.cwd(), file);
388
+ if (!stat(questionsFile)?.isFile()) {
389
+ console.log(`no recall questions at ${file}. one object per line: {"q":"...","needle":"exact answer phrase"}`);
390
+ return 1;
391
+ }
392
+ const maxOpens = Number(flagValue(args, '--max-opens')) || undefined;
393
+ const report = runRecallExam(root, questionsFile, { bootFiles: BOOT_FILES, maxOpens });
394
+ // A held-out set is scored without echoing its questions back to the improver.
395
+ const summaryOnly = args.includes('--summary');
396
+ if (args.includes('--json')) {
397
+ const { results, ...totals } = report;
398
+ console.log(JSON.stringify({ ok: true, action: 'doc-health recall', ...totals, ...(summaryOnly ? {} : { results }) }, null, 2));
399
+ } else console.log(renderRecall(report, summaryOnly));
400
+ return 0;
401
+ }
402
+
360
403
  function docHealthCommand(args = [], options = {}) {
361
404
  if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
362
- console.log('usage: atris doc-health [--json] [--questions <path>] [--fix-refs]');
405
+ console.log('usage: atris doc-health [--json] [--questions <path>] [--fix-refs] [--recall <path> [--summary] [--max-opens N]]');
363
406
  console.log(' --fix-refs rewrite map line refs whose code moved; list the ones a human must fix');
407
+ console.log(' --recall follow links from the boot files to answer each question; report found, files opened, tokens read');
408
+ console.log(' --summary with --recall, print totals only so a held-out set stays unseen');
364
409
  return 0;
365
410
  }
411
+ const recallFile = flagValue(args, '--recall');
412
+ if (recallFile) return recallCommand(args, options, recallFile);
366
413
  if (args.includes('--fix-refs')) {
367
414
  const root = resolveWorkspaceRoot(options.cwd || process.cwd());
368
415
  if (!stat(path.join(root, 'atris', 'MAP.md'))?.isFile()) {
@@ -96,6 +96,7 @@ function parseImproveArgs(argv = []) {
96
96
  timeoutMs: DEFAULT_TIMEOUT_MS,
97
97
  help: false,
98
98
  history: false,
99
+ unknownCommand: null,
99
100
  };
100
101
 
101
102
  for (let i = 0; i < args.length; i++) {
@@ -113,6 +114,7 @@ function parseImproveArgs(argv = []) {
113
114
  if (a.startsWith('--timeout=')) { const v = Number(a.split('=')[1]); if (v > 0) opts.timeoutMs = Math.round(v * 1000); continue; }
114
115
  // positional mode (plan|full|delegate)
115
116
  if (!a.startsWith('-') && VALID_MODES.has(a)) { opts.mode = a; continue; }
117
+ if (i === 0 && !a.startsWith('-')) { opts.unknownCommand = a; continue; }
116
118
  }
117
119
 
118
120
  if (!VALID_MODES.has(opts.mode)) opts.mode = 'full';
@@ -300,7 +302,7 @@ function formatTickHistory(summary = {}) {
300
302
  if (summary.rewardTrend && summary.rewardTrend.length) {
301
303
  lines.push(` trend: ${summary.rewardTrend.map((r) => (r == null ? '·' : r)).join(' → ')}`);
302
304
  }
303
- if (!summary.ticks) lines.push(' (no ticks yet, run `atris improve`)');
305
+ if (!summary.ticks) lines.push(' (no ticks yet, run `atris improve tick`)');
304
306
  return lines.join('\n');
305
307
  }
306
308
 
@@ -1049,6 +1051,7 @@ const REVISION_WINDOW_HOURS = 72;
1049
1051
  const REVISION_IGNORED_FILES = [
1050
1052
  /^atris\/MAP\.md$/,
1051
1053
  /^atris\/refs\/MAP-NOTES\.md$/,
1054
+ /^atris\/refs\/map\/[^/]+\.md$/,
1052
1055
  /^test\/fast-tests\.txt$/,
1053
1056
  /^atris\/logs\//,
1054
1057
  /^atris\/runs\//,
@@ -1507,7 +1510,7 @@ Modes (positional or --mode):
1507
1510
 
1508
1511
  Options:
1509
1512
  --member <name> attribute the tick to a member (the loop's owner)
1510
- --model <id> override the model (e.g. claude-sonnet-4-6)
1513
+ --model <id> override the model (e.g. sonnet)
1511
1514
  --dry-run plan and run but do not commit
1512
1515
  --no-fallback do not fall back to a local tick if the backend is down
1513
1516
  --workspace <p> workspace path (default: cwd)
@@ -1523,7 +1526,11 @@ the backend is unreachable or you are not logged in.`);
1523
1526
  }
1524
1527
 
1525
1528
  function isBareVitalsArgs(argv = []) {
1526
- return argv.length === 0 || (argv.length === 1 && argv[0] === '--json');
1529
+ if (argv.length === 0 || (argv.length === 1 && argv[0] === '--json')) return true;
1530
+ if (argv[0] === 'status' || argv[0] === 'vitals') {
1531
+ return argv.length === 1 || (argv.length === 2 && argv[1] === '--json');
1532
+ }
1533
+ return false;
1527
1534
  }
1528
1535
 
1529
1536
  const LOOP_DOCTOR_OPEN_STATUSES = new Set(['planning', 'ready', 'running', 'paused', 'blocked']);
@@ -1693,6 +1700,12 @@ async function run(argv = [], deps = {}) {
1693
1700
  const routedArgs = args[0] === 'tick' ? args.slice(1) : args;
1694
1701
  const opts = parseImproveArgs(routedArgs);
1695
1702
  if (opts.help) { showHelp(); return 0; }
1703
+ if (opts.unknownCommand) {
1704
+ const error = `unknown improve subcommand "${opts.unknownCommand}". try: atris improve, atris improve tick, atris improve history, atris improve doctor, or atris improve revisions.`;
1705
+ if (opts.json) console.log(JSON.stringify({ ok: false, error, subcommand: opts.unknownCommand }));
1706
+ else console.error(error);
1707
+ return 1;
1708
+ }
1696
1709
 
1697
1710
  if (opts.history) {
1698
1711
  const summary = summarizeTickHistory(readTickHistory(opts.workspace));
package/commands/init.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const fs = require('fs');
2
+ const { ensureProjectTasteFile } = require('../lib/taste-notes');
2
3
  const path = require('path');
3
4
  const { ensureMemberBundle } = require('../lib/member-scaffold');
4
5
  const { ensureExperimentsFramework } = require('./experiments');
@@ -507,6 +508,9 @@ function initAtris() {
507
508
  markReady('workspace', 'lessons.md', '✓ Created lessons.md');
508
509
  }
509
510
 
511
+ // Project taste starts empty and is never overwritten; sync leaves it alone.
512
+ if (ensureProjectTasteFile(targetDir)) markReady('workspace', 'taste/TASTE.md', '✓ Created taste/TASTE.md');
513
+
510
514
  // Create features directory and README
511
515
  const featuresDir = path.join(targetDir, 'features');
512
516
  const templatesDir = path.join(featuresDir, '_templates');