atris 3.74.0 → 3.74.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +4 -8
- package/commands/avail.js +36 -7
- package/commands/computer.js +3 -1
- package/commands/console.js +1 -1
- package/commands/context-sync.js +1 -60
- package/commands/fleet.js +0 -1
- package/commands/lifecycle.js +17 -6
- package/commands/log-sync.js +1 -1
- package/commands/member.js +17 -11
- package/commands/mission.js +1 -4
- package/commands/now.js +0 -7
- package/commands/plugin.js +1 -1
- package/commands/pull.js +1 -1
- 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/setup.js +6 -2
- package/commands/task.js +0 -522
- package/commands/terminal.js +1 -1
- package/commands/youtube.js +4 -6
- package/commands/zen.js +4 -4
- package/lib/agent-today.js +412 -0
- 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/file-ops.js +6 -1
- package/lib/first-minute.js +2 -0
- package/lib/gm-bet.js +41 -5
- package/lib/gm-phone-page.js +14 -3
- package/lib/gm-phone.js +3 -0
- package/lib/manifest.js +0 -10
- package/lib/member-switches.js +0 -11
- package/lib/review-integrity.js +0 -4
- 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
|
@@ -1163,7 +1163,10 @@ function lessonSlug(text, maxLen = 40) {
|
|
|
1163
1163
|
*/
|
|
1164
1164
|
function writeLesson(cwd, slug, status, explanation) {
|
|
1165
1165
|
const lessonsPath = path.join(cwd, 'atris', 'lessons.md');
|
|
1166
|
-
|
|
1166
|
+
// Local calendar day (YYYY-MM-DD); toISOString is UTC and dates an
|
|
1167
|
+
// evening lesson tomorrow anywhere west of Greenwich.
|
|
1168
|
+
const now = new Date();
|
|
1169
|
+
const today = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
|
|
1167
1170
|
const lessonLine = `- **[${today}] ${slug}** - ${status} - ${explanation}`;
|
|
1168
1171
|
|
|
1169
1172
|
if (!fs.existsSync(lessonsPath)) {
|
|
@@ -4043,13 +4046,6 @@ Search the codebase to verify. Reply: YES <reason> or NO <reason>`;
|
|
|
4043
4046
|
}
|
|
4044
4047
|
}
|
|
4045
4048
|
|
|
4046
|
-
/**
|
|
4047
|
-
* Entry point when called without a description.
|
|
4048
|
-
*/
|
|
4049
|
-
async function autopilotFromTodo(options = {}) {
|
|
4050
|
-
return autopilotAtris(null, options);
|
|
4051
|
-
}
|
|
4052
|
-
|
|
4053
4049
|
module.exports = {
|
|
4054
4050
|
askModel,
|
|
4055
4051
|
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/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/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/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/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) {
|
package/commands/plugin.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const { execSync } = require('child_process');
|
|
5
|
-
const { findAllSkills
|
|
5
|
+
const { findAllSkills } = require('./skill');
|
|
6
6
|
|
|
7
7
|
// --- Recursive Copy Helper ---
|
|
8
8
|
|
package/commands/pull.js
CHANGED
|
@@ -7,7 +7,7 @@ const { loadConfig } = require('../utils/config');
|
|
|
7
7
|
const { getLogPath } = require('../lib/file-ops');
|
|
8
8
|
const { parseJournalSections, mergeSections, reconstructJournal } = require('../lib/journal');
|
|
9
9
|
const { loadBusinesses, businessMatchesSlug } = require('./business');
|
|
10
|
-
const { loadManifest, saveManifest,
|
|
10
|
+
const { loadManifest, saveManifest, buildManifest, computeLocalHashes, threeWayCompare, isIgnoredSyncPath } = require('../lib/manifest');
|
|
11
11
|
const { normalizeWikiOnlyPrefix } = require('../lib/wiki');
|
|
12
12
|
const { emitSyncEvent, startTimer } = require('../lib/sync-telemetry');
|
|
13
13
|
const { resolveSafeOutputDir } = require('../lib/workspace-safety');
|
package/commands/push.js
CHANGED
package/commands/radar.js
CHANGED
|
@@ -685,10 +685,6 @@ function taskBinding(source, scope = null) {
|
|
|
685
685
|
return { task_source: source, task_scope: scope };
|
|
686
686
|
}
|
|
687
687
|
|
|
688
|
-
function taskBindingForProjection(task) {
|
|
689
|
-
return task ? taskBinding('repo_task_projection', 'repo') : taskBinding(null, null);
|
|
690
|
-
}
|
|
691
|
-
|
|
692
688
|
function taskSourceLabel(sources = []) {
|
|
693
689
|
if (sources.includes('member_loop_lock')) return 'member loop lock';
|
|
694
690
|
if (sources.includes('mission_lock')) return 'mission lock';
|
package/commands/recap.js
CHANGED
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
taskCommand,
|
|
14
14
|
} = require('../lib/first-minute');
|
|
15
15
|
const { isRealTestRunnerProof, quoteVerifierCommand } = require('../lib/verifier-quality');
|
|
16
|
+
const { buildAgentToday, renderAgentToday, hasActivity, ranAgent } = require('../lib/agent-today');
|
|
16
17
|
|
|
17
18
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
18
19
|
const DEFAULT_DAYS = 7;
|
|
@@ -38,7 +39,8 @@ function readProjection(root) {
|
|
|
38
39
|
|
|
39
40
|
function loadTasks(root) {
|
|
40
41
|
const taskDb = loadTaskDb();
|
|
41
|
-
|
|
42
|
+
// Reading never creates the db: a first run before any setup writes nothing.
|
|
43
|
+
if (taskDb && fs.existsSync(taskDb.getDbPath())) {
|
|
42
44
|
try {
|
|
43
45
|
const db = taskDb.open();
|
|
44
46
|
const ws = taskDb.workspaceRoot(root);
|
|
@@ -335,11 +337,14 @@ function printRecapHelp() {
|
|
|
335
337
|
atris recap - what got done, in spoken lines
|
|
336
338
|
|
|
337
339
|
atris recap a few spoken lines: waiting for your ok, still being checked
|
|
340
|
+
atris recap today what your ai coding agent did in this folder today
|
|
338
341
|
atris recap --verbose the old report
|
|
339
342
|
atris recap --days 30 widen the window
|
|
340
343
|
atris recap --share paste-ready summary for Slack, email, or a customer
|
|
341
344
|
atris recap --json structured output for agents and dashboards
|
|
342
345
|
|
|
346
|
+
in a folder with no atris setup, recap shows today by default.
|
|
347
|
+
today reads claude code and codex logs on this computer plus git. it sends nothing.
|
|
343
348
|
certified work names atris task accept. uncertified stays still being checked.
|
|
344
349
|
use --verbose or --share for the old report.
|
|
345
350
|
`);
|
|
@@ -351,6 +356,11 @@ function recapAtris(args = []) {
|
|
|
351
356
|
return;
|
|
352
357
|
}
|
|
353
358
|
const cwd = process.cwd();
|
|
359
|
+
if (args[0] === 'today') {
|
|
360
|
+
const today = buildAgentToday({ cwd });
|
|
361
|
+
console.log(args.includes('--json') ? JSON.stringify(today, null, 2) : renderAgentToday(today));
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
354
364
|
const taskDb = loadTaskDb();
|
|
355
365
|
let root = cwd;
|
|
356
366
|
try {
|
|
@@ -370,6 +380,20 @@ function recapAtris(args = []) {
|
|
|
370
380
|
const daysIdx = args.indexOf('--days');
|
|
371
381
|
const days = daysIdx !== -1 ? Number(args[daysIdx + 1]) : DEFAULT_DAYS;
|
|
372
382
|
const data = buildRecapData(root, { days });
|
|
383
|
+
// Before any setup, or when the board has nothing to say, the useful
|
|
384
|
+
// first minute is what your ai agent already did in this folder today.
|
|
385
|
+
const boardQuiet = data.empty
|
|
386
|
+
|| ['shipped', 'waiting', 'checking', 'inProgress'].every((k) => !(data[k] || []).length);
|
|
387
|
+
if (boardQuiet && !args.includes('--verbose') && !args.includes('--full') && !args.includes('--share')) {
|
|
388
|
+
const today = buildAgentToday({ cwd });
|
|
389
|
+
// A brand new folder already names its last commit in the first
|
|
390
|
+
// minute, so commits alone keep that; an ai session here wins.
|
|
391
|
+
const fresh = data.empty && isFreshWorkspace(root) && !inProjectSubdir;
|
|
392
|
+
if (hasActivity(today) && (ranAgent(today) || !fresh)) {
|
|
393
|
+
console.log(args.includes('--json') ? JSON.stringify(today, null, 2) : renderAgentToday(today));
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
373
397
|
if (
|
|
374
398
|
data.empty
|
|
375
399
|
&& isFreshWorkspace(root)
|
package/commands/report.js
CHANGED
|
@@ -110,10 +110,6 @@ function buildDoneTasks(root, cutoff) {
|
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
function buildLandings(root, cutoff) {
|
|
114
|
-
return buildDoneTasks(root, cutoff).filter(task => task.landed);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
113
|
function landingStatusText(task) {
|
|
118
114
|
const status = String(task?.landing_status || 'unchecked').replace(/_/g, ' ');
|
|
119
115
|
const reason = String(task?.landing_reason || '').replace(/_/g, ' ');
|
package/commands/review.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
const fs = require('fs');
|
|
11
11
|
const path = require('path');
|
|
12
|
-
const {
|
|
12
|
+
const { spawnSync } = require('child_process');
|
|
13
13
|
|
|
14
14
|
function findReviewEngine() {
|
|
15
15
|
// Look for review_engine.py in common locations
|
package/commands/setup.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
1
|
const { loadCredentials, promptUser } = require('../utils/auth');
|
|
4
2
|
const { apiRequestJson } = require('../utils/api');
|
|
5
3
|
|
|
@@ -73,6 +71,12 @@ async function setupAtris() {
|
|
|
73
71
|
token: creds.token,
|
|
74
72
|
});
|
|
75
73
|
|
|
74
|
+
if (!result.ok && result.status === 401) {
|
|
75
|
+
console.error('\n Your saved login has expired, so Atris could not list your businesses.');
|
|
76
|
+
console.error(' Run "atris login", then "atris setup" again.');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
76
80
|
if (!result.ok) {
|
|
77
81
|
console.error(`\n Could not fetch businesses: ${result.error || 'Unknown error'}`);
|
|
78
82
|
console.error(' You can add one later with: atris business add <slug>');
|