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.
@@ -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
- // Read TODO.md (or legacy TASK_CONTEXTS.md) for backlog and in-progress tasks
23
+ // Parse TODO.md
14
24
  const todoFile = path.join(targetDir, 'TODO.md');
15
- const legacyTaskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
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
- // Extract recent completions
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
- // Read lessons count
150
- let lessonsCount = 0;
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
- // Quick mode: one-line summary
88
+ // Get team activity
89
+ const teamActivity = getTeamActivity(targetDir);
90
+
91
+ // Quick mode
158
92
  if (isQuick) {
159
- console.log(`📥 ${inboxItems.length} | 📋 ${backlogTasks.length} | 🔨 ${inProgressTasks.length} | ${completions.length} | 📚 ${lessonsCount}`);
93
+ console.log(`📋 ${todo.backlog.length} | 🔨 ${todo.inProgress.length} | ${todo.completed.length} | 📥 ${inboxItems.length} | 📚 ${lessonsCount}`);
160
94
  return;
161
95
  }
162
96
 
163
- // Full display status
164
- console.log('');
165
- console.log('┌─────────────────────────────────────────────────────────────┐');
166
- console.log(`│ Atris Status — ${dateFormatted}${' '.repeat(40 - dateFormatted.length)}│`);
167
- console.log('└─────────────────────────────────────────────────────────────┘');
168
- console.log('');
169
-
170
- // Backlog tasks
171
- console.log(`📋 Backlog (unclaimed): ${backlogTasks.length}`);
172
- if (backlogTasks.length > 0) {
173
- backlogTasks.forEach(t => {
174
- console.log(` • ${t}${t.length > 60 ? '...' : ''}`);
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
- console.log(' (No backlog tasks)');
122
+ o(`│ ${pad(' (none)')} │`);
178
123
  }
179
- console.log('');
180
-
181
- // In Progress tasks
182
- console.log(`🔨 In Progress (claimed): ${inProgressTasks.length}`);
183
- if (inProgressTasks.length > 0) {
184
- inProgressTasks.forEach(t => {
185
- console.log(` • ${t.title}${t.title.length > 40 ? '...' : ''}`);
186
- console.log(` Claimed by: ${t.claimed}`);
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
- console.log(' (No tasks being worked on)');
142
+ o(`│ ${pad(' (none)')} │`);
190
143
  }
191
- console.log('');
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
- console.log(` I${i.id}: ${i.title.substring(0, 50)}${i.title.length > 50 ? '...' : ''}`);
157
+ o(`│ ${pad(` ├─ I${i.id}: ${i.title.substring(0, W - 14)}`)} │`);
198
158
  });
199
159
  if (inboxItems.length > 3) {
200
- console.log(` ... and ${inboxItems.length - 3} more`);
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
- // Recent completions
208
- console.log(`✅ Recent Completions: ${completions.length}`);
209
- if (completions.length > 0) {
210
- completions.forEach(c => {
211
- console.log(` • C${c.id}: ${c.title.substring(0, 50)}${c.title.length > 50 ? '...' : ''}`);
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
- console.log(' (No completions yet)');
188
+ o(`│ ${pad(' (no journal entries yet)')} │`);
215
189
  }
216
- console.log('');
217
190
 
218
- console.log('─────────────────────────────────────────────────────────────');
219
- console.log('Next: atris plan → do → review (or atris log to add ideas)');
220
- console.log('');
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 srcContent = fs.readFileSync(srcPath, 'utf8');
324
- const destContent = fs.existsSync(destPath) ? fs.readFileSync(destPath, 'utf8') : '';
325
- if (srcContent !== destContent) {
326
- fs.writeFileSync(destPath, srcContent);
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
  }
@@ -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 plan artifacts: feature idea/build (+ validate) OR TODO Backlog');
171
- console.log('3) Stop. Do NOT execute (run `atris do` to build).');
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 tasks to Backlog section\n`;
236
- userPrompt += ` - Format: Task number, description, file refs, acceptance criteria\n`;
237
- userPrompt += ` - Quality over speed - tasks must be perfect for systems player execution\n\n`;
238
- userPrompt += `STEP 4: Create Validation Artifact (validate.md)\n`;
239
- userPrompt += ` - You MUST create a validate.md file for the feature.\n`;
240
- userPrompt += ` - This file acts as the executable simulation script for the Validator.\n`;
241
- userPrompt += ` - Include verifiable steps (e.g., 'Run curl...', 'Check DB...').\n\n`;
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) If a feature has `build.md`, execute it step-by-step (no redesign).');
482
- console.log('2) Otherwise, pull the next item from TODO Backlog In Progress → Completed.');
483
- console.log('3) Run tests as you go; stop when tasks are done.');
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. After completion: Move task to TODO.md <completed> section\n`;
574
- userPrompt += `5. Follow PERSONA.md for communication style\n`;
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) Update TODO.md + today\'s journal with results; propose tool upgrades if drift repeats.');
822
- console.log('4) If anything surprised you (broke, worked unexpectedly), append to atris/lessons.md:');
823
- console.log(' - **[YYYY-MM-DD] [feature-name]** — (pass|fail) — One-line lesson');
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 += ` • Update docs if needed (MAP.md, TODO.md)\n`;
913
- userPrompt += ` Clean TODO.md (move completed tasks to Completed section, then delete)\n`;
914
- userPrompt += ` • Extract learnings for journal\n`;
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? │');
@@ -42,10 +42,21 @@ function getBacklogTasks(atrisDir) {
42
42
  ? todoFile
43
43
  : (fs.existsSync(legacyTaskContextFile) ? legacyTaskContextFile : null);
44
44
 
45
- if (!taskFilePath) return [];
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
- const content = fs.readFileSync(taskFilePath, 'utf8');
48
- return getTasksFromTodoSection(content, 'Backlog');
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;