atris 2.3.8 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/atris/AGENTS.md +1 -2
- package/atris/atris.md +40 -6
- package/atris/features/_templates/changelog.md.template +11 -0
- package/atris/skills/clawhub/chief-of-staff/SKILL.md +65 -0
- package/atris/skills/clawhub/member-runtime/SKILL.md +112 -0
- package/atris/skills/create-app/SKILL.md +278 -0
- package/atris/skills/github/SKILL.md +207 -0
- package/atris/skills/magic-inbox/SKILL.md +216 -0
- package/atris/skills/youtube/SKILL.md +207 -0
- package/atris/team/_template/MEMBER.md +16 -0
- package/atris/team/brainstormer/MEMBER.md +29 -1
- package/atris/team/executor/MEMBER.md +35 -1
- package/atris/team/launcher/MEMBER.md +30 -1
- package/atris/team/navigator/MEMBER.md +28 -6
- package/atris/team/navigator/journal/2026-02-23.md +6 -0
- package/atris/team/researcher/MEMBER.md +27 -1
- package/atris/team/validator/MEMBER.md +35 -9
- package/bin/atris.js +241 -1776
- package/commands/analytics.js +2 -2
- package/commands/brainstorm.js +1 -2
- package/commands/init.js +35 -6
- package/commands/member.js +197 -8
- package/commands/status.js +128 -143
- package/commands/sync.js +4 -4
- package/commands/workflow.js +36 -33
- package/lib/state-detection.js +15 -4
- package/lib/todo.js +184 -0
- package/package.json +1 -1
package/commands/status.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { getLogPath, ensureLogDirectory, createLogFile } = require('../lib/journal');
|
|
4
|
+
const { parseTodo, getTeamActivity } = require('../lib/todo');
|
|
5
|
+
|
|
6
|
+
// Box drawing helpers
|
|
7
|
+
const W = 64; // inner width
|
|
8
|
+
const line = (char = '─') => char.repeat(W);
|
|
9
|
+
const pad = (str, w = W) => {
|
|
10
|
+
const visible = str.replace(/[\u{1F4CB}\u{1F528}\u{2705}\u{1F4E5}\u{1F4DA}\u{26A1}\u{1F916}\u{1F4DD}]/gu, 'XX'); // emoji = ~2 chars
|
|
11
|
+
const len = visible.length;
|
|
12
|
+
return len >= w ? str : str + ' '.repeat(w - len);
|
|
13
|
+
};
|
|
4
14
|
|
|
5
15
|
function statusAtris(isQuick = false) {
|
|
6
16
|
const targetDir = path.join(process.cwd(), 'atris');
|
|
@@ -10,104 +20,15 @@ function statusAtris(isQuick = false) {
|
|
|
10
20
|
process.exit(1);
|
|
11
21
|
}
|
|
12
22
|
|
|
13
|
-
//
|
|
23
|
+
// Parse TODO.md
|
|
14
24
|
const todoFile = path.join(targetDir, 'TODO.md');
|
|
15
|
-
const
|
|
16
|
-
let backlogTasks = [];
|
|
17
|
-
let inProgressTasks = [];
|
|
18
|
-
const taskFilePath = fs.existsSync(todoFile)
|
|
19
|
-
? todoFile
|
|
20
|
-
: (fs.existsSync(legacyTaskContextsFile) ? legacyTaskContextsFile : null);
|
|
21
|
-
|
|
22
|
-
if (taskFilePath && fs.existsSync(taskFilePath)) {
|
|
23
|
-
const taskContent = fs.readFileSync(taskFilePath, 'utf8');
|
|
24
|
-
|
|
25
|
-
function parseTodoBulletBlocks(sectionText) {
|
|
26
|
-
const tasks = [];
|
|
27
|
-
const lines = String(sectionText || '').split('\n');
|
|
28
|
-
let current = null;
|
|
29
|
-
|
|
30
|
-
const flush = () => {
|
|
31
|
-
if (!current) return;
|
|
32
|
-
tasks.push(current);
|
|
33
|
-
current = null;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
for (const rawLine of lines) {
|
|
37
|
-
const line = rawLine.trimEnd();
|
|
38
|
-
const startMatch = line.match(/^- \*\*([A-Z]\d+):\*\*\s*(.+)$/);
|
|
39
|
-
if (startMatch) {
|
|
40
|
-
flush();
|
|
41
|
-
current = {
|
|
42
|
-
id: startMatch[1],
|
|
43
|
-
title: startMatch[2].trim(),
|
|
44
|
-
claimed: null,
|
|
45
|
-
};
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (!current) continue;
|
|
50
|
-
|
|
51
|
-
if (!line.startsWith(' ')) continue;
|
|
52
|
-
|
|
53
|
-
const claimMatch = line.match(/\*\*Claimed by:\*\*\s*(.+)$/) || line.match(/Claimed by:\s*(.+)$/);
|
|
54
|
-
if (claimMatch && !current.claimed) {
|
|
55
|
-
current.claimed = claimMatch[1].trim();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
flush();
|
|
60
|
-
return tasks.filter(t => t && t.title);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Extract Backlog
|
|
64
|
-
const backlogMatch = taskContent.match(/## Backlog\n([\s\S]*?)(?=\n##|$)/);
|
|
65
|
-
if (backlogMatch && backlogMatch[1].trim() && !backlogMatch[1].includes('(No active tasks)')) {
|
|
66
|
-
const tasks = backlogMatch[1].trim().split('\n### ').filter(t => t.trim());
|
|
67
|
-
backlogTasks = tasks.map(t => {
|
|
68
|
-
const match = t.match(/Task:\s*(.+)/);
|
|
69
|
-
return match ? match[1].substring(0, 60) : null;
|
|
70
|
-
}).filter(Boolean);
|
|
71
|
-
|
|
72
|
-
// Newer TODO.md format: bullet tasks (e.g., "- **T1:** ...").
|
|
73
|
-
if (backlogTasks.length === 0) {
|
|
74
|
-
backlogTasks = parseTodoBulletBlocks(backlogMatch[1])
|
|
75
|
-
.map(t => t.title.substring(0, 60))
|
|
76
|
-
.filter(Boolean);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Extract In Progress
|
|
81
|
-
const inProgressMatch = taskContent.match(/## In Progress\n([\s\S]*?)(?=\n##|$)/);
|
|
82
|
-
if (inProgressMatch && inProgressMatch[1].trim() && !inProgressMatch[1].includes('Format:')) {
|
|
83
|
-
const tasks = inProgressMatch[1].trim().split('\n### ').filter(t => t.trim() && !t.startsWith('(Tasks') && !t.startsWith('Format:'));
|
|
84
|
-
inProgressTasks = tasks.map(t => {
|
|
85
|
-
const titleMatch = t.match(/Task:\s*(.+)/);
|
|
86
|
-
const claimMatch = t.match(/\*\*Claimed by:\*\*\s*(.+)/);
|
|
87
|
-
if (titleMatch) {
|
|
88
|
-
const title = titleMatch[1].substring(0, 40);
|
|
89
|
-
const claimed = claimMatch ? claimMatch[1].substring(0, 20) : 'Unknown';
|
|
90
|
-
return { title, claimed };
|
|
91
|
-
}
|
|
92
|
-
return null;
|
|
93
|
-
}).filter(Boolean);
|
|
94
|
-
|
|
95
|
-
// Newer TODO.md format: bullet tasks (e.g., "- **T1:** ...").
|
|
96
|
-
if (inProgressTasks.length === 0) {
|
|
97
|
-
inProgressTasks = parseTodoBulletBlocks(inProgressMatch[1]).map(t => {
|
|
98
|
-
const claimed = t.claimed ? t.claimed.substring(0, 20) : 'Unknown';
|
|
99
|
-
return { title: t.title.substring(0, 40), claimed };
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
25
|
+
const todo = parseTodo(todoFile);
|
|
104
26
|
|
|
105
27
|
// Read journal for inbox and completions
|
|
106
28
|
const { logFile, dateFormatted } = getLogPath();
|
|
107
29
|
let inboxItems = [];
|
|
108
30
|
let completions = [];
|
|
109
31
|
|
|
110
|
-
// Ensure today's journal exists so status doesn't show an empty slate on day rollover.
|
|
111
32
|
ensureLogDirectory();
|
|
112
33
|
if (!fs.existsSync(logFile)) {
|
|
113
34
|
createLogFile(logFile, dateFormatted);
|
|
@@ -116,12 +37,9 @@ function statusAtris(isQuick = false) {
|
|
|
116
37
|
if (fs.existsSync(logFile)) {
|
|
117
38
|
const logContent = fs.readFileSync(logFile, 'utf8');
|
|
118
39
|
|
|
119
|
-
// Extract inbox
|
|
120
40
|
const inboxMatch = logContent.match(/## Inbox\n([\s\S]*?)(?=\n##|$)/);
|
|
121
41
|
if (inboxMatch && inboxMatch[1].trim()) {
|
|
122
|
-
inboxItems = inboxMatch[1]
|
|
123
|
-
.trim()
|
|
124
|
-
.split('\n')
|
|
42
|
+
inboxItems = inboxMatch[1].trim().split('\n')
|
|
125
43
|
.filter(l => l.match(/^- \*\*I\d+:/))
|
|
126
44
|
.map(l => {
|
|
127
45
|
const match = l.match(/^- \*\*I(\d+):\s+(.+)$|^- \*\*I(\d+):\*\*\s*(.+)$/);
|
|
@@ -130,12 +48,9 @@ function statusAtris(isQuick = false) {
|
|
|
130
48
|
.filter(Boolean);
|
|
131
49
|
}
|
|
132
50
|
|
|
133
|
-
|
|
134
|
-
const completedMatch = logContent.match(/## Completed ✅\n([\s\S]*?)(?=\n##|---)/);
|
|
51
|
+
const completedMatch = logContent.match(/## Completed ✅\n([\s\S]*?)(?=\n##|---|$)/);
|
|
135
52
|
if (completedMatch && completedMatch[1].trim()) {
|
|
136
|
-
completions = completedMatch[1]
|
|
137
|
-
.trim()
|
|
138
|
-
.split('\n')
|
|
53
|
+
completions = completedMatch[1].trim().split('\n')
|
|
139
54
|
.filter(l => l.match(/^- \*\*C\d+:/))
|
|
140
55
|
.slice(-3)
|
|
141
56
|
.map(l => {
|
|
@@ -146,80 +61,150 @@ function statusAtris(isQuick = false) {
|
|
|
146
61
|
}
|
|
147
62
|
}
|
|
148
63
|
|
|
149
|
-
//
|
|
150
|
-
|
|
64
|
+
// Fallback: check journal for backlog tasks if TODO.md has none
|
|
65
|
+
if (todo.backlog.length === 0 && fs.existsSync(logFile)) {
|
|
66
|
+
const journalContent = fs.readFileSync(logFile, 'utf8');
|
|
67
|
+
const backlogMatch = journalContent.match(/## Backlog\n([\s\S]*?)(?=\n##|---|$)/);
|
|
68
|
+
if (backlogMatch && backlogMatch[1].trim()) {
|
|
69
|
+
const journalTasks = backlogMatch[1].trim().split('\n')
|
|
70
|
+
.filter(l => /^-\s+/.test(l) && !/\(empty|\(see /i.test(l));
|
|
71
|
+
if (journalTasks.length > 0) {
|
|
72
|
+
todo.backlog = journalTasks.map(l => ({
|
|
73
|
+
id: (l.match(/\*\*([A-Z]\d+):/)?.[1]) || '?',
|
|
74
|
+
title: l.replace(/^-\s*\*\*[A-Z]\d+:\*?\*?\s*/, '').trim(),
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Count lessons
|
|
151
81
|
const lessonsFile = path.join(targetDir, 'lessons.md');
|
|
82
|
+
let lessonsCount = 0;
|
|
152
83
|
if (fs.existsSync(lessonsFile)) {
|
|
153
84
|
const lessonsContent = fs.readFileSync(lessonsFile, 'utf8');
|
|
154
85
|
lessonsCount = (lessonsContent.match(/^- \*\*/gm) || []).length;
|
|
155
86
|
}
|
|
156
87
|
|
|
157
|
-
//
|
|
88
|
+
// Get team activity
|
|
89
|
+
const teamActivity = getTeamActivity(targetDir);
|
|
90
|
+
|
|
91
|
+
// Quick mode
|
|
158
92
|
if (isQuick) {
|
|
159
|
-
console.log(
|
|
93
|
+
console.log(`📋 ${todo.backlog.length} | 🔨 ${todo.inProgress.length} | ✅ ${todo.completed.length} | 📥 ${inboxItems.length} | 📚 ${lessonsCount}`);
|
|
160
94
|
return;
|
|
161
95
|
}
|
|
162
96
|
|
|
163
|
-
//
|
|
164
|
-
console.log(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
97
|
+
// ─── FULL VISUAL STATUS ────────────────────────────────────
|
|
98
|
+
const o = (s) => console.log(s);
|
|
99
|
+
|
|
100
|
+
o('');
|
|
101
|
+
o(`┌─${'─'.repeat(W)}─┐`);
|
|
102
|
+
o(`│ ${pad(`TASK BOARD — ${dateFormatted}`)} │`);
|
|
103
|
+
o(`├─${'─'.repeat(W)}─┤`);
|
|
104
|
+
|
|
105
|
+
// Backlog
|
|
106
|
+
o(`│ ${pad('')} │`);
|
|
107
|
+
o(`│ ${pad(` 📋 Backlog (${todo.backlog.length})`)} │`);
|
|
108
|
+
if (todo.backlog.length > 0) {
|
|
109
|
+
todo.backlog.slice(0, 5).forEach((t, i) => {
|
|
110
|
+
const id = t.id ? `${t.id}: ` : '';
|
|
111
|
+
const tag = t.tag ? ` [${t.tag}]` : '';
|
|
112
|
+
const full = `${id}${t.title}${tag}`;
|
|
113
|
+
const maxLen = W - 8;
|
|
114
|
+
const label = full.length > maxLen ? full.substring(0, maxLen - 3) + '...' : full;
|
|
115
|
+
const branch = (i === todo.backlog.length - 1 || i === 4) ? '└─' : '├─';
|
|
116
|
+
o(`│ ${pad(` ${branch} ${label}`)} │`);
|
|
175
117
|
});
|
|
118
|
+
if (todo.backlog.length > 5) {
|
|
119
|
+
o(`│ ${pad(` └─ ... +${todo.backlog.length - 5} more`)} │`);
|
|
120
|
+
}
|
|
176
121
|
} else {
|
|
177
|
-
|
|
122
|
+
o(`│ ${pad(' (none)')} │`);
|
|
178
123
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
124
|
+
|
|
125
|
+
// In Progress
|
|
126
|
+
o(`│ ${pad('')} │`);
|
|
127
|
+
o(`│ ${pad(` 🔨 In Progress (${todo.inProgress.length})`)} │`);
|
|
128
|
+
if (todo.inProgress.length > 0) {
|
|
129
|
+
todo.inProgress.forEach((t, i) => {
|
|
130
|
+
const id = t.id ? `${t.id}: ` : '';
|
|
131
|
+
const full = `${id}${t.title}`;
|
|
132
|
+
const maxLen = W - 8;
|
|
133
|
+
const label = full.length > maxLen ? full.substring(0, maxLen - 3) + '...' : full;
|
|
134
|
+
const isLast = i === todo.inProgress.length - 1;
|
|
135
|
+
o(`│ ${pad(` ${isLast ? '└─' : '├─'} ${label}`)} │`);
|
|
136
|
+
const agent = t.claimed || 'unclaimed';
|
|
137
|
+
const stage = t.stage || '';
|
|
138
|
+
const detail = stage ? `${agent} · ${stage}` : agent;
|
|
139
|
+
o(`│ ${pad(` ${isLast ? ' ' : '│'} └─ ${detail}`)} │`);
|
|
187
140
|
});
|
|
188
141
|
} else {
|
|
189
|
-
|
|
142
|
+
o(`│ ${pad(' (none)')} │`);
|
|
190
143
|
}
|
|
191
|
-
|
|
144
|
+
|
|
145
|
+
// Completed (target = 0)
|
|
146
|
+
o(`│ ${pad('')} │`);
|
|
147
|
+
const doneLabel = todo.completed.length === 0
|
|
148
|
+
? ' ✅ Done (0) ← target state'
|
|
149
|
+
: ` ✅ Done (${todo.completed.length}) ← clean these up`;
|
|
150
|
+
o(`│ ${pad(doneLabel)} │`);
|
|
192
151
|
|
|
193
152
|
// Inbox
|
|
194
|
-
console.log(`📥 Inbox Items: ${inboxItems.length}`);
|
|
195
153
|
if (inboxItems.length > 0) {
|
|
154
|
+
o(`│ ${pad('')} │`);
|
|
155
|
+
o(`│ ${pad(` 📥 Inbox (${inboxItems.length})`)} │`);
|
|
196
156
|
inboxItems.slice(0, 3).forEach(i => {
|
|
197
|
-
|
|
157
|
+
o(`│ ${pad(` ├─ I${i.id}: ${i.title.substring(0, W - 14)}`)} │`);
|
|
198
158
|
});
|
|
199
159
|
if (inboxItems.length > 3) {
|
|
200
|
-
|
|
160
|
+
o(`│ ${pad(` └─ ... +${inboxItems.length - 3} more`)} │`);
|
|
201
161
|
}
|
|
202
|
-
} else {
|
|
203
|
-
console.log(' (No items in inbox)');
|
|
204
162
|
}
|
|
205
|
-
console.log('');
|
|
206
163
|
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
164
|
+
// Lessons
|
|
165
|
+
o(`│ ${pad('')} │`);
|
|
166
|
+
o(`│ ${pad(` 📚 Lessons (${lessonsCount})`)} │`);
|
|
167
|
+
|
|
168
|
+
// Team Activity
|
|
169
|
+
o(`│ ${pad('')} │`);
|
|
170
|
+
o(`├─${'─'.repeat(W)}─┤`);
|
|
171
|
+
o(`│ ${pad('TEAM')} │`);
|
|
172
|
+
o(`│ ${pad('')} │`);
|
|
173
|
+
|
|
174
|
+
if (teamActivity.length > 0) {
|
|
175
|
+
teamActivity.forEach(a => {
|
|
176
|
+
const name = a.member.padEnd(12);
|
|
177
|
+
const dateShort = formatDateShort(a.date);
|
|
178
|
+
// Show the most interesting field: pattern > learned > delivered > task
|
|
179
|
+
const insight = a.pattern || a.learned || a.delivered || a.task || '';
|
|
180
|
+
const maxLen = W - 22;
|
|
181
|
+
const truncated = insight.length > maxLen ? insight.substring(0, maxLen - 3) + '...' : insight;
|
|
182
|
+
o(`│ ${pad(` ${name} · ${dateShort}`)} │`);
|
|
183
|
+
if (insight) {
|
|
184
|
+
o(`│ ${pad(` ${' '.repeat(12)} "${truncated}"`)} │`);
|
|
185
|
+
}
|
|
212
186
|
});
|
|
213
187
|
} else {
|
|
214
|
-
|
|
188
|
+
o(`│ ${pad(' (no journal entries yet)')} │`);
|
|
215
189
|
}
|
|
216
|
-
console.log('');
|
|
217
190
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
191
|
+
o(`│ ${pad('')} │`);
|
|
192
|
+
o(`└─${'─'.repeat(W)}─┘`);
|
|
193
|
+
o('');
|
|
194
|
+
o(' plan → do → review (or: atris log to add ideas)');
|
|
195
|
+
o('');
|
|
221
196
|
}
|
|
222
197
|
|
|
198
|
+
function formatDateShort(dateStr) {
|
|
199
|
+
// "2026-02-25" → "Feb 25"
|
|
200
|
+
try {
|
|
201
|
+
const [, month, day] = dateStr.match(/(\d{2})-(\d{2})$/);
|
|
202
|
+
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
203
|
+
return `${months[parseInt(month, 10) - 1]} ${parseInt(day, 10)}`;
|
|
204
|
+
} catch {
|
|
205
|
+
return dateStr;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
223
208
|
|
|
224
209
|
module.exports = {
|
|
225
210
|
statusAtris
|
package/commands/sync.js
CHANGED
|
@@ -320,10 +320,10 @@ function syncRecursiveCount(src, dest, label, silent) {
|
|
|
320
320
|
if (fs.statSync(srcPath).isDirectory()) {
|
|
321
321
|
count += syncRecursiveCount(srcPath, destPath, `${label}/${entry}`, silent);
|
|
322
322
|
} else {
|
|
323
|
-
const
|
|
324
|
-
const
|
|
325
|
-
if (
|
|
326
|
-
fs.writeFileSync(destPath,
|
|
323
|
+
const srcBuf = fs.readFileSync(srcPath);
|
|
324
|
+
const destBuf = fs.existsSync(destPath) ? fs.readFileSync(destPath) : Buffer.alloc(0);
|
|
325
|
+
if (!srcBuf.equals(destBuf)) {
|
|
326
|
+
fs.writeFileSync(destPath, srcBuf);
|
|
327
327
|
if (entry.endsWith('.sh')) {
|
|
328
328
|
fs.chmodSync(destPath, 0o755);
|
|
329
329
|
}
|
package/commands/workflow.js
CHANGED
|
@@ -167,8 +167,11 @@ async function planAtris(userInput = null) {
|
|
|
167
167
|
}
|
|
168
168
|
console.log('Workflow:');
|
|
169
169
|
console.log('1) ASCII visualize + wait for approval');
|
|
170
|
-
console.log('2) Write
|
|
171
|
-
console.log('
|
|
170
|
+
console.log('2) Write tasks to atris/TODO.md under ## Backlog');
|
|
171
|
+
console.log(' Format: - **T#:** Description [explore|execute]');
|
|
172
|
+
console.log('3) Log to atris/team/navigator/journal/YYYY-MM-DD.md');
|
|
173
|
+
console.log(' (Task, Delivered, User reaction, Pattern)');
|
|
174
|
+
console.log('4) Stop. Do NOT execute (run `atris do` to build).');
|
|
172
175
|
console.log('');
|
|
173
176
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
174
177
|
console.log('💡 After planning: Run "atris do" to execute the build');
|
|
@@ -231,14 +234,15 @@ async function planAtris(userInput = null) {
|
|
|
231
234
|
userPrompt += ` - Include file:line references from MAP.md\n`;
|
|
232
235
|
userPrompt += ` - List dependencies between tasks\n`;
|
|
233
236
|
userPrompt += ` - Add acceptance criteria for each task\n\n`;
|
|
234
|
-
userPrompt += `STEP 3: Write tasks to TODO.md\n`;
|
|
235
|
-
userPrompt += ` - Add
|
|
236
|
-
userPrompt += ` - Format:
|
|
237
|
-
userPrompt += ` -
|
|
238
|
-
userPrompt += `
|
|
239
|
-
userPrompt += `
|
|
240
|
-
userPrompt += ` -
|
|
241
|
-
userPrompt += ` - Include
|
|
237
|
+
userPrompt += `STEP 3: Write tasks to atris/TODO.md\n`;
|
|
238
|
+
userPrompt += ` - Add to ## Backlog section\n`;
|
|
239
|
+
userPrompt += ` - Format: - **T#:** Description [explore|execute]\n`;
|
|
240
|
+
userPrompt += ` - Each task: one job, clear exit condition\n`;
|
|
241
|
+
userPrompt += ` - Include file:line references from MAP.md\n\n`;
|
|
242
|
+
userPrompt += `STEP 4: Log to your journal\n`;
|
|
243
|
+
userPrompt += ` - Write to atris/team/navigator/journal/YYYY-MM-DD.md\n`;
|
|
244
|
+
userPrompt += ` - Include: Task, Delivered, User reaction, Pattern\n`;
|
|
245
|
+
userPrompt += ` - Your journal is how you learn — record what worked\n\n`;
|
|
242
246
|
userPrompt += `Start planning now. Read MAP.md for file references.`;
|
|
243
247
|
|
|
244
248
|
console.log('');
|
|
@@ -478,9 +482,12 @@ async function doAtris() {
|
|
|
478
482
|
console.log('');
|
|
479
483
|
}
|
|
480
484
|
console.log('Workflow:');
|
|
481
|
-
console.log('1)
|
|
482
|
-
console.log('
|
|
483
|
-
console.log('
|
|
485
|
+
console.log('1) Read atris/TODO.md — claim next unclaimed Backlog task');
|
|
486
|
+
console.log(' Move to ## In Progress: add "Claimed by: executor at YYYY-MM-DD HH:MM"');
|
|
487
|
+
console.log('2) Execute step-by-step. Run tests as you go.');
|
|
488
|
+
console.log('3) When done, move task to ## Completed');
|
|
489
|
+
console.log('4) Log to atris/team/executor/journal/YYYY-MM-DD.md');
|
|
490
|
+
console.log(' (Task, Delivered, Errors hit, Learned)');
|
|
484
491
|
console.log('');
|
|
485
492
|
console.log('⛔ Do NOT plan — just execute what\'s written.');
|
|
486
493
|
console.log('');
|
|
@@ -510,12 +517,6 @@ async function doAtris() {
|
|
|
510
517
|
console.log('');
|
|
511
518
|
}
|
|
512
519
|
|
|
513
|
-
if (taskContexts && taskContexts.trim()) {
|
|
514
|
-
console.log('📝 TODO.md (full):');
|
|
515
|
-
console.log('─────────────────────────────────────────────────────────────');
|
|
516
|
-
console.log(taskContexts);
|
|
517
|
-
console.log('');
|
|
518
|
-
}
|
|
519
520
|
}
|
|
520
521
|
|
|
521
522
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
@@ -562,16 +563,13 @@ async function doAtris() {
|
|
|
562
563
|
userPrompt += `## TASKS TO EXECUTE:\n(No tasks found - check TODO.md)\n\n`;
|
|
563
564
|
}
|
|
564
565
|
|
|
565
|
-
if (taskContexts) {
|
|
566
|
-
userPrompt += `## TODO.md (Additional Context):\n${taskContexts}\n\n`;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
566
|
userPrompt += `Your process (EXECUTE these steps):\n`;
|
|
570
567
|
userPrompt += `1. Read tasks from TODO.md (shown above)\n`;
|
|
571
568
|
userPrompt += `2. For each task: Show ASCII visualization first (especially complex changes)\n`;
|
|
572
569
|
userPrompt += `3. Execute task: Use file edit tools, terminal commands, etc.\n`;
|
|
573
|
-
userPrompt += `4.
|
|
574
|
-
userPrompt += `5.
|
|
570
|
+
userPrompt += `4. Move task to ## Completed in TODO.md\n`;
|
|
571
|
+
userPrompt += `5. Log to atris/team/executor/journal/YYYY-MM-DD.md\n`;
|
|
572
|
+
userPrompt += ` (Task, Delivered, Errors hit, Learned)\n`;
|
|
575
573
|
userPrompt += `6. Use MAP.md to navigate codebase\n\n`;
|
|
576
574
|
userPrompt += `DO NOT just describe what you would do - actually edit files and execute commands!\n`;
|
|
577
575
|
userPrompt += `Context: ${context}\n`;
|
|
@@ -818,11 +816,13 @@ async function reviewAtris() {
|
|
|
818
816
|
console.log('Workflow:');
|
|
819
817
|
console.log('1) Run the project test suite (follow TESTING_GUIDE if present).');
|
|
820
818
|
console.log('2) Execute any `atris/features/*/validate.md` scripts; if a step fails, fix + rerun.');
|
|
821
|
-
console.log('3)
|
|
822
|
-
console.log('
|
|
823
|
-
console.log('
|
|
819
|
+
console.log('3) Clean TODO.md: delete completed tasks. Target state = 0.');
|
|
820
|
+
console.log(' If a task fails validation, move back to ## Backlog with note.');
|
|
821
|
+
console.log('4) Log to atris/team/validator/journal/YYYY-MM-DD.md');
|
|
822
|
+
console.log(' (Task, Result, Issues found, Learned)');
|
|
823
|
+
console.log('5) If anything surprised you, append to atris/lessons.md.');
|
|
824
824
|
console.log('');
|
|
825
|
-
console.log('Done when: ✅ All good. Ready for human testing.');
|
|
825
|
+
console.log('Done when: ✅ All good. TODO.md clean. Ready for human testing.');
|
|
826
826
|
console.log('');
|
|
827
827
|
|
|
828
828
|
if (showFull) {
|
|
@@ -909,9 +909,10 @@ async function reviewAtris() {
|
|
|
909
909
|
userPrompt += `Your job:\n`;
|
|
910
910
|
userPrompt += ` • Verify everything works\n`;
|
|
911
911
|
userPrompt += ` • Test thoroughly (unless user says no)\n`;
|
|
912
|
-
userPrompt += ` •
|
|
913
|
-
userPrompt += `
|
|
914
|
-
userPrompt += ` •
|
|
912
|
+
userPrompt += ` • Clean TODO.md — DELETE completed tasks. Target state = 0.\n`;
|
|
913
|
+
userPrompt += ` If a task fails, move it back to ## Backlog with a note.\n`;
|
|
914
|
+
userPrompt += ` • Log to atris/team/validator/journal/YYYY-MM-DD.md\n`;
|
|
915
|
+
userPrompt += ` (Task, Result, Issues found, Learned)\n`;
|
|
915
916
|
userPrompt += ` • If anything surprised you, append to atris/lessons.md\n`;
|
|
916
917
|
userPrompt += ` • EVOLUTION: If you see drift in the logs, propose a tool upgrade.\n\n`;
|
|
917
918
|
userPrompt += `The cycle: do → review → [issues] → do → review → ✅ Ready\n`;
|
|
@@ -996,7 +997,9 @@ async function reviewAtris() {
|
|
|
996
997
|
}
|
|
997
998
|
}
|
|
998
999
|
|
|
999
|
-
// Prompt for learnings
|
|
1000
|
+
// Prompt for learnings (skip if stdin is not a TTY)
|
|
1001
|
+
if (!process.stdin.isTTY) return;
|
|
1002
|
+
|
|
1000
1003
|
console.log('');
|
|
1001
1004
|
console.log('┌─────────────────────────────────────────────────────────────┐');
|
|
1002
1005
|
console.log('│ 💡 Any learnings? │');
|
package/lib/state-detection.js
CHANGED
|
@@ -42,10 +42,21 @@ function getBacklogTasks(atrisDir) {
|
|
|
42
42
|
? todoFile
|
|
43
43
|
: (fs.existsSync(legacyTaskContextFile) ? legacyTaskContextFile : null);
|
|
44
44
|
|
|
45
|
-
if (
|
|
45
|
+
if (taskFilePath) {
|
|
46
|
+
const content = fs.readFileSync(taskFilePath, 'utf8');
|
|
47
|
+
const tasks = getTasksFromTodoSection(content, 'Backlog');
|
|
48
|
+
if (tasks.length > 0) return tasks;
|
|
49
|
+
}
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
// Fallback: check today's journal Backlog section
|
|
52
|
+
const todayLog = getTodayLogPath(path.join(atrisDir, 'logs'));
|
|
53
|
+
if (fs.existsSync(todayLog)) {
|
|
54
|
+
const journalContent = fs.readFileSync(todayLog, 'utf8');
|
|
55
|
+
const journalTasks = getTasksFromTodoSection(journalContent, 'Backlog');
|
|
56
|
+
if (journalTasks.length > 0) return journalTasks;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return [];
|
|
49
60
|
}
|
|
50
61
|
|
|
51
62
|
function getInProgressTasks(atrisDir) {
|
|
@@ -82,7 +93,7 @@ function getTasksFromTodoSection(content, sectionName) {
|
|
|
82
93
|
if (!match) return [];
|
|
83
94
|
|
|
84
95
|
const body = (match[1] || '').trim();
|
|
85
|
-
if (!body || /\(empty/i.test(body)) return [];
|
|
96
|
+
if (!body || /\(empty|\(see /i.test(body)) return [];
|
|
86
97
|
|
|
87
98
|
const tasks = [];
|
|
88
99
|
const titleRegex = /^Task:\s*(.+)$/gim;
|