atris 3.35.0 → 3.37.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 (133) hide show
  1. package/AGENTS.md +37 -0
  2. package/README.md +5 -3
  3. package/atris/GETTING_STARTED.md +1 -1
  4. package/atris/atris.md +3 -0
  5. package/atris/policies/day-loop-voice.md +102 -0
  6. package/atris/policies/outbound-artifact-gate.md +2 -0
  7. package/atris/skills/design/SKILL.md +56 -32
  8. package/atris/skills/endgame/SKILL.md +12 -6
  9. package/atris/skills/engines/SKILL.md +22 -4
  10. package/atris/skills/fable-method/SKILL.md +66 -0
  11. package/atris/skills/improve/SKILL.md +65 -45
  12. package/atris/skills/youtube/SKILL.md +10 -1
  13. package/atris.md +2 -0
  14. package/ax +147 -19
  15. package/bin/atris.js +576 -267
  16. package/commands/activate.js +194 -88
  17. package/commands/agents.js +166 -0
  18. package/commands/autoland.js +459 -107
  19. package/commands/autopilot-front.js +20 -2
  20. package/commands/autopilot.js +118 -2
  21. package/commands/avail.js +407 -0
  22. package/commands/bench.js +188 -0
  23. package/commands/brain.js +3 -0
  24. package/commands/brief.js +651 -0
  25. package/commands/business-sync.js +192 -6
  26. package/commands/clean.js +50 -24
  27. package/commands/close.js +1083 -0
  28. package/commands/cloud.js +245 -0
  29. package/commands/compile.js +292 -1
  30. package/commands/computer.js +150 -3
  31. package/commands/dream.js +365 -0
  32. package/commands/drill.js +371 -0
  33. package/commands/engine.js +993 -32
  34. package/commands/experiments.js +28 -0
  35. package/commands/feedback.js +34 -12
  36. package/commands/fleet-report.js +206 -0
  37. package/commands/gm.js +23 -0
  38. package/commands/goal.js +247 -0
  39. package/commands/improve.js +642 -26
  40. package/commands/init.js +72 -44
  41. package/commands/interview.js +67 -1
  42. package/commands/land.js +152 -52
  43. package/commands/lifecycle.js +39 -3
  44. package/commands/log.js +84 -1
  45. package/commands/loops.js +220 -16
  46. package/commands/meet.js +220 -0
  47. package/commands/member.js +511 -34
  48. package/commands/mission.js +3144 -338
  49. package/commands/next.js +137 -0
  50. package/commands/now.js +220 -25
  51. package/commands/one-lap.js +776 -0
  52. package/commands/orb.js +314 -0
  53. package/commands/pack-craft.js +179 -0
  54. package/commands/pack.js +823 -0
  55. package/commands/play.js +3 -2
  56. package/commands/probe.js +30 -3
  57. package/commands/pulse.js +241 -46
  58. package/commands/push.js +260 -82
  59. package/commands/rainmaker.js +49 -0
  60. package/commands/report.js +415 -0
  61. package/commands/scout.js +147 -0
  62. package/commands/search.js +363 -0
  63. package/commands/skill.js +47 -3
  64. package/commands/slop.js +50 -2
  65. package/commands/soul.js +1 -1
  66. package/commands/stream.js +861 -0
  67. package/commands/study.js +693 -0
  68. package/commands/sync.js +67 -54
  69. package/commands/task.js +1346 -117
  70. package/commands/team.js +73 -0
  71. package/commands/verify.js +96 -0
  72. package/commands/watch.js +303 -0
  73. package/commands/wish.js +500 -0
  74. package/commands/workflow.js +11 -5
  75. package/commands/worktree.js +234 -13
  76. package/commands/xp.js +29 -11
  77. package/lib/auto-accept-certified.js +331 -34
  78. package/lib/autoland.js +319 -54
  79. package/lib/ax-auto-lane.js +96 -0
  80. package/lib/bench/context.js +147 -0
  81. package/lib/bench/engines.js +141 -0
  82. package/lib/bench/report.js +140 -0
  83. package/lib/bench/runner.js +512 -0
  84. package/lib/brief-ledger.js +350 -0
  85. package/lib/cloud-mission.js +259 -0
  86. package/lib/codex-flight.js +154 -0
  87. package/lib/default-runner.js +45 -0
  88. package/lib/default-verifier.js +70 -0
  89. package/lib/engine-registry.js +232 -0
  90. package/lib/experiments/daily.js +640 -0
  91. package/lib/fleet.js +2219 -67
  92. package/lib/improve-vitals-html.js +171 -0
  93. package/lib/known-commands.js +58 -0
  94. package/lib/loop-doctor.js +416 -0
  95. package/lib/member-switches.js +144 -0
  96. package/lib/mission-room.js +1 -0
  97. package/lib/mission-root.js +52 -0
  98. package/lib/next-moves.js +327 -10
  99. package/lib/one-lap-validator.js +60 -0
  100. package/lib/orb-context.js +477 -0
  101. package/lib/orb-scorecard.js +224 -0
  102. package/lib/policy-lessons.js +52 -1
  103. package/lib/pulse.js +277 -3
  104. package/lib/receipt-block.js +168 -0
  105. package/lib/receipt-evidence.js +65 -4
  106. package/lib/router-brain.js +352 -0
  107. package/lib/runner-command.js +10 -0
  108. package/lib/self-drive.js +258 -0
  109. package/lib/short-name.js +103 -0
  110. package/lib/spawn-env.js +18 -0
  111. package/lib/state-detection.js +56 -1
  112. package/lib/sync-status.js +59 -0
  113. package/lib/task-db.js +108 -29
  114. package/lib/task-proof.js +23 -1
  115. package/lib/team-presence.js +260 -0
  116. package/lib/tool-result-encode.js +7 -0
  117. package/lib/trust-tiers.js +90 -0
  118. package/lib/usage.js +107 -0
  119. package/lib/voice-gate.js +163 -0
  120. package/lib/wish-audit.js +1368 -0
  121. package/lib/wish-delegate.js +1840 -0
  122. package/lib/wish-design.js +110 -0
  123. package/lib/wish-stats.js +183 -0
  124. package/lib/wish-store.js +354 -0
  125. package/lib/zip.js +221 -0
  126. package/package.json +3 -1
  127. package/templates/loops/atris/loops/LOOPS.md +55 -0
  128. package/templates/loops/atris/loops/TICK.md +24 -0
  129. package/templates/loops/atris/loops/feedback.md +22 -0
  130. package/templates/loops/atris/loops/quality.md +22 -0
  131. package/templates/loops/atris/wiki/systems/loops.md +41 -0
  132. package/utils/api.js +5 -1
  133. package/utils/auth.js +57 -21
package/commands/init.js CHANGED
@@ -249,13 +249,34 @@ function initAtris() {
249
249
  // shouldn't have file-system side effects.
250
250
  const args = process.argv.slice(3);
251
251
  if (args[0] === '-h' || args[0] === '--help' || args[0] === 'help') {
252
- console.log('Usage: atris init [--force] [--yes|-y]');
252
+ console.log('Usage: atris init [--force] [--yes|-y] [--verbose]');
253
253
  console.log('');
254
254
  console.log(' Scaffold the atris/ workspace in the current directory.');
255
255
  console.log(' Refuses to run inside an existing atris/ folder unless --force is passed.');
256
256
  console.log(' Use --yes or -y to skip the interactive context gatherer after scaffolding.');
257
+ console.log(' Use --verbose to list every file installed.');
257
258
  return;
258
259
  }
260
+ const verbose = args.includes('--verbose');
261
+ const ready = {
262
+ workspace: new Set(),
263
+ team: new Set(),
264
+ adapters: new Set(),
265
+ skills: new Set(),
266
+ };
267
+ const detail = (line) => {
268
+ if (verbose) console.log(line);
269
+ };
270
+ const markReady = (group, item, line) => {
271
+ ready[group].add(item);
272
+ detail(line);
273
+ };
274
+ const printReadyCounts = () => {
275
+ console.log(`workspace files ready (${ready.workspace.size})`);
276
+ console.log(`team members ready (${ready.team.size})`);
277
+ console.log(`agent adapters ready (${ready.adapters.size})`);
278
+ console.log(`skills installed (${ready.skills.size})`);
279
+ };
259
280
  // GUARD: Refuse nested init.
260
281
  // Bug: running `atris init` inside an existing `atris/` folder creates
261
282
  // `atris/atris/` nesting hell. A cloud business workspace had this exact problem.
@@ -305,9 +326,9 @@ function initAtris() {
305
326
 
306
327
  if (!fs.existsSync(targetDir)) {
307
328
  fs.mkdirSync(targetDir, { recursive: true });
308
- console.log('✓ Created atris/ folder');
329
+ detail('✓ Created atris/ folder');
309
330
  } else {
310
- console.log('✓ atris/ folder already exists');
331
+ detail('✓ atris/ folder already exists');
311
332
  }
312
333
 
313
334
  // MIGRATION: agent_team/ → team/ (v2.0.x → v2.1.0)
@@ -330,20 +351,20 @@ function initAtris() {
330
351
  if (!fs.existsSync(destPath)) {
331
352
  if (fs.statSync(srcPath).isFile()) {
332
353
  fs.copyFileSync(srcPath, destPath);
333
- console.log(` ✓ Migrated ${file}`);
354
+ markReady('workspace', `migration:${file}`, ` ✓ Migrated ${file}`);
334
355
  }
335
356
  }
336
357
  }
337
358
 
338
359
  // Remove old agent_team/ folder
339
360
  fs.rmSync(legacyAgentTeamDir, { recursive: true, force: true });
340
- console.log(' ✓ Removed old agent_team/ folder');
361
+ detail(' ✓ Removed old agent_team/ folder');
341
362
  console.log('');
342
363
  }
343
364
 
344
365
  if (!fs.existsSync(teamDir)) {
345
366
  fs.mkdirSync(teamDir, { recursive: true });
346
- console.log('✓ Created atris/team/ folder');
367
+ detail('✓ Created atris/team/ folder');
347
368
  }
348
369
 
349
370
  const gettingStartedFile = path.join(targetDir, 'GETTING_STARTED.md');
@@ -363,29 +384,29 @@ function initAtris() {
363
384
 
364
385
  if (!fs.existsSync(gettingStartedFile) && fs.existsSync(gettingStartedSource)) {
365
386
  fs.copyFileSync(gettingStartedSource, gettingStartedFile);
366
- console.log('✓ Created GETTING_STARTED.md');
387
+ markReady('workspace', 'GETTING_STARTED.md', '✓ Created GETTING_STARTED.md');
367
388
  }
368
389
 
369
390
  if (!fs.existsSync(forAgentsFile) && fs.existsSync(forAgentsSource)) {
370
391
  fs.copyFileSync(forAgentsSource, forAgentsFile);
371
- console.log('✓ Created FOR_AGENTS.md');
392
+ markReady('workspace', 'FOR_AGENTS.md', '✓ Created FOR_AGENTS.md');
372
393
  }
373
394
 
374
395
  if (!fs.existsSync(personaFile) && fs.existsSync(personaSource)) {
375
396
  fs.copyFileSync(personaSource, personaFile);
376
- console.log('✓ Created PERSONA.md');
397
+ markReady('workspace', 'PERSONA.md', '✓ Created PERSONA.md');
377
398
  }
378
399
 
379
400
  const wikiDir = path.join(targetDir, 'wiki');
380
401
  const wikiAlreadyExists = fs.existsSync(wikiDir);
381
402
  ensureWikiScaffold(process.cwd());
382
403
  if (!wikiAlreadyExists) {
383
- console.log('✓ Created wiki/ scaffold');
404
+ markReady('workspace', 'wiki/', '✓ Created wiki/ scaffold');
384
405
  }
385
406
 
386
407
  if (!fs.existsSync(mapFile)) {
387
408
  fs.writeFileSync(mapFile, '# MAP.md\n\n> Generated by your AI agent after reading atris.md\n\nRun your AI agent with atris.md to populate this file.\n');
388
- console.log('✓ Created MAP.md placeholder');
409
+ markReady('workspace', 'MAP.md', '✓ Created MAP.md placeholder');
389
410
  }
390
411
 
391
412
  // Create INTUITION.md - captures learnings, preferences, dead ends
@@ -421,7 +442,7 @@ function initAtris() {
421
442
 
422
443
  ---
423
444
  `);
424
- console.log('✓ Created INTUITION.md');
445
+ markReady('workspace', 'INTUITION.md', '✓ Created INTUITION.md');
425
446
  }
426
447
 
427
448
  if (!fs.existsSync(todoFile)) {
@@ -450,14 +471,14 @@ function initAtris() {
450
471
 
451
472
  ---
452
473
  `);
453
- console.log('✓ Created TODO.md placeholder');
474
+ markReady('workspace', 'TODO.md', '✓ Created TODO.md placeholder');
454
475
  }
455
476
 
456
477
  const nowFile = path.join(targetDir, 'now.md');
457
478
  if (!fs.existsSync(nowFile)) {
458
479
  const { renderDefaultNow } = require('./now');
459
480
  fs.writeFileSync(nowFile, renderDefaultNow(cwd), 'utf8');
460
- console.log('✓ Created now.md');
481
+ markReady('workspace', 'now.md', '✓ Created now.md');
461
482
  }
462
483
 
463
484
  // Create lessons.md (feedback loop for learning across features)
@@ -470,7 +491,7 @@ function initAtris() {
470
491
  ---
471
492
 
472
493
  `);
473
- console.log('✓ Created lessons.md');
494
+ markReady('workspace', 'lessons.md', '✓ Created lessons.md');
474
495
  }
475
496
 
476
497
  // Create logs directory and today's journal with bootstrap tasks
@@ -481,7 +502,7 @@ function initAtris() {
481
502
 
482
503
  if (!fs.existsSync(yearDir)) {
483
504
  fs.mkdirSync(yearDir, { recursive: true });
484
- console.log(`✓ Created logs/${new Date().getFullYear()}/ folder`);
505
+ detail(`✓ Created logs/${new Date().getFullYear()}/ folder`);
485
506
  }
486
507
 
487
508
  if (!fs.existsSync(journalFile)) {
@@ -510,7 +531,7 @@ function initAtris() {
510
531
  ## Inbox
511
532
 
512
533
  `);
513
- console.log(`✓ Created today's journal with bootstrap tasks`);
534
+ markReady('workspace', `logs/${today}.md`, `✓ Created today's journal with bootstrap tasks`);
514
535
  }
515
536
 
516
537
  // Create features directory and README
@@ -521,7 +542,7 @@ function initAtris() {
521
542
  fs.mkdirSync(featuresDir, { recursive: true });
522
543
  const featuresReadme = path.join(featuresDir, 'README.md');
523
544
  fs.writeFileSync(featuresReadme, '# Features\n\nThis directory tracks all features built using the Atris protocol.\n\nEach feature has:\n- `[feature-name]/idea.md` - Problem, solution, diagrams, success criteria\n- `[feature-name]/build.md` - Implementation plan, files changed, testing\n- `[feature-name]/validate.md` - End-to-end simulation script\n\n---\n\n## Features Built\n\n*Features will appear here as you build them.*\n');
524
- console.log('✓ Created features/ directory with README');
545
+ markReady('workspace', 'features/README.md', '✓ Created features/ directory with README');
525
546
  }
526
547
 
527
548
  // Create feature templates (idea/build/validate)
@@ -553,16 +574,22 @@ function initAtris() {
553
574
  const source = path.join(__dirname, '..', 'atris', 'features', '_templates', name);
554
575
  if (fs.existsSync(source)) {
555
576
  fs.copyFileSync(source, target);
556
- console.log(`✓ Created features/_templates/${name}`);
577
+ markReady('workspace', `features/_templates/${name}`, `✓ Created features/_templates/${name}`);
557
578
  return;
558
579
  }
559
580
 
560
581
  fs.writeFileSync(target, fallback);
561
- console.log(`✓ Created features/_templates/${name} (fallback)`);
582
+ markReady('workspace', `features/_templates/${name}`, `✓ Created features/_templates/${name} (fallback)`);
562
583
  });
563
584
 
564
585
  // Create experiments directory and packaged validation harness
565
- ensureExperimentsFramework(process.cwd(), { silent: false });
586
+ const experimentSetup = ensureExperimentsFramework(process.cwd(), { silent: true });
587
+ experimentSetup.created.forEach((item) => ready.workspace.add(item));
588
+ if (experimentSetup.created.length > 0) {
589
+ detail(`✓ Prepared atris/experiments/ (${experimentSetup.created.length} item${experimentSetup.created.length === 1 ? '' : 's'})`);
590
+ } else {
591
+ detail('✓ atris/experiments/ already ready');
592
+ }
566
593
 
567
594
 
568
595
  // Copy team members (MEMBER.md format — directory per member with skills/tools/context)
@@ -582,7 +609,7 @@ function initAtris() {
582
609
  fs.mkdirSync(path.join(targetMemberDir, 'tools'), { recursive: true });
583
610
  fs.mkdirSync(path.join(targetMemberDir, 'context'), { recursive: true });
584
611
  fs.copyFileSync(sourceFile, targetFile);
585
- console.log(`✓ Created team/${name}/ (MEMBER.md + skills/ + tools/ + context/)`);
612
+ markReady('team', name, `✓ Created team/${name}/ (MEMBER.md + skills/ + tools/ + context/)`);
586
613
  }
587
614
  });
588
615
 
@@ -609,18 +636,18 @@ tools: []
609
636
  # Insert persona, workflow, and rules below
610
637
  `;
611
638
  fs.writeFileSync(path.join(templateTargetDir, 'MEMBER.md'), templateContent);
612
- console.log('✓ Created team/_template/MEMBER.md');
639
+ markReady('team', '_template', '✓ Created team/_template/MEMBER.md');
613
640
  }
614
641
 
615
642
  // Detect project context and generate profile
616
643
  const profile = detectProjectContext(process.cwd());
617
644
  const profileFile = path.join(targetDir, '.project-profile.json');
618
645
  fs.writeFileSync(profileFile, JSON.stringify(profile, null, 2));
619
- console.log(`✓ Generated .project-profile.json (detected: ${profile.type}${profile.framework !== 'none' ? '/' + profile.framework : ''})`);
646
+ markReady('workspace', '.project-profile.json', `✓ Generated .project-profile.json (detected: ${profile.type}${profile.framework !== 'none' ? '/' + profile.framework : ''})`);
620
647
 
621
648
  // Inject project patterns into agent specs
622
649
  injectProjectPatterns(teamDir, profile);
623
- console.log('✓ Injected project patterns into team specs');
650
+ detail('✓ Injected project patterns into team specs');
624
651
 
625
652
  // Create agent instruction files for different tools
626
653
  const agentInstructions = `# AGENTS.md — Universal Agent Instructions
@@ -665,7 +692,7 @@ Every agent should leave four artifacts another agent can trust:
665
692
  | Objective | \`atris task note <id> "Goal / files / done / check"\` |
666
693
  | Navigation | \`atris/MAP.md\` when a new route or file location is learned |
667
694
  | Change | Small git diff in declared files only |
668
- | Proof ready | \`atris task ready <id> --proof "<commands or receipt>"\` |
695
+ | Proof ready | \`atris task ready <id> --proof "<commands or receipt>" --result "<day-one PM sentence>"\` |
669
696
  | Human accept | \`atris task accept <id>\` |
670
697
 
671
698
  Do not rely on chat context. Put the task, file pointers, and proof on disk.
@@ -748,7 +775,7 @@ member -> mission start --verify -> status --status active -> one bounded step -
748
775
  - [ ] Check MAP.md before touching code
749
776
  - [ ] Run \`atris task list\` or \`atris task next\` before picking work
750
777
  - [ ] Claim tasks with \`atris task claim <id> --as <functional-member>\`
751
- - [ ] Move agent-completed work to Review via \`atris task ready <id> --proof "..."\`
778
+ - [ ] Move agent-completed work to Review via \`atris task ready <id> --proof "..." --result "<day-one PM sentence>"\`
752
779
  - [ ] Complete native Codex/Claude goals after proof is in Review, so always-on work can continue
753
780
  - [ ] Only use \`atris task accept <id>\` when the human has approved the proof
754
781
  - [ ] Keep durable learning in Atris-owned policy/skill/wiki/task state; keep \`AGENTS.md\` as a generated/pointer layer
@@ -771,7 +798,7 @@ member -> mission start --verify -> status --status active -> one bounded step -
771
798
  const cursorRulesFile = path.join(process.cwd(), '.cursorrules');
772
799
  if (!fs.existsSync(cursorRulesFile)) {
773
800
  fs.writeFileSync(cursorRulesFile, agentInstructions);
774
- console.log('✓ Created .cursorrules (for Cursor)');
801
+ markReady('adapters', '.cursorrules', '✓ Created .cursorrules (for Cursor)');
775
802
  }
776
803
 
777
804
  // .cursor/rules/atris.mdc for Cursor (new format)
@@ -780,14 +807,14 @@ member -> mission start --verify -> status --status active -> one bounded step -
780
807
  if (!fs.existsSync(cursorMdcFile)) {
781
808
  fs.mkdirSync(cursorRulesDir, { recursive: true });
782
809
  fs.writeFileSync(cursorMdcFile, agentInstructions);
783
- console.log('✓ Created .cursor/rules/atris.mdc (for Cursor)');
810
+ markReady('adapters', '.cursor/rules/atris.mdc', '✓ Created .cursor/rules/atris.mdc (for Cursor)');
784
811
  }
785
812
 
786
813
  // AGENTS.md for Codex
787
814
  const agentsMdFile = path.join(process.cwd(), 'AGENTS.md');
788
815
  if (!fs.existsSync(agentsMdFile)) {
789
816
  fs.writeFileSync(agentsMdFile, agentInstructions);
790
- console.log('✓ Created AGENTS.md (for Codex)');
817
+ markReady('adapters', 'AGENTS.md', '✓ Created AGENTS.md (for Codex)');
791
818
  }
792
819
 
793
820
  // .devin/config.local.json for Devin for Terminal
@@ -803,7 +830,7 @@ member -> mission start --verify -> status --status active -> one bounded step -
803
830
  },
804
831
  };
805
832
  fs.writeFileSync(devinConfigFile, `${JSON.stringify(devinConfig, null, 2)}\n`);
806
- console.log('✓ Created .devin/config.local.json (for Devin)');
833
+ markReady('adapters', '.devin/config.local.json', '✓ Created .devin/config.local.json (for Devin)');
807
834
  }
808
835
 
809
836
  // .claude/commands/atris.md for Claude Code
@@ -822,7 +849,7 @@ Follow the workflow: plan → do → review
822
849
 
823
850
  Rules: 3-4 sentences max, ASCII visuals, check MAP.md first.`;
824
851
  fs.writeFileSync(claudeCommandFile, claudeCommand);
825
- console.log('✓ Created .claude/commands/atris.md (for Claude Code)');
852
+ markReady('adapters', '.claude/commands/atris.md', '✓ Created .claude/commands/atris.md (for Claude Code)');
826
853
  }
827
854
 
828
855
  // .claude/commands/atris-autopilot.md for autonomous loops
@@ -878,7 +905,7 @@ STATEEOF
878
905
 
879
906
  Read atris/MAP.md. Begin iteration 1.`;
880
907
  fs.writeFileSync(autopilotCommandFile, autopilotCommand);
881
- console.log('✓ Created .claude/commands/atris-autopilot.md (autonomous loops)');
908
+ markReady('adapters', '.claude/commands/atris-autopilot.md', '✓ Created .claude/commands/atris-autopilot.md (autonomous loops)');
882
909
  }
883
910
 
884
911
  // Copy skills from package to atris/skills/ and symlink to .claude/skills/
@@ -918,7 +945,7 @@ Read atris/MAP.md. Begin iteration 1.`;
918
945
  }
919
946
  };
920
947
  copyRecursive(srcSkillDir, destSkillDir);
921
- console.log(`✓ Copied skill: ${skill}`);
948
+ markReady('skills', skill, `✓ Copied skill: ${skill}`);
922
949
  }
923
950
  }
924
951
 
@@ -948,7 +975,7 @@ Read atris/MAP.md. Begin iteration 1.`;
948
975
  if (!fs.existsSync(symlinkPath)) {
949
976
  try {
950
977
  fs.symlinkSync(targetPath, symlinkPath);
951
- console.log(`✓ Linked .claude/skills/${skill} → atris/skills/${skill}`);
978
+ markReady('skills', skill, `✓ Linked .claude/skills/${skill} → atris/skills/${skill}`);
952
979
  } catch (e) {
953
980
  // Fallback: copy instead of symlink (Windows compatibility)
954
981
  const srcDir = path.join(skillsTargetDir, skill);
@@ -957,7 +984,7 @@ Read atris/MAP.md. Begin iteration 1.`;
957
984
  for (const file of files) {
958
985
  fs.copyFileSync(path.join(srcDir, file), path.join(symlinkPath, file));
959
986
  }
960
- console.log(`✓ Copied .claude/skills/${skill} (symlink failed)`);
987
+ markReady('skills', skill, `✓ Copied .claude/skills/${skill} (symlink failed)`);
961
988
  }
962
989
  }
963
990
  }
@@ -968,7 +995,7 @@ Read atris/MAP.md. Begin iteration 1.`;
968
995
  const claudeMdFile = path.join(targetDir, 'CLAUDE.md');
969
996
  if (!fs.existsSync(claudeMdFile) && fs.existsSync(claudeMdSource)) {
970
997
  fs.copyFileSync(claudeMdSource, claudeMdFile);
971
- console.log('✓ Created atris/CLAUDE.md (for Claude Code)');
998
+ markReady('adapters', 'atris/CLAUDE.md', '✓ Created atris/CLAUDE.md (for Claude Code)');
972
999
  }
973
1000
 
974
1001
  // .claude/settings.json with SessionStart hook for auto-loading Atris
@@ -1003,14 +1030,14 @@ Read atris/MAP.md. Begin iteration 1.`;
1003
1030
  }
1004
1031
  };
1005
1032
  fs.writeFileSync(claudeSettingsFile, JSON.stringify(claudeSettings, null, 2));
1006
- console.log('✓ Created .claude/settings.json (auto-loads Atris on startup)');
1033
+ markReady('adapters', '.claude/settings.json', '✓ Created .claude/settings.json (auto-loads Atris on startup)');
1007
1034
  }
1008
1035
 
1009
1036
  // Co-author trailer: commits in this workspace credit Atris, same as Claude/Cursor do
1010
1037
  try {
1011
1038
  const { installSignHook } = require('./sign');
1012
1039
  const { already } = installSignHook();
1013
- if (!already) console.log('✓ Installed co-author hook (commits credit Atris — remove with `atris sign off`)');
1040
+ if (!already) markReady('adapters', 'co-author hook', '✓ Installed co-author hook (commits credit Atris, remove with `atris sign off`)');
1014
1041
  } catch {} // not a git repo — skip quietly
1015
1042
 
1016
1043
  // Update root CLAUDE.md with Atris block (prepend with markers)
@@ -1044,22 +1071,23 @@ After displaying the boot output, respond to the user naturally.
1044
1071
  content = atrisBlock + content.slice(0, startIdx) + content.slice(endIdx).replace(/^\n+/, '');
1045
1072
  }
1046
1073
  fs.writeFileSync(rootClaudeMd, content);
1047
- console.log('✓ Updated Atris block in CLAUDE.md');
1074
+ markReady('adapters', 'CLAUDE.md', '✓ Updated Atris block in CLAUDE.md');
1048
1075
  } else {
1049
1076
  // Prepend Atris block
1050
1077
  fs.writeFileSync(rootClaudeMd, atrisBlock + content);
1051
- console.log('✓ Prepended Atris block to CLAUDE.md');
1078
+ markReady('adapters', 'CLAUDE.md', '✓ Prepended Atris block to CLAUDE.md');
1052
1079
  }
1053
1080
  } else {
1054
1081
  // Create new CLAUDE.md with just Atris block
1055
1082
  fs.writeFileSync(rootClaudeMd, atrisBlock.trim() + '\n');
1056
- console.log('✓ Created CLAUDE.md with Atris block');
1083
+ markReady('adapters', 'CLAUDE.md', '✓ Created CLAUDE.md with Atris block');
1057
1084
  }
1058
1085
 
1059
1086
  if (fs.existsSync(sourceFile)) {
1060
1087
  fs.copyFileSync(sourceFile, targetFile);
1061
- console.log('✓ Copied atris.md to atris/ folder');
1062
- console.log('\n✓ Atris initialized.');
1088
+ markReady('workspace', 'atris.md', '✓ Copied atris.md to atris/ folder');
1089
+ printReadyCounts();
1090
+ console.log('\natris initialized.');
1063
1091
  } else {
1064
1092
  console.error('✗ Error: atris.md not found in package');
1065
1093
  process.exit(1);
@@ -16,6 +16,7 @@
16
16
  const fs = require('fs');
17
17
  const path = require('path');
18
18
  const { spawn } = require('child_process');
19
+ const readline = require('readline');
19
20
 
20
21
  function findWorkspaceRoot(start) {
21
22
  let dir = start;
@@ -50,6 +51,71 @@ function showHelp() {
50
51
  console.log('');
51
52
  }
52
53
 
54
+ function unquote(value) {
55
+ const text = String(value);
56
+ if ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'"))) {
57
+ return text.slice(1, -1);
58
+ }
59
+ return text;
60
+ }
61
+
62
+ function parseSimpleFlags(args = [], valueFlagNames = []) {
63
+ const valueFlags = new Set(valueFlagNames);
64
+ const values = {};
65
+ const positionals = [];
66
+ for (let i = 0; i < args.length; i += 1) {
67
+ const arg = String(args[i] || '');
68
+ if (arg.startsWith('--')) {
69
+ const flagName = arg.split('=')[0];
70
+ if (valueFlags.has(flagName)) {
71
+ const prefix = `${flagName}=`;
72
+ if (arg.startsWith(prefix)) values[flagName] = unquote(arg.slice(prefix.length));
73
+ else if (args[i + 1] && !String(args[i + 1]).startsWith('--')) {
74
+ values[flagName] = unquote(args[i + 1]);
75
+ i += 1;
76
+ }
77
+ } else {
78
+ values[flagName] = true;
79
+ }
80
+ continue;
81
+ }
82
+ positionals.push(args[i]);
83
+ }
84
+ return { values, positionals };
85
+ }
86
+
87
+ function askPlain(rl, question) {
88
+ return new Promise((resolve) => rl.question(question, (answer) => resolve(answer)));
89
+ }
90
+
91
+ async function runPlainInterview({ args = [], fields = [], input = process.stdin, output = process.stdout } = {}) {
92
+ const parsed = parseSimpleFlags(args, fields.map((field) => field.flag));
93
+ const answers = {};
94
+ for (const field of fields) {
95
+ const value = String(parsed.values[field.flag] || '').trim();
96
+ if (value) answers[field.key] = value;
97
+ }
98
+
99
+ const missing = fields.filter((field) => !answers[field.key]);
100
+ if (!missing.length) return { ok: true, answers, positionals: parsed.positionals };
101
+ if (!input.isTTY) return { ok: false, missing: missing.map((field) => field.flag), answers, positionals: parsed.positionals };
102
+
103
+ const rl = readline.createInterface({ input, output });
104
+ try {
105
+ for (const field of missing) {
106
+ // eslint-disable-next-line no-await-in-loop
107
+ const answer = (await askPlain(rl, field.question)).trim();
108
+ if (answer) answers[field.key] = answer;
109
+ }
110
+ } finally {
111
+ rl.close();
112
+ }
113
+
114
+ const stillMissing = fields.filter((field) => !answers[field.key]);
115
+ if (stillMissing.length) return { ok: false, missing: stillMissing.map((field) => field.flag), answers, positionals: parsed.positionals };
116
+ return { ok: true, answers, positionals: parsed.positionals };
117
+ }
118
+
53
119
  function composePrompt(mode, subject, contextPaths, root) {
54
120
  const ctx = contextPaths.length
55
121
  ? `Read these first (this is what the system already knows — open with an observation from them, never a blank question):\n${contextPaths.map((p) => `- ${p}`).join('\n')}`
@@ -140,4 +206,4 @@ async function interviewCommand(args = [], root = process.cwd()) {
140
206
  });
141
207
  }
142
208
 
143
- module.exports = { interviewCommand };
209
+ module.exports = { interviewCommand, parseSimpleFlags, runPlainInterview };