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.
- package/AGENTS.md +37 -0
- package/README.md +5 -3
- package/atris/GETTING_STARTED.md +1 -1
- package/atris/atris.md +3 -0
- package/atris/policies/day-loop-voice.md +102 -0
- package/atris/policies/outbound-artifact-gate.md +2 -0
- package/atris/skills/design/SKILL.md +56 -32
- package/atris/skills/endgame/SKILL.md +12 -6
- package/atris/skills/engines/SKILL.md +22 -4
- package/atris/skills/fable-method/SKILL.md +66 -0
- package/atris/skills/improve/SKILL.md +65 -45
- package/atris/skills/youtube/SKILL.md +10 -1
- package/atris.md +2 -0
- package/ax +147 -19
- package/bin/atris.js +576 -267
- package/commands/activate.js +194 -88
- package/commands/agents.js +166 -0
- package/commands/autoland.js +459 -107
- package/commands/autopilot-front.js +20 -2
- package/commands/autopilot.js +118 -2
- package/commands/avail.js +407 -0
- package/commands/bench.js +188 -0
- package/commands/brain.js +3 -0
- package/commands/brief.js +651 -0
- package/commands/business-sync.js +192 -6
- package/commands/clean.js +50 -24
- package/commands/close.js +1083 -0
- package/commands/cloud.js +245 -0
- package/commands/compile.js +292 -1
- package/commands/computer.js +150 -3
- package/commands/dream.js +365 -0
- package/commands/drill.js +371 -0
- package/commands/engine.js +993 -32
- package/commands/experiments.js +28 -0
- package/commands/feedback.js +34 -12
- package/commands/fleet-report.js +206 -0
- package/commands/gm.js +23 -0
- package/commands/goal.js +247 -0
- package/commands/improve.js +642 -26
- package/commands/init.js +72 -44
- package/commands/interview.js +67 -1
- package/commands/land.js +152 -52
- package/commands/lifecycle.js +39 -3
- package/commands/log.js +84 -1
- package/commands/loops.js +220 -16
- package/commands/meet.js +220 -0
- package/commands/member.js +511 -34
- package/commands/mission.js +3144 -338
- package/commands/next.js +137 -0
- package/commands/now.js +220 -25
- package/commands/one-lap.js +776 -0
- package/commands/orb.js +314 -0
- package/commands/pack-craft.js +179 -0
- package/commands/pack.js +823 -0
- package/commands/play.js +3 -2
- package/commands/probe.js +30 -3
- package/commands/pulse.js +241 -46
- package/commands/push.js +260 -82
- package/commands/rainmaker.js +49 -0
- package/commands/report.js +415 -0
- package/commands/scout.js +147 -0
- package/commands/search.js +363 -0
- package/commands/skill.js +47 -3
- package/commands/slop.js +50 -2
- package/commands/soul.js +1 -1
- package/commands/stream.js +861 -0
- package/commands/study.js +693 -0
- package/commands/sync.js +67 -54
- package/commands/task.js +1346 -117
- package/commands/team.js +73 -0
- package/commands/verify.js +96 -0
- package/commands/watch.js +303 -0
- package/commands/wish.js +500 -0
- package/commands/workflow.js +11 -5
- package/commands/worktree.js +234 -13
- package/commands/xp.js +29 -11
- package/lib/auto-accept-certified.js +331 -34
- package/lib/autoland.js +319 -54
- package/lib/ax-auto-lane.js +96 -0
- package/lib/bench/context.js +147 -0
- package/lib/bench/engines.js +141 -0
- package/lib/bench/report.js +140 -0
- package/lib/bench/runner.js +512 -0
- package/lib/brief-ledger.js +350 -0
- package/lib/cloud-mission.js +259 -0
- package/lib/codex-flight.js +154 -0
- package/lib/default-runner.js +45 -0
- package/lib/default-verifier.js +70 -0
- package/lib/engine-registry.js +232 -0
- package/lib/experiments/daily.js +640 -0
- package/lib/fleet.js +2219 -67
- package/lib/improve-vitals-html.js +171 -0
- package/lib/known-commands.js +58 -0
- package/lib/loop-doctor.js +416 -0
- package/lib/member-switches.js +144 -0
- package/lib/mission-room.js +1 -0
- package/lib/mission-root.js +52 -0
- package/lib/next-moves.js +327 -10
- package/lib/one-lap-validator.js +60 -0
- package/lib/orb-context.js +477 -0
- package/lib/orb-scorecard.js +224 -0
- package/lib/policy-lessons.js +52 -1
- package/lib/pulse.js +277 -3
- package/lib/receipt-block.js +168 -0
- package/lib/receipt-evidence.js +65 -4
- package/lib/router-brain.js +352 -0
- package/lib/runner-command.js +10 -0
- package/lib/self-drive.js +258 -0
- package/lib/short-name.js +103 -0
- package/lib/spawn-env.js +18 -0
- package/lib/state-detection.js +56 -1
- package/lib/sync-status.js +59 -0
- package/lib/task-db.js +108 -29
- package/lib/task-proof.js +23 -1
- package/lib/team-presence.js +260 -0
- package/lib/tool-result-encode.js +7 -0
- package/lib/trust-tiers.js +90 -0
- package/lib/usage.js +107 -0
- package/lib/voice-gate.js +163 -0
- package/lib/wish-audit.js +1368 -0
- package/lib/wish-delegate.js +1840 -0
- package/lib/wish-design.js +110 -0
- package/lib/wish-stats.js +183 -0
- package/lib/wish-store.js +354 -0
- package/lib/zip.js +221 -0
- package/package.json +3 -1
- package/templates/loops/atris/loops/LOOPS.md +55 -0
- package/templates/loops/atris/loops/TICK.md +24 -0
- package/templates/loops/atris/loops/feedback.md +22 -0
- package/templates/loops/atris/loops/quality.md +22 -0
- package/templates/loops/atris/wiki/systems/loops.md +41 -0
- package/utils/api.js +5 -1
- 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
|
-
|
|
329
|
+
detail('✓ Created atris/ folder');
|
|
309
330
|
} else {
|
|
310
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
577
|
+
markReady('workspace', `features/_templates/${name}`, `✓ Created features/_templates/${name}`);
|
|
557
578
|
return;
|
|
558
579
|
}
|
|
559
580
|
|
|
560
581
|
fs.writeFileSync(target, fallback);
|
|
561
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1062
|
-
|
|
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);
|
package/commands/interview.js
CHANGED
|
@@ -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 };
|