atris 3.15.23 → 3.15.31
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/README.md +53 -0
- package/ax +625 -0
- package/commands/computer.js +176 -1
- package/commands/gm.js +27 -9
- package/commands/play.js +43 -9
- package/commands/sync.js +9 -4
- package/commands/xp.js +237 -1
- package/package.json +5 -2
package/commands/computer.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* atris computer — Open SMART mode (cloud in business workspace, local elsewhere)
|
|
5
5
|
* atris computer --cloud — Open CLOUD workspace mode
|
|
6
|
+
* atris computer create <name> — Create and wake a business computer
|
|
6
7
|
* atris computer wake — Start the computer
|
|
7
8
|
* atris computer sleep — Stop (files persist)
|
|
8
9
|
* atris computer card — Show the local computer card
|
|
@@ -19,7 +20,7 @@ const path = require('path');
|
|
|
19
20
|
const readline = require('readline');
|
|
20
21
|
const { spawnSync } = require('child_process');
|
|
21
22
|
const { loadCredentials, decodeJwtClaims } = require('../utils/auth');
|
|
22
|
-
const { apiRequestJson, getApiBaseUrl } = require('../utils/api');
|
|
23
|
+
const { apiRequestJson, getApiBaseUrl, getAppBaseUrl } = require('../utils/api');
|
|
23
24
|
const { loadBusinesses, saveBusinesses } = require('./business');
|
|
24
25
|
const { consoleCommand, gatherAtrisContext, buildSystemPrompt } = require('./console');
|
|
25
26
|
const { streamSession } = require('./serve');
|
|
@@ -544,6 +545,36 @@ function parseComputerOptions(argv) {
|
|
|
544
545
|
};
|
|
545
546
|
}
|
|
546
547
|
|
|
548
|
+
function parseComputerCreateArgs(argv = []) {
|
|
549
|
+
const nameParts = [];
|
|
550
|
+
let businessSlug = null;
|
|
551
|
+
let help = false;
|
|
552
|
+
|
|
553
|
+
for (let i = 0; i < argv.length; i++) {
|
|
554
|
+
const arg = argv[i];
|
|
555
|
+
if (arg === '--help' || arg === '-h' || arg === 'help') {
|
|
556
|
+
help = true;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if ((arg === '--business' || arg === '-b') && argv[i + 1]) {
|
|
560
|
+
businessSlug = argv[i + 1];
|
|
561
|
+
i++;
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
if (arg.startsWith('--business=')) {
|
|
565
|
+
businessSlug = arg.split('=', 2)[1] || null;
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
nameParts.push(arg);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
name: nameParts.join(' ').trim(),
|
|
573
|
+
businessSlug: businessSlug ? String(businessSlug).trim() : null,
|
|
574
|
+
help,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
|
|
547
578
|
function formatCloudSelection(options = {}) {
|
|
548
579
|
const worker = activeWorker(options.worker);
|
|
549
580
|
const parts = [`worker=${worker}`];
|
|
@@ -953,6 +984,55 @@ async function resolveBusinessContextBySlug(token, slug) {
|
|
|
953
984
|
return null;
|
|
954
985
|
}
|
|
955
986
|
|
|
987
|
+
async function resolveBusinessOwnerForCreate(token, businessSlug = null) {
|
|
988
|
+
const wantedSlug = businessSlug ? String(businessSlug).trim() : null;
|
|
989
|
+
if (wantedSlug) {
|
|
990
|
+
const fromApi = await resolveBusinessContextBySlug(token, wantedSlug);
|
|
991
|
+
if (fromApi) return fromApi;
|
|
992
|
+
|
|
993
|
+
const cached = loadBusinesses()[wantedSlug];
|
|
994
|
+
if (cached?.business_id) {
|
|
995
|
+
return {
|
|
996
|
+
slug: cached.slug || wantedSlug,
|
|
997
|
+
businessId: cached.business_id,
|
|
998
|
+
workspaceId: cached.workspace_id || null,
|
|
999
|
+
businessName: cached.name || cached.slug || wantedSlug,
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
return null;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
const binding = readBusinessBinding();
|
|
1006
|
+
if (binding?.business_id) {
|
|
1007
|
+
return {
|
|
1008
|
+
slug: binding.slug || binding.canonical_slug || null,
|
|
1009
|
+
businessId: binding.business_id,
|
|
1010
|
+
workspaceId: binding.workspace_id || null,
|
|
1011
|
+
businessName: binding.name || binding.slug || 'business',
|
|
1012
|
+
};
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
return resolveBusinessContext(token);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
function rememberCreatedComputer(ctx, workspace, endpoint = null) {
|
|
1019
|
+
const slug = ctx.slug || (ctx.businessName || '').toLowerCase().replace(/\s+/g, '-');
|
|
1020
|
+
if (!slug) return;
|
|
1021
|
+
const businesses = loadBusinesses();
|
|
1022
|
+
businesses[slug] = {
|
|
1023
|
+
...(businesses[slug] || {}),
|
|
1024
|
+
business_id: ctx.businessId,
|
|
1025
|
+
workspace_id: workspace.id,
|
|
1026
|
+
name: ctx.businessName,
|
|
1027
|
+
slug,
|
|
1028
|
+
computer_name: workspace.name,
|
|
1029
|
+
endpoint: endpoint || undefined,
|
|
1030
|
+
added_at: businesses[slug]?.added_at || new Date().toISOString(),
|
|
1031
|
+
updated_at: new Date().toISOString(),
|
|
1032
|
+
};
|
|
1033
|
+
saveBusinesses(businesses);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
956
1036
|
function shellQuote(value) {
|
|
957
1037
|
return `'${String(value).replace(/'/g, `'\\''`)}'`;
|
|
958
1038
|
}
|
|
@@ -1224,6 +1304,90 @@ async function computerWake(token, ctx = null) {
|
|
|
1224
1304
|
console.log(` Endpoint: ${result.data.endpoint}`);
|
|
1225
1305
|
}
|
|
1226
1306
|
|
|
1307
|
+
async function computerCreate(token, args = []) {
|
|
1308
|
+
const options = parseComputerCreateArgs(args);
|
|
1309
|
+
if (options.help || !options.name) {
|
|
1310
|
+
console.log('Usage: atris computer create <name> --business <slug>');
|
|
1311
|
+
console.log('');
|
|
1312
|
+
console.log('Create a business computer, activate it, and wake it in one command.');
|
|
1313
|
+
console.log('');
|
|
1314
|
+
console.log('Examples:');
|
|
1315
|
+
console.log(' atris computer create "My Business Computer" --business atris-labs');
|
|
1316
|
+
console.log(' atris computer create "Recruiting Computer"');
|
|
1317
|
+
if (!options.name && !options.help) process.exitCode = 1;
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
const ctx = await resolveBusinessOwnerForCreate(token, options.businessSlug);
|
|
1322
|
+
if (!ctx?.businessId) {
|
|
1323
|
+
console.error('No business found.');
|
|
1324
|
+
console.error('Run inside a bound business workspace or pass: --business <slug>');
|
|
1325
|
+
process.exitCode = 1;
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
console.log(`Creating computer "${options.name}" for ${ctx.businessName}...`);
|
|
1330
|
+
const created = await apiRequestJson(`/business/${ctx.businessId}/workspaces`, {
|
|
1331
|
+
method: 'POST',
|
|
1332
|
+
token,
|
|
1333
|
+
body: { name: options.name, type: 'general' },
|
|
1334
|
+
});
|
|
1335
|
+
if (!created.ok) {
|
|
1336
|
+
console.error(`Failed to create workspace: ${created.errorMessage || created.error || created.status}`);
|
|
1337
|
+
process.exitCode = 1;
|
|
1338
|
+
return;
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
const workspace = created.data || {};
|
|
1342
|
+
const workspaceId = workspace.id || workspace.workspace_id;
|
|
1343
|
+
if (!workspaceId) {
|
|
1344
|
+
console.error('Failed to create workspace: response did not include workspace id');
|
|
1345
|
+
process.exitCode = 1;
|
|
1346
|
+
return;
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
const activate = await apiRequestJson(`/business/${ctx.businessId}/workspaces/${workspaceId}/activate`, {
|
|
1350
|
+
method: 'POST',
|
|
1351
|
+
token,
|
|
1352
|
+
body: {},
|
|
1353
|
+
});
|
|
1354
|
+
if (!activate.ok && activate.status !== 409) {
|
|
1355
|
+
console.error(`Failed to activate computer: ${activate.errorMessage || activate.error || activate.status}`);
|
|
1356
|
+
process.exitCode = 1;
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
const wake = await apiRequestJson(`/business/${ctx.businessId}/ai-computer/wake`, {
|
|
1361
|
+
method: 'POST',
|
|
1362
|
+
token,
|
|
1363
|
+
body: {},
|
|
1364
|
+
});
|
|
1365
|
+
if (!wake.ok && !activate.ok) {
|
|
1366
|
+
console.error(`Failed to wake computer: ${wake.errorMessage || wake.error || wake.status}`);
|
|
1367
|
+
process.exitCode = 1;
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
const endpoint = activate.data?.endpoint || wake.data?.endpoint || null;
|
|
1372
|
+
const status = endpoint
|
|
1373
|
+
? 'running'
|
|
1374
|
+
: (wake.data?.status || (activate.ok ? 'activated' : 'warming_up'));
|
|
1375
|
+
rememberCreatedComputer(ctx, { ...workspace, id: workspaceId, name: workspace.name || options.name }, endpoint);
|
|
1376
|
+
|
|
1377
|
+
const appBase = getAppBaseUrl();
|
|
1378
|
+
console.log('');
|
|
1379
|
+
console.log(`Computer created: ${workspaceId}`);
|
|
1380
|
+
console.log(` Name: ${workspace.name || options.name}`);
|
|
1381
|
+
console.log(` Business: ${ctx.businessName}`);
|
|
1382
|
+
console.log(` Status: ${status}`);
|
|
1383
|
+
if (endpoint) console.log(` Endpoint: ${endpoint}`);
|
|
1384
|
+
console.log(` Dashboard: ${appBase}/dashboard/gm/${ctx.businessId}`);
|
|
1385
|
+
console.log('');
|
|
1386
|
+
console.log('Next:');
|
|
1387
|
+
console.log(` atris pull ${ctx.slug || ctx.businessId}`);
|
|
1388
|
+
console.log(' atris computer');
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1227
1391
|
async function computerSleep(token, ctx = null) {
|
|
1228
1392
|
if (ctx) {
|
|
1229
1393
|
console.log(`Sleeping computer for ${ctx.businessName}...`);
|
|
@@ -2562,6 +2726,14 @@ async function runComputer() {
|
|
|
2562
2726
|
return;
|
|
2563
2727
|
}
|
|
2564
2728
|
|
|
2729
|
+
if (sub === 'create') {
|
|
2730
|
+
const createOptions = parseComputerCreateArgs(args.slice(1));
|
|
2731
|
+
if (createOptions.help || !createOptions.name) {
|
|
2732
|
+
await computerCreate(null, args.slice(1));
|
|
2733
|
+
return;
|
|
2734
|
+
}
|
|
2735
|
+
}
|
|
2736
|
+
|
|
2565
2737
|
if (sub === '--help') {
|
|
2566
2738
|
console.log('Usage: atris computer [mode|command]');
|
|
2567
2739
|
console.log('');
|
|
@@ -2593,6 +2765,7 @@ async function runComputer() {
|
|
|
2593
2765
|
console.log(' claude|codex Legacy local console backends');
|
|
2594
2766
|
console.log('');
|
|
2595
2767
|
console.log('Cloud commands:');
|
|
2768
|
+
console.log(' create <name> Create and wake a business computer');
|
|
2596
2769
|
console.log(' chat Interactive cloud workspace chat');
|
|
2597
2770
|
console.log(' Ctrl-C during a cloud run interrupts it');
|
|
2598
2771
|
console.log(' /start shows the beginner flow');
|
|
@@ -2614,6 +2787,7 @@ async function runComputer() {
|
|
|
2614
2787
|
console.log('Examples:');
|
|
2615
2788
|
console.log(' atris computer');
|
|
2616
2789
|
console.log(' atris computer card --write');
|
|
2790
|
+
console.log(' atris computer create "My Business Computer" --business atris-labs');
|
|
2617
2791
|
console.log(' atris business init "My Lab" # shared owner + first/default computer');
|
|
2618
2792
|
console.log(' atris computer proof');
|
|
2619
2793
|
console.log(' atris computer local');
|
|
@@ -2695,6 +2869,7 @@ async function runComputer() {
|
|
|
2695
2869
|
switch (sub) {
|
|
2696
2870
|
case 'chat': return computerChat(token, ctx, cloudOptions);
|
|
2697
2871
|
case 'card': return computerCard(args.slice(1));
|
|
2872
|
+
case 'create': return computerCreate(token, args.slice(1));
|
|
2698
2873
|
case 'proof': return computerProof(token, ctx, cloudOptions);
|
|
2699
2874
|
case 'status': return computerStatus(token, ctx);
|
|
2700
2875
|
case 'wake': return computerWake(token, ctx);
|
package/commands/gm.js
CHANGED
|
@@ -4,6 +4,9 @@ const fs = require('fs');
|
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const path = require('path');
|
|
6
6
|
|
|
7
|
+
const AGENTXP_LEADERBOARD_URL = 'https://api.atris.ai/api/agentxp/leaderboard';
|
|
8
|
+
const AGENTXP_GLOBAL_SYNC_RULE = 'Use the owner-provided sync token first; fallback is atris login before sync.';
|
|
9
|
+
|
|
7
10
|
const ROLE_PLAYERS_TO_IGNORE = new Set([
|
|
8
11
|
'game-manager',
|
|
9
12
|
'navigator',
|
|
@@ -26,7 +29,7 @@ function showHelp() {
|
|
|
26
29
|
console.log('');
|
|
27
30
|
console.log('Options:');
|
|
28
31
|
console.log(' --manager <id> Manager id. Defaults to game-manager when present.');
|
|
29
|
-
console.log(' --as <id> Alias for --
|
|
32
|
+
console.log(' --as <id> Alias for --player.');
|
|
30
33
|
console.log(' --player <id> Preferred player when seeding a first local mission.');
|
|
31
34
|
console.log(' --workspace <p> Read missions from another Atris workspace.');
|
|
32
35
|
console.log(' --no-seed Do not create a starter player mission.');
|
|
@@ -102,7 +105,7 @@ function teamMembers(workspaceRoot) {
|
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
function inferManager(workspaceRoot, args = []) {
|
|
105
|
-
const explicit = flag(args, '--manager') ||
|
|
108
|
+
const explicit = flag(args, '--manager') || positional(args)[0];
|
|
106
109
|
if (explicit) return { manager: slugify(explicit), source: 'flag' };
|
|
107
110
|
|
|
108
111
|
for (const value of [process.env.ATRIS_GM, process.env.ATRIS_MANAGER, process.env.ATRIS_AGENT_ID]) {
|
|
@@ -160,7 +163,7 @@ function starterMissionPrompt(player) {
|
|
|
160
163
|
}
|
|
161
164
|
|
|
162
165
|
function pickSeedPlayer(workspaceRoot, tasks, args = []) {
|
|
163
|
-
const explicit = flag(args, '--player') || flag(args, '--user');
|
|
166
|
+
const explicit = flag(args, '--player') || flag(args, '--user') || flag(args, '--as');
|
|
164
167
|
if (explicit) return slugify(explicit);
|
|
165
168
|
|
|
166
169
|
const fromTasks = playersFromTasks(tasks);
|
|
@@ -261,19 +264,30 @@ function compactTask(task) {
|
|
|
261
264
|
};
|
|
262
265
|
}
|
|
263
266
|
|
|
264
|
-
function
|
|
267
|
+
function globalSyncCommands(player) {
|
|
268
|
+
return [
|
|
269
|
+
`atris xp sync --local --as ${player} --token <owner-provided-token>`,
|
|
270
|
+
'atris login',
|
|
271
|
+
`atris xp sync --local --as ${player}`,
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function nextCommands({ seeded, reviewQueue, missions, players, manager }) {
|
|
265
276
|
if (reviewQueue.length) {
|
|
266
|
-
const
|
|
277
|
+
const task = reviewQueue[0];
|
|
278
|
+
const ref = task.ref;
|
|
279
|
+
const player = task.assigned_to || task.claimed_by || players[0]?.player || 'player';
|
|
267
280
|
return [
|
|
268
281
|
`atris task show ${ref}`,
|
|
269
|
-
`atris task accept ${ref} --proof "<human review>"`,
|
|
270
|
-
`atris task revise ${ref} --note "<what must change>"`,
|
|
282
|
+
`atris task accept ${ref} --as ${player} --proof "<human review>"`,
|
|
283
|
+
`atris task revise ${ref} --as ${player} --note "<what must change>"`,
|
|
284
|
+
...globalSyncCommands(player),
|
|
271
285
|
];
|
|
272
286
|
}
|
|
273
287
|
if (missions.length) {
|
|
274
288
|
const mission = missions[0];
|
|
275
289
|
const player = mission.assigned_to || mission.claimed_by || players[0]?.player || 'player';
|
|
276
|
-
if (mission.status === 'open') return [`atris task claim ${mission.ref} --as ${
|
|
290
|
+
if (mission.status === 'open') return [`atris task claim ${mission.ref} --as ${manager || 'game-manager'}`];
|
|
277
291
|
return [`atris play --as ${player}`];
|
|
278
292
|
}
|
|
279
293
|
if (seeded) return [`atris play --as ${seeded.assigned_to || 'player'}`];
|
|
@@ -297,7 +311,7 @@ function gmState(args = []) {
|
|
|
297
311
|
const reviewQueue = missions.filter(task => task.status === 'review');
|
|
298
312
|
const players = groupPlayers(tasks, workspaceRoot);
|
|
299
313
|
const seeded = compactTask(starter.seeded);
|
|
300
|
-
const commands = nextCommands({ seeded, reviewQueue, missions, players });
|
|
314
|
+
const commands = nextCommands({ seeded, reviewQueue, missions, players, manager: detected.manager });
|
|
301
315
|
|
|
302
316
|
return {
|
|
303
317
|
schema: 'atris.agentxp_gm_mode.v1',
|
|
@@ -318,6 +332,8 @@ function gmState(args = []) {
|
|
|
318
332
|
review_queue: reviewQueue,
|
|
319
333
|
next_commands: commands,
|
|
320
334
|
xp_rule: 'GM can route missions and review proof, but AgentXP still lands only after human accept.',
|
|
335
|
+
global_sync_rule: AGENTXP_GLOBAL_SYNC_RULE,
|
|
336
|
+
leaderboard_url: AGENTXP_LEADERBOARD_URL,
|
|
321
337
|
};
|
|
322
338
|
}
|
|
323
339
|
|
|
@@ -351,6 +367,8 @@ function render(state) {
|
|
|
351
367
|
|
|
352
368
|
console.log('');
|
|
353
369
|
console.log('XP rule: no proof, no AgentXP; accept/revise stays human-gated.');
|
|
370
|
+
console.log('Global sync: use owner token first; fallback to atris login before hosted leaderboard sync.');
|
|
371
|
+
console.log(`Leaderboard: ${state.leaderboard_url}`);
|
|
354
372
|
console.log('');
|
|
355
373
|
console.log('Next commands:');
|
|
356
374
|
for (const command of state.next_commands) console.log(`- ${command}`);
|
package/commands/play.js
CHANGED
|
@@ -6,6 +6,9 @@ const fs = require('fs');
|
|
|
6
6
|
const { spawnSync } = require('child_process');
|
|
7
7
|
const { getSessionProfile, loadCredentials } = require('../utils/auth');
|
|
8
8
|
|
|
9
|
+
const AGENTXP_LEADERBOARD_URL = 'https://api.atris.ai/api/agentxp/leaderboard';
|
|
10
|
+
const AGENTXP_GLOBAL_SYNC_RULE = 'Use the owner-provided sync token first; fallback is atris login before sync.';
|
|
11
|
+
|
|
9
12
|
function showHelp() {
|
|
10
13
|
console.log('');
|
|
11
14
|
console.log('Usage: atris play [--as <player>] [--workspace <path>] [--json]');
|
|
@@ -252,10 +255,18 @@ function starterMissionPrompt(player) {
|
|
|
252
255
|
].join(' ');
|
|
253
256
|
}
|
|
254
257
|
|
|
258
|
+
function globalSyncCommands(player) {
|
|
259
|
+
return [
|
|
260
|
+
`atris xp sync --local --as ${player} --token <owner-provided-token>`,
|
|
261
|
+
'atris login',
|
|
262
|
+
`atris xp sync --local --as ${player}`,
|
|
263
|
+
];
|
|
264
|
+
}
|
|
265
|
+
|
|
255
266
|
function ensureStarterMission(taskDb, db, workspaceRoot, player, tasks, args = []) {
|
|
256
267
|
if (hasFlag(args, '--no-seed')) return { tasks, seeded: null };
|
|
257
268
|
if (selectMission(tasks, player)) return { tasks, seeded: null };
|
|
258
|
-
|
|
269
|
+
fs.mkdirSync(path.join(workspaceRoot, 'atris'), { recursive: true });
|
|
259
270
|
|
|
260
271
|
const result = taskDb.addTask(db, {
|
|
261
272
|
title: starterMissionTitle(),
|
|
@@ -282,7 +293,21 @@ function ensureStarterMission(taskDb, db, workspaceRoot, player, tasks, args = [
|
|
|
282
293
|
return { tasks: refreshed, seeded };
|
|
283
294
|
}
|
|
284
295
|
|
|
296
|
+
function playWorkspaceRoot(taskDb, workspaceArg) {
|
|
297
|
+
let requested = path.resolve(workspaceArg || process.cwd());
|
|
298
|
+
try { requested = fs.realpathSync(requested); } catch {}
|
|
299
|
+
if (
|
|
300
|
+
fs.existsSync(path.join(requested, '.git'))
|
|
301
|
+
|| fs.existsSync(path.join(requested, 'atris'))
|
|
302
|
+
|| fs.existsSync(path.join(requested, '.atris'))
|
|
303
|
+
) {
|
|
304
|
+
return taskDb.workspaceRoot(requested);
|
|
305
|
+
}
|
|
306
|
+
return requested;
|
|
307
|
+
}
|
|
308
|
+
|
|
285
309
|
function nextCommands(task, player) {
|
|
310
|
+
const helper = 'game-manager';
|
|
286
311
|
if (!task) {
|
|
287
312
|
return [
|
|
288
313
|
`atris task delegate "AgentXP first rep: one proof-backed mission" --to ${player} --tag agent-xp`,
|
|
@@ -293,40 +318,45 @@ function nextCommands(task, player) {
|
|
|
293
318
|
const ref = taskRef(task);
|
|
294
319
|
if (task.status === 'open') {
|
|
295
320
|
return [
|
|
296
|
-
`atris task claim ${ref} --as ${
|
|
297
|
-
`atris task ready ${ref} --proof "<artifact path + verifier result>"`,
|
|
298
|
-
`atris task accept ${ref} --proof "<human review>"`,
|
|
321
|
+
`atris task claim ${ref} --as ${helper}`,
|
|
322
|
+
`atris task ready ${ref} --as ${helper} --proof "<artifact path + verifier result>"`,
|
|
323
|
+
`atris task accept ${ref} --as ${player} --proof "<human review>"`,
|
|
299
324
|
'atris xp card --local',
|
|
325
|
+
...globalSyncCommands(player),
|
|
300
326
|
];
|
|
301
327
|
}
|
|
302
328
|
|
|
303
329
|
if (task.status === 'claimed') {
|
|
330
|
+
const actor = task.claimed_by || helper;
|
|
304
331
|
return [
|
|
305
|
-
`atris task ready ${ref} --proof "<artifact path + verifier result>"`,
|
|
306
|
-
`atris task accept ${ref} --proof "<human review>"`,
|
|
332
|
+
`atris task ready ${ref} --as ${actor} --proof "<artifact path + verifier result>"`,
|
|
333
|
+
`atris task accept ${ref} --as ${player} --proof "<human review>"`,
|
|
307
334
|
'atris xp card --local',
|
|
335
|
+
...globalSyncCommands(player),
|
|
308
336
|
];
|
|
309
337
|
}
|
|
310
338
|
|
|
311
339
|
if (task.status === 'review') {
|
|
312
340
|
return [
|
|
313
341
|
`atris task show ${ref}`,
|
|
314
|
-
`atris task accept ${ref} --proof "<human review>"`,
|
|
315
|
-
`atris task revise ${ref} --note "<what must change>"`,
|
|
342
|
+
`atris task accept ${ref} --as ${player} --proof "<human review>"`,
|
|
343
|
+
`atris task revise ${ref} --as ${player} --note "<what must change>"`,
|
|
316
344
|
'atris xp card --local',
|
|
345
|
+
...globalSyncCommands(player),
|
|
317
346
|
];
|
|
318
347
|
}
|
|
319
348
|
|
|
320
349
|
return [
|
|
321
350
|
`atris task show ${ref}`,
|
|
322
351
|
'atris xp card --local',
|
|
352
|
+
...globalSyncCommands(player),
|
|
323
353
|
];
|
|
324
354
|
}
|
|
325
355
|
|
|
326
356
|
function modeState(args = []) {
|
|
327
357
|
const taskDb = require('../lib/task-db');
|
|
328
358
|
const workspaceArg = flag(args, '--workspace') || flag(args, '--root') || process.cwd();
|
|
329
|
-
const workspaceRoot = taskDb
|
|
359
|
+
const workspaceRoot = playWorkspaceRoot(taskDb, workspaceArg);
|
|
330
360
|
const db = taskDb.open();
|
|
331
361
|
const rows = taskDb.listTasks(db, {
|
|
332
362
|
workspaceRoot,
|
|
@@ -367,6 +397,8 @@ function modeState(args = []) {
|
|
|
367
397
|
prompt: latestMessage(events),
|
|
368
398
|
} : null,
|
|
369
399
|
xp_rule: 'AgentXP lands only after proof is ready and a human accepts the task.',
|
|
400
|
+
global_sync_rule: AGENTXP_GLOBAL_SYNC_RULE,
|
|
401
|
+
leaderboard_url: AGENTXP_LEADERBOARD_URL,
|
|
370
402
|
next_commands: commandList,
|
|
371
403
|
};
|
|
372
404
|
}
|
|
@@ -399,6 +431,8 @@ function render(state) {
|
|
|
399
431
|
console.log('');
|
|
400
432
|
console.log('Win condition: real artifact + verifier + human accept.');
|
|
401
433
|
console.log('XP rule: no proof, no AgentXP; accept/revise stays human-gated.');
|
|
434
|
+
console.log('Global sync: use owner token first; fallback to atris login before hosted leaderboard sync.');
|
|
435
|
+
console.log(`Leaderboard: ${state.leaderboard_url}`);
|
|
402
436
|
console.log('');
|
|
403
437
|
console.log('Next commands:');
|
|
404
438
|
for (const command of state.next_commands) console.log(`- ${command}`);
|
package/commands/sync.js
CHANGED
|
@@ -48,6 +48,10 @@ function _substituteParams(content, params) {
|
|
|
48
48
|
.replace(/\{\{workspace_template\}\}/g, params.workspace_template || 'business');
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function _templateTargetRelPath(relPath) {
|
|
52
|
+
return relPath === 'persona.md' ? 'PERSONA.md' : relPath;
|
|
53
|
+
}
|
|
54
|
+
|
|
51
55
|
/**
|
|
52
56
|
* Sync the canonical skill set from atris-cli/atris/skills/* into a
|
|
53
57
|
* workspace's atris/skills/* (plus ensure .claude/skills/ symlinks).
|
|
@@ -212,14 +216,15 @@ function syncWorkspaceTemplate(targetRoot, bizMeta, options = {}) {
|
|
|
212
216
|
const addedList = [], updatedList = [], preservedList = [];
|
|
213
217
|
|
|
214
218
|
for (const relPath of templateFiles) {
|
|
219
|
+
const targetRelPath = _templateTargetRelPath(relPath);
|
|
215
220
|
const templatePath = path.join(template.dir, relPath);
|
|
216
|
-
const targetPath = path.join(targetAtrisDir,
|
|
221
|
+
const targetPath = path.join(targetAtrisDir, targetRelPath);
|
|
217
222
|
let templateContent;
|
|
218
223
|
try { templateContent = fs.readFileSync(templatePath, 'utf-8'); } catch { continue; }
|
|
219
224
|
const finalContent = _substituteParams(templateContent, params);
|
|
220
225
|
|
|
221
226
|
if (!fs.existsSync(targetPath)) {
|
|
222
|
-
addedList.push(
|
|
227
|
+
addedList.push(targetRelPath); added++;
|
|
223
228
|
if (!dryRun) {
|
|
224
229
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
225
230
|
fs.writeFileSync(targetPath, finalContent);
|
|
@@ -229,10 +234,10 @@ function syncWorkspaceTemplate(targetRoot, bizMeta, options = {}) {
|
|
|
229
234
|
if (existing === finalContent) {
|
|
230
235
|
skipped++;
|
|
231
236
|
} else if (force) {
|
|
232
|
-
updatedList.push(
|
|
237
|
+
updatedList.push(targetRelPath); updated++;
|
|
233
238
|
if (!dryRun) fs.writeFileSync(targetPath, finalContent);
|
|
234
239
|
} else {
|
|
235
|
-
preservedList.push(
|
|
240
|
+
preservedList.push(targetRelPath); preserved++;
|
|
236
241
|
}
|
|
237
242
|
}
|
|
238
243
|
}
|