atris 3.74.0 → 3.74.2
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/bin/atris.js +85 -82
- package/commands/aeo.js +2 -2
- package/commands/align.js +10 -19
- package/commands/auth.js +1 -1
- package/commands/autopilot.js +9 -10
- package/commands/avail.js +36 -7
- package/commands/clean.js +2 -1
- package/commands/close.js +7 -4
- package/commands/computer.js +3 -1
- package/commands/console.js +1 -1
- package/commands/context-sync.js +1 -60
- package/commands/experiments.js +4 -1
- package/commands/fleet.js +0 -1
- package/commands/improve.js +3 -1
- package/commands/interview.js +13 -5
- package/commands/land.js +5 -1
- package/commands/lifecycle.js +17 -6
- package/commands/log-sync.js +1 -1
- package/commands/loops.js +5 -2
- package/commands/member.js +17 -11
- package/commands/mission.js +1 -4
- package/commands/now.js +0 -7
- package/commands/play.js +3 -1
- package/commands/plugin.js +1 -1
- package/commands/pull.js +5 -3
- package/commands/push.js +0 -1
- package/commands/radar.js +0 -4
- package/commands/recap.js +25 -1
- package/commands/report.js +0 -4
- package/commands/review.js +1 -1
- package/commands/security-review.js +4 -1
- package/commands/setup.js +6 -2
- package/commands/soul.js +2 -1
- package/commands/sync.js +8 -2
- package/commands/task.js +0 -522
- package/commands/terminal.js +1 -1
- package/commands/x-search.js +5 -3
- package/commands/youtube.js +9 -7
- package/commands/zen.js +4 -4
- package/lib/agent-today.js +412 -0
- package/lib/apply-gate.js +5 -3
- package/lib/autoland.js +0 -8
- package/lib/bench/runner.js +0 -4
- package/lib/chat-log-scan.js +0 -28
- package/lib/cli-scope.js +4 -2
- package/lib/engine-registry.js +0 -4
- package/lib/experiments/daily.js +2 -1
- package/lib/file-ops.js +6 -1
- package/lib/first-minute.js +2 -0
- package/lib/gm-bet.js +73 -9
- package/lib/gm-phone-page.js +42 -5
- package/lib/gm-phone.js +3 -0
- package/lib/local-day.js +10 -0
- package/lib/manifest.js +0 -10
- package/lib/member-switches.js +0 -11
- package/lib/review-integrity.js +0 -4
- package/lib/scorecard.js +7 -6
- package/lib/wiki.js +4 -1
- package/package.json +1 -1
package/commands/align.js
CHANGED
|
@@ -42,15 +42,6 @@ function sleep(ms) {
|
|
|
42
42
|
return new Promise((r) => setTimeout(r, ms));
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
/**
|
|
46
|
-
* Compute SHA-256 hash of content string.
|
|
47
|
-
* @param {string} content - Content to hash
|
|
48
|
-
* @returns {string} Hex-encoded hash
|
|
49
|
-
*/
|
|
50
|
-
function hashContent(content) {
|
|
51
|
-
return crypto.createHash('sha256').update(content).digest('hex');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
45
|
/**
|
|
55
46
|
* Compute SHA-256 hash of a file.
|
|
56
47
|
* @param {string} absPath - Absolute path to file
|
|
@@ -472,19 +463,19 @@ async function alignAtris() {
|
|
|
472
463
|
const printUsage = () => {
|
|
473
464
|
console.log('Usage: atris align [business] [--fix] [--hard] [--from cloud|local] [--dry-run]');
|
|
474
465
|
console.log('');
|
|
475
|
-
console.log(' atris align
|
|
476
|
-
console.log(' atris align example-co
|
|
477
|
-
console.log(' atris align example-co --fix
|
|
478
|
-
console.log(' atris align example-co --fix --hard
|
|
479
|
-
console.log(' atris align example-co --fix --from cloud
|
|
480
|
-
console.log(' atris align example-co --dry-run Show what would change,
|
|
466
|
+
console.log(' atris align Show what differs between this folder and its cloud copy');
|
|
467
|
+
console.log(' atris align example-co Show what differs for the example-co workspace');
|
|
468
|
+
console.log(' atris align example-co --fix Make them match, keeping this computer\'s version');
|
|
469
|
+
console.log(' atris align example-co --fix --hard Make the cloud an exact copy of this computer: delete cloud-only files, upload the rest. Fast.');
|
|
470
|
+
console.log(' atris align example-co --fix --from cloud Keep the cloud version: copy cloud-only files down, delete extra local files');
|
|
471
|
+
console.log(' atris align example-co --dry-run Show what would change, change nothing');
|
|
481
472
|
};
|
|
482
473
|
|
|
483
474
|
// Explicit help must win before we try to auto-detect a business slug,
|
|
484
475
|
// otherwise `--help` gets overwritten by the .atris/business.json fallback
|
|
485
476
|
// and the command attempts a real align instead of printing usage.
|
|
486
|
-
const
|
|
487
|
-
if (
|
|
477
|
+
const alignArgs = process.argv.slice(3);
|
|
478
|
+
if (alignArgs[0] === 'help' || alignArgs.includes('--help') || alignArgs.includes('-h')) {
|
|
488
479
|
printUsage();
|
|
489
480
|
process.exit(0);
|
|
490
481
|
}
|
|
@@ -515,7 +506,7 @@ async function alignAtris() {
|
|
|
515
506
|
}
|
|
516
507
|
|
|
517
508
|
if (hard && fromSide !== 'local') {
|
|
518
|
-
console.error('--hard only
|
|
509
|
+
console.error('--hard only works with --from local: it overwrites the cloud with this computer\'s files, never the other way.');
|
|
519
510
|
process.exit(1);
|
|
520
511
|
}
|
|
521
512
|
|
|
@@ -546,7 +537,7 @@ async function alignAtris() {
|
|
|
546
537
|
// Wake EC2 (the rule)
|
|
547
538
|
const endpoint = await ensureAwake(creds.token, biz.businessId);
|
|
548
539
|
if (!endpoint) {
|
|
549
|
-
console.error(
|
|
540
|
+
console.error(` The cloud computer for "${slug}" did not wake up in time, so nothing ran. Wait a minute, then run the same command again.`);
|
|
550
541
|
process.exit(1);
|
|
551
542
|
}
|
|
552
543
|
|
package/commands/auth.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { AGENT_TOKEN_EXPIRED_DETAIL, decodeJwtClaims, loadCredentials, saveCredentials, saveAgentClientCredentials, preserveAgentClientLogin, deleteCredentials, getCredentialsPath, openBrowser, promptUser, displayAccountSummary, ensureValidCredentials, loadProfile, listProfiles, profileNameFromEmail, deleteProfile, saveProfile, getTokenExpiryEpochSeconds, getTerminalSessionId, setSessionProfile, getSessionProfile,
|
|
1
|
+
const { AGENT_TOKEN_EXPIRED_DETAIL, decodeJwtClaims, loadCredentials, saveCredentials, saveAgentClientCredentials, preserveAgentClientLogin, deleteCredentials, getCredentialsPath, openBrowser, promptUser, displayAccountSummary, ensureValidCredentials, loadProfile, listProfiles, profileNameFromEmail, deleteProfile, saveProfile, getTokenExpiryEpochSeconds, getTerminalSessionId, setSessionProfile, getSessionProfile, cleanStaleSessions, getSessionsDir } = require('../utils/auth');
|
|
2
2
|
const { getAppBaseUrl, getApiBaseUrl, apiRequestJson } = require('../utils/api');
|
|
3
3
|
const { isNonInteractive, wantsJson } = require('../lib/noninteractive');
|
|
4
4
|
const { hasFlag, readFlag } = require('../lib/arg-parser');
|
package/commands/autopilot.js
CHANGED
|
@@ -1157,13 +1157,19 @@ function lessonSlug(text, maxLen = 40) {
|
|
|
1157
1157
|
return trimmed.replace(/-+$/g, '') || 'unknown';
|
|
1158
1158
|
}
|
|
1159
1159
|
|
|
1160
|
+
// Local calendar day (YYYY-MM-DD). toISOString() is the UTC day, which is
|
|
1161
|
+
// already tomorrow in the evening anywhere west of Greenwich.
|
|
1162
|
+
function localDay(date = new Date()) {
|
|
1163
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1160
1166
|
/**
|
|
1161
1167
|
* Write a lesson to atris/lessons.md
|
|
1162
1168
|
* Appends a line in format: - **[YYYY-MM-DD] slug** - pass/fail - explanation
|
|
1163
1169
|
*/
|
|
1164
1170
|
function writeLesson(cwd, slug, status, explanation) {
|
|
1165
1171
|
const lessonsPath = path.join(cwd, 'atris', 'lessons.md');
|
|
1166
|
-
const today =
|
|
1172
|
+
const today = localDay();
|
|
1167
1173
|
const lessonLine = `- **[${today}] ${slug}** - ${status} - ${explanation}`;
|
|
1168
1174
|
|
|
1169
1175
|
if (!fs.existsSync(lessonsPath)) {
|
|
@@ -2959,7 +2965,7 @@ function markLessonAttempted(cwd, slug, reason) {
|
|
|
2959
2965
|
if (!meta[slug] || typeof meta[slug] !== 'object') meta[slug] = {};
|
|
2960
2966
|
meta[slug].status = 'attempted';
|
|
2961
2967
|
meta[slug].attempts = (typeof meta[slug].attempts === 'number' ? meta[slug].attempts : 0) + 1;
|
|
2962
|
-
meta[slug].last_attempt =
|
|
2968
|
+
meta[slug].last_attempt = localDay();
|
|
2963
2969
|
if (reason) meta[slug].last_attempt_reason = String(reason);
|
|
2964
2970
|
try {
|
|
2965
2971
|
const atrisDir = path.join(cwd, 'atris');
|
|
@@ -3181,7 +3187,7 @@ function hasRecentVerifyPrePass(cwd, taskTitle, windowDays = 7) {
|
|
|
3181
3187
|
if (lessons.length === 0) return false;
|
|
3182
3188
|
const cutoff = new Date();
|
|
3183
3189
|
cutoff.setDate(cutoff.getDate() - windowDays);
|
|
3184
|
-
const cutoffDate = cutoff
|
|
3190
|
+
const cutoffDate = localDay(cutoff);
|
|
3185
3191
|
const needle = `"${taskTitle}"`;
|
|
3186
3192
|
for (const l of lessons) {
|
|
3187
3193
|
if (l.id !== 'verify-not-falsifiable') continue;
|
|
@@ -4043,13 +4049,6 @@ Search the codebase to verify. Reply: YES <reason> or NO <reason>`;
|
|
|
4043
4049
|
}
|
|
4044
4050
|
}
|
|
4045
4051
|
|
|
4046
|
-
/**
|
|
4047
|
-
* Entry point when called without a description.
|
|
4048
|
-
*/
|
|
4049
|
-
async function autopilotFromTodo(options = {}) {
|
|
4050
|
-
return autopilotAtris(null, options);
|
|
4051
|
-
}
|
|
4052
|
-
|
|
4053
4052
|
module.exports = {
|
|
4054
4053
|
askModel,
|
|
4055
4054
|
snapshotRepoHeads,
|
package/commands/avail.js
CHANGED
|
@@ -117,6 +117,21 @@ function resolveTimezone({ tz, where }) {
|
|
|
117
117
|
return 'America/Los_Angeles';
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
function parseWholeNumber(raw, min, max) {
|
|
121
|
+
if (raw === undefined || !/^\d+$/.test(String(raw).trim())) return null;
|
|
122
|
+
const n = parseInt(raw, 10);
|
|
123
|
+
if (n < min || n > max) return null;
|
|
124
|
+
return n;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function requireWholeNumber(flag, raw, min, max) {
|
|
128
|
+
const n = parseWholeNumber(raw, min, max);
|
|
129
|
+
if (n === null) {
|
|
130
|
+
throw new Error(`${flag} needs a whole number from ${min} to ${max}, got: ${raw === undefined ? 'nothing' : raw}`);
|
|
131
|
+
}
|
|
132
|
+
return n;
|
|
133
|
+
}
|
|
134
|
+
|
|
120
135
|
function parseSetArgs(argv) {
|
|
121
136
|
const opts = {
|
|
122
137
|
wake: null,
|
|
@@ -138,10 +153,10 @@ function parseSetArgs(argv) {
|
|
|
138
153
|
else if (a === '--tz') opts.tz = argv[++i];
|
|
139
154
|
else if (a === '--where') opts.where = argv[++i];
|
|
140
155
|
else if (a === '--days') opts.days = argv[++i];
|
|
141
|
-
else if (a === '--slot-minutes') opts.slotMinutes =
|
|
142
|
-
else if (a === '--window-days') opts.windowDays =
|
|
156
|
+
else if (a === '--slot-minutes') opts.slotMinutes = requireWholeNumber(a, argv[++i], 5, 480);
|
|
157
|
+
else if (a === '--window-days') opts.windowDays = requireWholeNumber(a, argv[++i], 1, 365);
|
|
143
158
|
else if (a === '--help' || a === '-h') return { help: true };
|
|
144
|
-
else throw new Error(`
|
|
159
|
+
else throw new Error(`atris avail set does not take "${a}". Run: atris avail --help`);
|
|
145
160
|
}
|
|
146
161
|
if (opts.wake == null || opts.sleep == null) {
|
|
147
162
|
throw new Error('--wake and --sleep are required (e.g. --wake 2:30pm --sleep 9pm)');
|
|
@@ -287,18 +302,30 @@ async function setAvail(argv) {
|
|
|
287
302
|
return 0;
|
|
288
303
|
}
|
|
289
304
|
|
|
305
|
+
function parseSlotsDays(raw) {
|
|
306
|
+
return parseWholeNumber(raw, 1, 365);
|
|
307
|
+
}
|
|
308
|
+
|
|
290
309
|
async function slotsAvail(argv) {
|
|
291
310
|
const opts = { days: 7, username: null, json: false };
|
|
292
311
|
for (let i = 0; i < argv.length; i += 1) {
|
|
293
312
|
const a = argv[i];
|
|
294
313
|
if (a === '--json') opts.json = true;
|
|
295
|
-
else if (a === '--days')
|
|
314
|
+
else if (a === '--days') {
|
|
315
|
+
const raw = argv[++i];
|
|
316
|
+
const days = parseSlotsDays(raw);
|
|
317
|
+
if (days === null) {
|
|
318
|
+
console.error(`--days needs a whole number from 1 to 365, got: ${raw === undefined ? 'nothing' : raw}`);
|
|
319
|
+
return 1;
|
|
320
|
+
}
|
|
321
|
+
opts.days = days;
|
|
322
|
+
}
|
|
296
323
|
else if (a === '--user' || a === '--username') opts.username = argv[++i];
|
|
297
324
|
else if (a === '--help' || a === '-h') {
|
|
298
325
|
printHelp();
|
|
299
326
|
return 0;
|
|
300
327
|
} else {
|
|
301
|
-
console.error(`
|
|
328
|
+
console.error(`atris avail slots does not take "${a}". Run: atris avail --help`);
|
|
302
329
|
return 1;
|
|
303
330
|
}
|
|
304
331
|
}
|
|
@@ -320,7 +347,7 @@ async function slotsAvail(argv) {
|
|
|
320
347
|
try {
|
|
321
348
|
data = JSON.parse(result.body.toString('utf8'));
|
|
322
349
|
} catch {
|
|
323
|
-
console.error(
|
|
350
|
+
console.error(`the booking server sent back a reply atris could not read (HTTP ${result.status}). Try again in a minute.`);
|
|
324
351
|
return 1;
|
|
325
352
|
}
|
|
326
353
|
if (result.status < 200 || result.status >= 300) {
|
|
@@ -392,7 +419,7 @@ async function availCommand(argv = []) {
|
|
|
392
419
|
const sub = argv[0].toLowerCase();
|
|
393
420
|
if (sub === 'set' || sub === 'update') return setAvail(argv.slice(1));
|
|
394
421
|
if (sub === 'slots') return slotsAvail(argv.slice(1));
|
|
395
|
-
console.error(`
|
|
422
|
+
console.error(`atris avail does not know "${sub}". Use show, set, or slots.`);
|
|
396
423
|
printHelp();
|
|
397
424
|
return 1;
|
|
398
425
|
}
|
|
@@ -402,6 +429,8 @@ module.exports = {
|
|
|
402
429
|
formatBookingLink,
|
|
403
430
|
parseTimeToDecimal,
|
|
404
431
|
parseDaysArg,
|
|
432
|
+
parseSlotsDays,
|
|
433
|
+
parseSetArgs,
|
|
405
434
|
buildAvailableHours,
|
|
406
435
|
resolveTimezone,
|
|
407
436
|
formatDecimalHour,
|
package/commands/clean.js
CHANGED
|
@@ -3,6 +3,7 @@ const os = require('os');
|
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { spawnSync } = require('child_process');
|
|
5
5
|
const escapeRegExp = require('../lib/escape-regexp');
|
|
6
|
+
const { localDay } = require('../lib/local-day');
|
|
6
7
|
const { wikiMetabolismNudge } = require('../lib/wiki');
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -723,7 +724,7 @@ function checkPageStaleness(cwd, filePath) {
|
|
|
723
724
|
page: filePath,
|
|
724
725
|
staleSource: source,
|
|
725
726
|
compiledDate: compiledMatch[1],
|
|
726
|
-
sourceDate: changed
|
|
727
|
+
sourceDate: localDay(changed)
|
|
727
728
|
};
|
|
728
729
|
}
|
|
729
730
|
} catch {
|
package/commands/close.js
CHANGED
|
@@ -85,8 +85,10 @@ function appendEvent(event, cwd = process.cwd()) {
|
|
|
85
85
|
fs.appendFileSync(ledgerPath(cwd), `${JSON.stringify(event)}\n`);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
// Local calendar day (YYYY-MM-DD). toISOString() is the UTC day, which is
|
|
89
|
+
// already tomorrow in the evening anywhere west of Greenwich.
|
|
90
|
+
function localDay(date) {
|
|
91
|
+
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
function parseDate(value) {
|
|
@@ -156,7 +158,8 @@ function foldEvents(events, options = {}) {
|
|
|
156
158
|
delete flag.parked_at;
|
|
157
159
|
delete flag.why;
|
|
158
160
|
} else if (event.kind === 'escalated') {
|
|
159
|
-
const
|
|
161
|
+
const at = event.at ? new Date(event.at) : null;
|
|
162
|
+
const day = event.day || (at && Number.isFinite(at.getTime()) ? localDay(at) : null);
|
|
160
163
|
if (day && !flag.escalated_days.includes(day)) flag.escalated_days.push(day);
|
|
161
164
|
} else if (event.kind === 'probed') {
|
|
162
165
|
flag.probe_runs.push({
|
|
@@ -753,7 +756,7 @@ function commandSnooze(args, context = {}) {
|
|
|
753
756
|
// Escalation core shared by the CLI verb and the hourly pulse tick: append
|
|
754
757
|
// one escalated event per overdue flag per day, return the resulting state.
|
|
755
758
|
function sweepState(cwd = process.cwd(), now = new Date(), options = {}) {
|
|
756
|
-
const today =
|
|
759
|
+
const today = localDay(now);
|
|
757
760
|
const autoClosed = autoCloseResolvedTaskReviewFlags(cwd, now);
|
|
758
761
|
const autoParked = migrateOpenSlots(cwd, now, options);
|
|
759
762
|
let flags = openFlags(cwd, { now });
|
package/commands/computer.js
CHANGED
|
@@ -35,7 +35,7 @@ const {
|
|
|
35
35
|
runEngineDeviceLoginCommand,
|
|
36
36
|
validEngineLoginSeat,
|
|
37
37
|
} = require('./engine');
|
|
38
|
-
const { consoleCommand
|
|
38
|
+
const { consoleCommand } = require('./console');
|
|
39
39
|
const { streamSession } = require('./serve');
|
|
40
40
|
const { buildRemoteAtrisBootstrapCommand } = require('../lib/runtime-bootstrap');
|
|
41
41
|
const { envForcesNonInteractive } = require('../lib/noninteractive');
|
|
@@ -4380,6 +4380,7 @@ async function runComputer(argv = process.argv.slice(3), deps = {}) {
|
|
|
4380
4380
|
if (!codeopsCtx) {
|
|
4381
4381
|
console.error('Atris CodeOps is not available for this account.');
|
|
4382
4382
|
console.error('Ask an Atris CodeOps admin to add you to the atris-codeops business.');
|
|
4383
|
+
process.exitCode = 1;
|
|
4383
4384
|
return;
|
|
4384
4385
|
}
|
|
4385
4386
|
const codeopsOptions = codeOpsCloudOptions(cloudOptions);
|
|
@@ -4418,6 +4419,7 @@ async function runComputer(argv = process.argv.slice(3), deps = {}) {
|
|
|
4418
4419
|
default:
|
|
4419
4420
|
console.error(`Unknown CodeOps subcommand: ${codeopsSub}`);
|
|
4420
4421
|
console.log('Run: atris computer codeops help');
|
|
4422
|
+
process.exitCode = 1;
|
|
4421
4423
|
return;
|
|
4422
4424
|
}
|
|
4423
4425
|
return;
|
package/commands/console.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
|
-
const {
|
|
4
|
+
const { spawnSync } = require('child_process');
|
|
5
5
|
const readline = require('readline');
|
|
6
6
|
const { resolveClaudeRunnerBin } = require('../lib/runner-command');
|
|
7
7
|
|
package/commands/context-sync.js
CHANGED
|
@@ -222,65 +222,6 @@ async function businessDiff(slug) {
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
|
|
225
|
-
/**
|
|
226
|
-
* atris log <business-slug>
|
|
227
|
-
* Shows human-readable commit history.
|
|
228
|
-
*/
|
|
229
|
-
async function businessLog(slug) {
|
|
230
|
-
const biz = await resolveBusiness(slug);
|
|
231
|
-
|
|
232
|
-
if (!biz.workspaceId) {
|
|
233
|
-
console.error(`Business "${slug}" has no workspace.`);
|
|
234
|
-
process.exit(1);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const limit = 20;
|
|
238
|
-
const pathFilter = process.argv.includes('--path') ? process.argv[process.argv.indexOf('--path') + 1] : null;
|
|
239
|
-
|
|
240
|
-
const params = `limit=${limit}` + (pathFilter ? `&path=${encodeURIComponent(pathFilter)}` : '');
|
|
241
|
-
const result = await apiRequestJson(
|
|
242
|
-
`/business/${biz.businessId}/workspaces/${biz.workspaceId}/git/log?${params}`,
|
|
243
|
-
{ method: 'GET', token: biz.token }
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
if (!result.ok) {
|
|
247
|
-
if (result.status === 409) {
|
|
248
|
-
console.log('\n Computer is sleeping. Wake it first.\n');
|
|
249
|
-
} else {
|
|
250
|
-
console.error(`\n Failed to get history: ${result.errorMessage || result.status}\n`);
|
|
251
|
-
}
|
|
252
|
-
process.exit(1);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const commits = result.data.commits || [];
|
|
256
|
-
if (commits.length === 0) {
|
|
257
|
-
console.log(`\n ${biz.name}: no history yet.\n`);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
console.log(`\n ${biz.name}: history\n`);
|
|
262
|
-
|
|
263
|
-
for (const commit of commits) {
|
|
264
|
-
const date = _timeSince(commit.date) || commit.date;
|
|
265
|
-
const msg = commit.message || '';
|
|
266
|
-
|
|
267
|
-
// Parse actor from message format "actor/name: message"
|
|
268
|
-
const colonIdx = msg.indexOf(': ');
|
|
269
|
-
let actor = '';
|
|
270
|
-
let description = msg;
|
|
271
|
-
if (colonIdx > 0 && colonIdx < 40) {
|
|
272
|
-
actor = msg.substring(0, colonIdx);
|
|
273
|
-
description = msg.substring(colonIdx + 2);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const actorDisplay = actor ? ` ${actor}` : '';
|
|
277
|
-
console.log(` ${date.padEnd(12)} ${actorDisplay}`);
|
|
278
|
-
console.log(` ${description}`);
|
|
279
|
-
console.log('');
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
225
|
/**
|
|
285
226
|
* Convert ISO timestamp to human-readable relative time (e.g., "5m ago").
|
|
286
227
|
* @param {string} isoString - ISO 8601 timestamp
|
|
@@ -300,4 +241,4 @@ function _timeSince(isoString) {
|
|
|
300
241
|
}
|
|
301
242
|
|
|
302
243
|
|
|
303
|
-
module.exports = { businessStatus, businessDiff
|
|
244
|
+
module.exports = { businessStatus, businessDiff };
|
package/commands/experiments.js
CHANGED
|
@@ -600,7 +600,10 @@ function buildBenchmarkArtifact(name, packDir, options) {
|
|
|
600
600
|
function recordExperimentRun(experimentId) {
|
|
601
601
|
const root = process.cwd();
|
|
602
602
|
const state = daily.readDailyState(root);
|
|
603
|
-
|
|
603
|
+
// Local calendar day; toISOString is UTC and records an evening run as
|
|
604
|
+
// tomorrow's anywhere west of Greenwich.
|
|
605
|
+
const now = new Date();
|
|
606
|
+
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
|
604
607
|
state.last_run_date = today;
|
|
605
608
|
if (!Array.isArray(state.history)) state.history = [];
|
|
606
609
|
if (!state.history.includes(experimentId)) {
|
package/commands/fleet.js
CHANGED
|
@@ -18,7 +18,6 @@ const https = require('https');
|
|
|
18
18
|
const fs = require('fs');
|
|
19
19
|
const path = require('path');
|
|
20
20
|
const os = require('os');
|
|
21
|
-
const crypto = require('crypto');
|
|
22
21
|
const { parseScopeFlag } = require('../lib/cli-scope');
|
|
23
22
|
|
|
24
23
|
// ── Config ──────────────────────────────────────────────
|
package/commands/improve.js
CHANGED
|
@@ -25,6 +25,7 @@ const path = require('path');
|
|
|
25
25
|
const { spawnSync } = require('child_process');
|
|
26
26
|
const gitSpawn = require('../lib/git-spawn');
|
|
27
27
|
const ignoredCache = require('../lib/ignored-cache');
|
|
28
|
+
const { localDay } = require('../lib/local-day');
|
|
28
29
|
const { apiRequestJson, getApiBaseUrl } = require('../utils/api');
|
|
29
30
|
const { loadCredentials } = require('../utils/auth');
|
|
30
31
|
const pulse = require('../lib/pulse');
|
|
@@ -375,7 +376,7 @@ function agePhrase(ts, nowMs = Date.now()) {
|
|
|
375
376
|
if (delta < 60 * 60 * 1000) return `${plural(Math.round(delta / (60 * 1000)), 'minute')} ago`;
|
|
376
377
|
if (delta < DAY_MS) return `${plural(Math.round(delta / (60 * 60 * 1000)), 'hour')} ago`;
|
|
377
378
|
if (delta < 30 * DAY_MS) return `${plural(Math.round(delta / DAY_MS), 'day')} ago`;
|
|
378
|
-
return `on ${new Date(ms)
|
|
379
|
+
return `on ${localDay(new Date(ms))}`;
|
|
379
380
|
}
|
|
380
381
|
|
|
381
382
|
function plainSentence(value) {
|
|
@@ -1715,6 +1716,7 @@ async function run(argv = [], deps = {}) {
|
|
|
1715
1716
|
|
|
1716
1717
|
module.exports = {
|
|
1717
1718
|
run,
|
|
1719
|
+
agePhrase,
|
|
1718
1720
|
runImprove,
|
|
1719
1721
|
runImproveCore,
|
|
1720
1722
|
parseImproveArgs,
|
package/commands/interview.js
CHANGED
|
@@ -27,9 +27,17 @@ function findWorkspaceRoot(start) {
|
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// The local calendar day (YYYY-MM-DD), offsetDays back. toISOString() is the
|
|
31
|
+
// UTC day, which is already tomorrow in the evening west of Greenwich.
|
|
30
32
|
function today(offsetDays = 0) {
|
|
31
|
-
const d = new Date(
|
|
32
|
-
|
|
33
|
+
const d = new Date();
|
|
34
|
+
d.setDate(d.getDate() - offsetDays);
|
|
35
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function journalPath(ws, offsetDays = 0) {
|
|
39
|
+
const day = today(offsetDays);
|
|
40
|
+
return path.join(ws, 'atris', 'logs', day.slice(0, 4), `${day}.md`);
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
function existing(...candidates) {
|
|
@@ -178,8 +186,8 @@ async function interviewCommand(args = [], root = process.cwd(), { mode: forcedM
|
|
|
178
186
|
if (subjectRaw === 'me' || subjectRaw === '') {
|
|
179
187
|
mode = 'operator';
|
|
180
188
|
contextPaths = existing(
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
journalPath(ws),
|
|
190
|
+
journalPath(ws, 1),
|
|
183
191
|
path.join(ws, 'atris', 'now.md'),
|
|
184
192
|
path.join(ws, 'atris', 'TODO.md')
|
|
185
193
|
);
|
|
@@ -200,7 +208,7 @@ async function interviewCommand(args = [], root = process.cwd(), { mode: forcedM
|
|
|
200
208
|
path.join(memberDir, 'now.md'),
|
|
201
209
|
path.join(memberDir, 'logs'),
|
|
202
210
|
path.join(memberDir, 'proofs'),
|
|
203
|
-
|
|
211
|
+
journalPath(ws)
|
|
204
212
|
);
|
|
205
213
|
} else {
|
|
206
214
|
mode = 'expert';
|
package/commands/land.js
CHANGED
|
@@ -473,7 +473,10 @@ function landSummary(cwd = process.cwd(), ttlDays = DEFAULT_TTL_DAYS) {
|
|
|
473
473
|
}
|
|
474
474
|
|
|
475
475
|
function salvageDir(root) {
|
|
476
|
-
|
|
476
|
+
// Local calendar day; toISOString is UTC and files an evening backup
|
|
477
|
+
// under tomorrow anywhere west of Greenwich.
|
|
478
|
+
const now = new Date();
|
|
479
|
+
const stamp = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
|
477
480
|
const dir = path.join(root, '.atris', 'salvage', stamp);
|
|
478
481
|
fs.mkdirSync(dir, { recursive: true });
|
|
479
482
|
return dir;
|
|
@@ -1086,5 +1089,6 @@ module.exports = {
|
|
|
1086
1089
|
landCommand,
|
|
1087
1090
|
landSummary,
|
|
1088
1091
|
reap,
|
|
1092
|
+
salvageDir,
|
|
1089
1093
|
sharedBoard,
|
|
1090
1094
|
};
|
package/commands/lifecycle.js
CHANGED
|
@@ -11,6 +11,15 @@ const {
|
|
|
11
11
|
setMemberLoopAwake,
|
|
12
12
|
} = require('../lib/member-switches');
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* True when the user asked for help. Checked before anything else so
|
|
16
|
+
* `atris sleep --help` inside a bound folder never sleeps that business.
|
|
17
|
+
* @returns {boolean}
|
|
18
|
+
*/
|
|
19
|
+
function wantsHelp() {
|
|
20
|
+
return process.argv.slice(3).some((a) => a === '--help' || a === '-h' || a === 'help');
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
/**
|
|
15
24
|
* Resolve business slug from CLI arg or local .atris/business.json.
|
|
16
25
|
* @returns {string|null} The resolved slug, or null if not found.
|
|
@@ -121,11 +130,12 @@ async function waitForBusinessComputer(token, business, maxWait = 90000) {
|
|
|
121
130
|
* @returns {Promise<void>}
|
|
122
131
|
*/
|
|
123
132
|
async function sleepAtris() {
|
|
124
|
-
|
|
133
|
+
const help = wantsHelp();
|
|
134
|
+
if (!help && handleMemberSwitch(false)) return;
|
|
125
135
|
|
|
126
|
-
const slug = resolveSlug();
|
|
136
|
+
const slug = help ? null : resolveSlug();
|
|
127
137
|
|
|
128
|
-
if (!slug
|
|
138
|
+
if (!slug) {
|
|
129
139
|
console.log('Usage: atris sleep [business|member] [--loop <loop-id>]');
|
|
130
140
|
console.log('');
|
|
131
141
|
console.log(' Pause a workspace to save compute. Storage only.');
|
|
@@ -172,11 +182,12 @@ async function sleepAtris() {
|
|
|
172
182
|
* @returns {Promise<void>}
|
|
173
183
|
*/
|
|
174
184
|
async function wakeAtris() {
|
|
175
|
-
|
|
185
|
+
const help = wantsHelp();
|
|
186
|
+
if (!help && handleMemberSwitch(true)) return;
|
|
176
187
|
|
|
177
|
-
const slug = resolveSlug();
|
|
188
|
+
const slug = help ? null : resolveSlug();
|
|
178
189
|
|
|
179
|
-
if (!slug
|
|
190
|
+
if (!slug) {
|
|
180
191
|
console.log('Usage: atris wake [business|member] [--loop <loop-id>]');
|
|
181
192
|
console.log('');
|
|
182
193
|
console.log(' Wake a sleeping workspace. Wake signal sent; loops pick it up on their next pass.');
|
package/commands/log-sync.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const { getLogPath,
|
|
3
|
+
const { getLogPath, createLogFile } = require('../lib/file-ops');
|
|
4
4
|
const { parseJournalSections, mergeSections, reconstructJournal, showLogDiff } = require('../lib/journal');
|
|
5
5
|
const { computeContentHash, isSameTimestamp } = require('../lib/journal');
|
|
6
6
|
const { loadConfig, loadLogSyncState, saveLogSyncState } = require('../utils/config');
|
package/commands/loops.js
CHANGED
|
@@ -160,7 +160,7 @@ function setRegistryJob(id, enabled) {
|
|
|
160
160
|
delete job.disabled_reason;
|
|
161
161
|
} else {
|
|
162
162
|
job.disabled = true;
|
|
163
|
-
job.disabled_reason = `stopped via atris loops ${
|
|
163
|
+
job.disabled_reason = `stopped via atris loops ${todayIso()}`;
|
|
164
164
|
}
|
|
165
165
|
saveRegistry(registry);
|
|
166
166
|
return true;
|
|
@@ -204,8 +204,11 @@ function controlLoop(id, action) {
|
|
|
204
204
|
return 0;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
// Local calendar day (YYYY-MM-DD). toISOString() is the UTC day, which is
|
|
208
|
+
// already tomorrow in the evening anywhere west of Greenwich.
|
|
207
209
|
function todayIso() {
|
|
208
|
-
|
|
210
|
+
const d = new Date();
|
|
211
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
|
209
212
|
}
|
|
210
213
|
|
|
211
214
|
function renderTemplate(text) {
|
package/commands/member.js
CHANGED
|
@@ -4513,13 +4513,15 @@ function memberList(...flags) {
|
|
|
4513
4513
|
|
|
4514
4514
|
console.log('');
|
|
4515
4515
|
console.log('Team Members');
|
|
4516
|
-
console.log('─'.repeat(
|
|
4516
|
+
console.log('─'.repeat(72));
|
|
4517
4517
|
|
|
4518
4518
|
const nameW = 16;
|
|
4519
|
-
const roleW =
|
|
4520
|
-
const fmtW =
|
|
4519
|
+
const roleW = 18;
|
|
4520
|
+
const fmtW = 8;
|
|
4521
4521
|
const skillW = 8;
|
|
4522
|
-
const ctxW =
|
|
4522
|
+
const ctxW = 9;
|
|
4523
|
+
// Keep one space between columns, even when a value fills its column.
|
|
4524
|
+
const cell = (value, width) => `${clipCell(value, width - 1)} `;
|
|
4523
4525
|
|
|
4524
4526
|
console.log(
|
|
4525
4527
|
' ' +
|
|
@@ -4530,18 +4532,18 @@ function memberList(...flags) {
|
|
|
4530
4532
|
'Context'.padEnd(ctxW) +
|
|
4531
4533
|
'Version'
|
|
4532
4534
|
);
|
|
4533
|
-
console.log(' ' + '─'.repeat(
|
|
4535
|
+
console.log(' ' + '─'.repeat(70));
|
|
4534
4536
|
|
|
4535
4537
|
for (const m of members) {
|
|
4536
4538
|
const skills = m.format === 'directory' ? String(m.skillCount) : '-';
|
|
4537
4539
|
const context = m.format === 'directory' ? String(m.contextCount) : '-';
|
|
4538
4540
|
console.log(
|
|
4539
4541
|
' ' +
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4542
|
+
cell(m.name, nameW) +
|
|
4543
|
+
cell(m.role, roleW) +
|
|
4544
|
+
cell(m.format === 'directory' ? 'dir' : 'flat', fmtW) +
|
|
4545
|
+
cell(skills, skillW) +
|
|
4546
|
+
cell(context, ctxW) +
|
|
4545
4547
|
(m.version || '-') +
|
|
4546
4548
|
(parked.has(m.name) ? ' parked' : '')
|
|
4547
4549
|
);
|
|
@@ -9887,7 +9889,11 @@ async function memberImprovements(businessId, ...args) {
|
|
|
9887
9889
|
console.error('Not logged in. Run: atris login');
|
|
9888
9890
|
process.exit(1);
|
|
9889
9891
|
}
|
|
9890
|
-
|
|
9892
|
+
if (flags.days !== null && !(Number.isInteger(flags.days) && flags.days >= 1)) {
|
|
9893
|
+
console.error('--days needs a whole number of days, 1 or more (example: --days 7).');
|
|
9894
|
+
process.exit(1);
|
|
9895
|
+
}
|
|
9896
|
+
const days = flags.days || 30;
|
|
9891
9897
|
const result = await apiRequestJson(`/business/${encodeURIComponent(businessId)}/improvements/daily?days=${days}`, { token: creds.token });
|
|
9892
9898
|
if (!result.ok) {
|
|
9893
9899
|
console.error(`Failed to fetch improvements: ${result.error || 'Unknown error'}`);
|
package/commands/mission.js
CHANGED
|
@@ -38,6 +38,7 @@ const {
|
|
|
38
38
|
resolveEngineForRoleWithPreference,
|
|
39
39
|
engineFailureHealthStatus,
|
|
40
40
|
engineStallReason,
|
|
41
|
+
engineRegistryView,
|
|
41
42
|
recordEngineRunHealth,
|
|
42
43
|
setEngineHealth,
|
|
43
44
|
} = require('../lib/engine-registry');
|
|
@@ -3862,10 +3863,6 @@ function chooseNextMissionAnalysis(mission, root = process.cwd()) {
|
|
|
3862
3863
|
return { target: null, near_misses: [] };
|
|
3863
3864
|
}
|
|
3864
3865
|
|
|
3865
|
-
function chooseNextMissionTarget(mission, root = process.cwd()) {
|
|
3866
|
-
return chooseNextMissionAnalysis(mission, root).target;
|
|
3867
|
-
}
|
|
3868
|
-
|
|
3869
3866
|
function normalizeMissionOwner(value) {
|
|
3870
3867
|
return String(value || '').trim().toLowerCase();
|
|
3871
3868
|
}
|
package/commands/now.js
CHANGED
|
@@ -273,13 +273,6 @@ function taskReceiptTitle(row) {
|
|
|
273
273
|
return compactLine(row?.state?.title || row?.title || row?.task_id || 'untitled work');
|
|
274
274
|
}
|
|
275
275
|
|
|
276
|
-
// The receipts behind "Completed receipts today", as lines a human can read:
|
|
277
|
-
// one per receipt, landed result + human proof state. Shares the collector
|
|
278
|
-
// with the count so the number and the visible list always match.
|
|
279
|
-
function todayTaskReceiptLines(root = process.cwd(), date = new Date(), limit = 6) {
|
|
280
|
-
return formatTaskReceiptLines(collectTaskReceiptsToday(root, date), limit);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
276
|
// Format an already-collected receipt list, so a caller that needs both the
|
|
284
277
|
// count and the lines (renderDefaultNow) parses the receipt files only once.
|
|
285
278
|
function formatTaskReceiptLines(receipts, limit = 6) {
|