atris 3.15.13 → 3.15.22
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 +84 -8
- package/README.md +5 -1
- package/atris/AGENTS.md +46 -1
- package/atris/CLAUDE.md +36 -1
- package/atris/GEMINI.md +14 -1
- package/atris/atris.md +12 -1
- package/atris/atrisDev.md +3 -2
- package/atris/context/README.md +11 -0
- package/atris/features/company-brain-sync/validate.md +5 -5
- package/atris/learnings.jsonl +1 -0
- package/atris/policies/atris-design.md +2 -0
- package/atris/skills/aeo/SKILL.md +2 -2
- package/atris/skills/atris/SKILL.md +15 -62
- package/atris/skills/design/SKILL.md +2 -0
- package/atris/skills/imessage/SKILL.md +19 -2
- package/atris/skills/loop/SKILL.md +6 -5
- package/atris/skills/magic-inbox/SKILL.md +1 -1
- package/atris/team/_template/MEMBER.md +23 -1
- package/atris/team/brainstormer/START_HERE.md +6 -0
- package/atris/team/executor/MEMBER.md +13 -0
- package/atris/team/executor/START_HERE.md +6 -0
- package/atris/team/launcher/START_HERE.md +6 -0
- package/atris/team/mission-lead/MEMBER.md +39 -0
- package/atris/team/mission-lead/MISSION.md +33 -0
- package/atris/team/mission-lead/START_HERE.md +6 -0
- package/atris/team/navigator/MEMBER.md +11 -0
- package/atris/team/navigator/START_HERE.md +6 -0
- package/atris/team/opus-overnight/MEMBER.md +39 -0
- package/atris/team/opus-overnight/MISSION.md +61 -0
- package/atris/team/opus-overnight/START_HERE.md +6 -0
- package/atris/team/opus-overnight/STEERING.md +35 -0
- package/atris/team/researcher/START_HERE.md +6 -0
- package/atris/team/validator/MEMBER.md +26 -6
- package/atris/team/validator/START_HERE.md +6 -0
- package/atris/wiki/concepts/agent-activation-contract.md +79 -0
- package/atris/wiki/concepts/workspace-initialization-contract.md +73 -0
- package/atris/wiki/index.md +27 -0
- package/atris/wiki/sources/atris-labs-2026-05-10.txt +17 -0
- package/atris/wiki/sources/atris-labs-goals-2026-05-10.txt +15 -0
- package/atris/wiki/sources/atrisos-generative-ui-product-surface-2026-05-10.txt +10 -0
- package/atris/wiki/sources/jack-dorsey-2026-05-10.txt +12 -0
- package/atris.md +49 -13
- package/bin/atris.js +660 -22
- package/commands/activate.js +12 -3
- package/commands/aeo.js +1 -1
- package/commands/align.js +10 -10
- package/commands/analytics.js +9 -4
- package/commands/app.js +2 -0
- package/commands/apps.js +276 -0
- package/commands/auth.js +1 -1
- package/commands/autopilot.js +74 -5
- package/commands/brain.js +536 -61
- package/commands/brainstorm.js +12 -12
- package/commands/business-sync.js +142 -24
- package/commands/clean.js +9 -6
- package/commands/codex-goal.js +311 -0
- package/commands/errors.js +11 -1
- package/commands/feedback.js +55 -17
- package/commands/fork.js +2 -2
- package/commands/gm.js +376 -0
- package/commands/init.js +80 -3
- package/commands/integrations.js +524 -0
- package/commands/learn.js +25 -16
- package/commands/lesson.js +41 -0
- package/commands/lifecycle.js +2 -2
- package/commands/member.js +2416 -9
- package/commands/mission.js +1776 -0
- package/commands/now.js +48 -7
- package/commands/play.js +425 -0
- package/commands/publish.js +2 -1
- package/commands/pull.js +72 -29
- package/commands/push.js +199 -17
- package/commands/review.js +51 -13
- package/commands/skill.js +2 -2
- package/commands/soul.js +19 -13
- package/commands/status.js +6 -1
- package/commands/sync.js +5 -4
- package/commands/task.js +1041 -147
- package/commands/terminal.js +5 -5
- package/commands/verify.js +7 -5
- package/commands/visualize.js +7 -0
- package/commands/wiki.js +53 -16
- package/commands/workflow.js +298 -54
- package/commands/workspace-clean.js +1 -1
- package/commands/worktree.js +468 -0
- package/commands/xp.js +1608 -0
- package/lib/manifest.js +34 -4
- package/lib/scorecard.js +3 -2
- package/lib/task-db.js +408 -27
- package/lib/todo-fallback.js +28 -2
- package/lib/todo.js +5 -3
- package/package.json +23 -2
- package/utils/update-check.js +51 -1
package/commands/now.js
CHANGED
|
@@ -3,8 +3,15 @@ const path = require('path');
|
|
|
3
3
|
|
|
4
4
|
const NOW_PATH = path.join('atris', 'now.md');
|
|
5
5
|
|
|
6
|
+
function formatLocalDate(date = new Date()) {
|
|
7
|
+
const year = String(date.getFullYear());
|
|
8
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
9
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
10
|
+
return `${year}-${month}-${day}`;
|
|
11
|
+
}
|
|
12
|
+
|
|
6
13
|
function todayIso() {
|
|
7
|
-
return new Date()
|
|
14
|
+
return formatLocalDate(new Date());
|
|
8
15
|
}
|
|
9
16
|
|
|
10
17
|
function ensureAtrisDir(root = process.cwd()) {
|
|
@@ -57,6 +64,37 @@ function countMatches(filePath, pattern) {
|
|
|
57
64
|
return (content.match(pattern) || []).length;
|
|
58
65
|
}
|
|
59
66
|
|
|
67
|
+
function countOpenTodoItems(filePath) {
|
|
68
|
+
if (!fs.existsSync(filePath)) return 0;
|
|
69
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
70
|
+
const hasRenderedSections = /^##\s+(Backlog|In Progress|Blocked|Completed)\s*$/m.test(content);
|
|
71
|
+
let section = null;
|
|
72
|
+
let count = 0;
|
|
73
|
+
|
|
74
|
+
for (const line of content.split(/\r?\n/)) {
|
|
75
|
+
const heading = line.match(/^##\s+(.+?)\s*$/);
|
|
76
|
+
if (heading) {
|
|
77
|
+
section = heading[1];
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const isTaskBullet = /^-\s+(?:\[[ ]\]\s+)?\*\*.+?\*\*/.test(line);
|
|
81
|
+
if (!isTaskBullet) continue;
|
|
82
|
+
if (!hasRenderedSections || ['Backlog', 'In Progress', 'Blocked'].includes(section)) {
|
|
83
|
+
count += 1;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return count;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function countJournalCompletedReceipts(filePath) {
|
|
91
|
+
if (!fs.existsSync(filePath)) return 0;
|
|
92
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
93
|
+
const proofReceipts = content.match(/^\s*Proof:\s+\S/gm) || [];
|
|
94
|
+
if (proofReceipts.length > 0) return proofReceipts.length;
|
|
95
|
+
return countMatches(filePath, /^-\s+\*\*C\d+:/gm);
|
|
96
|
+
}
|
|
97
|
+
|
|
60
98
|
function currentJournalPath(root = process.cwd()) {
|
|
61
99
|
const now = new Date();
|
|
62
100
|
const year = String(now.getFullYear());
|
|
@@ -69,9 +107,9 @@ function renderDefaultNow(root = process.cwd()) {
|
|
|
69
107
|
const mapHeading = readFirstHeading(path.join(atrisDir, 'MAP.md')) || 'MAP not filled yet';
|
|
70
108
|
const todoPath = path.join(atrisDir, 'TODO.md');
|
|
71
109
|
const journalPath = currentJournalPath(root);
|
|
72
|
-
const
|
|
110
|
+
const openTodoCount = countOpenTodoItems(todoPath);
|
|
73
111
|
const inboxCount = countMatches(journalPath, /^-\s+\*\*I\d+:/gm);
|
|
74
|
-
const completedCount =
|
|
112
|
+
const completedCount = countJournalCompletedReceipts(journalPath);
|
|
75
113
|
const generated = todayIso();
|
|
76
114
|
|
|
77
115
|
return `# now
|
|
@@ -92,7 +130,7 @@ Last updated: ${generated}
|
|
|
92
130
|
## Signals
|
|
93
131
|
|
|
94
132
|
- Map: ${mapHeading}
|
|
95
|
-
- TODO items
|
|
133
|
+
- Open TODO items: ${openTodoCount}
|
|
96
134
|
- Inbox items today: ${inboxCount}
|
|
97
135
|
- Completed receipts today: ${completedCount}
|
|
98
136
|
|
|
@@ -123,9 +161,9 @@ function renderPortfolioNow(root = process.cwd()) {
|
|
|
123
161
|
const generated = todayIso();
|
|
124
162
|
const lines = workspaces.map((workspace) => {
|
|
125
163
|
const heading = readFirstHeading(workspace.mapPath) || workspace.slug;
|
|
126
|
-
const todoCount =
|
|
164
|
+
const todoCount = countOpenTodoItems(workspace.todoPath);
|
|
127
165
|
const nowState = fs.existsSync(workspace.nowPath) ? 'has now.md' : 'needs now.md';
|
|
128
|
-
return `- ${workspace.slug}: ${heading}; ${todoCount}
|
|
166
|
+
return `- ${workspace.slug}: ${heading}; ${todoCount} open TODO item${todoCount === 1 ? '' : 's'}; ${nowState}.`;
|
|
129
167
|
});
|
|
130
168
|
|
|
131
169
|
return `# now
|
|
@@ -199,7 +237,7 @@ function refreshNowFile(root = process.cwd()) {
|
|
|
199
237
|
}
|
|
200
238
|
|
|
201
239
|
function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
|
|
202
|
-
const help = args.includes('--help') || args.includes('-h');
|
|
240
|
+
const help = args.includes('--help') || args.includes('-h') || args[0] === 'help';
|
|
203
241
|
if (help) {
|
|
204
242
|
console.log('Usage: atris now [--init|--refresh|--all|--path]');
|
|
205
243
|
console.log('');
|
|
@@ -255,6 +293,9 @@ function nowAtris(args = process.argv.slice(3), root = process.cwd()) {
|
|
|
255
293
|
module.exports = {
|
|
256
294
|
NOW_PATH,
|
|
257
295
|
ensureNowFile,
|
|
296
|
+
formatLocalDate,
|
|
297
|
+
countJournalCompletedReceipts,
|
|
298
|
+
countOpenTodoItems,
|
|
258
299
|
findChildWorkspaces,
|
|
259
300
|
nowAtris,
|
|
260
301
|
refreshNowFile,
|
package/commands/play.js
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
7
|
+
const { getSessionProfile, loadCredentials } = require('../utils/auth');
|
|
8
|
+
|
|
9
|
+
function showHelp() {
|
|
10
|
+
console.log('');
|
|
11
|
+
console.log('Usage: atris play [--as <player>] [--workspace <path>] [--json]');
|
|
12
|
+
console.log('');
|
|
13
|
+
console.log('Description:');
|
|
14
|
+
console.log(' Enter AgentXP Mode for a player in the current Atris workspace.');
|
|
15
|
+
console.log(' Shows the next proof-backed mission, the win condition, and the');
|
|
16
|
+
console.log(' exact claim, proof, accept/revise, and XP card commands.');
|
|
17
|
+
console.log('');
|
|
18
|
+
console.log('Options:');
|
|
19
|
+
console.log(' --as <player> Player id, for example justin.');
|
|
20
|
+
console.log(' --player <player> Alias for --as.');
|
|
21
|
+
console.log(' --workspace <path> Read missions from another Atris workspace.');
|
|
22
|
+
console.log(' --no-seed Do not create a starter mission when none exists.');
|
|
23
|
+
console.log(' --json Print machine-readable mode state.');
|
|
24
|
+
console.log(' --help, -h Show this help.');
|
|
25
|
+
console.log('');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function flag(args, name) {
|
|
29
|
+
const inline = args.find(arg => arg.startsWith(`${name}=`));
|
|
30
|
+
if (inline) return inline.slice(name.length + 1);
|
|
31
|
+
const index = args.indexOf(name);
|
|
32
|
+
if (index >= 0 && args[index + 1] && !args[index + 1].startsWith('--')) {
|
|
33
|
+
return args[index + 1];
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hasFlag(args, name) {
|
|
39
|
+
return args.includes(name) || args.some(arg => arg.startsWith(`${name}=`));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function positional(args) {
|
|
43
|
+
return args.filter((arg, index) => {
|
|
44
|
+
if (arg.startsWith('--')) return false;
|
|
45
|
+
if (index > 0 && args[index - 1].startsWith('--')) return false;
|
|
46
|
+
return true;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeOwner(value) {
|
|
51
|
+
return String(value || '').trim().toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function slugify(value) {
|
|
55
|
+
return normalizeOwner(value)
|
|
56
|
+
.replace(/@.*$/, '')
|
|
57
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
58
|
+
.replace(/^-+|-+$/g, '');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function taskAssignee(task) {
|
|
62
|
+
const metadata = task && task.metadata && typeof task.metadata === 'object'
|
|
63
|
+
? task.metadata
|
|
64
|
+
: {};
|
|
65
|
+
return metadata.assigned_to
|
|
66
|
+
|| metadata.owner
|
|
67
|
+
|| metadata.assignee
|
|
68
|
+
|| task.claimed_by
|
|
69
|
+
|| null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function taskRef(task) {
|
|
73
|
+
return task.display_id || task.legacy_ref || String(task.id || '').slice(0, 8);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function clip(value, max = 320) {
|
|
77
|
+
const text = String(value || '').replace(/\s+/g, ' ').trim();
|
|
78
|
+
if (text.length <= max) return text;
|
|
79
|
+
return `${text.slice(0, Math.max(0, max - 3)).trim()}...`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function latestMessage(events) {
|
|
83
|
+
const messages = (events || [])
|
|
84
|
+
.filter(event => event.event_type === 'message' && event.payload && event.payload.content)
|
|
85
|
+
.sort((a, b) => Number(b.created_at || 0) - Number(a.created_at || 0));
|
|
86
|
+
return messages.length ? clip(messages[0].payload.content, 520) : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function explicitPlayer(args) {
|
|
90
|
+
return flag(args, '--as')
|
|
91
|
+
|| flag(args, '--player')
|
|
92
|
+
|| flag(args, '--user')
|
|
93
|
+
|| positional(args)[0]
|
|
94
|
+
|| null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function activePlayerCounts(tasks) {
|
|
98
|
+
const counts = new Map();
|
|
99
|
+
for (const task of tasks || []) {
|
|
100
|
+
if (!task || ['done', 'failed'].includes(task.status)) continue;
|
|
101
|
+
const owner = slugify(taskAssignee(task));
|
|
102
|
+
if (!owner) continue;
|
|
103
|
+
counts.set(owner, (counts.get(owner) || 0) + 1);
|
|
104
|
+
}
|
|
105
|
+
return counts;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function uniqueAssignedPlayer(tasks) {
|
|
109
|
+
const counts = activePlayerCounts(tasks);
|
|
110
|
+
if (counts.size !== 1) return null;
|
|
111
|
+
return Array.from(counts.keys())[0] || null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function gitIdentityCandidate(workspaceRoot) {
|
|
115
|
+
const candidates = [];
|
|
116
|
+
for (const key of ['user.email', 'user.name']) {
|
|
117
|
+
const result = spawnSync('git', ['config', '--get', key], {
|
|
118
|
+
cwd: workspaceRoot,
|
|
119
|
+
encoding: 'utf8',
|
|
120
|
+
timeout: 1000,
|
|
121
|
+
});
|
|
122
|
+
if (result.status === 0 && result.stdout.trim()) {
|
|
123
|
+
candidates.push(slugify(result.stdout.trim()));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return candidates.find(Boolean) || null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function activeAccountCandidate() {
|
|
130
|
+
const envPlayer = slugify(process.env.ATRIS_PLAYER || process.env.ATRIS_USERNAME);
|
|
131
|
+
if (envPlayer) return { player: envPlayer, source: 'env' };
|
|
132
|
+
|
|
133
|
+
const envProfile = slugify(process.env.ATRIS_PROFILE);
|
|
134
|
+
if (envProfile) return { player: envProfile, source: 'atris_profile' };
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const sessionProfile = slugify(getSessionProfile());
|
|
138
|
+
if (sessionProfile) return { player: sessionProfile, source: 'atris_session' };
|
|
139
|
+
} catch {}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const credentials = loadCredentials();
|
|
143
|
+
const credentialPlayer = slugify(
|
|
144
|
+
credentials?.username
|
|
145
|
+
|| credentials?.handle
|
|
146
|
+
|| credentials?.display_name
|
|
147
|
+
|| credentials?.name
|
|
148
|
+
);
|
|
149
|
+
if (credentialPlayer) return { player: credentialPlayer, source: 'atris_account' };
|
|
150
|
+
} catch {}
|
|
151
|
+
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function teamMemberExists(workspaceRoot, player) {
|
|
156
|
+
const slug = slugify(player);
|
|
157
|
+
return Boolean(slug) && fs.existsSync(path.join(workspaceRoot, 'atris', 'team', slug));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function onlyTeamMember(workspaceRoot) {
|
|
161
|
+
const teamDir = path.join(workspaceRoot, 'atris', 'team');
|
|
162
|
+
try {
|
|
163
|
+
const members = fs.readdirSync(teamDir, { withFileTypes: true })
|
|
164
|
+
.filter(entry => entry.isDirectory())
|
|
165
|
+
.map(entry => entry.name)
|
|
166
|
+
.filter(name => !name.startsWith('_') && name !== 'template')
|
|
167
|
+
.filter(name => fs.existsSync(path.join(teamDir, name, 'MEMBER.md')) || fs.existsSync(path.join(teamDir, name, 'START_HERE.md')));
|
|
168
|
+
return members.length === 1 ? slugify(members[0]) : null;
|
|
169
|
+
} catch {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function inferPlayer(workspaceRoot, tasks, args = []) {
|
|
175
|
+
const explicit = explicitPlayer(args);
|
|
176
|
+
if (explicit) return { player: slugify(explicit), source: 'flag' };
|
|
177
|
+
|
|
178
|
+
const account = activeAccountCandidate();
|
|
179
|
+
if (account) return account;
|
|
180
|
+
|
|
181
|
+
const gitPlayer = gitIdentityCandidate(workspaceRoot);
|
|
182
|
+
if (gitPlayer && teamMemberExists(workspaceRoot, gitPlayer)) {
|
|
183
|
+
return { player: gitPlayer, source: 'git_team_match' };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for (const value of [process.env.USER, os.userInfo().username]) {
|
|
187
|
+
const player = slugify(value);
|
|
188
|
+
if (player && teamMemberExists(workspaceRoot, player)) {
|
|
189
|
+
return { player, source: 'local_user_team_match' };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const soleMember = onlyTeamMember(workspaceRoot);
|
|
194
|
+
if (soleMember) return { player: soleMember, source: 'only_team_member' };
|
|
195
|
+
|
|
196
|
+
const fallback = slugify(process.env.USER || os.userInfo().username || 'player') || 'player';
|
|
197
|
+
if (fallback) return { player: fallback, source: 'local_user' };
|
|
198
|
+
|
|
199
|
+
const assigned = uniqueAssignedPlayer(tasks);
|
|
200
|
+
if (assigned) return { player: assigned, source: 'active_task_assignment' };
|
|
201
|
+
|
|
202
|
+
return { player: 'player', source: 'fallback' };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function rankTask(task, player) {
|
|
206
|
+
const assignedTo = normalizeOwner(taskAssignee(task));
|
|
207
|
+
const claimedBy = normalizeOwner(task.claimed_by);
|
|
208
|
+
const target = normalizeOwner(player);
|
|
209
|
+
const assignedMatch = target && assignedTo === target;
|
|
210
|
+
const claimedMatch = target && claimedBy === target;
|
|
211
|
+
|
|
212
|
+
if (task.status === 'claimed' && claimedMatch) return 0;
|
|
213
|
+
if (task.status === 'review' && (claimedMatch || assignedMatch)) return 1;
|
|
214
|
+
if (task.status === 'open' && assignedMatch) return 2;
|
|
215
|
+
if (task.status === 'claimed') return 3;
|
|
216
|
+
if (task.status === 'review') return 4;
|
|
217
|
+
if (task.status === 'open') return 5;
|
|
218
|
+
return 99;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function taskMatchesPlayer(task, player) {
|
|
222
|
+
const target = normalizeOwner(player);
|
|
223
|
+
return Boolean(target) && (
|
|
224
|
+
normalizeOwner(taskAssignee(task)) === target
|
|
225
|
+
|| normalizeOwner(task?.claimed_by) === target
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function selectMission(tasks, player) {
|
|
230
|
+
const active = (tasks || [])
|
|
231
|
+
.filter(task => !['done', 'failed'].includes(task.status))
|
|
232
|
+
.filter(task => taskMatchesPlayer(task, player));
|
|
233
|
+
if (!active.length) return null;
|
|
234
|
+
return [...active].sort((a, b) => {
|
|
235
|
+
const rank = rankTask(a, player) - rankTask(b, player);
|
|
236
|
+
if (rank) return rank;
|
|
237
|
+
return Number(b.updated_at || b.created_at || 0) - Number(a.updated_at || a.created_at || 0);
|
|
238
|
+
})[0] || null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function starterMissionTitle() {
|
|
242
|
+
return 'AgentXP Mode first rep: complete one proof-backed customer-motion mission';
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function starterMissionPrompt(player) {
|
|
246
|
+
return [
|
|
247
|
+
`Player ${player}: enter AgentXP Mode in this local workspace.`,
|
|
248
|
+
'Pick one concrete Atris Labs customer-motion rep you can finish today.',
|
|
249
|
+
'Have an agent help create a real artifact and verifier proof.',
|
|
250
|
+
'Do not self-accept; when proof is ready, show accept/revise.',
|
|
251
|
+
'Win condition: one accepted proof-backed rep visible on the local AgentXP card.',
|
|
252
|
+
].join(' ');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function ensureStarterMission(taskDb, db, workspaceRoot, player, tasks, args = []) {
|
|
256
|
+
if (hasFlag(args, '--no-seed')) return { tasks, seeded: null };
|
|
257
|
+
if (selectMission(tasks, player)) return { tasks, seeded: null };
|
|
258
|
+
if (!fs.existsSync(path.join(workspaceRoot, 'atris'))) return { tasks, seeded: null };
|
|
259
|
+
|
|
260
|
+
const result = taskDb.addTask(db, {
|
|
261
|
+
title: starterMissionTitle(),
|
|
262
|
+
tag: 'agent-xp',
|
|
263
|
+
workspaceRoot,
|
|
264
|
+
metadata: {
|
|
265
|
+
assigned_to: player,
|
|
266
|
+
delegate_via: 'agentxp_play',
|
|
267
|
+
auto_seeded_by: 'atris play',
|
|
268
|
+
created_for_day: new Date().toISOString().slice(0, 10),
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
taskDb.noteTask(db, {
|
|
272
|
+
id: result.id,
|
|
273
|
+
actor: 'game-manager',
|
|
274
|
+
content: starterMissionPrompt(player),
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const refreshed = taskDb.withTaskDisplayRefs(taskDb.listTasks(db, {
|
|
278
|
+
workspaceRoot,
|
|
279
|
+
limit: 500,
|
|
280
|
+
}));
|
|
281
|
+
const seeded = refreshed.find(task => task.id === result.id) || null;
|
|
282
|
+
return { tasks: refreshed, seeded };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function nextCommands(task, player) {
|
|
286
|
+
if (!task) {
|
|
287
|
+
return [
|
|
288
|
+
`atris task delegate "AgentXP first rep: one proof-backed mission" --to ${player} --tag agent-xp`,
|
|
289
|
+
`atris play --as ${player}`,
|
|
290
|
+
];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const ref = taskRef(task);
|
|
294
|
+
if (task.status === 'open') {
|
|
295
|
+
return [
|
|
296
|
+
`atris task claim ${ref} --as ${player}`,
|
|
297
|
+
`atris task ready ${ref} --proof "<artifact path + verifier result>"`,
|
|
298
|
+
`atris task accept ${ref} --proof "<human review>"`,
|
|
299
|
+
'atris xp card --local',
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (task.status === 'claimed') {
|
|
304
|
+
return [
|
|
305
|
+
`atris task ready ${ref} --proof "<artifact path + verifier result>"`,
|
|
306
|
+
`atris task accept ${ref} --proof "<human review>"`,
|
|
307
|
+
'atris xp card --local',
|
|
308
|
+
];
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (task.status === 'review') {
|
|
312
|
+
return [
|
|
313
|
+
`atris task show ${ref}`,
|
|
314
|
+
`atris task accept ${ref} --proof "<human review>"`,
|
|
315
|
+
`atris task revise ${ref} --note "<what must change>"`,
|
|
316
|
+
'atris xp card --local',
|
|
317
|
+
];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return [
|
|
321
|
+
`atris task show ${ref}`,
|
|
322
|
+
'atris xp card --local',
|
|
323
|
+
];
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function modeState(args = []) {
|
|
327
|
+
const taskDb = require('../lib/task-db');
|
|
328
|
+
const workspaceArg = flag(args, '--workspace') || flag(args, '--root') || process.cwd();
|
|
329
|
+
const workspaceRoot = taskDb.workspaceRoot(path.resolve(workspaceArg));
|
|
330
|
+
const db = taskDb.open();
|
|
331
|
+
const rows = taskDb.listTasks(db, {
|
|
332
|
+
workspaceRoot,
|
|
333
|
+
limit: 500,
|
|
334
|
+
});
|
|
335
|
+
let tasks = taskDb.withTaskDisplayRefs(rows);
|
|
336
|
+
const detected = inferPlayer(workspaceRoot, tasks, args);
|
|
337
|
+
const player = detected.player || 'player';
|
|
338
|
+
const starter = ensureStarterMission(taskDb, db, workspaceRoot, player, tasks, args);
|
|
339
|
+
tasks = starter.tasks;
|
|
340
|
+
const mission = selectMission(tasks, player);
|
|
341
|
+
const events = mission
|
|
342
|
+
? taskDb.listTaskEvents(db, { taskId: mission.id, limit: 20, order: 'desc' })
|
|
343
|
+
: [];
|
|
344
|
+
const commandList = nextCommands(mission, player);
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
schema: 'atris.agentxp_play_mode.v1',
|
|
348
|
+
mode: 'AgentXP Mode',
|
|
349
|
+
generated_at: new Date().toISOString(),
|
|
350
|
+
player,
|
|
351
|
+
player_source: detected.source,
|
|
352
|
+
workspace_root: workspaceRoot,
|
|
353
|
+
workspace_name: path.basename(workspaceRoot),
|
|
354
|
+
seeded: starter.seeded ? {
|
|
355
|
+
id: starter.seeded.id,
|
|
356
|
+
ref: taskRef(starter.seeded),
|
|
357
|
+
title: starter.seeded.title,
|
|
358
|
+
} : null,
|
|
359
|
+
mission: mission ? {
|
|
360
|
+
id: mission.id,
|
|
361
|
+
ref: taskRef(mission),
|
|
362
|
+
title: mission.title,
|
|
363
|
+
status: mission.status,
|
|
364
|
+
tag: mission.tag || null,
|
|
365
|
+
assigned_to: taskAssignee(mission),
|
|
366
|
+
claimed_by: mission.claimed_by || null,
|
|
367
|
+
prompt: latestMessage(events),
|
|
368
|
+
} : null,
|
|
369
|
+
xp_rule: 'AgentXP lands only after proof is ready and a human accepts the task.',
|
|
370
|
+
next_commands: commandList,
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function render(state) {
|
|
375
|
+
console.log('');
|
|
376
|
+
console.log('AgentXP Mode');
|
|
377
|
+
console.log(`Player ${state.player} | Workspace ${state.workspace_name}`);
|
|
378
|
+
console.log('');
|
|
379
|
+
|
|
380
|
+
if (!state.mission) {
|
|
381
|
+
console.log('No active mission found.');
|
|
382
|
+
console.log('');
|
|
383
|
+
console.log('Seed one:');
|
|
384
|
+
for (const command of state.next_commands) console.log(`- ${command}`);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
const mission = state.mission;
|
|
389
|
+
if (state.seeded) {
|
|
390
|
+
console.log(`Starter mission created locally: ${state.seeded.ref}`);
|
|
391
|
+
console.log('');
|
|
392
|
+
}
|
|
393
|
+
console.log(`Mission ${mission.ref}: ${mission.title}`);
|
|
394
|
+
console.log(`State ${mission.status} | Assigned ${mission.assigned_to || 'unassigned'} | Claimed ${mission.claimed_by || 'none'}`);
|
|
395
|
+
if (mission.prompt) {
|
|
396
|
+
console.log('');
|
|
397
|
+
console.log(`Prompt: ${mission.prompt}`);
|
|
398
|
+
}
|
|
399
|
+
console.log('');
|
|
400
|
+
console.log('Win condition: real artifact + verifier + human accept.');
|
|
401
|
+
console.log('XP rule: no proof, no AgentXP; accept/revise stays human-gated.');
|
|
402
|
+
console.log('');
|
|
403
|
+
console.log('Next commands:');
|
|
404
|
+
for (const command of state.next_commands) console.log(`- ${command}`);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async function playCommand(...args) {
|
|
408
|
+
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
409
|
+
showHelp();
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
const state = modeState(args);
|
|
414
|
+
if (hasFlag(args, '--json')) {
|
|
415
|
+
console.log(JSON.stringify(state, null, 2));
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
render(state);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
module.exports = {
|
|
422
|
+
playCommand,
|
|
423
|
+
modeState,
|
|
424
|
+
selectMission,
|
|
425
|
+
};
|
package/commands/publish.js
CHANGED
|
@@ -5,7 +5,8 @@ const { loadCredentials } = require('../utils/auth');
|
|
|
5
5
|
const { apiRequestJson } = require('../utils/api');
|
|
6
6
|
|
|
7
7
|
async function publishAtris() {
|
|
8
|
-
|
|
8
|
+
const firstArg = process.argv[3];
|
|
9
|
+
if (firstArg === '--help' || firstArg === '-h' || firstArg === 'help') {
|
|
9
10
|
console.log('Usage: atris publish [--name <name>] [--description <desc>]');
|
|
10
11
|
console.log('');
|
|
11
12
|
console.log(' atris publish Publish current workspace as a template');
|