atris 2.4.0 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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;
package/lib/todo.js ADDED
@@ -0,0 +1,184 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Parse TODO.md into structured task objects.
6
+ * Supports format: - **T1:** Description
7
+ * with optional **Claimed by:** and **Stage:** lines
8
+ */
9
+ function parseTodo(todoPath) {
10
+ if (!fs.existsSync(todoPath)) return { backlog: [], inProgress: [], completed: [] };
11
+
12
+ const content = fs.readFileSync(todoPath, 'utf8');
13
+ return {
14
+ backlog: parseSection(content, 'Backlog'),
15
+ inProgress: parseSection(content, 'In Progress'),
16
+ completed: parseSection(content, 'Completed'),
17
+ };
18
+ }
19
+
20
+ function parseSection(content, sectionName) {
21
+ const escaped = sectionName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
22
+ const match = content.match(new RegExp(`##\\s+${escaped}\\n([\\s\\S]*?)(?=\\n##|$)`, 'i'));
23
+ if (!match) return [];
24
+
25
+ const body = (match[1] || '').trim();
26
+ if (!body || /^\(clean\)/i.test(body) || /^\(empty/i.test(body) || /^\(see /i.test(body)) return [];
27
+
28
+ const tasks = [];
29
+ const lines = body.split('\n');
30
+ let current = null;
31
+
32
+ for (const rawLine of lines) {
33
+ const line = rawLine.trimEnd();
34
+
35
+ // New task line: - **T1:** Description or - **T1:** Description [tag]
36
+ const taskMatch = line.match(/^- \*\*([A-Z]\d+):\*\*\s*(.+)$/);
37
+ if (taskMatch) {
38
+ if (current) tasks.push(current);
39
+ const tagMatch = taskMatch[2].match(/\[(\w+)\]\s*$/);
40
+ current = {
41
+ id: taskMatch[1],
42
+ title: tagMatch ? taskMatch[2].replace(/\s*\[\w+\]\s*$/, '').trim() : taskMatch[2].trim(),
43
+ tag: tagMatch ? tagMatch[1] : null,
44
+ claimed: null,
45
+ stage: null,
46
+ };
47
+ continue;
48
+ }
49
+
50
+ // Also support checkbox format: - [x] Description
51
+ const checkMatch = line.match(/^- \[[ x]\]\s+(.+)$/);
52
+ if (checkMatch && !current) {
53
+ tasks.push({
54
+ id: null,
55
+ title: checkMatch[1].trim(),
56
+ tag: null,
57
+ claimed: null,
58
+ stage: null,
59
+ });
60
+ continue;
61
+ }
62
+
63
+ // Plain bullet without ID: - Description
64
+ const plainMatch = line.match(/^- (.+)$/);
65
+ if (plainMatch && !current && !plainMatch[1].startsWith('**')) {
66
+ tasks.push({
67
+ id: null,
68
+ title: plainMatch[1].trim(),
69
+ tag: null,
70
+ claimed: null,
71
+ stage: null,
72
+ });
73
+ continue;
74
+ }
75
+
76
+ if (!current) continue;
77
+
78
+ // Claimed by line
79
+ const claimMatch = line.match(/\*\*Claimed by:\*\*\s*(.+)$/) || line.match(/Claimed by:\s*(.+)$/);
80
+ if (claimMatch) {
81
+ current.claimed = claimMatch[1].trim();
82
+ continue;
83
+ }
84
+
85
+ // Stage line
86
+ const stageMatch = line.match(/\*\*Stage:\*\*\s*(.+)$/) || line.match(/Stage:\s*(.+)$/);
87
+ if (stageMatch) {
88
+ current.stage = stageMatch[1].trim();
89
+ continue;
90
+ }
91
+ }
92
+
93
+ if (current) tasks.push(current);
94
+ return tasks;
95
+ }
96
+
97
+ /**
98
+ * Get latest journal entry for a team member.
99
+ * Reads the most recent file in atris/team/[member]/journal/
100
+ * Returns { date, entries[] } or null
101
+ */
102
+ function getTeamMemberJournal(atrisDir, memberName) {
103
+ const journalDir = path.join(atrisDir, 'team', memberName, 'journal');
104
+ if (!fs.existsSync(journalDir)) return null;
105
+
106
+ let files;
107
+ try {
108
+ files = fs.readdirSync(journalDir)
109
+ .filter(f => f.endsWith('.md'))
110
+ .sort()
111
+ .reverse();
112
+ } catch {
113
+ return null;
114
+ }
115
+
116
+ if (files.length === 0) return null;
117
+
118
+ const latestFile = files[0];
119
+ let content;
120
+ try {
121
+ content = fs.readFileSync(path.join(journalDir, latestFile), 'utf8');
122
+ } catch {
123
+ return null;
124
+ }
125
+ const date = latestFile.replace('.md', '');
126
+
127
+ // Extract key fields from journal entry
128
+ const taskMatch = content.match(/\*\*Task:\*\*\s*(.+)/);
129
+ const deliveredMatch = content.match(/\*\*Delivered:\*\*\s*(.+)/);
130
+ const patternMatch = content.match(/\*\*Pattern:\*\*\s*(.+)/);
131
+ const learnedMatch = content.match(/\*\*Learned:\*\*\s*(.+)/);
132
+
133
+ return {
134
+ date,
135
+ file: path.join(journalDir, latestFile),
136
+ task: taskMatch ? taskMatch[1].trim() : null,
137
+ delivered: deliveredMatch ? deliveredMatch[1].trim() : null,
138
+ pattern: patternMatch ? patternMatch[1].trim() : null,
139
+ learned: learnedMatch ? learnedMatch[1].trim() : null,
140
+ raw: content,
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Get all team members that have directories in atris/team/
146
+ */
147
+ function listTeamMembers(atrisDir) {
148
+ const teamDir = path.join(atrisDir, 'team');
149
+ if (!fs.existsSync(teamDir)) return [];
150
+
151
+ return fs.readdirSync(teamDir)
152
+ .filter(name => {
153
+ if (name.startsWith('_') || name.startsWith('.')) return false;
154
+ const full = path.join(teamDir, name);
155
+ try { return fs.statSync(full).isDirectory(); } catch { return false; }
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Get team activity: latest journal entry per member
161
+ */
162
+ function getTeamActivity(atrisDir) {
163
+ const members = listTeamMembers(atrisDir);
164
+ const activity = [];
165
+
166
+ for (const member of members) {
167
+ const journal = getTeamMemberJournal(atrisDir, member);
168
+ if (journal) {
169
+ activity.push({ member, ...journal });
170
+ }
171
+ }
172
+
173
+ // Sort by date descending (most recent first)
174
+ activity.sort((a, b) => b.date.localeCompare(a.date));
175
+ return activity;
176
+ }
177
+
178
+ module.exports = {
179
+ parseTodo,
180
+ parseSection,
181
+ getTeamMemberJournal,
182
+ listTeamMembers,
183
+ getTeamActivity,
184
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "2.4.0",
3
+ "version": "2.5.1",
4
4
  "description": "atrisDev (atris dev) - CLI for AI coding agents. Works with Claude Code, Cursor, Windsurf. Make any codebase AI-navigable.",
5
5
  "main": "bin/atris.js",
6
6
  "bin": {