atris 3.30.12 → 3.32.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 +16 -3
- package/FOR_AGENTS.md +81 -0
- package/README.md +6 -4
- package/atris/CLAUDE.md +8 -0
- package/atris/atris.md +51 -19
- package/atris/skills/README.md +1 -0
- package/atris/skills/blocks/SKILL.md +134 -0
- package/atris/skills/clawhub/philosophy-of-work/SKILL.md +56 -0
- package/atris/skills/youtube/SKILL.md +31 -11
- package/atris.md +15 -0
- package/ax +189 -7
- package/bin/atris.js +273 -225
- package/commands/autoland.js +379 -0
- package/commands/autopilot-front.js +273 -0
- package/commands/autopilot.js +94 -4
- package/commands/business.js +1 -1
- package/commands/clean.js +72 -9
- package/commands/codex-goal.js +72 -22
- package/commands/computer.js +48 -3
- package/commands/gm.js +1 -1
- package/commands/harvest.js +179 -0
- package/commands/init.js +22 -1
- package/commands/land.js +442 -0
- package/commands/loop-front.js +122 -4
- package/commands/member.js +551 -19
- package/commands/mission.js +3674 -278
- package/commands/play.js +1 -1
- package/commands/pulse.js +65 -7
- package/commands/run-front.js +144 -0
- package/commands/run.js +10 -7
- package/commands/sign.js +90 -0
- package/commands/strings.js +301 -0
- package/commands/task.js +782 -108
- package/commands/truth.js +171 -0
- package/commands/xp.js +32 -8
- package/commands/youtube.js +72 -5
- package/decks/README.md +6 -12
- package/lib/auto-accept-certified.js +10 -0
- package/lib/autoland.js +391 -0
- package/lib/context-gatherer.js +0 -8
- package/lib/mission-artifact.js +504 -0
- package/lib/mission-room.js +846 -0
- package/lib/next-moves.js +212 -6
- package/lib/operator-next.js +7 -0
- package/lib/pulse.js +78 -4
- package/lib/runner-command.js +51 -20
- package/lib/runs-prune.js +242 -0
- package/lib/task-db.js +19 -2
- package/lib/task-proof.js +1 -1
- package/package.json +4 -4
- package/decks/atris-seed-pitch-v3.json +0 -118
- package/decks/atris-seed-pitch-v4-skeleton.json +0 -106
- package/decks/atris-seed-pitch-v5.json +0 -109
- package/decks/atris-seed-pitch-v6.json +0 -137
- package/decks/atris-seed-pitch-v7.json +0 -133
- package/decks/mark-pincus-narrative.json +0 -102
- package/decks/mark-pincus-sourcery.json +0 -94
- package/decks/yash-applied-compute-detailed.json +0 -150
- package/decks/yash-applied-compute-generalist.json +0 -82
- package/decks/yash-applied-compute-narrative.json +0 -54
- package/lib/ax-chat-input.js +0 -164
- package/lib/ax-goal.js +0 -307
- package/lib/ax-prefs.js +0 -63
- package/lib/ax-shimmer.js +0 -63
package/commands/member.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const os = require('os');
|
|
5
|
-
const { execFileSync } = require('child_process');
|
|
5
|
+
const { execFileSync, spawnSync } = require('child_process');
|
|
6
6
|
const { loadCredentials } = require('../utils/auth');
|
|
7
7
|
const { apiRequestJson } = require('../utils/api');
|
|
8
8
|
const { runAliveTick } = require('../lib/member-alive');
|
|
@@ -406,39 +406,275 @@ function missionPurpose(paths) {
|
|
|
406
406
|
};
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
+
const MEMBER_RUN_RUNNABLE_STATUSES = new Set(['planning', 'running', 'ready']);
|
|
410
|
+
|
|
411
|
+
function memberRunMissionMap() {
|
|
412
|
+
try {
|
|
413
|
+
return require('./mission').loadMissionMap(process.cwd());
|
|
414
|
+
} catch {
|
|
415
|
+
return new Map();
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function memberRunRunnableMissionId(candidateId, missionMap) {
|
|
420
|
+
const id = String(candidateId || '').trim();
|
|
421
|
+
if (!id) return '';
|
|
422
|
+
const mission = missionMap.get(id);
|
|
423
|
+
// Unknown here can mean a worktree-held mission; let mission run decide.
|
|
424
|
+
if (!mission) return id;
|
|
425
|
+
return MEMBER_RUN_RUNNABLE_STATUSES.has(mission.status) ? id : '';
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function memberRunOwnedActiveMissionId(name, missionMap) {
|
|
429
|
+
// Discovery fallback when the member's own pointers are stale: resume the
|
|
430
|
+
// member's newest moving mission. ready is excluded — it waits for review.
|
|
431
|
+
const owner = String(name || '').trim().toLowerCase();
|
|
432
|
+
const candidates = Array.from(missionMap.values())
|
|
433
|
+
.filter((mission) => String(mission?.owner || '').trim().toLowerCase() === owner)
|
|
434
|
+
.filter((mission) => mission.status === 'running' || mission.status === 'planning')
|
|
435
|
+
.filter((mission) => !/^decide and start the next useful mission after:/i.test(String(mission?.objective || '')))
|
|
436
|
+
.sort((a, b) => {
|
|
437
|
+
if (a.status !== b.status) return a.status === 'running' ? -1 : 1;
|
|
438
|
+
return String(b.updated_at || b.created_at || '').localeCompare(String(a.updated_at || a.created_at || ''));
|
|
439
|
+
});
|
|
440
|
+
return candidates[0]?.id || '';
|
|
441
|
+
}
|
|
442
|
+
|
|
409
443
|
function resolveMemberRunMissionId(name, args = []) {
|
|
410
444
|
const override = readFlag(args, '--mission', '') || readFlag(args, '--mission-id', '');
|
|
411
445
|
if (override) return override;
|
|
412
446
|
|
|
413
447
|
const paths = requireMemberDir(name);
|
|
448
|
+
const missionMap = memberRunMissionMap();
|
|
414
449
|
const purpose = missionPurpose(paths);
|
|
415
|
-
|
|
450
|
+
const runtimeId = memberRunRunnableMissionId(purpose.runtimeMission?.id, missionMap);
|
|
451
|
+
if (runtimeId) return runtimeId;
|
|
416
452
|
|
|
417
453
|
const goals = loadMemberGoals(name, paths);
|
|
418
454
|
const goal = activeGoal(goals);
|
|
419
|
-
|
|
455
|
+
const goalId = memberRunRunnableMissionId(goal?.mission_id, missionMap);
|
|
456
|
+
if (goalId) return goalId;
|
|
457
|
+
|
|
458
|
+
return memberRunOwnedActiveMissionId(paths.storageName || name, missionMap);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const MEMBER_RUN_START_VALUE_FLAGS = [
|
|
462
|
+
'--mission',
|
|
463
|
+
'--mission-id',
|
|
464
|
+
'--owner',
|
|
465
|
+
'--runner',
|
|
466
|
+
'--lane',
|
|
467
|
+
'--cadence',
|
|
468
|
+
'--verify',
|
|
469
|
+
'--stop',
|
|
470
|
+
'--model',
|
|
471
|
+
'--max-ticks',
|
|
472
|
+
'--max-wall',
|
|
473
|
+
'--minutes',
|
|
474
|
+
'--hours',
|
|
475
|
+
'--industry',
|
|
476
|
+
'--domain',
|
|
477
|
+
'--value',
|
|
478
|
+
'--outcome',
|
|
479
|
+
'--truth',
|
|
480
|
+
'--proof',
|
|
481
|
+
];
|
|
482
|
+
|
|
483
|
+
const MEMBER_RUN_START_BOOLEAN_FLAGS = [
|
|
484
|
+
'--json',
|
|
485
|
+
'--worktree',
|
|
486
|
+
'--shared-checkout',
|
|
487
|
+
'--no-worktree',
|
|
488
|
+
'--always-on',
|
|
489
|
+
'--xp-task',
|
|
490
|
+
'--agent-xp',
|
|
491
|
+
'--spend-full-budget',
|
|
492
|
+
'--use-whole-budget',
|
|
493
|
+
'--stop-when-done',
|
|
494
|
+
'--choose-work',
|
|
495
|
+
'--auto',
|
|
496
|
+
];
|
|
497
|
+
|
|
498
|
+
function memberRunMissionText(args = []) {
|
|
499
|
+
return stripKnownFlags(args, MEMBER_RUN_START_VALUE_FLAGS, MEMBER_RUN_START_BOOLEAN_FLAGS).join(' ').trim();
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function cleanMemberRunPhrase(value) {
|
|
503
|
+
return String(value || '').replace(/\s+/g, ' ').trim();
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function memberRunDomain(args = []) {
|
|
507
|
+
return cleanMemberRunPhrase(readFlag(args, '--industry', '') || readFlag(args, '--domain', '')) || 'this workspace and its users';
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function memberRunOutcome(args = []) {
|
|
511
|
+
return cleanMemberRunPhrase(readFlag(args, '--outcome', '') || readFlag(args, '--value', ''))
|
|
512
|
+
|| 'revenue, reliability, speed, security, workflow clarity, or customer trust';
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function memberRunTruthRule(args = []) {
|
|
516
|
+
return cleanMemberRunPhrase(readFlag(args, '--truth', '') || readFlag(args, '--proof', ''))
|
|
517
|
+
|| 'say what changed, what was checked, and what is still unproven';
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function memberRunUsefulTarget() {
|
|
521
|
+
try {
|
|
522
|
+
const { nextMoves: pickNextMoves, isGenericInboxPlaceholder } = require('../lib/next-moves');
|
|
523
|
+
const moves = pickNextMoves(process.cwd(), 5);
|
|
524
|
+
const candidate = moves.find((move) => {
|
|
525
|
+
const title = cleanMemberRunPhrase(move?.title);
|
|
526
|
+
if (!title) return false;
|
|
527
|
+
if (isGenericInboxPlaceholder(title)) return false;
|
|
528
|
+
if (/^mission xp\s*:/i.test(title)) return false;
|
|
529
|
+
if (/^decide and start the next useful mission after:/i.test(title)) return false;
|
|
530
|
+
return true;
|
|
531
|
+
});
|
|
532
|
+
return candidate?.title ? cleanMemberRunPhrase(candidate.title) : '';
|
|
533
|
+
} catch {
|
|
534
|
+
return '';
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function memberRunAutoMissionText(name, args = []) {
|
|
539
|
+
const domain = memberRunDomain(args);
|
|
540
|
+
const outcome = memberRunOutcome(args);
|
|
541
|
+
const truth = memberRunTruthRule(args);
|
|
542
|
+
const target = memberRunUsefulTarget();
|
|
543
|
+
const work = target
|
|
544
|
+
? `Start with this concrete candidate: ${target}.`
|
|
545
|
+
: 'First inspect current Atris state, then choose one bounded useful task.';
|
|
546
|
+
return [
|
|
547
|
+
`Run ${name} on meaningful work for ${domain}.`,
|
|
548
|
+
work,
|
|
549
|
+
`Useful means it improves ${outcome}.`,
|
|
550
|
+
`Truth rule: ${truth}.`,
|
|
551
|
+
'Explain the choice in plain language, prove the result before Review, and stop instead of doing fake busywork when no useful task exists.',
|
|
552
|
+
].join(' ');
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function memberRunBudgetSeconds(args = []) {
|
|
556
|
+
const hours = readNumberFlag(args, '--hours', null);
|
|
557
|
+
if (Number.isFinite(hours) && hours > 0) return Math.round(hours * 3600);
|
|
558
|
+
const minutes = readNumberFlag(args, '--minutes', null);
|
|
559
|
+
if (Number.isFinite(minutes) && minutes > 0) return Math.round(minutes * 60);
|
|
560
|
+
return null;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function pushFlagValue(out, args, name) {
|
|
564
|
+
const value = readFlag(args, name, '');
|
|
565
|
+
if (value) out.push(name, String(value));
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function pushFlagWhenPresent(out, args, name) {
|
|
569
|
+
if (hasFlag(args, name)) out.push(name);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function startMemberRunMission(name, missionText, args = []) {
|
|
573
|
+
const paths = requireMemberDir(name);
|
|
574
|
+
const owner = paths.storageName || name;
|
|
575
|
+
const startArgs = [
|
|
576
|
+
'mission',
|
|
577
|
+
'start',
|
|
578
|
+
missionText,
|
|
579
|
+
'--owner',
|
|
580
|
+
owner,
|
|
581
|
+
'--runner',
|
|
582
|
+
readFlag(args, '--runner', 'codex_goal'),
|
|
583
|
+
'--lane',
|
|
584
|
+
readFlag(args, '--lane', 'workspace'),
|
|
585
|
+
];
|
|
586
|
+
pushFlagValue(startArgs, args, '--cadence');
|
|
587
|
+
pushFlagValue(startArgs, args, '--verify');
|
|
588
|
+
pushFlagValue(startArgs, args, '--stop');
|
|
589
|
+
pushFlagValue(startArgs, args, '--model');
|
|
590
|
+
pushFlagValue(startArgs, args, '--max-wall');
|
|
591
|
+
pushFlagValue(startArgs, args, '--minutes');
|
|
592
|
+
pushFlagValue(startArgs, args, '--hours');
|
|
593
|
+
pushFlagValue(startArgs, args, '--base');
|
|
594
|
+
pushFlagWhenPresent(startArgs, args, '--always-on');
|
|
595
|
+
pushFlagWhenPresent(startArgs, args, '--xp-task');
|
|
596
|
+
pushFlagWhenPresent(startArgs, args, '--agent-xp');
|
|
597
|
+
pushFlagWhenPresent(startArgs, args, '--spend-full-budget');
|
|
598
|
+
pushFlagWhenPresent(startArgs, args, '--use-whole-budget');
|
|
599
|
+
pushFlagWhenPresent(startArgs, args, '--stop-when-done');
|
|
600
|
+
if (!hasFlag(args, '--shared-checkout') && !hasFlag(args, '--no-worktree')) startArgs.push('--worktree');
|
|
601
|
+
if (hasFlag(args, '--json')) startArgs.push('--json');
|
|
602
|
+
|
|
603
|
+
const cliPath = path.join(__dirname, '..', 'bin', 'atris.js');
|
|
604
|
+
try {
|
|
605
|
+
execFileSync(process.execPath, [cliPath, ...startArgs], {
|
|
606
|
+
cwd: process.cwd(),
|
|
607
|
+
stdio: 'inherit',
|
|
608
|
+
});
|
|
609
|
+
} catch (error) {
|
|
610
|
+
process.exitCode = Number(error?.status) || 1;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// atris member ping <name> "<message>" — talk to an always-on member mid-run.
|
|
615
|
+
// Finds the member's most recently touched live mission (including --worktree
|
|
616
|
+
// missions in sibling checkouts) and leaves the note its next tick consumes.
|
|
617
|
+
function memberPing(name, ...args) {
|
|
618
|
+
if (!name || name === '--help' || name === '-h') {
|
|
619
|
+
console.log('Usage: atris member ping <name> "<message>" [--json]');
|
|
620
|
+
console.log('Leaves a note on the member\'s active mission; the next tick reads it as operator direction.');
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
const text = args.filter((a) => !a.startsWith('--')).join(' ').trim();
|
|
624
|
+
if (!text) {
|
|
625
|
+
console.error('atris member ping: message required');
|
|
626
|
+
process.exit(2);
|
|
627
|
+
}
|
|
628
|
+
const missionMod = require('./mission');
|
|
629
|
+
const terminal = new Set(['complete', 'stopped', 'failed']);
|
|
630
|
+
const candidates = [
|
|
631
|
+
...missionMod.listMissions(process.cwd()),
|
|
632
|
+
...missionMod.listWorktreeRollupMissions(process.cwd()),
|
|
633
|
+
]
|
|
634
|
+
.filter((m) => m && m.owner === name && !terminal.has(m.status))
|
|
635
|
+
.sort((a, b) => String(b.updated_at || b.created_at || '').localeCompare(String(a.updated_at || a.created_at || '')));
|
|
636
|
+
if (!candidates.length) {
|
|
637
|
+
console.error(`atris member ping: no live mission owned by "${name}". Start one: atris member run ${name} "<objective>"`);
|
|
638
|
+
process.exit(1);
|
|
639
|
+
}
|
|
640
|
+
const passthrough = args.filter((a) => a.startsWith('--'));
|
|
641
|
+
return missionMod.pingMission([candidates[0].id, text, ...passthrough]);
|
|
420
642
|
}
|
|
421
643
|
|
|
422
644
|
function memberRun(name, ...args) {
|
|
423
645
|
if (!name || name === '--help' || name === '-h' || hasFlag(args, '--help') || hasFlag(args, '-h')) {
|
|
424
|
-
console.log('Usage: atris member run <name> [mission
|
|
425
|
-
console.log('
|
|
426
|
-
console.log('
|
|
646
|
+
console.log('Usage: atris member run <name> ["mission text"] [--minutes N|--hours N] [--json]');
|
|
647
|
+
console.log('New mission: atris member run growth "Improve onboarding proof" --minutes 30 --json');
|
|
648
|
+
console.log('Choose work: atris member run growth --industry logistics --value "reduce support time" --minutes 30 --json');
|
|
649
|
+
console.log('Existing mission: atris member run growth --mission <mission-id> --max-ticks 1 --json');
|
|
650
|
+
console.log('Meaning: if mission text is omitted, the member chooses one useful bounded task from Atris state.');
|
|
651
|
+
console.log('Truth: useful work must improve revenue, reliability, speed, security, clarity, or trust, then show plain proof.');
|
|
652
|
+
console.log('Isolation: new missions use --worktree by default; add --shared-checkout to stay here.');
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const missionText = memberRunMissionText(args);
|
|
657
|
+
const hasMissionOverride = Boolean(readFlag(args, '--mission', '') || readFlag(args, '--mission-id', ''));
|
|
658
|
+
if (missionText && !hasMissionOverride) {
|
|
659
|
+
startMemberRunMission(name, missionText, args);
|
|
427
660
|
return;
|
|
428
661
|
}
|
|
429
662
|
|
|
430
663
|
const missionId = resolveMemberRunMissionId(name, args);
|
|
431
664
|
if (!missionId) {
|
|
432
|
-
|
|
433
|
-
console.error(`Try: atris member goal-from-mission ${name} --json`);
|
|
434
|
-
console.error(`Or: atris mission start "..." --owner ${name}`);
|
|
435
|
-
process.exitCode = 1;
|
|
665
|
+
startMemberRunMission(name, memberRunAutoMissionText(name, args), args);
|
|
436
666
|
return;
|
|
437
667
|
}
|
|
438
668
|
|
|
439
|
-
const runArgs = stripKnownFlags(args, ['--mission', '--mission-id']);
|
|
440
|
-
|
|
441
|
-
|
|
669
|
+
const runArgs = stripKnownFlags(args, ['--mission', '--mission-id', '--minutes', '--hours'], ['--worktree', '--shared-checkout', '--no-worktree']);
|
|
670
|
+
const budgetSeconds = memberRunBudgetSeconds(args);
|
|
671
|
+
// A timed run is a loop contract, not one tick: keep picking the next useful
|
|
672
|
+
// move until the wall clock (--max-wall) spends the budget. Only untimed runs
|
|
673
|
+
// default to a single tick.
|
|
674
|
+
if (!readFlag(runArgs, '--max-ticks', '')) {
|
|
675
|
+
runArgs.push('--max-ticks', budgetSeconds ? String(Math.max(4, Math.ceil(budgetSeconds / 300))) : '1');
|
|
676
|
+
}
|
|
677
|
+
if (!readFlag(runArgs, '--max-wall', '')) runArgs.push('--max-wall', String(budgetSeconds || 900));
|
|
442
678
|
|
|
443
679
|
const cliPath = path.join(__dirname, '..', 'bin', 'atris.js');
|
|
444
680
|
try {
|
|
@@ -2023,6 +2259,247 @@ function memberLoopPaths(name) {
|
|
|
2023
2259
|
lockPath: path.join(stateDir, `${name}.lock.json`),
|
|
2024
2260
|
stopPath: path.join(stateDir, `${name}.stop.json`),
|
|
2025
2261
|
latestPath: path.join(stateDir, `${name}.latest.json`),
|
|
2262
|
+
cronScriptPath: path.join(stateDir, `${name}.hourly-alive.sh`),
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
function memberAliveCronMarker(name) {
|
|
2267
|
+
return `ATRIS_MEMBER_ALIVE_${String(name || '').replace(/[^a-zA-Z0-9]/g, '_').toUpperCase()}`;
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
function shellSingleQuote(value) {
|
|
2271
|
+
return `'${String(value || '').replace(/'/g, "'\\''")}'`;
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
function resolveAtrisCommand() {
|
|
2275
|
+
return path.join(__dirname, '..', 'bin', 'atris.js');
|
|
2276
|
+
}
|
|
2277
|
+
|
|
2278
|
+
function buildMemberAliveCronScript(name, args = [], paths = memberLoopPaths(name)) {
|
|
2279
|
+
const root = process.cwd();
|
|
2280
|
+
const atrisBin = resolveAtrisCommand();
|
|
2281
|
+
const logDir = path.join(paths.stateDir, 'logs');
|
|
2282
|
+
const command = [
|
|
2283
|
+
process.execPath,
|
|
2284
|
+
atrisBin,
|
|
2285
|
+
'member',
|
|
2286
|
+
'alive',
|
|
2287
|
+
name,
|
|
2288
|
+
'--hourly',
|
|
2289
|
+
'--forever',
|
|
2290
|
+
'--ticks',
|
|
2291
|
+
'1',
|
|
2292
|
+
'--json',
|
|
2293
|
+
...(hasFlag(args, '--execute') ? ['--execute'] : []),
|
|
2294
|
+
...(hasFlag(args, '--confirm-autonomy-policy') ? ['--confirm-autonomy-policy'] : []),
|
|
2295
|
+
];
|
|
2296
|
+
const agent = String(readFlag(args, '--agent', '') || '').trim();
|
|
2297
|
+
const model = String(readFlag(args, '--model', '') || '').trim();
|
|
2298
|
+
const maxWall = String(readFlag(args, '--operate-max-wall', readFlag(args, '--max-wall', '')) || '').trim();
|
|
2299
|
+
const autoAcceptLimit = String(readFlag(args, '--auto-accept-limit', '') || '').trim();
|
|
2300
|
+
if (agent) command.push('--agent', agent);
|
|
2301
|
+
if (model) command.push('--model', model);
|
|
2302
|
+
if (maxWall) command.push('--operate-max-wall', maxWall);
|
|
2303
|
+
if (autoAcceptLimit) command.push('--auto-accept-limit', autoAcceptLimit);
|
|
2304
|
+
const renderedCommand = command.map(shellSingleQuote).join(' ');
|
|
2305
|
+
return {
|
|
2306
|
+
command,
|
|
2307
|
+
rendered_command: renderedCommand,
|
|
2308
|
+
script: [
|
|
2309
|
+
'#!/bin/zsh',
|
|
2310
|
+
'set -u',
|
|
2311
|
+
`ROOT=${shellSingleQuote(root)}`,
|
|
2312
|
+
`LOG_DIR=${shellSingleQuote(logDir)}`,
|
|
2313
|
+
'mkdir -p "$LOG_DIR"',
|
|
2314
|
+
'stamp="$(date +"%Y%m%d-%H%M%S")"',
|
|
2315
|
+
'cd "$ROOT" || exit 1',
|
|
2316
|
+
'export ATRIS_SKIP_UPDATE_CHECK=1',
|
|
2317
|
+
`${renderedCommand} >> "$LOG_DIR/$stamp.log" 2>&1`,
|
|
2318
|
+
'echo "done: $(date -Iseconds) exit=$?" >> "$LOG_DIR/$stamp.log"',
|
|
2319
|
+
'',
|
|
2320
|
+
].join('\n'),
|
|
2321
|
+
};
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
function memberAliveCronCadence(args = []) {
|
|
2325
|
+
const pulse = require('../lib/pulse');
|
|
2326
|
+
const raw = hasFlag(args, '--hourly') || hasFlag(args, '--every-hour')
|
|
2327
|
+
? 'hourly'
|
|
2328
|
+
: readFlag(args, '--cadence', readFlag(args, '--cron', readFlag(args, '--interval', 'hourly')));
|
|
2329
|
+
return pulse.normalizeCronCadence(raw);
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
function buildMemberAliveCrontabLine(name, args = [], paths = memberLoopPaths(name)) {
|
|
2333
|
+
return `${memberAliveCronCadence(args)} ${paths.cronScriptPath} # ${memberAliveCronMarker(name)}`;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
function dirtyGitStatus(root = process.cwd()) {
|
|
2337
|
+
const result = spawnSync('git', ['status', '--porcelain'], { cwd: root, encoding: 'utf8', timeout: 10000 });
|
|
2338
|
+
if (result.status !== 0) {
|
|
2339
|
+
return { inside_git: false, dirty: false, dirty_count: 0, dirty_files: [] };
|
|
2340
|
+
}
|
|
2341
|
+
const dirtyFiles = String(result.stdout || '')
|
|
2342
|
+
.split(/\r?\n/)
|
|
2343
|
+
.map((line) => line.trim())
|
|
2344
|
+
.filter(Boolean);
|
|
2345
|
+
return {
|
|
2346
|
+
inside_git: true,
|
|
2347
|
+
dirty: dirtyFiles.length > 0,
|
|
2348
|
+
dirty_count: dirtyFiles.length,
|
|
2349
|
+
dirty_files: dirtyFiles.slice(0, 12),
|
|
2350
|
+
};
|
|
2351
|
+
}
|
|
2352
|
+
|
|
2353
|
+
function memberAliveInstallPreview(name, args = [], paths = memberLoopPaths(name), script = buildMemberAliveCronScript(name, args, paths)) {
|
|
2354
|
+
return {
|
|
2355
|
+
schema: 'atris.member_alive_install_preview.v1',
|
|
2356
|
+
member: name,
|
|
2357
|
+
cadence: memberAliveCronCadence(args),
|
|
2358
|
+
mission_text: memberRunAutoMissionText(name, args),
|
|
2359
|
+
run: script.rendered_command,
|
|
2360
|
+
verify: `atris member alive ${name} --status --json`,
|
|
2361
|
+
stop_when: `Run atris member alive ${name} --uninstall, or create the stop marker at ${paths.stopPath}.`,
|
|
2362
|
+
receipts: path.join(paths.stateDir, 'logs'),
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
function memberAliveInstallPreviewLines(preview) {
|
|
2367
|
+
return [
|
|
2368
|
+
'Every hour this will:',
|
|
2369
|
+
` run: ${preview.run}`,
|
|
2370
|
+
` verify: ${preview.verify}`,
|
|
2371
|
+
` stop when: ${preview.stop_when}`,
|
|
2372
|
+
` receipts: ${preview.receipts}`,
|
|
2373
|
+
];
|
|
2374
|
+
}
|
|
2375
|
+
|
|
2376
|
+
function installMemberAliveCron(name, args = []) {
|
|
2377
|
+
const asJson = hasFlag(args, '--json');
|
|
2378
|
+
const dryRun = hasFlag(args, '--dry-run');
|
|
2379
|
+
if (hasFlag(args, '--execute') && !hasFlag(args, '--confirm-autonomy-policy')) {
|
|
2380
|
+
const payload = {
|
|
2381
|
+
ok: false,
|
|
2382
|
+
action: 'alive_install',
|
|
2383
|
+
member: name,
|
|
2384
|
+
status: 'blocked',
|
|
2385
|
+
reason: 'execute_requires_confirm_autonomy_policy',
|
|
2386
|
+
};
|
|
2387
|
+
printJsonOrText(payload, [
|
|
2388
|
+
`Install blocked for ${name}: execute requires --confirm-autonomy-policy.`,
|
|
2389
|
+
], asJson);
|
|
2390
|
+
process.exitCode = 1;
|
|
2391
|
+
return payload;
|
|
2392
|
+
}
|
|
2393
|
+
const paths = memberLoopPaths(name);
|
|
2394
|
+
fs.mkdirSync(paths.stateDir, { recursive: true });
|
|
2395
|
+
const script = buildMemberAliveCronScript(name, args, paths);
|
|
2396
|
+
const crontabLine = buildMemberAliveCrontabLine(name, args, paths);
|
|
2397
|
+
const preview = memberAliveInstallPreview(name, args, paths, script);
|
|
2398
|
+
if (hasFlag(args, '--execute') && !dryRun) {
|
|
2399
|
+
const gitStatus = dirtyGitStatus();
|
|
2400
|
+
if (gitStatus.dirty) {
|
|
2401
|
+
const payload = {
|
|
2402
|
+
ok: false,
|
|
2403
|
+
action: 'alive_install',
|
|
2404
|
+
member: name,
|
|
2405
|
+
status: 'blocked',
|
|
2406
|
+
reason: 'install_requires_clean_git',
|
|
2407
|
+
git: gitStatus,
|
|
2408
|
+
preview,
|
|
2409
|
+
};
|
|
2410
|
+
printJsonOrText(payload, [
|
|
2411
|
+
`Install blocked for ${name}: commit or clean local changes before installing an execute loop.`,
|
|
2412
|
+
...memberAliveInstallPreviewLines(preview),
|
|
2413
|
+
], asJson);
|
|
2414
|
+
process.exitCode = 1;
|
|
2415
|
+
return payload;
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
fs.writeFileSync(paths.cronScriptPath, script.script, { mode: 0o755 });
|
|
2419
|
+
const marker = memberAliveCronMarker(name);
|
|
2420
|
+
let installed = false;
|
|
2421
|
+
let error = null;
|
|
2422
|
+
if (!dryRun) {
|
|
2423
|
+
const existing = spawnSync('crontab', ['-l'], { encoding: 'utf8', timeout: 10000 });
|
|
2424
|
+
const current = existing.status === 0 ? existing.stdout : '';
|
|
2425
|
+
const kept = String(current || '').split(/\r?\n/).filter((line) => line.trim() && !line.includes(marker));
|
|
2426
|
+
const next = `${kept.join('\n')}${kept.length ? '\n' : ''}${crontabLine}\n`;
|
|
2427
|
+
const apply = spawnSync('crontab', ['-'], { input: next, encoding: 'utf8', timeout: 10000 });
|
|
2428
|
+
installed = apply.status === 0;
|
|
2429
|
+
if (!installed) error = apply.stderr || `crontab exited ${apply.status}`;
|
|
2430
|
+
}
|
|
2431
|
+
const payload = {
|
|
2432
|
+
ok: dryRun || installed,
|
|
2433
|
+
action: 'alive_install',
|
|
2434
|
+
member: name,
|
|
2435
|
+
cadence: 'hourly',
|
|
2436
|
+
forever: true,
|
|
2437
|
+
installed,
|
|
2438
|
+
dry_run: dryRun,
|
|
2439
|
+
script_path: paths.cronScriptPath,
|
|
2440
|
+
crontab_line: crontabLine,
|
|
2441
|
+
command: script.command,
|
|
2442
|
+
preview,
|
|
2443
|
+
stop_command: `atris member alive ${name} --uninstall`,
|
|
2444
|
+
error,
|
|
2445
|
+
};
|
|
2446
|
+
writeJsonFile(paths.latestPath, payload);
|
|
2447
|
+
printJsonOrText(payload, [
|
|
2448
|
+
`${dryRun ? 'Would install' : installed ? 'Installed' : 'Install failed'} hourly alive loop: ${name}`,
|
|
2449
|
+
`Cron: ${crontabLine}`,
|
|
2450
|
+
...memberAliveInstallPreviewLines(preview),
|
|
2451
|
+
`Stop: ${payload.stop_command}`,
|
|
2452
|
+
], asJson);
|
|
2453
|
+
if (!payload.ok) process.exitCode = 1;
|
|
2454
|
+
return payload;
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
function uninstallMemberAliveCron(name, args = []) {
|
|
2458
|
+
const asJson = hasFlag(args, '--json');
|
|
2459
|
+
const paths = memberLoopPaths(name);
|
|
2460
|
+
const marker = memberAliveCronMarker(name);
|
|
2461
|
+
const existing = spawnSync('crontab', ['-l'], { encoding: 'utf8', timeout: 10000 });
|
|
2462
|
+
if (existing.status !== 0) {
|
|
2463
|
+
const payload = { ok: true, action: 'alive_uninstall', member: name, removed: false, reason: 'no_crontab' };
|
|
2464
|
+
printJsonOrText(payload, [`No hourly alive crontab found for ${name}.`], asJson);
|
|
2465
|
+
return payload;
|
|
2466
|
+
}
|
|
2467
|
+
const before = String(existing.stdout || '').split(/\r?\n/);
|
|
2468
|
+
const after = before.filter((line) => !line.includes(marker));
|
|
2469
|
+
const removed = after.length !== before.length;
|
|
2470
|
+
const apply = spawnSync('crontab', ['-'], { input: after.filter(Boolean).join('\n') + (after.filter(Boolean).length ? '\n' : ''), encoding: 'utf8', timeout: 10000 });
|
|
2471
|
+
const payload = {
|
|
2472
|
+
ok: apply.status === 0,
|
|
2473
|
+
action: 'alive_uninstall',
|
|
2474
|
+
member: name,
|
|
2475
|
+
removed,
|
|
2476
|
+
marker,
|
|
2477
|
+
script_path: paths.cronScriptPath,
|
|
2478
|
+
error: apply.status === 0 ? null : apply.stderr || `crontab exited ${apply.status}`,
|
|
2479
|
+
};
|
|
2480
|
+
printJsonOrText(payload, [
|
|
2481
|
+
removed ? `Removed hourly alive loop: ${name}` : `No hourly alive line found for ${name}.`,
|
|
2482
|
+
], asJson);
|
|
2483
|
+
if (!payload.ok) process.exitCode = 1;
|
|
2484
|
+
return payload;
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
function memberAliveCronStatus(name, paths = memberLoopPaths(name)) {
|
|
2488
|
+
const marker = memberAliveCronMarker(name);
|
|
2489
|
+
let crontabLine = null;
|
|
2490
|
+
const crontab = spawnSync('crontab', ['-l'], { encoding: 'utf8', timeout: 10000 });
|
|
2491
|
+
if (crontab.status === 0) {
|
|
2492
|
+
crontabLine = String(crontab.stdout || '').split(/\r?\n/).find((line) => line.includes(marker)) || null;
|
|
2493
|
+
}
|
|
2494
|
+
return {
|
|
2495
|
+
schema: 'atris.member_alive_cron_status.v1',
|
|
2496
|
+
marker,
|
|
2497
|
+
installed: Boolean(crontabLine),
|
|
2498
|
+
crontab_line: crontabLine,
|
|
2499
|
+
script_path: paths.cronScriptPath,
|
|
2500
|
+
script_exists: fs.existsSync(paths.cronScriptPath),
|
|
2501
|
+
install_command: `atris member alive ${name} --install --hourly --forever --execute --confirm-autonomy-policy --json`,
|
|
2502
|
+
uninstall_command: `atris member alive ${name} --uninstall`,
|
|
2026
2503
|
};
|
|
2027
2504
|
}
|
|
2028
2505
|
|
|
@@ -2145,6 +2622,11 @@ function stripAutoImproverTitleNoise(text) {
|
|
|
2145
2622
|
function isAutoImproverGeneratedLogLine(line, relativePath = '') {
|
|
2146
2623
|
const text = String(line || '').trim();
|
|
2147
2624
|
if (!text) return false;
|
|
2625
|
+
if (/^[-*]?\s*Next tick will stop until a human looks at the error\.?$/i.test(text)) return true;
|
|
2626
|
+
if (/^[-*]?\s*Next tick will verify failed, halting\.?$/i.test(text)) return true;
|
|
2627
|
+
if (/^##\s+\d{1,2}:\d{2}\s+·\s+Task failed$/i.test(text)) return true;
|
|
2628
|
+
if (/^[-*]?\s*(status|action|state|verifier)\s*:\s*(failed|blocked)\s*$/i.test(text)) return true;
|
|
2629
|
+
if (/^[-*]?\s*(title|proof):\s*/i.test(text)) return true;
|
|
2148
2630
|
if (String(relativePath || '').startsWith('atris/team/auto-improver/logs/') && /^-\s*summary:\s*/i.test(text)) return true;
|
|
2149
2631
|
if (/\bauto[- ]improver\b/i.test(text) && /\b(dogfood|receipt|prevented|pain_)\b/i.test(text)) return true;
|
|
2150
2632
|
if (/\bauto_improver\b/i.test(text)) return true;
|
|
@@ -2162,12 +2644,28 @@ function normalizeFailurePattern(line) {
|
|
|
2162
2644
|
.trim());
|
|
2163
2645
|
}
|
|
2164
2646
|
|
|
2647
|
+
function failureCoveredByPassLesson(line, passLessonText = '') {
|
|
2648
|
+
const text = String(line || '');
|
|
2649
|
+
if (!text || !passLessonText) return false;
|
|
2650
|
+
if (/spawnSync\s+\/bin\/sh\s+ETIMEDOUT/i.test(text)) {
|
|
2651
|
+
const target = text.match(/"([^"]+)"/)?.[1] || '';
|
|
2652
|
+
const targetTail = target ? target.split('/').slice(-2).join('/') : '';
|
|
2653
|
+
return /spawnSync\s+\/bin\/sh\s+ETIMEDOUT/i.test(passLessonText)
|
|
2654
|
+
&& (!target || passLessonText.includes(target) || (targetTail && passLessonText.includes(targetTail)));
|
|
2655
|
+
}
|
|
2656
|
+
return false;
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2165
2659
|
function collectAutoImproverLogSignals(root) {
|
|
2166
2660
|
const roots = [
|
|
2167
2661
|
path.join(root, 'atris', 'logs'),
|
|
2168
2662
|
path.join(root, 'atris', 'team'),
|
|
2169
2663
|
path.join(root, 'atris', 'wiki'),
|
|
2170
2664
|
].filter((candidate) => fs.existsSync(candidate));
|
|
2665
|
+
const passLessonText = safeReadText(path.join(root, 'atris', 'lessons.md'), 500000)
|
|
2666
|
+
.split(/\r?\n/)
|
|
2667
|
+
.filter((line) => /\s+pass\s+—/.test(line))
|
|
2668
|
+
.join('\n');
|
|
2171
2669
|
const failureRegex = /\b(error|failed|failure|blocked|timeout|regression|crash|missing proof|naraka|suffering)\b/i;
|
|
2172
2670
|
const unclearRegex = /\b(tbd|unclear|unknown|needs user|needs owner|needs proof|no next|blocked)\b/i;
|
|
2173
2671
|
const counts = new Map();
|
|
@@ -2203,6 +2701,7 @@ function collectAutoImproverLogSignals(root) {
|
|
|
2203
2701
|
if (/^\s*[-*]?\s*check:\s/i.test(line)) continue;
|
|
2204
2702
|
if (/\b(errors?|fail(?:ed|ures?)|blocked|timeouts?)\s*:\s*0\b/i.test(line)) continue;
|
|
2205
2703
|
if (/\bblocked\s*(?:->|→|to)\s*ready\b/i.test(line)) continue;
|
|
2704
|
+
if (failureCoveredByPassLesson(line, passLessonText)) continue;
|
|
2206
2705
|
if (unclearRegex.test(line)) {
|
|
2207
2706
|
unclearNextActions += 1;
|
|
2208
2707
|
unclearActions.push({
|
|
@@ -6900,6 +7399,14 @@ async function memberLoop(name, ...args) {
|
|
|
6900
7399
|
const paths = memberLoopPaths(name);
|
|
6901
7400
|
fs.mkdirSync(paths.stateDir, { recursive: true });
|
|
6902
7401
|
|
|
7402
|
+
if (hasFlag(args, '--install')) {
|
|
7403
|
+
return installMemberAliveCron(name, args);
|
|
7404
|
+
}
|
|
7405
|
+
|
|
7406
|
+
if (hasFlag(args, '--uninstall')) {
|
|
7407
|
+
return uninstallMemberAliveCron(name, args);
|
|
7408
|
+
}
|
|
7409
|
+
|
|
6903
7410
|
if (status) {
|
|
6904
7411
|
const active = readJsonIfExists(paths.lockPath);
|
|
6905
7412
|
const latest = readJsonIfExists(paths.latestPath);
|
|
@@ -6910,12 +7417,14 @@ async function memberLoop(name, ...args) {
|
|
|
6910
7417
|
active: Boolean(active && Number(active.expires_at_ms || 0) > Date.now() && isPidAlive(active.pid)),
|
|
6911
7418
|
lease: active || null,
|
|
6912
7419
|
latest: latest || null,
|
|
7420
|
+
hourly_alive: memberAliveCronStatus(name, paths),
|
|
6913
7421
|
lock_path: paths.lockPath,
|
|
6914
7422
|
latest_path: paths.latestPath,
|
|
6915
7423
|
};
|
|
6916
7424
|
printJsonOrText(payload, [
|
|
6917
7425
|
`Loop status: ${name}`,
|
|
6918
7426
|
`Active: ${payload.active ? 'yes' : 'no'}`,
|
|
7427
|
+
`Hourly: ${payload.hourly_alive.installed ? 'installed' : payload.hourly_alive.script_exists ? 'script ready' : 'not installed'}`,
|
|
6919
7428
|
`Latest: ${latest?.receipt_path ? path.relative(process.cwd(), latest.receipt_path) : 'none'}`,
|
|
6920
7429
|
], asJson);
|
|
6921
7430
|
return;
|
|
@@ -6953,11 +7462,21 @@ async function memberLoop(name, ...args) {
|
|
|
6953
7462
|
const ticksFlag = readNumberFlag(args, '--ticks', null);
|
|
6954
7463
|
const minutes = readNumberFlag(args, '--minutes', null);
|
|
6955
7464
|
const durationSeconds = readNumberFlag(args, '--duration-seconds', readNumberFlag(args, '--seconds', null));
|
|
6956
|
-
const
|
|
7465
|
+
const hourly = hasFlag(args, '--hourly') || hasFlag(args, '--every-hour');
|
|
7466
|
+
const forever = hasFlag(args, '--forever') || hasFlag(args, '--until-stopped');
|
|
7467
|
+
const intervalSeconds = hourly
|
|
7468
|
+
? 3600
|
|
7469
|
+
: readNumberFlag(args, '--interval', readNumberFlag(args, '--interval-seconds', 60));
|
|
6957
7470
|
const intervalMs = Math.max(0, Math.floor(Number(intervalSeconds == null ? 60 : intervalSeconds) * 1000));
|
|
6958
|
-
const durationMs =
|
|
7471
|
+
const durationMs = forever && ticksFlag == null
|
|
7472
|
+
? 0
|
|
7473
|
+
: Math.max(0, Math.floor(durationSeconds != null ? Number(durationSeconds) * 1000 : Number(minutes == null ? 10 : minutes) * 60 * 1000));
|
|
6959
7474
|
const ticks = ticksFlag != null
|
|
6960
7475
|
? Math.max(1, Math.floor(Number(ticksFlag)))
|
|
7476
|
+
: forever
|
|
7477
|
+
? execute && confirmed
|
|
7478
|
+
? Number.MAX_SAFE_INTEGER
|
|
7479
|
+
: 1
|
|
6961
7480
|
: intervalMs > 0
|
|
6962
7481
|
? Math.max(1, Math.floor(durationMs / intervalMs))
|
|
6963
7482
|
: 1;
|
|
@@ -6967,7 +7486,7 @@ async function memberLoop(name, ...args) {
|
|
|
6967
7486
|
const ttlMs = Math.max(
|
|
6968
7487
|
30000,
|
|
6969
7488
|
Math.floor(Number(ttlSeconds == null ? 0 : ttlSeconds) * 1000),
|
|
6970
|
-
durationMs + 60000,
|
|
7489
|
+
forever ? intervalMs + (2 * 60 * 60 * 1000) : durationMs + 60000,
|
|
6971
7490
|
intervalMs + 60000,
|
|
6972
7491
|
);
|
|
6973
7492
|
|
|
@@ -7162,6 +7681,12 @@ async function memberLoop(name, ...args) {
|
|
|
7162
7681
|
alive: aliveMode,
|
|
7163
7682
|
status: failed ? 'failed' : stopped ? 'stopped' : earlyExit ? (earlyExit.blocked_on_human ? 'blocked_on_human' : 'idle') : 'completed',
|
|
7164
7683
|
mode: execute ? 'execute' : 'dry_run',
|
|
7684
|
+
cadence: hourly ? 'hourly' : 'custom',
|
|
7685
|
+
forever,
|
|
7686
|
+
stop_command: `atris member ${aliveMode ? 'alive' : 'loop'} ${name} --stop`,
|
|
7687
|
+
operating_contract: forever
|
|
7688
|
+
? 'Run on this cadence until stopped; each tick must choose useful work, prove it plainly, or stop/ask instead of faking activity.'
|
|
7689
|
+
: 'Run for the requested bounded window; each tick must choose useful work, prove it plainly, or stop/ask instead of faking activity.',
|
|
7165
7690
|
run_id: runId,
|
|
7166
7691
|
ticks_requested: ticks,
|
|
7167
7692
|
ticks: tickResults.length,
|
|
@@ -7191,8 +7716,10 @@ async function memberLoop(name, ...args) {
|
|
|
7191
7716
|
printJsonOrText(payload, [
|
|
7192
7717
|
`${aliveMode ? 'Alive' : 'Loop'}: ${name}`,
|
|
7193
7718
|
`Status: ${payload.status}`,
|
|
7719
|
+
`Cadence: ${payload.cadence}${payload.forever ? ' forever' : ''}`,
|
|
7194
7720
|
`Ticks: ${payload.ticks}/${payload.ticks_requested}`,
|
|
7195
7721
|
`Decisions: ${Object.entries(decisions).map(([key, count]) => `${key} x${count}`).join(', ') || 'none'}`,
|
|
7722
|
+
`Stop: ${payload.stop_command}`,
|
|
7196
7723
|
`Receipt: ${path.relative(process.cwd(), receiptPath)}`,
|
|
7197
7724
|
], asJson);
|
|
7198
7725
|
}
|
|
@@ -7533,7 +8060,7 @@ async function memberCommand(subcommand, ...args) {
|
|
|
7533
8060
|
// Subcommands that take a member name as args[0] otherwise treat `--help` as
|
|
7534
8061
|
// a name and error with "Member '--help' not found". `create`/`new` handle
|
|
7535
8062
|
// help themselves (with subcommand-specific usage) — leave those alone.
|
|
7536
|
-
const HELP_AWARE_SUBCOMMANDS = new Set(['create', 'new']);
|
|
8063
|
+
const HELP_AWARE_SUBCOMMANDS = new Set(['create', 'new', 'run']);
|
|
7537
8064
|
if (!HELP_AWARE_SUBCOMMANDS.has(subcommand) && (args[0] === '-h' || args[0] === '--help')) {
|
|
7538
8065
|
subcommand = undefined;
|
|
7539
8066
|
}
|
|
@@ -7566,6 +8093,8 @@ async function memberCommand(subcommand, ...args) {
|
|
|
7566
8093
|
return memberWake(args[0], ...args.slice(1));
|
|
7567
8094
|
case 'run':
|
|
7568
8095
|
return memberRun(args[0], ...args.slice(1));
|
|
8096
|
+
case 'ping':
|
|
8097
|
+
return memberPing(args[0], ...args.slice(1));
|
|
7569
8098
|
case 'loop':
|
|
7570
8099
|
return memberLoop(args[0], ...args.slice(1));
|
|
7571
8100
|
case 'alive':
|
|
@@ -7603,7 +8132,7 @@ async function memberCommand(subcommand, ...args) {
|
|
|
7603
8132
|
console.log(' goal-from-mission <name> Create/reuse a goal from MISSION.md and now.md');
|
|
7604
8133
|
console.log(' goal-from-score <name> Create/reuse an active goal from Team score evidence');
|
|
7605
8134
|
console.log(' wake <name> Read Mission state and decide tick/wait/ask/stop');
|
|
7606
|
-
console.log(
|
|
8135
|
+
console.log(' run <name> ["..."] Start a budgeted member mission, or run its active Mission Runtime');
|
|
7607
8136
|
console.log(' loop <name> Repeat wake on a bounded cadence with a no-overlap lease');
|
|
7608
8137
|
console.log(' tick <name> Propose the next bounded experiment');
|
|
7609
8138
|
console.log(' review <name> <id> Accept/discard an experiment with proof');
|
|
@@ -7633,13 +8162,16 @@ async function memberCommand(subcommand, ...args) {
|
|
|
7633
8162
|
console.log(' atris member goal-from-mission growth --json');
|
|
7634
8163
|
console.log(' atris member goal-from-score growth --score-json team-score.json --json');
|
|
7635
8164
|
console.log(' atris member wake growth --json');
|
|
7636
|
-
console.log(' atris member run growth
|
|
8165
|
+
console.log(' atris member run growth "Improve onboarding proof" --minutes 30 --json');
|
|
8166
|
+
console.log(' atris member run growth --mission <mission-id> --max-ticks 1 --json');
|
|
7637
8167
|
console.log(' atris member wake growth --execute --confirm-autonomy-policy');
|
|
7638
8168
|
console.log(' atris member supervisor recommendations --json');
|
|
7639
8169
|
console.log(' atris member objective-generator proposals --json');
|
|
7640
8170
|
console.log(' atris member generalist proof --json');
|
|
7641
8171
|
console.log(' atris member generalist patterns --json');
|
|
7642
8172
|
console.log(' atris member loop growth --minutes 10 --interval 60 --json');
|
|
8173
|
+
console.log(' atris member alive growth --hourly --forever --execute --confirm-autonomy-policy --json');
|
|
8174
|
+
console.log(' atris member alive growth --install --hourly --forever --execute --confirm-autonomy-policy --json');
|
|
7643
8175
|
console.log(' atris member alive growth --minutes 480 --interval 900 --execute --confirm-autonomy-policy --json');
|
|
7644
8176
|
console.log(' atris member loop growth --ticks 2 --interval 0 --json');
|
|
7645
8177
|
console.log(' atris member tick growth --json');
|