atris 1.9.6 → 2.0.2

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/bin/atris.js CHANGED
@@ -108,21 +108,32 @@ function showHelp() {
108
108
  console.log(' init - Initialize ATRIS in current project');
109
109
  console.log(' update - Update local files to latest version');
110
110
  console.log('');
111
- console.log('Daily workflow:');
112
- console.log(' atris - Load context and start (recommended)');
111
+ console.log('Core workflow:');
112
+ console.log(' plan - Create build spec with visualization');
113
+ console.log(' do - Execute tasks');
114
+ console.log(' review - Validate work (tests, safety checks, docs)');
115
+ console.log('');
116
+ console.log('Context & tracking:');
113
117
  console.log(' log - Add ideas to inbox');
114
118
  console.log(' status - See active work and completions');
115
- console.log(' analytics - View productivity insights');
119
+ console.log(' analytics - Show recent productivity from journals');
116
120
  console.log('');
117
- console.log('Legacy commands (still work, but "atris" is easier):');
118
- console.log(' plan - Manual planning mode');
119
- console.log(' do - Execute tasks');
120
- console.log(' review - Validate work');
121
+ console.log('Optional helpers:');
122
+ console.log(' brainstorm - Explore ideas conversationally before planning');
123
+ console.log(' autopilot - Guided loop that can clarify TODOs and run plan → do → review');
124
+ console.log(' visualize - Legacy visualization helper (prefer "atris plan")');
121
125
  console.log('');
122
- console.log('Other:');
126
+ console.log('Quick commands:');
127
+ console.log(' atris - Load context and start (natural language)');
128
+ console.log('');
129
+ console.log('Cloud & agents:');
130
+ console.log(' agent - Select which Atris agent to use');
131
+ console.log(' chat - Chat with the selected Atris agent');
123
132
  console.log(' login - Authenticate with Atris cloud (optional)');
124
133
  console.log(' logout - Remove credentials');
125
134
  console.log(' whoami - Show auth status');
135
+ console.log('');
136
+ console.log('Other:');
126
137
  console.log(' version - Show ATRIS version');
127
138
  console.log(' help - Show this help');
128
139
  console.log('');
@@ -130,31 +141,91 @@ function showHelp() {
130
141
  console.log('');
131
142
  }
132
143
 
144
+ function showPlanHelp() {
145
+ console.log('');
146
+ console.log('Usage: atris plan [--execute]');
147
+ console.log('');
148
+ console.log('Description:');
149
+ console.log(' Activate the Navigator agent to plan work.');
150
+ console.log(' Reads your journal Inbox, TODO.md, MAP.md, and features/, then prints a');
151
+ console.log(' visualization + build spec instructions for your coding agent.');
152
+ console.log('');
153
+ console.log('Options:');
154
+ console.log(' --execute Run in agent mode via Atris cloud (requires login + agent).');
155
+ console.log('');
156
+ }
157
+
158
+ function showDoHelp() {
159
+ console.log('');
160
+ console.log('Usage: atris do [--execute]');
161
+ console.log('');
162
+ console.log('Description:');
163
+ console.log(' Activate the Executor agent to build tasks.');
164
+ console.log(' Reads TODO.md and features/*/build.md, then prints step-by-step');
165
+ console.log(' execution instructions (and, in agent mode, edits code + runs commands).');
166
+ console.log('');
167
+ console.log('Options:');
168
+ console.log(' --execute Run in agent mode via Atris cloud (requires login + agent).');
169
+ console.log('');
170
+ }
171
+
172
+ function showReviewHelp() {
173
+ console.log('');
174
+ console.log('Usage: atris review [--execute]');
175
+ console.log('');
176
+ console.log('Description:');
177
+ console.log(' Activate the Validator agent to verify recent changes.');
178
+ console.log(' Reads TODO.md, MAP.md, and today\'s journal, then prints a validation');
179
+ console.log(' checklist (and, in agent mode, runs tests and updates docs).');
180
+ console.log('');
181
+ console.log('Options:');
182
+ console.log(' --execute Run in agent mode via Atris cloud (requires login + agent).');
183
+ console.log('');
184
+ }
185
+
186
+ function showAutopilotHelp() {
187
+ console.log('');
188
+ console.log('Usage: atris autopilot [idea]');
189
+ console.log('');
190
+ console.log('Description:');
191
+ console.log(' Run a guided plan → do → review loop around a single idea or current TODOs.');
192
+ console.log(' In auto mode, it will:');
193
+ console.log(' - Add the idea to today\'s Inbox');
194
+ console.log(' - Define success criteria');
195
+ console.log(' - Generate .atris-workflow.json');
196
+ console.log(' - Walk through Navigator, Executor, and Validator prompts');
197
+ console.log(' - Finish with a launch summary for you to review.');
198
+ console.log('');
199
+ }
200
+
201
+ // Command handlers - must load BEFORE interactiveEntry() is called (TDZ issue)
202
+ const { initAtris: initCmd } = require('../commands/init');
203
+ const { syncAtris: syncCmd } = require('../commands/sync');
204
+ const { logAtris: logCmd } = require('../commands/log');
205
+ const { logSyncAtris: logSyncCmd } = require('../commands/log-sync');
206
+ const { loginAtris: loginCmd, logoutAtris: logoutCmd, whoamiAtris: whoamiCmd } = require('../commands/auth');
207
+ const { showVersion: versionCmd } = require('../commands/version');
208
+ const { planAtris: planCmd, doAtris: doCmd, reviewAtris: reviewCmd } = require('../commands/workflow');
209
+ const { visualizeAtris: visualizeCmd } = require('../commands/visualize');
210
+ const { brainstormAtris: brainstormCmd, autopilotAtris: autopilotCmd } = require('../commands/brainstorm');
211
+ const { statusAtris: statusCmd } = require('../commands/status');
212
+ const { analyticsAtris: analyticsCmd } = require('../commands/analytics');
213
+
133
214
  // Check if this is a known command or natural language input
134
- const knownCommands = ['init', 'log', 'status', 'analytics', 'visualize', 'plan', 'do', 'review',
215
+ const knownCommands = ['init', 'log', 'status', 'analytics', 'visualize', 'brainstorm', 'autopilot', 'plan', 'do', 'review',
135
216
  'agent', 'chat', 'login', 'logout', 'whoami', 'update', 'version', 'help'];
136
217
 
137
218
  // If no command OR command is not recognized, treat as natural language
138
219
  if (!command || !knownCommands.includes(command)) {
139
220
  const userInput = process.argv.slice(2).join(' ');
140
221
 
141
- if (!userInput) {
142
- // Cold start - no input, show context
143
- atrisDevEntry()
144
- .then(() => process.exit(0))
145
- .catch((error) => {
146
- console.error(`✗ Error: ${error.message || error}`);
147
- process.exit(1);
148
- });
149
- } else {
150
- // Hot start - user provided task description
151
- atrisDevEntry(userInput)
152
- .then(() => process.exit(0))
153
- .catch((error) => {
154
- console.error(`✗ Error: ${error.message || error}`);
155
- process.exit(1);
156
- });
157
- }
222
+ // Launch interactive entry (the "Performance")
223
+ interactiveEntry(userInput)
224
+ .then(() => process.exit(0))
225
+ .catch((error) => {
226
+ console.error(`✗ Error: ${error.message || error}`);
227
+ process.exit(1);
228
+ });
158
229
  return;
159
230
  }
160
231
 
@@ -163,13 +234,98 @@ if (command === 'help' || command === '--help' || command === '-h') {
163
234
  process.exit(0);
164
235
  }
165
236
 
166
- // Command handlers - using modular commands where available
167
- const { initAtris: initCmd } = require('../commands/init');
168
- const { syncAtris: syncCmd } = require('../commands/sync');
169
- const { logAtris: logCmd } = require('../commands/log');
170
- const { logSyncAtris: logSyncCmd } = require('../commands/log-sync');
171
- const { loginAtris: loginCmd, logoutAtris: logoutCmd, whoamiAtris: whoamiCmd } = require('../commands/auth');
172
- const { showVersion: versionCmd } = require('../commands/version');
237
+ async function interactiveEntry(userInput) {
238
+ const workspaceDir = process.cwd();
239
+ const state = detectWorkspaceState(workspaceDir);
240
+ const context = loadContext(workspaceDir);
241
+
242
+ // Case 1: Hot Start (User provided input: "atris fix bug")
243
+ if (userInput) {
244
+ console.log('');
245
+ console.log(`✨ Request: "${userInput}"`);
246
+ console.log(' Initializing Navigator...');
247
+ await planCmd(userInput);
248
+ return;
249
+ }
250
+
251
+ // Case 2: Cold Start (User typed "atris")
252
+ // We present a "Warm Up" interface based on state
253
+
254
+ console.log('');
255
+ console.log('┌─────────────────────────────────────────────────────────────┐');
256
+ console.log('│ ATRIS │');
257
+ console.log('└─────────────────────────────────────────────────────────────┘');
258
+
259
+ const rl = readline.createInterface({
260
+ input: process.stdin,
261
+ output: process.stdout
262
+ });
263
+
264
+ const ask = (q) => new Promise(r => rl.question(q, r));
265
+
266
+ // Logic: Detect State -> Offer "Path of Least Resistance"
267
+
268
+ // State: Fresh Install
269
+ if (state.state === 'fresh') {
270
+ console.log('\n👋 Welcome to ATRIS.');
271
+ console.log(' This folder is not initialized yet.');
272
+ const answer = await ask('\n Initialize project structure? [Y/n] ');
273
+ rl.close();
274
+ if (answer.toLowerCase() === '' || answer.toLowerCase() === 'y') {
275
+ initCmd();
276
+ } else {
277
+ console.log('Cancelled.');
278
+ }
279
+ return;
280
+ }
281
+
282
+ // State: In-Progress Work
283
+ if (context.inProgressFeatures.length > 0) {
284
+ const active = context.inProgressFeatures[0].replace('.md', '');
285
+ console.log(`\n🔥 Active Feature: ${active}`);
286
+ const answer = await ask(` Resume building this? [Y/n] `);
287
+ if (answer.toLowerCase() === '' || answer.toLowerCase() === 'y') {
288
+ console.log('\n🚀 Starting Executor...');
289
+ rl.close();
290
+ await doCmd();
291
+ return;
292
+ }
293
+ }
294
+
295
+ // State: Inbox Items
296
+ if (context.hasInbox && context.inboxItems.length > 0) {
297
+ console.log(`\n📥 Inbox: You have ${context.inboxItems.length} unprocessed items.`);
298
+ const answer = await ask(` Process into a plan? [Y/n] `);
299
+ if (answer.toLowerCase() === '' || answer.toLowerCase() === 'y') {
300
+ console.log('\n🚀 Starting Navigator...');
301
+ rl.close();
302
+ await planCmd();
303
+ return;
304
+ }
305
+ }
306
+
307
+ // State: Idle / Default
308
+ console.log('\n✨ Canvas is clean. What are we building?');
309
+ console.log(' (Type a request, "brainstorm", "status", or press Enter to exit)');
310
+
311
+ const request = await ask('\n> ');
312
+ rl.close();
313
+
314
+ if (!request.trim()) {
315
+ console.log('See you later! 👋');
316
+ return;
317
+ }
318
+
319
+ if (request.toLowerCase() === 'brainstorm') {
320
+ await brainstormCmd();
321
+ } else if (request.toLowerCase() === 'status') {
322
+ await statusCmd();
323
+ } else {
324
+ // Treat as new plan request
325
+ console.log('\n🚀 Starting Navigator...');
326
+ await planCmd(request);
327
+ }
328
+ }
173
329
 
174
330
  if (command === 'init') {
175
331
  initCmd();
@@ -205,23 +361,65 @@ if (command === 'init') {
205
361
  } else if (command === 'whoami') {
206
362
  whoamiCmd();
207
363
  } else if (command === 'visualize') {
208
- visualizeAtris();
364
+ console.log('ℹ️ "atris visualize" is a legacy helper. Visualization is now built into "atris plan".');
365
+ console.log(' Prefer: atris plan');
366
+ console.log('');
367
+ visualizeCmd();
368
+ } else if (command === 'autopilot') {
369
+ const args = process.argv.slice(3);
370
+ if (args.includes('--help') || args.includes('-h')) {
371
+ showAutopilotHelp();
372
+ process.exit(0);
373
+ }
374
+ const initialIdea = args.join(' ').trim() || null;
375
+ autopilotCmd(initialIdea)
376
+ .then(() => process.exit(0))
377
+ .catch((error) => {
378
+ if (error && error.__autopilotAbort) {
379
+ console.log('\nAutopilot cancelled.');
380
+ process.exit(0);
381
+ }
382
+ console.error(`✗ Autopilot failed: ${error.message || error}`);
383
+ process.exit(1);
384
+ });
385
+ } else if (command === 'brainstorm') {
386
+ brainstormCmd()
387
+ .then(() => process.exit(0))
388
+ .catch((error) => {
389
+ console.error(`✗ Brainstorm failed: ${error.message || error}`);
390
+ process.exit(1);
391
+ });
209
392
  } else if (command === 'plan') {
210
- planAtris()
393
+ const args = process.argv.slice(3);
394
+ if (args.includes('--help') || args.includes('-h')) {
395
+ showPlanHelp();
396
+ process.exit(0);
397
+ }
398
+ planCmd()
211
399
  .then(() => process.exit(0))
212
400
  .catch((error) => {
213
401
  console.error(`✗ Plan failed: ${error.message || error}`);
214
402
  process.exit(1);
215
403
  });
216
404
  } else if (command === 'do') {
217
- doAtris()
405
+ const args = process.argv.slice(3);
406
+ if (args.includes('--help') || args.includes('-h')) {
407
+ showDoHelp();
408
+ process.exit(0);
409
+ }
410
+ doCmd()
218
411
  .then(() => process.exit(0))
219
412
  .catch((error) => {
220
413
  console.error(`✗ Do failed: ${error.message || error}`);
221
414
  process.exit(1);
222
415
  });
223
416
  } else if (command === 'review') {
224
- reviewAtris()
417
+ const args = process.argv.slice(3);
418
+ if (args.includes('--help') || args.includes('-h')) {
419
+ showReviewHelp();
420
+ process.exit(0);
421
+ }
422
+ reviewCmd()
225
423
  .then(() => process.exit(0))
226
424
  .catch((error) => {
227
425
  console.error(`✗ Review failed: ${error.message || error}`);
@@ -229,9 +427,9 @@ if (command === 'init') {
229
427
  });
230
428
  } else if (command === 'status') {
231
429
  const isQuick = process.argv.includes('--quick') || process.argv.includes('-q');
232
- statusAtris(isQuick);
430
+ statusCmd(isQuick);
233
431
  } else if (command === 'analytics') {
234
- analyticsAtris();
432
+ analyticsCmd();
235
433
  } else {
236
434
  console.log(`Unknown command: ${command}`);
237
435
  console.log('Run "atris help" to see available commands');
@@ -1815,2214 +2013,155 @@ async function chatInteractive(config, credentials) {
1815
2013
  });
1816
2014
  }
1817
2015
 
1818
- function visualizeAtris() {
1819
- const { logFile, dateFormatted } = getLogPath();
2016
+ async function atrisDevEntry(userInput = null) {
2017
+ // Load workspace context and present planning-ready state
2018
+ // userInput: optional task description for hot start
2019
+ const targetDir = path.join(process.cwd(), 'atris');
1820
2020
 
1821
- // Check if log exists
1822
- if (!fs.existsSync(logFile)) {
1823
- console.log('✗ No journal entry for today. Run "atris log" to create one.');
1824
- process.exit(1);
2021
+ // Check if ATRIS is initialized
2022
+ if (!fs.existsSync(targetDir)) {
2023
+ console.log('');
2024
+ console.log('🚀 Welcome to ATRIS\n');
2025
+ console.log('Not initialized yet. Let\'s get started:\n');
2026
+ console.log(' → atris init Set up your workspace');
2027
+ console.log(' → atris help See all commands\n');
2028
+ return;
1825
2029
  }
1826
2030
 
1827
- // Read the log file
1828
- const logContent = fs.readFileSync(logFile, 'utf8');
1829
-
1830
- // Extract Inbox section
1831
- const inboxMatch = logContent.match(/## Inbox\n([\s\S]*?)(?=\n##|$)/);
1832
- if (!inboxMatch || !inboxMatch[1].trim()) {
1833
- console.log('✗ No items in Inbox. Add ideas to your journal first.');
1834
- process.exit(1);
2031
+ ensureLogDirectory();
2032
+ const { logFile, dateFormatted } = getLogPath();
2033
+ if (!fs.existsSync(logFile)) {
2034
+ createLogFile(logFile, dateFormatted);
1835
2035
  }
1836
2036
 
1837
- const inboxItems = inboxMatch[1]
1838
- .trim()
1839
- .split('\n')
1840
- .filter(line => line.match(/^- \*\*I\d+:/))
1841
- .map(line => {
1842
- const match = line.match(/^- \*\*I\d+:\s+(.+)$|^- \*\*I\d+:\*\*\s*(.+)$/);
1843
- return match ? (match[1] || match[2]) : line;
1844
- });
2037
+ // Load context
2038
+ const workspaceDir = process.cwd();
2039
+ const state = detectWorkspaceState(workspaceDir);
2040
+ const context = loadContext(workspaceDir);
1845
2041
 
1846
- if (inboxItems.length === 0) {
1847
- console.log('✗ No formatted inbox items. Use format: - **I#: Description**');
1848
- process.exit(1);
2042
+ // Detect existing features
2043
+ const featuresDir = path.join(targetDir, 'features');
2044
+ let existingFeatures = [];
2045
+ if (fs.existsSync(featuresDir)) {
2046
+ existingFeatures = fs.readdirSync(featuresDir)
2047
+ .filter(name => {
2048
+ const featurePath = path.join(featuresDir, name);
2049
+ return fs.statSync(featurePath).isDirectory() && !name.startsWith('_');
2050
+ });
1849
2051
  }
1850
2052
 
1851
- // Display visualization template
1852
2053
  console.log('');
1853
2054
  console.log('┌─────────────────────────────────────────────────────────────┐');
1854
- console.log('│ ATRIS Visualize — Break Down & Approval Gate │');
2055
+ console.log('│ ATRIS MODE │');
1855
2056
  console.log('└─────────────────────────────────────────────────────────────┘');
1856
2057
  console.log('');
1857
-
1858
- inboxItems.forEach((item, idx) => {
1859
- console.log(`\n📌 Idea ${idx + 1}: ${item}`);
1860
- console.log('─────────────────────────────────────────');
1861
- console.log('AGENT PROMPT TEMPLATE:\n');
1862
- console.log('1. Break this idea into 3-4 concrete steps.');
1863
- console.log('2. Create ASCII diagram showing flow/structure.');
1864
- console.log('3. Get user approval before creating task.\n');
1865
- console.log('EXAMPLE ASCII (for UI ideas):');
1866
- console.log('```');
1867
- console.log(' Journal Entry');
1868
- console.log(' ↓');
1869
- console.log(' Extract Ideas');
1870
- console.log(' ↓');
1871
- console.log(' Visualize Plan');
1872
- console.log(' ↓');
1873
- console.log(' User Approval');
1874
- console.log(' ↓');
1875
- console.log(' Create Task');
1876
- console.log('```\n');
1877
- });
1878
-
1879
- console.log('─────────────────────────────────────────');
1880
- console.log('✓ Ready to pass to agents with approval gate enabled.');
2058
+ console.log(`📅 ${dateFormatted}`);
1881
2059
  console.log('');
1882
- }
1883
2060
 
1884
- async function brainstormAtris() {
1885
- const targetDir = path.join(process.cwd(), 'atris');
1886
- if (!fs.existsSync(targetDir)) {
1887
- throw new Error('atris/ folder not found. Run "atris init" first.');
2061
+ // Show existing features
2062
+ if (existingFeatures.length > 0) {
2063
+ console.log('📦 Features: ' + existingFeatures.join(', '));
2064
+ console.log('');
1888
2065
  }
1889
2066
 
1890
- ensureLogDirectory();
1891
- const { logFile, dateFormatted } = getLogPath();
1892
- if (!fs.existsSync(logFile)) {
1893
- createLogFile(logFile, dateFormatted);
2067
+ // Show active work
2068
+ if (context.inProgressFeatures.length > 0) {
2069
+ console.log('⚡ Active: ' + context.inProgressFeatures.join(', '));
2070
+ console.log('');
1894
2071
  }
1895
2072
 
1896
- console.log('');
1897
- console.log('┌─────────────────────────────────────────────────────────────┐');
1898
- console.log('│ ATRIS Brainstorm — structured prompt generator │');
1899
- console.log('└─────────────────────────────────────────────────────────────┘');
1900
- console.log('');
1901
- console.log(`Date: ${dateFormatted}`);
1902
- console.log('Type "exit" at any prompt to cancel.');
1903
- console.log('');
1904
-
1905
- // Try to fetch latest journal entry from backend (optional)
1906
- let journalContext = '';
1907
- const config = loadConfig();
1908
- const credentials = loadCredentials();
1909
-
1910
- if (config.agent_id && credentials && credentials.token) {
1911
- try {
1912
- console.log('📖 Fetching latest journal entry from AtrisOS...');
1913
- const journalResult = await apiRequestJson(`/agents/${config.agent_id}/journal/today`, {
1914
- method: 'GET',
1915
- token: credentials.token,
1916
- });
1917
-
1918
- if (journalResult.ok && journalResult.data?.content) {
1919
- journalContext = journalResult.data.content;
1920
- console.log('✓ Loaded journal entry from backend');
1921
- } else {
1922
- // Try fetching latest entry if today doesn't exist
1923
- const listResult = await apiRequestJson(`/agents/${config.agent_id}/journal/?limit=1`, {
1924
- method: 'GET',
1925
- token: credentials.token,
1926
- });
1927
-
1928
- if (listResult.ok && listResult.data?.entries?.length > 0) {
1929
- journalContext = listResult.data.entries[0].content || '';
1930
- console.log('✓ Loaded latest journal entry from backend');
1931
- }
1932
- }
1933
- } catch (error) {
1934
- // Silently fail - we'll use local log file instead
1935
- console.log('ℹ️ Using local journal file (backend unavailable)');
2073
+ // Show inbox
2074
+ if (context.hasInbox && context.inboxItems.length > 0) {
2075
+ console.log(`📥 Inbox (${context.inboxItems.length}):`);
2076
+ context.inboxItems.slice(0, 3).forEach((item, i) => {
2077
+ const preview = item.length > 50 ? item.substring(0, 47) + '...' : item;
2078
+ console.log(` ${i + 1}. ${preview}`);
2079
+ });
2080
+ if (context.inboxItems.length > 3) {
2081
+ console.log(` ... and ${context.inboxItems.length - 3} more`);
1936
2082
  }
1937
2083
  console.log('');
1938
2084
  }
1939
2085
 
1940
- // Fallback to local log file if no backend context
1941
- if (!journalContext) {
1942
- if (fs.existsSync(logFile)) {
1943
- journalContext = fs.readFileSync(logFile, 'utf8');
2086
+ // Show recent completions
2087
+ const logContent = fs.existsSync(logFile) ? fs.readFileSync(logFile, 'utf8') : '';
2088
+ const completedMatch = logContent.match(/## Completed ✅\n([\s\S]*?)(?=\n##|$)/);
2089
+ if (completedMatch && completedMatch[1].trim()) {
2090
+ const completedItems = completedMatch[1].trim().split('\n')
2091
+ .filter(line => line.match(/^- \*\*C\d+:/))
2092
+ .slice(-2);
2093
+ if (completedItems.length > 0) {
2094
+ console.log('✅ Recent:');
2095
+ completedItems.forEach(item => {
2096
+ const match = item.match(/^- \*\*C\d+:\s*(.+)\*\*/);
2097
+ if (match) {
2098
+ const text = match[1].length > 50 ? match[1].substring(0, 47) + '...' : match[1];
2099
+ console.log(` • ${text}`);
2100
+ }
2101
+ });
2102
+ console.log('');
1944
2103
  }
1945
2104
  }
1946
2105
 
1947
- const rl = readline.createInterface({
1948
- input: process.stdin,
1949
- output: process.stdout,
1950
- });
2106
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2107
+ console.log('🤖 atrisDev Protocol — Navigator Agent');
2108
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2109
+ console.log('');
1951
2110
 
1952
- const ask = async (promptText, options = {}) => {
1953
- const { allowEmpty = false } = options;
1954
- while (true) {
1955
- const answer = await new Promise((resolve) => rl.question(promptText, resolve));
1956
- const trimmed = answer.trim();
1957
- if (trimmed.toLowerCase() === 'exit') {
1958
- throw brainstormAbortError();
1959
- }
1960
- if (!allowEmpty && trimmed === '') {
1961
- console.log('Please enter a value (or type "exit" to abort).');
1962
- continue;
1963
- }
1964
- return trimmed;
1965
- }
1966
- };
2111
+ if (userInput) {
2112
+ // Hot start - user provided task
2113
+ console.log('User wants:');
2114
+ console.log(`"${userInput}"`);
2115
+ console.log('');
2116
+ } else {
2117
+ // Cold start - no specific task
2118
+ console.log('Wait for user to describe what they want.');
2119
+ console.log('');
2120
+ }
1967
2121
 
1968
- const askYesNo = async (promptText) => {
1969
- while (true) {
1970
- const response = (await ask(promptText)).toLowerCase();
1971
- if (response === 'y' || response === 'yes') return true;
1972
- if (response === 'n' || response === 'no') return false;
1973
- console.log('Please answer with "y" or "n" (or type "exit" to abort).');
1974
- }
1975
- };
2122
+ console.log('⚠️ APPROVAL REQUIRED Follow this workflow:');
2123
+ console.log('');
2124
+ console.log('STEP 1: Show ASCII visualization');
2125
+ console.log(' Create diagrams showing architecture/flow/UI');
2126
+ console.log(' SHOW diagrams to user and WAIT for approval.');
2127
+ console.log('');
2128
+ console.log('STEP 2: After approval, determine scope');
2129
+ if (existingFeatures.length > 0) {
2130
+ console.log(' Existing: ' + existingFeatures.join(', '));
2131
+ }
2132
+ console.log(' NEW feature → atris/features/[name]/idea.md + build.md');
2133
+ console.log(' EXISTING → Update that feature\'s docs');
2134
+ console.log(' SIMPLE → TODO.md only');
2135
+ console.log('');
2136
+ console.log('STEP 3: Create/update docs');
2137
+ console.log(' idea.md = intent (any format)');
2138
+ console.log(' build.md = technical spec');
2139
+ console.log('');
2140
+ console.log('⛔ DO NOT execute — that\'s for "atris do"');
2141
+ console.log('');
2142
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2143
+ console.log('');
2144
+ }
1976
2145
 
1977
- const collectList = async (label, options = {}) => {
1978
- const { minimum = 0 } = options;
1979
- const items = [];
1980
- while (true) {
1981
- const promptSuffix = items.length === 0 ? '' : ' (blank to finish)';
1982
- const value = await ask(`${label} ${items.length + 1}${promptSuffix}: `, {
1983
- allowEmpty: items.length >= minimum,
1984
- });
1985
- if (!value) {
1986
- if (items.length < minimum) {
1987
- console.log(`Please provide at least ${minimum} ${minimum === 1 ? 'item' : 'items'}.`);
1988
- continue;
1989
- }
1990
- break;
1991
- }
1992
- items.push(value);
1993
- }
1994
- return items;
1995
- };
2146
+ function launchAtris() {
2147
+ const targetDir = path.join(process.cwd(), 'atris');
2148
+ const launcherFile = path.join(targetDir, 'agent_team', 'launcher.md');
1996
2149
 
1997
- let selectedInboxItem = null;
1998
- let topicSummary = '';
2150
+ if (!fs.existsSync(launcherFile)) {
2151
+ console.log('✗ launcher.md not found. Run "atris init" first.');
2152
+ process.exit(1);
2153
+ }
1999
2154
 
2000
- try {
2001
- const initialContent = journalContext || (fs.existsSync(logFile) ? fs.readFileSync(logFile, 'utf8') : '');
2002
- let inboxItems = parseInboxItems(initialContent);
2155
+ // Read launcher.md
2156
+ const launcherSpec = fs.readFileSync(launcherFile, 'utf8');
2003
2157
 
2004
- if (inboxItems.length > 0) {
2005
- console.log('Choose a brainstorm source:');
2006
- console.log(' 1. Select an item from today\'s Inbox');
2007
- console.log(' 2. Enter a new idea');
2008
- console.log('');
2158
+ // Reference TODO.md (agents read on-demand, legacy TASK_CONTEXTS.md supported)
2159
+ const todoFile = path.join(targetDir, 'TODO.md');
2160
+ const legacyTaskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
2009
2161
 
2010
- let choice;
2011
- while (true) {
2012
- choice = await ask('Choice (1-2): ');
2013
- if (choice === '1' || choice === '2') {
2014
- break;
2015
- }
2016
- console.log('Please enter 1 or 2.');
2017
- }
2018
-
2019
- if (choice === '1') {
2020
- console.log('');
2021
- console.log('Today\'s Inbox:');
2022
- inboxItems.forEach((item, index) => {
2023
- console.log(` ${index + 1}. I${item.id} — ${item.text}`);
2024
- });
2025
- console.log('');
2026
-
2027
- while (true) {
2028
- const selection = await ask(`Pick an item (1-${inboxItems.length}): `);
2029
- const index = parseInt(selection, 10);
2030
- if (!Number.isNaN(index) && index >= 1 && index <= inboxItems.length) {
2031
- selectedInboxItem = inboxItems[index - 1];
2032
- break;
2033
- }
2034
- console.log(`Enter a number between 1 and ${inboxItems.length}.`);
2035
- }
2036
-
2037
- const editedSummary = await ask('Brainstorm topic (press Enter to keep original): ', { allowEmpty: true });
2038
- topicSummary = editedSummary ? editedSummary : selectedInboxItem.text;
2039
- } else {
2040
- console.log('');
2041
- topicSummary = await ask('Describe the brainstorm topic: ');
2042
- const newId = addInboxIdea(logFile, topicSummary);
2043
- console.log(`✓ Added I${newId} to today\'s Inbox.`);
2044
- selectedInboxItem = { id: newId, text: topicSummary };
2045
- }
2046
- } else {
2047
- console.log('No items in today\'s Inbox. Capture a new idea to begin.');
2048
- topicSummary = await ask('Describe the brainstorm topic: ');
2049
- const newId = addInboxIdea(logFile, topicSummary);
2050
- console.log(`✓ Added I${newId} to today\'s Inbox.`);
2051
- selectedInboxItem = { id: newId, text: topicSummary };
2052
- }
2053
-
2054
- const sourceLabel = selectedInboxItem ? `I${selectedInboxItem.id}` : 'Ad-hoc';
2055
-
2056
- console.log('');
2057
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2058
- console.log('📖 Step 1: Craft the Story');
2059
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2060
- console.log('What should the output be? How should it feel?');
2061
- console.log('This helps us capture the vision before diving into details.');
2062
- console.log('');
2063
-
2064
- const userStory = await ask('Describe the desired outcome (what should users experience?): ');
2065
- const feelingsVibe = await ask('Feelings/vibes we\'re aiming for? (optional): ', { allowEmpty: true });
2066
-
2067
- console.log('');
2068
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2069
- console.log('🧠 Step 2: Brainstorm Session');
2070
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2071
- console.log('Now let\'s uncover what we need to build.');
2072
- console.log('');
2073
-
2074
- const constraints = await ask('Constraints or guardrails? (optional): ', { allowEmpty: true });
2075
-
2076
- // Build concise, spaced-out prompt (4-5 sentences max, lots of spacing)
2077
- const promptLines = [];
2078
-
2079
- // Extract key snippets from journal if available (very brief)
2080
- let journalHint = '';
2081
- if (journalContext && journalContext.trim()) {
2082
- const maxHint = 200;
2083
- const lines = journalContext.split('\n').slice(0, 5).join(' ').trim();
2084
- if (lines.length > maxHint) {
2085
- journalHint = lines.substring(0, maxHint) + '...';
2086
- } else {
2087
- journalHint = lines;
2088
- }
2089
- }
2090
-
2091
- promptLines.push('You:');
2092
- promptLines.push('');
2093
- promptLines.push(`I want to brainstorm: ${topicSummary}`);
2094
- promptLines.push('');
2095
-
2096
- if (userStory) {
2097
- promptLines.push(`The outcome should be: ${userStory}`);
2098
- promptLines.push('');
2099
- }
2100
-
2101
- if (feelingsVibe) {
2102
- promptLines.push(`Vibe we\'re going for: ${feelingsVibe}`);
2103
- promptLines.push('');
2104
- }
2105
-
2106
- if (journalHint) {
2107
- promptLines.push(`Recent context: ${journalHint}`);
2108
- promptLines.push('');
2109
- }
2110
-
2111
- if (constraints) {
2112
- promptLines.push(`Constraints: ${constraints}`);
2113
- promptLines.push('');
2114
- }
2115
-
2116
- promptLines.push('Help me uncover what we need to build. Keep responses short (4-5 sentences), pause for alignment, sketch ASCII when structure helps.');
2117
- promptLines.push('');
2118
- promptLines.push('Claude:');
2119
-
2120
- const promptText = promptLines.join('\n');
2121
-
2122
- console.log('');
2123
- console.log('Copy this prompt into Claude Code (or your agent of choice):');
2124
- console.log('');
2125
- console.log('```');
2126
- console.log(promptText);
2127
- console.log('```');
2128
- console.log('');
2129
-
2130
- const logChoice = await askYesNo('Log this brainstorm session to today\'s journal? (y/n): ');
2131
- if (logChoice) {
2132
- const sessionSummary = await ask('Session summary (1-2 sentences): ');
2133
- const nextStepsRaw = await ask('Next steps (optional, separate with ";"): ', { allowEmpty: true });
2134
- const nextSteps = nextStepsRaw
2135
- ? nextStepsRaw.split(';').map((item) => item.trim()).filter(Boolean)
2136
- : [];
2137
- recordBrainstormSession(
2138
- logFile,
2139
- sourceLabel,
2140
- topicSummary,
2141
- userStory,
2142
- [],
2143
- [],
2144
- constraints,
2145
- '',
2146
- feelingsVibe || '',
2147
- nextSteps,
2148
- sessionSummary
2149
- );
2150
- if (selectedInboxItem) {
2151
- const archive = await askYesNo('Archive this Inbox idea now? (y/n): ');
2152
- if (archive) {
2153
- let latestContent = fs.readFileSync(logFile, 'utf8');
2154
- latestContent = removeInboxItemFromContent(latestContent, selectedInboxItem.id);
2155
- fs.writeFileSync(logFile, latestContent);
2156
- console.log(`✓ Archived I${selectedInboxItem.id} from Inbox.`);
2157
- }
2158
- }
2159
- console.log('✓ Brainstorm session logged.');
2160
- } else {
2161
- console.log('Skipped journaling. Prompt is ready for your agent.');
2162
- }
2163
-
2164
- console.log('\nBrainstorm complete.');
2165
- } finally {
2166
- rl.close();
2167
- }
2168
- }
2169
-
2170
- function brainstormAbortError() {
2171
- const error = new Error('Brainstorm cancelled by user.');
2172
- error.__brainstormAbort = true;
2173
- return error;
2174
- }
2175
-
2176
- function generateWorkflowFile(workflowFile, metadata) {
2177
- const targetDir = path.join(process.cwd(), 'atris');
2178
-
2179
- // Load all context needed for agents
2180
- const navigatorFile = path.join(targetDir, 'agent_team', 'navigator.md');
2181
- const executorFile = path.join(targetDir, 'agent_team', 'executor.md');
2182
- const validatorFile = path.join(targetDir, 'agent_team', 'validator.md');
2183
- const launcherFile = path.join(targetDir, 'agent_team', 'launcher.md');
2184
- const personaFile = path.join(targetDir, 'PERSONA.md');
2185
- const mapFile = path.join(targetDir, 'MAP.md');
2186
- const taskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
2187
- const { logFile } = getLogPath();
2188
-
2189
- const workflow = {
2190
- version: '1.0',
2191
- createdAt: new Date().toISOString(),
2192
- metadata: {
2193
- feature: metadata.feature,
2194
- userStory: metadata.userStory,
2195
- constraints: metadata.constraints || '',
2196
- successCriteria: metadata.successCriteria || [],
2197
- riskNotes: metadata.riskNotes || '',
2198
- journalPath: metadata.logFile
2199
- },
2200
- states: {
2201
- NAVIGATOR: {
2202
- agentSpec: fs.existsSync(navigatorFile) ? fs.readFileSync(navigatorFile, 'utf8') : '',
2203
- context: {
2204
- inboxPath: metadata.logFile,
2205
- taskContextsPath: 'atris/TASK_CONTEXTS.md',
2206
- mapPath: 'atris/MAP.md'
2207
- },
2208
- instructions: 'Take ideas from Inbox → break them down into perfect, manageable tasks. Create visualizations (ASCII diagrams) for logic flows, DB tables, architecture, UI/UX. Write tasks to TASK_CONTEXTS.md.'
2209
- },
2210
- EXECUTOR: {
2211
- agentSpec: fs.existsSync(executorFile) ? fs.readFileSync(executorFile, 'utf8') : '',
2212
- context: {
2213
- personaPath: 'atris/PERSONA.md',
2214
- mapPath: 'atris/MAP.md',
2215
- taskContextsPath: 'atris/TASK_CONTEXTS.md'
2216
- },
2217
- instructions: 'Get it done, precisely, following instructions perfectly. Show ASCII visualization for complex changes. Execute tasks following executor spec. Move completed tasks to <completed> section.'
2218
- },
2219
- VALIDATOR: {
2220
- agentSpec: fs.existsSync(validatorFile) ? fs.readFileSync(validatorFile, 'utf8') : '',
2221
- context: {
2222
- taskContextsPath: 'atris/TASK_CONTEXTS.md',
2223
- mapPath: 'atris/MAP.md',
2224
- journalPath: metadata.logFile
2225
- },
2226
- instructions: 'Auto-activated after "atris do" completes. Ultrathink, check requirements → build → edge cases → errors → integration. Run tests. Repeat until: "✅ All good. Ready for human testing."'
2227
- },
2228
- LAUNCHER: {
2229
- agentSpec: fs.existsSync(launcherFile) ? fs.readFileSync(launcherFile, 'utf8') : '',
2230
- context: {
2231
- taskContextsPath: 'atris/TASK_CONTEXTS.md',
2232
- mapPath: 'atris/MAP.md',
2233
- journalPath: metadata.logFile
2234
- },
2235
- instructions: 'Ship it clean. Document what was shipped, extract learnings, update MAP.md and docs, clean up, Git commit + push, celebrate!'
2236
- }
2237
- },
2238
- currentState: null,
2239
- currentIteration: 0,
2240
- history: []
2241
- };
2242
-
2243
- fs.writeFileSync(workflowFile, JSON.stringify(workflow, null, 2));
2244
- }
2245
-
2246
- function updateWorkflowState(workflowFile, stateName, iteration) {
2247
- if (!fs.existsSync(workflowFile)) return;
2248
-
2249
- const workflow = JSON.parse(fs.readFileSync(workflowFile, 'utf8'));
2250
- workflow.currentState = stateName;
2251
- workflow.currentIteration = iteration;
2252
- workflow.history.push({
2253
- state: stateName,
2254
- iteration: iteration,
2255
- timestamp: new Date().toISOString()
2256
- });
2257
-
2258
- // Update context with latest file contents
2259
- const targetDir = path.join(process.cwd(), 'atris');
2260
- const taskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
2261
- const mapFile = path.join(targetDir, 'MAP.md');
2262
- const { logFile } = getLogPath();
2263
-
2264
- // Refresh task contexts if exists
2265
- if (fs.existsSync(taskContextsFile)) {
2266
- workflow.states[stateName].context.taskContexts = fs.readFileSync(taskContextsFile, 'utf8').substring(0, 5000); // Limit size
2267
- }
2268
-
2269
- // Reference map path (agents read on-demand)
2270
- if (fs.existsSync(mapFile)) {
2271
- workflow.states[stateName].context.mapPath = path.relative(process.cwd(), mapFile);
2272
- }
2273
-
2274
- // Refresh journal inbox if exists
2275
- if (fs.existsSync(logFile)) {
2276
- const logContent = fs.readFileSync(logFile, 'utf8');
2277
- const inboxMatch = logContent.match(/## Inbox\n([\s\S]*?)(?=\n##|$)/);
2278
- if (inboxMatch) {
2279
- workflow.states[stateName].context.inbox = inboxMatch[1].trim().substring(0, 2000); // Limit size
2280
- }
2281
- }
2282
-
2283
- fs.writeFileSync(workflowFile, JSON.stringify(workflow, null, 2));
2284
- }
2285
-
2286
- async function autopilotAtris(initialIdea = null) {
2287
- const targetDir = path.join(process.cwd(), 'atris');
2288
- if (!fs.existsSync(targetDir)) {
2289
- throw new Error('atris/ folder not found. Run "atris init" first.');
2290
- }
2291
-
2292
- const navigatorFile = path.join(targetDir, 'agent_team', 'navigator.md');
2293
- const executorFile = path.join(targetDir, 'agent_team', 'executor.md');
2294
- const validatorFile = path.join(targetDir, 'agent_team', 'validator.md');
2295
- const launcherFile = path.join(targetDir, 'agent_team', 'launcher.md');
2296
-
2297
- const missingSpecs = [];
2298
- if (!fs.existsSync(navigatorFile)) missingSpecs.push('navigator.md');
2299
- if (!fs.existsSync(executorFile)) missingSpecs.push('executor.md');
2300
- if (!fs.existsSync(validatorFile)) missingSpecs.push('validator.md');
2301
- if (!fs.existsSync(launcherFile)) missingSpecs.push('launcher.md');
2302
-
2303
- if (missingSpecs.length > 0) {
2304
- throw new Error(`Missing agent spec(s): ${missingSpecs.join(', ')}. Run "atris init" to restore them.`);
2305
- }
2306
-
2307
- ensureLogDirectory();
2308
- const { logFile, dateFormatted } = getLogPath();
2309
- if (!fs.existsSync(logFile)) {
2310
- createLogFile(logFile, dateFormatted);
2311
- }
2312
-
2313
- console.log('');
2314
- console.log('┌─────────────────────────────────────────────────────────────┐');
2315
- console.log('│ ATRIS Autopilot — Full Cycle Automation │');
2316
- console.log('│ brainstorm → plan → do → review → launch │');
2317
- console.log('└─────────────────────────────────────────────────────────────┘');
2318
- console.log('');
2319
- console.log(`Date: ${dateFormatted}`);
2320
- console.log('Type "exit" at any prompt to cancel.');
2321
- console.log('');
2322
-
2323
- // Detect if running in chat/non-interactive mode
2324
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
2325
- const isAutoMode = !isInteractive || !!initialIdea; // Auto-approve if non-interactive or idea provided
2326
-
2327
- if (isAutoMode && !initialIdea) {
2328
- console.log('💬 AUTO MODE: Running fully automated workflow.\n');
2329
- } else if (!isInteractive) {
2330
- console.log('💬 CHAT MODE: Autopilot will present prompts here for interactive conversation.');
2331
- console.log(' Respond to prompts in chat to continue the workflow.\n');
2332
- }
2333
-
2334
- const rl = isInteractive ? readline.createInterface({
2335
- input: process.stdin,
2336
- output: process.stdout,
2337
- }) : null;
2338
-
2339
- const ask = async (promptText, options = {}) => {
2340
- const { allowEmpty = false, defaultValue = null } = options;
2341
-
2342
- // In non-interactive mode, never try to use readline - just output and return default
2343
- if (!isInteractive) {
2344
- console.log(`\n📝 PROMPT FOR CHAT: ${promptText}`);
2345
- if (defaultValue !== null) {
2346
- console.log(` ✓ Using default: ${defaultValue}`);
2347
- return defaultValue;
2348
- }
2349
- if (allowEmpty) {
2350
- console.log(' (Empty allowed - continuing)');
2351
- return '';
2352
- }
2353
- // No default and not allowEmpty - use a safe default
2354
- const safeDefault = 'Continue workflow';
2355
- console.log(` ✓ Using safe default: ${safeDefault}`);
2356
- return safeDefault;
2357
- }
2358
-
2359
- // Interactive mode - auto-approve if in auto mode
2360
- if (isAutoMode && defaultValue !== null) {
2361
- console.log(`\n✓ ${promptText}${defaultValue ? ` → ${defaultValue}` : ' (auto-approved)'}`);
2362
- return defaultValue;
2363
- }
2364
-
2365
- // Only reach here if interactive mode AND rl exists
2366
- if (!rl || rl.closed) {
2367
- // Fallback: if readline closed, use safe default
2368
- if (allowEmpty) return '';
2369
- const safeDefault = 'Continue workflow';
2370
- console.log(` ⚠️ Readline unavailable, using safe default: ${safeDefault}`);
2371
- return safeDefault;
2372
- }
2373
-
2374
- while (true) {
2375
- try {
2376
- const answer = await new Promise((resolve, reject) => {
2377
- if (rl.closed) {
2378
- reject(new Error('readline was closed'));
2379
- return;
2380
- }
2381
- rl.question(promptText, resolve);
2382
- });
2383
- const trimmed = answer.trim();
2384
- if (trimmed.toLowerCase() === 'exit') {
2385
- throw autopilotAbortError();
2386
- }
2387
- if (!allowEmpty && trimmed === '') {
2388
- console.log('Please enter a value (or type "exit" to abort).');
2389
- continue;
2390
- }
2391
- return trimmed;
2392
- } catch (error) {
2393
- if (error.message === 'readline was closed' || rl.closed) {
2394
- // Readline closed mid-prompt - use safe default
2395
- if (allowEmpty) return '';
2396
- const safeDefault = 'Continue workflow';
2397
- console.log(` ⚠️ Readline closed, using safe default: ${safeDefault}`);
2398
- return safeDefault;
2399
- }
2400
- throw error;
2401
- }
2402
- }
2403
- };
2404
-
2405
- const askYesNo = async (promptText, defaultYes = true) => {
2406
- // In non-interactive mode, never try readline - just return default
2407
- if (!isInteractive) {
2408
- console.log(`\n📝 PROMPT FOR CHAT: ${promptText}`);
2409
- console.log(` ✓ Auto-approving: ${defaultYes ? 'yes' : 'no'}`);
2410
- return defaultYes;
2411
- }
2412
-
2413
- // Auto mode in interactive terminal - auto-approve
2414
- if (isAutoMode) {
2415
- console.log(`\n✓ ${promptText} → ${defaultYes ? 'yes (auto-approved)' : 'no (auto-approved)'}`);
2416
- return defaultYes;
2417
- }
2418
-
2419
- while (true) {
2420
- const response = (await ask(promptText)).toLowerCase();
2421
- if (response === 'y' || response === 'yes') return true;
2422
- if (response === 'n' || response === 'no') return false;
2423
- console.log('Please answer with "y" or "n" (or type "exit" to abort).');
2424
- }
2425
- };
2426
-
2427
- // ========================================
2428
- // STEP 1: Brainstorm with user
2429
- // ========================================
2430
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2431
- console.log('🧠 STEP 1: Brainstorm — Define the vision');
2432
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2433
- console.log('');
2434
-
2435
- let selectedInboxItem = null;
2436
- let topicSummary = '';
2437
- let userStory = '';
2438
- let feelingsVibe = '';
2439
- let constraints = '';
2440
-
2441
- // Try to fetch latest journal entry from backend
2442
- let journalContext = '';
2443
- const config = loadConfig();
2444
- const credentials = loadCredentials();
2445
-
2446
- if (config.agent_id && credentials && credentials.token) {
2447
- try {
2448
- const journalResult = await apiRequestJson(`/agents/${config.agent_id}/journal/today`, {
2449
- method: 'GET',
2450
- token: credentials.token,
2451
- });
2452
-
2453
- if (journalResult.ok && journalResult.data?.content) {
2454
- journalContext = journalResult.data.content;
2455
- } else {
2456
- const listResult = await apiRequestJson(`/agents/${config.agent_id}/journal/?limit=1`, {
2457
- method: 'GET',
2458
- token: credentials.token,
2459
- });
2460
-
2461
- if (listResult.ok && listResult.data?.entries?.length > 0) {
2462
- journalContext = listResult.data.entries[0].content || '';
2463
- }
2464
- }
2465
- } catch (error) {
2466
- // Fallback to local
2467
- }
2468
- }
2469
-
2470
- // Fallback to local log file
2471
- if (!journalContext && fs.existsSync(logFile)) {
2472
- journalContext = fs.readFileSync(logFile, 'utf8');
2473
- }
2474
-
2475
- try {
2476
- // If initial idea provided, use it directly (skip all prompts)
2477
- if (initialIdea) {
2478
- console.log(`🚀 Initial idea: "${initialIdea}"\n`);
2479
- topicSummary = initialIdea;
2480
- const newId = addInboxIdea(logFile, topicSummary);
2481
- console.log(`✓ Added I${newId} to today's Inbox.\n`);
2482
- selectedInboxItem = { id: newId, text: topicSummary };
2483
- } else {
2484
- // Normal flow: check inbox and prompt
2485
- const initialContent = journalContext || (fs.existsSync(logFile) ? fs.readFileSync(logFile, 'utf8') : '');
2486
- let inboxItems = parseInboxItems(initialContent);
2487
-
2488
- if (inboxItems.length > 0) {
2489
- // In non-interactive mode, auto-select first inbox item
2490
- if (!isInteractive) {
2491
- selectedInboxItem = inboxItems[0];
2492
- topicSummary = selectedInboxItem.text;
2493
- console.log(`✓ Auto-selected inbox item I${selectedInboxItem.id}: ${topicSummary}\n`);
2494
- } else {
2495
- console.log('Choose a brainstorm source:');
2496
- console.log(' 1. Select an item from today\'s Inbox');
2497
- console.log(' 2. Enter a new idea');
2498
- console.log('');
2499
-
2500
- let choice;
2501
- while (true) {
2502
- choice = await ask('Choice (1-2): ');
2503
- if (choice === '1' || choice === '2') {
2504
- break;
2505
- }
2506
- console.log('Please enter 1 or 2.');
2507
- }
2508
-
2509
- if (choice === '1') {
2510
- console.log('');
2511
- console.log('Today\'s Inbox:');
2512
- inboxItems.forEach((item, index) => {
2513
- console.log(` ${index + 1}. I${item.id} — ${item.text}`);
2514
- });
2515
- console.log('');
2516
-
2517
- while (true) {
2518
- const selection = await ask(`Pick an item (1-${inboxItems.length}): `);
2519
- const index = parseInt(selection, 10);
2520
- if (!Number.isNaN(index) && index >= 1 && index <= inboxItems.length) {
2521
- selectedInboxItem = inboxItems[index - 1];
2522
- break;
2523
- }
2524
- console.log(`Enter a number between 1 and ${inboxItems.length}.`);
2525
- }
2526
-
2527
- const editedSummary = await ask('Brainstorm topic (press Enter to keep original): ', { allowEmpty: true });
2528
- topicSummary = editedSummary ? editedSummary : selectedInboxItem.text;
2529
- } else {
2530
- if (!isInteractive) {
2531
- // Non-interactive: use default
2532
- topicSummary = 'New feature idea';
2533
- console.log(`✓ Using default idea: ${topicSummary}\n`);
2534
- } else {
2535
- console.log('');
2536
- topicSummary = await ask('Describe the brainstorm topic: ');
2537
- }
2538
- const newId = addInboxIdea(logFile, topicSummary);
2539
- console.log(`✓ Added I${newId} to today\'s Inbox.`);
2540
- selectedInboxItem = { id: newId, text: topicSummary };
2541
- }
2542
- }
2543
- } else {
2544
- // No inbox items AND no initial idea → trigger brainstorm mode
2545
- console.log('');
2546
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2547
- console.log('💡 No idea provided. Starting brainstorm mode...');
2548
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2549
- console.log('');
2550
- console.log('🧠 Let\'s shape an idea together. Answer a few questions to get started:');
2551
- console.log('');
2552
-
2553
- // Interactive brainstorm session (in chat/non-interactive, use defaults)
2554
- if (!isInteractive || isAutoMode) {
2555
- // Non-interactive or auto mode: use defaults
2556
- topicSummary = 'New feature idea';
2557
- userStory = 'Improved user experience';
2558
- feelingsVibe = '';
2559
- constraints = '';
2560
- console.log('✓ Using default brainstorm values');
2561
- console.log(` Idea: ${topicSummary}`);
2562
- console.log(` Outcome: ${userStory}\n`);
2563
- } else {
2564
- topicSummary = await ask('What problem or feature are you thinking about? (describe it briefly): ');
2565
- if (!topicSummary) {
2566
- throw new Error('Brainstorm cancelled. Provide an idea to continue.');
2567
- }
2568
-
2569
- userStory = await ask('What should users experience when this is done? (the outcome): ', {
2570
- allowEmpty: true
2571
- });
2572
-
2573
- feelingsVibe = await ask('What vibe/feelings are we aiming for? (optional): ', {
2574
- allowEmpty: true
2575
- });
2576
-
2577
- constraints = await ask('Any constraints or guardrails? (optional): ', {
2578
- allowEmpty: true
2579
- });
2580
- }
2581
-
2582
- const newId = addInboxIdea(logFile, topicSummary);
2583
- console.log(`✓ Added I${newId} to today's Inbox.`);
2584
- selectedInboxItem = { id: newId, text: topicSummary };
2585
-
2586
- console.log('');
2587
- console.log('✓ Idea shaped! Continuing with autopilot...');
2588
- console.log('');
2589
- }
2590
- }
2591
-
2592
- const sourceLabel = selectedInboxItem ? `I${selectedInboxItem.id}` : 'Ad-hoc';
2593
-
2594
- console.log('');
2595
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2596
- console.log('📖 Craft the Story — What should the output be?');
2597
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2598
- console.log('');
2599
-
2600
- // In auto mode or if already set from brainstorm above, skip story prompts
2601
- if (isAutoMode && !userStory) {
2602
- // Extract story from initial idea or use default
2603
- userStory = initialIdea || 'Build the feature completely and correctly';
2604
- feelingsVibe = feelingsVibe || '';
2605
- constraints = constraints || '';
2606
- console.log(`✓ Outcome: ${userStory}`);
2607
- console.log('');
2608
- } else if (!userStory) {
2609
- // Only prompt if we don't already have these from brainstorm mode
2610
- if (!isInteractive) {
2611
- // Non-interactive: use defaults
2612
- userStory = 'Build the feature completely and correctly';
2613
- feelingsVibe = '';
2614
- constraints = '';
2615
- console.log(`✓ Outcome: ${userStory} (using defaults for non-interactive mode)`);
2616
- console.log('');
2617
- } else {
2618
- userStory = await ask('Describe the desired outcome (what should users experience?): ');
2619
- feelingsVibe = await ask('Feelings/vibes we\'re aiming for? (optional): ', { allowEmpty: true });
2620
- constraints = await ask('Constraints or guardrails? (optional): ', { allowEmpty: true });
2621
- }
2622
- }
2623
-
2624
- // Generate and show brainstorm prompt (shorter in auto mode)
2625
- if (!isAutoMode) {
2626
- const promptLines = [];
2627
- promptLines.push('You:');
2628
- promptLines.push('');
2629
- promptLines.push(`I want to brainstorm: ${topicSummary}`);
2630
- promptLines.push('');
2631
-
2632
- if (userStory) {
2633
- promptLines.push(`The outcome should be: ${userStory}`);
2634
- promptLines.push('');
2635
- }
2636
-
2637
- if (feelingsVibe) {
2638
- promptLines.push(`Vibe we\'re going for: ${feelingsVibe}`);
2639
- promptLines.push('');
2640
- }
2641
-
2642
- if (constraints) {
2643
- promptLines.push(`Constraints: ${constraints}`);
2644
- promptLines.push('');
2645
- }
2646
-
2647
- promptLines.push('Help me uncover what we need to build. Keep responses short (4-5 sentences), pause for alignment, sketch ASCII when structure helps.');
2648
- promptLines.push('');
2649
- promptLines.push('Claude:');
2650
-
2651
- const promptText = promptLines.join('\n');
2652
-
2653
- console.log('');
2654
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2655
- console.log('📋 PROMPT FOR YOUR CODING EDITOR:');
2656
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2657
- console.log('');
2658
- console.log('```');
2659
- console.log(promptText);
2660
- console.log('```');
2661
- console.log('');
2662
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2663
- console.log('');
2664
-
2665
- // Get approval to proceed with autopilot
2666
- const proceed = await askYesNo('✓ Brainstorm complete. Ready to start autopilot (plan → do → review → launch)? (y/n): ');
2667
- if (!proceed) {
2668
- console.log('\nAutopilot cancelled. Brainstorm prompt is ready for your agent.');
2669
- return;
2670
- }
2671
- } else {
2672
- // Auto mode: just show brief summary
2673
- console.log('');
2674
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2675
- console.log('✓ Vision defined — proceeding automatically');
2676
- console.log(` Feature: ${topicSummary}`);
2677
- console.log(` Goal: ${userStory}`);
2678
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2679
- console.log('');
2680
- }
2681
-
2682
- // Log brainstorm session
2683
- const sessionSummary = isAutoMode || !isInteractive ? 'Auto brainstorm' : await ask('Brainstorm session summary (1-2 sentences, optional): ', { allowEmpty: true, defaultValue: 'Autopilot brainstorm session' });
2684
- recordBrainstormSession(
2685
- logFile,
2686
- sourceLabel,
2687
- topicSummary,
2688
- userStory,
2689
- [],
2690
- [],
2691
- constraints,
2692
- '',
2693
- feelingsVibe || '',
2694
- [],
2695
- sessionSummary || 'Autopilot brainstorm session'
2696
- );
2697
-
2698
- // Define success criteria
2699
- console.log('');
2700
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2701
- console.log('🎯 Define Success Criteria');
2702
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2703
- console.log('');
2704
-
2705
- let successCriteria = [];
2706
- let riskNotes = '';
2707
-
2708
- if (isAutoMode) {
2709
- // Auto mode: generate basic success criteria
2710
- successCriteria = [
2711
- 'Feature implemented and working',
2712
- 'Tests pass (if applicable)',
2713
- 'Code follows project standards',
2714
- 'Documentation updated (MAP.md, journal)'
2715
- ];
2716
- console.log('✓ Auto-generated success criteria:');
2717
- successCriteria.forEach((item, index) => {
2718
- console.log(` ${index + 1}. ${item}`);
2719
- });
2720
- console.log('');
2721
- } else {
2722
- while (true) {
2723
- const criteria = await ask(`Success criteria ${successCriteria.length + 1}: `, {
2724
- allowEmpty: successCriteria.length > 0,
2725
- });
2726
- if (!criteria) {
2727
- if (successCriteria.length === 0) {
2728
- console.log('Please provide at least one success criteria.');
2729
- continue;
2730
- }
2731
- break;
2732
- }
2733
- successCriteria.push(criteria);
2734
- }
2735
-
2736
- riskNotes = await ask('Any risks or notes? (optional): ', { allowEmpty: true });
2737
- }
2738
-
2739
- recordAutopilotVision(
2740
- logFile,
2741
- sourceLabel,
2742
- topicSummary,
2743
- successCriteria,
2744
- riskNotes ? riskNotes : ''
2745
- );
2746
-
2747
- console.log('');
2748
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2749
- console.log('✓ Vision locked in');
2750
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2751
- console.log(`• Source: ${sourceLabel}`);
2752
- console.log(`• Summary: ${topicSummary}`);
2753
- console.log('• Success Criteria:');
2754
- successCriteria.forEach((item, index) => {
2755
- console.log(` ${index + 1}. ${item}`);
2756
- });
2757
- if (riskNotes) {
2758
- console.log(`• Notes: ${riskNotes}`);
2759
- }
2760
- console.log('');
2761
- if (isAutoMode) {
2762
- console.log('🚀 AUTO MODE: Running fully automated cycle (plan → do → review → launch)');
2763
- } else {
2764
- console.log('🚀 Starting automated cycle: plan → do → review → launch');
2765
- console.log(' (No manual pauses - fully automated after this approval)');
2766
- }
2767
- console.log('');
2768
-
2769
- // ========================================
2770
- // Generate workflow file for coding agents
2771
- // ========================================
2772
- const workflowFile = path.join(targetDir, '.atris-workflow.json');
2773
- generateWorkflowFile(workflowFile, {
2774
- feature: topicSummary,
2775
- userStory,
2776
- constraints,
2777
- successCriteria,
2778
- riskNotes,
2779
- logFile: path.relative(process.cwd(), logFile)
2780
- });
2781
-
2782
- console.log('');
2783
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2784
- console.log('📄 WORKFLOW FILE GENERATED: .atris-workflow.json');
2785
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2786
- console.log(' Coding agents can read this file to enter workflow states');
2787
- console.log(' Each state includes all context needed for execution');
2788
- console.log('');
2789
-
2790
- // ========================================
2791
- // STEP 2-4: Automated plan → do → review loop
2792
- // ========================================
2793
- let iteration = 1;
2794
- while (true) {
2795
- console.log(`\n${'═'.repeat(70)}`);
2796
- console.log(`AUTOPILOT ITERATION ${iteration}`);
2797
- console.log(`${'═'.repeat(70)}\n`);
2798
-
2799
- // Plan
2800
- console.log('[STATE:NAVIGATOR]');
2801
- console.log('[1/4] 📋 Plan — Navigator creating tasks...');
2802
- planAtris();
2803
- updateWorkflowState(workflowFile, 'NAVIGATOR', iteration);
2804
- console.log(' ✓ Planning prompt displayed to agent');
2805
- console.log(' ✓ Workflow state updated: [STATE:NAVIGATOR]\n');
2806
-
2807
- // Do
2808
- console.log('[STATE:EXECUTOR]');
2809
- console.log('[2/4] 🔨 Do — Executor building...');
2810
- doAtris();
2811
- updateWorkflowState(workflowFile, 'EXECUTOR', iteration);
2812
- console.log(' ✓ Execution prompt displayed to agent');
2813
- console.log(' ✓ Workflow state updated: [STATE:EXECUTOR]\n');
2814
-
2815
- // Review
2816
- console.log('[STATE:VALIDATOR]');
2817
- console.log('[3/4] ✅ Review — Validator checking...');
2818
- reviewAtris();
2819
- updateWorkflowState(workflowFile, 'VALIDATOR', iteration);
2820
- console.log(' ✓ Validation prompt displayed to agent');
2821
- console.log(' ✓ Workflow state updated: [STATE:VALIDATOR]\n');
2822
-
2823
- // Check if success (auto-approve in auto mode, validator will verify)
2824
- console.log(`${'─'.repeat(70)}`);
2825
- let isSuccess;
2826
- if (isAutoMode) {
2827
- // In auto mode, assume success if we got through review without errors
2828
- // Validator should have caught issues already
2829
- isSuccess = true;
2830
- console.log('✓ Auto mode: Assuming success (validator verified)');
2831
- } else {
2832
- isSuccess = await askYesNo(`Did we meet the success criteria? (y/n): `);
2833
- }
2834
- console.log('');
2835
-
2836
- if (isSuccess) {
2837
- const successNotes = isAutoMode ? 'Completed via autopilot' : await ask('Notes for the log (optional): ', { allowEmpty: true, defaultValue: 'Completed via autopilot' });
2838
- recordAutopilotIteration(
2839
- logFile,
2840
- iteration,
2841
- 'Success',
2842
- successNotes ? successNotes : ''
2843
- );
2844
-
2845
- console.log('');
2846
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2847
- console.log('✓ Success criteria met!');
2848
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2849
- console.log('');
2850
-
2851
- // ========================================
2852
- // STEP 5: Launch
2853
- // ========================================
2854
- console.log('[STATE:LAUNCHER]');
2855
- console.log('[5/5] 🚀 Launch — Launcher shipping...');
2856
- launchAtris();
2857
- updateWorkflowState(workflowFile, 'LAUNCHER', iteration);
2858
- console.log(' ✓ Launch prompt displayed to agent');
2859
- console.log(' ✓ Workflow state updated: [STATE:LAUNCHER]\n');
2860
-
2861
- recordAutopilotSuccess(
2862
- logFile,
2863
- selectedInboxItem ? selectedInboxItem.id : null,
2864
- topicSummary
2865
- );
2866
-
2867
- console.log('');
2868
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2869
- console.log('🎉 AUTOPILOT COMPLETE');
2870
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
2871
- console.log('');
2872
- console.log('✓ Feature completed and shipped');
2873
- console.log('✓ All prompts displayed for agent workflow');
2874
- console.log('✓ Success recorded in journal');
2875
- console.log('');
2876
- break;
2877
- } else {
2878
- const followUp = isAutoMode ? 'Issues detected, needs iteration' : await ask('Describe remaining blockers / next steps (optional): ', {
2879
- allowEmpty: true,
2880
- defaultValue: 'Issues detected, needs iteration'
2881
- });
2882
- recordAutopilotIteration(
2883
- logFile,
2884
- iteration,
2885
- 'Follow-up required',
2886
- followUp || ''
2887
- );
2888
- const continueLoop = await askYesNo('Continue with another iteration? (y/n): ', isAutoMode);
2889
- if (!continueLoop) {
2890
- console.log('\nAutopilot paused. Success criteria not yet met.');
2891
- break;
2892
- }
2893
- iteration += 1;
2894
- }
2895
- }
2896
- } finally {
2897
- if (rl && isInteractive) {
2898
- rl.close();
2899
- }
2900
- }
2901
- }
2902
-
2903
- function autopilotAbortError() {
2904
- const error = new Error('Autopilot cancelled by user.');
2905
- error.__autopilotAbort = true;
2906
- return error;
2907
- }
2908
-
2909
- function addInboxIdea(logFile, summary) {
2910
- const content = fs.readFileSync(logFile, 'utf8');
2911
- const nextId = getNextInboxId(content);
2912
- const updated = addInboxItemToContent(content, nextId, summary);
2913
- fs.writeFileSync(logFile, updated);
2914
- return nextId;
2915
- }
2916
-
2917
- function parseInboxItems(content) {
2918
- const match = content.match(/## Inbox\n([\s\S]*?)(?=\n##|\n---|$)/);
2919
- if (!match) {
2920
- return [];
2921
- }
2922
- const body = match[1];
2923
- const lines = body.split('\n');
2924
- const items = [];
2925
- lines.forEach((line) => {
2926
- const trimmed = line.trim();
2927
- if (!trimmed) return;
2928
- if (trimmed.startsWith('(Empty')) return;
2929
- const parsed = trimmed.match(/^- \*\*I(\d+):\*\*\s*(.+)$|^- \*\*I(\d+):\s+(.+)$/);
2930
- if (parsed) {
2931
- const id = parseInt(parsed[1] || parsed[3], 10);
2932
- const text = parsed[2] || parsed[4];
2933
- items.push({ id, text, line: trimmed });
2934
- }
2935
- });
2936
- return items;
2937
- }
2938
-
2939
- function replaceInboxSection(content, items) {
2940
- const regex = /(## Inbox\n)([\s\S]*?)(\n---|\n##|$)/;
2941
- if (!regex.test(content)) {
2942
- const lines = items.length ? items.map((item) => item.line).join('\n') : '(Empty - inbox zero achieved)';
2943
- return `${content}\n\n## Inbox\n\n${lines}\n`;
2944
- }
2945
-
2946
- return content.replace(regex, (match, header, body, suffix) => {
2947
- const inner = items.length
2948
- ? `\n${items.map((item) => item.line).join('\n')}\n`
2949
- : '\n(Empty - inbox zero achieved)\n';
2950
- return `${header}${inner}${suffix}`;
2951
- });
2952
- }
2953
-
2954
- function addInboxItemToContent(content, id, summary) {
2955
- const items = parseInboxItems(content).filter((item) => item.id !== id);
2956
- const newItem = { id, text: summary, line: `- **I${id}:** ${summary}` };
2957
- const updatedItems = [newItem, ...items];
2958
- return replaceInboxSection(content, updatedItems);
2959
- }
2960
-
2961
- function removeInboxItemFromContent(content, id) {
2962
- const items = parseInboxItems(content).filter((item) => item.id !== id);
2963
- return replaceInboxSection(content, items);
2964
- }
2965
-
2966
- function getNextInboxId(content) {
2967
- const items = parseInboxItems(content);
2968
- if (items.length === 0) return 1;
2969
- return items.reduce((max, item) => (item.id > max ? item.id : max), 0) + 1;
2970
- }
2971
-
2972
- function parseCompletionItems(content) {
2973
- const match = content.match(/## Completed ✅\n([\s\S]*?)(?=\n##|\n---|$)/);
2974
- if (!match) {
2975
- return [];
2976
- }
2977
- const body = match[1];
2978
- const lines = body.split('\n');
2979
- const items = [];
2980
- lines.forEach((line) => {
2981
- const trimmed = line.trim();
2982
- if (!trimmed) return;
2983
- if (trimmed.startsWith('(Empty')) return;
2984
- const parsed = trimmed.match(/^- \*\*C(\d+):\*\*\s*(.+)$|^- \*\*C(\d+):\s+(.+)$/);
2985
- if (parsed) {
2986
- const id = parseInt(parsed[1] || parsed[3], 10);
2987
- const text = parsed[2] || parsed[4];
2988
- items.push({ id, text, line: trimmed });
2989
- }
2990
- });
2991
- return items;
2992
- }
2993
-
2994
- function replaceCompletedSection(content, items) {
2995
- const regex = /(## Completed ✅\n)([\s\S]*?)(\n---|\n##|$)/;
2996
- if (!regex.test(content)) {
2997
- const lines = items.length ? items.map((item) => item.line).join('\n') : '';
2998
- return `${content}\n\n## Completed ✅\n\n${lines}\n`;
2999
- }
3000
-
3001
- return content.replace(regex, (match, header, body, suffix) => {
3002
- const inner = items.length
3003
- ? `\n${items.map((item) => item.line).join('\n')}\n`
3004
- : '\n';
3005
- return `${header}${inner}${suffix}`;
3006
- });
3007
- }
3008
-
3009
- function addCompletionItemToContent(content, id, summary) {
3010
- const items = parseCompletionItems(content).filter((item) => item.id !== id);
3011
- const newItem = { id, text: summary, line: `- **C${id}:** ${summary}` };
3012
- const updatedItems = [...items, newItem];
3013
- return replaceCompletedSection(content, updatedItems);
3014
- }
3015
-
3016
- function getNextCompletionId(content) {
3017
- const items = parseCompletionItems(content);
3018
- if (items.length === 0) return 1;
3019
- return items.reduce((max, item) => (item.id > max ? item.id : max), 0) + 1;
3020
- }
3021
-
3022
- function insertIntoNotesSection(content, block) {
3023
- const regex = /(## Notes\n)([\s\S]*?)(\n---|\n##|$)/;
3024
- const match = content.match(regex);
3025
- if (!match) {
3026
- return `${content}\n\n## Notes\n\n${block}\n`;
3027
- }
3028
- const header = match[1];
3029
- const body = match[2];
3030
- const suffix = match[3];
3031
- const trimmedBody = body.replace(/\s*$/, '');
3032
- const newBody = trimmedBody
3033
- ? `${trimmedBody}\n\n${block}\n`
3034
- : `\n${block}\n`;
3035
- return content.replace(regex, `${header}${newBody}${suffix}`);
3036
- }
3037
-
3038
- function getTimeLabel() {
3039
- const now = new Date();
3040
- const hours = String(now.getHours()).padStart(2, '0');
3041
- const minutes = String(now.getMinutes()).padStart(2, '0');
3042
- return `${hours}:${minutes}`;
3043
- }
3044
-
3045
- function recordAutopilotVision(logFile, sourceLabel, summary, successCriteria, riskNotes) {
3046
- const content = fs.readFileSync(logFile, 'utf8');
3047
- const lines = [
3048
- `### Autopilot Vision — ${getTimeLabel()}`,
3049
- `**Source:** ${sourceLabel}`,
3050
- `**Summary:** ${summary}`,
3051
- '**Success Criteria:**',
3052
- ...successCriteria.map((item) => `- ${item}`),
3053
- ];
3054
- if (riskNotes && riskNotes.trim()) {
3055
- lines.push(`**Risks / Notes:** ${riskNotes}`);
3056
- }
3057
- const block = lines.join('\n');
3058
- const updated = insertIntoNotesSection(content, block);
3059
- fs.writeFileSync(logFile, updated);
3060
- }
3061
-
3062
- function recordAutopilotIteration(logFile, iteration, result, notes) {
3063
- const content = fs.readFileSync(logFile, 'utf8');
3064
- const lines = [
3065
- `### Autopilot Iteration ${iteration} — ${getTimeLabel()}`,
3066
- `**Validator Result:** ${result}`,
3067
- ];
3068
- if (notes && notes.trim()) {
3069
- lines.push(`**Notes:** ${notes}`);
3070
- }
3071
- const block = lines.join('\n');
3072
- const updated = insertIntoNotesSection(content, block);
3073
- fs.writeFileSync(logFile, updated);
3074
- }
3075
-
3076
- function recordAutopilotSuccess(logFile, inboxId, summary) {
3077
- let content = fs.readFileSync(logFile, 'utf8');
3078
- if (typeof inboxId === 'number' && !Number.isNaN(inboxId)) {
3079
- content = removeInboxItemFromContent(content, inboxId);
3080
- }
3081
- const nextId = getNextCompletionId(content);
3082
- content = addCompletionItemToContent(content, nextId, `Autopilot — ${summary}`);
3083
- fs.writeFileSync(logFile, content);
3084
- }
3085
-
3086
- function recordBrainstormSession(
3087
- logFile,
3088
- sourceLabel,
3089
- topic,
3090
- desiredOutcome,
3091
- keyQuestions,
3092
- focusAreas,
3093
- constraints,
3094
- references,
3095
- tonePreference,
3096
- nextSteps,
3097
- sessionSummary
3098
- ) {
3099
- let content = fs.readFileSync(logFile, 'utf8');
3100
- const lines = [
3101
- `### Brainstorm Session — ${getTimeLabel()}`,
3102
- `**Source:** ${sourceLabel}`,
3103
- `**Topic:** ${topic}`,
3104
- ];
3105
- if (desiredOutcome) {
3106
- lines.push(`**User Story / Desired Outcome:** ${desiredOutcome}`);
3107
- }
3108
- if (tonePreference) {
3109
- lines.push(`**Vibe / Feelings:** ${tonePreference}`);
3110
- }
3111
- if (keyQuestions && keyQuestions.length > 0) {
3112
- lines.push('**Key Questions:**');
3113
- keyQuestions.forEach((item) => lines.push(`- ${item}`));
3114
- }
3115
- if (focusAreas && focusAreas.length > 0) {
3116
- lines.push('**Focus Areas:**');
3117
- focusAreas.forEach((item) => lines.push(`- ${item}`));
3118
- }
3119
- if (constraints) {
3120
- lines.push(`**Constraints:** ${constraints}`);
3121
- }
3122
- if (references) {
3123
- lines.push(`**Context / References:** ${references}`);
3124
- }
3125
- if (sessionSummary) {
3126
- lines.push(`**Session Summary:** ${sessionSummary}`);
3127
- }
3128
- if (nextSteps && nextSteps.length > 0) {
3129
- lines.push('**Next Steps:**');
3130
- nextSteps.forEach((item) => lines.push(`- ${item}`));
3131
- }
3132
-
3133
- const block = lines.join('\n');
3134
- content = insertIntoNotesSection(content, block);
3135
- fs.writeFileSync(logFile, content);
3136
- }
3137
-
3138
- async function atrisDevEntry(userInput = null) {
3139
- // Load workspace context and present planning-ready state
3140
- // userInput: optional task description for hot start
3141
- const targetDir = path.join(process.cwd(), 'atris');
3142
-
3143
- // Check if ATRIS is initialized
3144
- if (!fs.existsSync(targetDir)) {
3145
- console.log('');
3146
- console.log('🚀 Welcome to ATRIS\n');
3147
- console.log('Not initialized yet. Let\'s get started:\n');
3148
- console.log(' → atris init Set up your workspace');
3149
- console.log(' → atris help See all commands\n');
3150
- return;
3151
- }
3152
-
3153
- ensureLogDirectory();
3154
- const { logFile, dateFormatted } = getLogPath();
3155
- if (!fs.existsSync(logFile)) {
3156
- createLogFile(logFile, dateFormatted);
3157
- }
3158
-
3159
- // Load context
3160
- const workspaceDir = process.cwd();
3161
- const state = detectWorkspaceState(workspaceDir);
3162
- const context = loadContext(workspaceDir);
3163
-
3164
- console.log('');
3165
- console.log('┌─────────────────────────────────────────────────────────────┐');
3166
- console.log('│ atrisDev — Context Loaded, Ready to Plan │');
3167
- console.log('└─────────────────────────────────────────────────────────────┘');
3168
- console.log('');
3169
- console.log(`📅 ${dateFormatted}`);
3170
- console.log('');
3171
-
3172
- // Show active work if any
3173
- if (context.inProgressFeatures.length > 0) {
3174
- console.log('⚡ Active Work:');
3175
- context.inProgressFeatures.forEach(feature => {
3176
- console.log(` • ${feature}`);
3177
- });
3178
- console.log('');
3179
- }
3180
-
3181
- // Show inbox items or cold start message
3182
- if (context.hasInbox && context.inboxItems.length > 0) {
3183
- console.log(`📥 Inbox (${context.inboxItems.length} idea${context.inboxItems.length > 1 ? 's' : ''}):`);
3184
- context.inboxItems.slice(0, 5).forEach((item, i) => {
3185
- const preview = item.length > 60 ? item.substring(0, 57) + '...' : item;
3186
- console.log(` ${i + 1}. ${preview}`);
3187
- });
3188
- if (context.inboxItems.length > 5) {
3189
- console.log(` ... and ${context.inboxItems.length - 5} more`);
3190
- }
3191
- console.log('');
3192
- } else {
3193
- console.log('💭 Fresh slate — ready to build something new\n');
3194
- }
3195
-
3196
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3197
- console.log('💬 atrisDev Protocol — Ready to build');
3198
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3199
- console.log('');
3200
-
3201
- // Show context only if there's something to show
3202
- let hasContext = false;
3203
-
3204
- if (context.inProgressFeatures.length > 0) {
3205
- hasContext = true;
3206
- console.log('⚡ Active Work:');
3207
- context.inProgressFeatures.forEach(feature => {
3208
- console.log(` • ${feature}`);
3209
- });
3210
- console.log('');
3211
- }
3212
-
3213
- if (context.hasInbox && context.inboxItems.length > 0) {
3214
- hasContext = true;
3215
- console.log('📥 Inbox Ideas:');
3216
- context.inboxItems.slice(0, 5).forEach((item, i) => {
3217
- const preview = item.length > 60 ? item.substring(0, 57) + '...' : item;
3218
- console.log(` ${i + 1}. ${preview}`);
3219
- });
3220
- if (context.inboxItems.length > 5) {
3221
- console.log(` ... and ${context.inboxItems.length - 5} more`);
3222
- }
3223
- console.log('');
3224
- }
3225
-
3226
- const logContent = fs.existsSync(logFile) ? fs.readFileSync(logFile, 'utf8') : '';
3227
- const completedMatch = logContent.match(/## Completed ✅\n([\s\S]*?)(?=\n##|$)/);
3228
- if (completedMatch && completedMatch[1].trim()) {
3229
- const completedItems = completedMatch[1].trim().split('\n')
3230
- .filter(line => line.match(/^- \*\*C\d+:/))
3231
- .slice(-3);
3232
- if (completedItems.length > 0) {
3233
- hasContext = true;
3234
- console.log('✅ Recent Completions:');
3235
- completedItems.forEach(item => {
3236
- const match = item.match(/^- \*\*C\d+:\s*(.+)\*\*/);
3237
- if (match) {
3238
- const text = match[1].length > 60 ? match[1].substring(0, 57) + '...' : match[1];
3239
- console.log(` • ${text}`);
3240
- }
3241
- });
3242
- console.log('');
3243
- }
3244
- }
3245
-
3246
- if (!hasContext) {
3247
- console.log('💭 Fresh slate — ready to build something new');
3248
- console.log('');
3249
- }
3250
-
3251
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3252
- console.log('🤖 EXECUTE THIS IMMEDIATELY');
3253
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3254
- console.log('');
3255
- console.log('Read atris.md (the full protocol)');
3256
- console.log('');
3257
-
3258
- if (userInput) {
3259
- // Hot start - user provided task
3260
- console.log('User wants:');
3261
- console.log(`"${userInput}"`);
3262
- console.log('');
3263
- console.log('Execute atrisDev protocol from atris.md for this request.');
3264
- console.log('');
3265
- } else {
3266
- // Cold start - no specific task
3267
- console.log('Wait for user to describe what they want, then execute atrisDev protocol.');
3268
- console.log('');
3269
- }
3270
-
3271
- console.log('atrisDev workflow (from atris.md Phase 5.2):');
3272
- console.log('1. Show atris visualization');
3273
- console.log('2. Wait for approval');
3274
- console.log('3. Create atris/features/[name]/idea.md + build.md');
3275
- console.log('4. Execute step by step');
3276
- console.log('5. Review and update atris/features/README.md');
3277
- console.log('');
3278
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3279
- console.log('');
3280
- }
3281
-
3282
- async function planAtris() {
3283
- const { loadConfig } = require('../utils/config');
3284
- const { loadCredentials } = require('../utils/auth');
3285
- const { executeCodeExecution } = require('../utils/claude_sdk');
3286
- const args = process.argv.slice(3);
3287
- const executeFlag = args.includes('--execute');
3288
-
3289
- const config = loadConfig();
3290
- const executionMode = executeFlag ? 'agent' : (config.execution_mode || 'prompt');
3291
-
3292
- const targetDir = path.join(process.cwd(), 'atris');
3293
- const navigatorFile = path.join(targetDir, 'agent_team', 'navigator.md');
3294
-
3295
- if (!fs.existsSync(navigatorFile)) {
3296
- console.log('✗ navigator.md not found. Run "atris init" first.');
3297
- process.exit(1);
3298
- }
3299
-
3300
- // Read navigator.md
3301
- const navigatorSpec = fs.readFileSync(navigatorFile, 'utf8');
3302
-
3303
- // Read journal Inbox for context
3304
- const { logFile } = getLogPath();
3305
- let inboxContext = '';
3306
-
3307
- if (fs.existsSync(logFile)) {
3308
- const logContent = fs.readFileSync(logFile, 'utf8');
3309
- const inboxMatch = logContent.match(/## Inbox\n([\s\S]*?)(?=\n##|$)/);
3310
- if (inboxMatch && inboxMatch[1].trim()) {
3311
- inboxContext = inboxMatch[1].trim();
3312
- }
3313
- }
3314
-
3315
- // Read TASK_CONTEXTS.md for current state
3316
- const taskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
3317
- let taskContexts = '';
3318
- if (fs.existsSync(taskContextsFile)) {
3319
- taskContexts = fs.readFileSync(taskContextsFile, 'utf8');
3320
- }
3321
-
3322
- console.log('');
3323
- console.log('┌─────────────────────────────────────────────────────────────┐');
3324
- console.log('│ ATRIS Plan — Navigator Agent Activated │');
3325
- console.log('└─────────────────────────────────────────────────────────────┘');
3326
- console.log('');
3327
- console.log('📋 AGENT SPEC:');
3328
- console.log('─────────────────────────────────────────────────────────────');
3329
- console.log(navigatorSpec);
3330
- console.log('');
3331
- console.log('─────────────────────────────────────────────────────────────');
3332
- console.log('');
3333
- console.log('📥 INBOX CONTEXT:');
3334
- console.log('─────────────────────────────────────────────────────────────');
3335
- if (inboxContext) {
3336
- console.log(inboxContext);
3337
- } else {
3338
- console.log('(No items in Inbox)');
3339
- }
3340
- console.log('');
3341
- console.log('─────────────────────────────────────────────────────────────');
3342
- console.log('');
3343
- console.log('📝 CURRENT TASK_CONTEXTS.md:');
3344
- console.log('─────────────────────────────────────────────────────────────');
3345
- console.log(taskContexts);
3346
- console.log('');
3347
- console.log('─────────────────────────────────────────────────────────────');
3348
- console.log('');
3349
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3350
- console.log('📋 INSTRUCTION PROMPT FOR YOUR CODING AGENT:');
3351
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3352
- console.log('');
3353
- console.log('You are the Navigator. Take ideas from Inbox → break them down into perfect, manageable tasks.');
3354
- console.log('');
3355
- console.log('⚠️ CRITICAL: You MUST create visualizations BEFORE writing tasks!');
3356
- console.log('');
3357
- console.log('Your job (execute these steps):');
3358
- console.log('');
3359
- console.log('STEP 1: Generate ASCII visualizations for user approval');
3360
- console.log(' Create diagrams showing:');
3361
- console.log(' - Logic/flow: How data/control flows through the system');
3362
- console.log(' - DB schema: Table structures, relationships, indexes');
3363
- console.log(' - Architecture: Component interactions, file structure changes');
3364
- console.log(' - UI/UX: User flow, screen layouts, interactions');
3365
- console.log(' SHOW these diagrams to the user and wait for approval before proceeding.');
3366
- console.log('');
3367
- console.log('STEP 2: Break approved ideas into concrete tasks');
3368
- console.log(' - Each task should be: Specific, Measurable, Actionable');
3369
- console.log(' - Include file:line references from MAP.md');
3370
- console.log(' - List dependencies between tasks');
3371
- console.log(' - Add acceptance criteria for each task');
3372
- console.log('');
3373
- console.log('STEP 3: Write tasks to TASK_CONTEXTS.md');
3374
- console.log(' - Add tasks to Backlog section');
3375
- console.log(' - Format: Task number, description, file refs, acceptance criteria');
3376
- console.log(' - Quality over speed - tasks must be perfect for systems player execution');
3377
- console.log('');
3378
- console.log('Load journal context if needed. Use MAP.md for file references.');
3379
- console.log('');
3380
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3381
- console.log('💡 After planning: Run "atris do" to execute tasks');
3382
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3383
- console.log('');
3384
-
3385
- // Check execution mode
3386
- if (executionMode === 'agent') {
3387
- // Agent mode: execute via backend API
3388
- if (!config.agent_id) {
3389
- throw new Error('No agent selected. Run "atris agent" first.');
3390
- }
3391
- const credentials = loadCredentials();
3392
- if (!credentials || !credentials.token) {
3393
- throw new Error('Not logged in. Run "atris login" first.');
3394
- }
3395
-
3396
- // Build system prompt
3397
- let systemPrompt = '';
3398
- if (navigatorSpec) {
3399
- systemPrompt += navigatorSpec + '\n\n';
3400
- }
3401
-
3402
- // Reference MAP.md and PERSONA.md
3403
- const personaFile = path.join(targetDir, 'PERSONA.md');
3404
- if (fs.existsSync(personaFile)) {
3405
- systemPrompt += '## PERSONA.md\n' + fs.readFileSync(personaFile, 'utf8') + '\n\n';
3406
- }
3407
-
3408
- const mapFile = path.join(targetDir, 'MAP.md');
3409
- const mapPath = fs.existsSync(mapFile) ? path.relative(process.cwd(), mapFile) : null;
3410
- if (mapPath) {
3411
- systemPrompt += `## MAP.md\nRead this file for file:line references: ${mapPath}\n\n`;
3412
- }
3413
-
3414
- // Build user prompt with context
3415
- let userPrompt = `You are the Navigator. Take ideas from Inbox → break them down into perfect, manageable tasks.\n\n`;
3416
- userPrompt += `⚠️ CRITICAL: You MUST create visualizations BEFORE writing tasks!\n\n`;
3417
-
3418
- if (inboxContext) {
3419
- userPrompt += `## INBOX CONTEXT:\n${inboxContext}\n\n`;
3420
- } else {
3421
- userPrompt += `## INBOX CONTEXT:\n(No items in Inbox - check logs/YYYY/YYYY-MM-DD.md for inbox items)\n\n`;
3422
- }
3423
-
3424
- if (taskContexts) {
3425
- userPrompt += `## CURRENT TASK_CONTEXTS.md:\n${taskContexts}\n\n`;
3426
- }
3427
-
3428
- userPrompt += `Your job (execute these steps):\n\n`;
3429
- userPrompt += `STEP 1: Generate ASCII visualizations for user approval\n`;
3430
- userPrompt += ` Create diagrams showing architecture, flows, schemas, UI/UX.\n`;
3431
- userPrompt += ` SHOW these diagrams and wait for approval before proceeding.\n\n`;
3432
- userPrompt += `STEP 2: Break approved ideas into concrete tasks\n`;
3433
- userPrompt += ` - Each task should be: Specific, Measurable, Actionable\n`;
3434
- userPrompt += ` - Include file:line references from MAP.md\n`;
3435
- userPrompt += ` - List dependencies between tasks\n`;
3436
- userPrompt += ` - Add acceptance criteria for each task\n\n`;
3437
- userPrompt += `STEP 3: Write tasks to TASK_CONTEXTS.md\n`;
3438
- userPrompt += ` - Add tasks to Backlog section\n`;
3439
- userPrompt += ` - Format: Task number, description, file refs, acceptance criteria\n`;
3440
- userPrompt += ` - Quality over speed - tasks must be perfect for systems player execution\n\n`;
3441
- userPrompt += `Start planning now. Read MAP.md for file references.`;
3442
-
3443
- console.log('');
3444
- console.log('🤖 AGENT MODE: Executing via backend API...');
3445
- console.log('');
3446
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3447
- console.log('');
3448
-
3449
- // Execute via API
3450
- try {
3451
- await executeCodeExecution({
3452
- prompt: userPrompt,
3453
- allowedTools: ['Read', 'Write', 'Edit'], // Navigator needs to write TASK_CONTEXTS.md
3454
- permissionMode: 'default',
3455
- maxTurns: 15,
3456
- systemPrompt,
3457
- workingDirectory: process.cwd(),
3458
- agentId: config.agent_id,
3459
- token: credentials.token,
3460
- onMessage: (data) => {
3461
- if (data.type === 'text' && data.content) {
3462
- process.stdout.write(data.content);
3463
- } else if (data.type === 'tool_use') {
3464
- console.log(`\n🛠️ [${data.tool || data.tool_name}] ${JSON.stringify(data.input || data.tool_input || {}).substring(0, 100)}`);
3465
- } else if (data.type === 'tool_result') {
3466
- const result = data.result || data.content || '';
3467
- const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
3468
- const preview = resultStr.substring(0, 200);
3469
- console.log(`\n✅ [Result] ${preview}${resultStr.length > 200 ? '...' : ''}`);
3470
- } else if (data.type === 'error') {
3471
- console.error(`\n❌ Error: ${data.error}`);
3472
- } else if (data.type === 'result') {
3473
- if (data.result) {
3474
- console.log(`\n🎯 [Final] ${data.result}`);
3475
- }
3476
- if (data.duration_ms) {
3477
- console.log(`⏱️ Duration: ${(data.duration_ms / 1000).toFixed(2)}s`);
3478
- }
3479
- if (data.cost_usd) {
3480
- console.log(`💰 Cost: $${data.cost_usd.toFixed(4)}`);
3481
- }
3482
- }
3483
- },
3484
- onError: (error) => {
3485
- console.error(`\n❌ Execution error: ${error.message}`);
3486
- },
3487
- });
3488
-
3489
- console.log('\n');
3490
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3491
- console.log('');
3492
- console.log('💡 After planning: Run "atris do" to execute tasks');
3493
- console.log('');
3494
- } catch (error) {
3495
- console.error(`\n✗ Agent execution failed: ${error.message}`);
3496
- throw error;
3497
- }
3498
- }
3499
- // Prompt mode continues with existing output (already logged above)
3500
- }
3501
-
3502
- async function doAtris() {
3503
- const { loadConfig } = require('../utils/config');
3504
- const { loadCredentials } = require('../utils/auth');
3505
- const { executeCodeExecution } = require('../utils/claude_sdk');
3506
- const args = process.argv.slice(3);
3507
- const executeFlag = args.includes('--execute');
3508
-
3509
- const config = loadConfig();
3510
- const executionMode = executeFlag ? 'agent' : (config.execution_mode || 'prompt');
3511
-
3512
- const cwd = process.cwd();
3513
- const targetDir = path.join(cwd, 'atris');
3514
- const executorFile = path.join(targetDir, 'agent_team', 'executor.md');
3515
-
3516
- if (!fs.existsSync(executorFile)) {
3517
- console.log('✗ executor.md not found. Run "atris init" first.');
3518
- process.exit(1);
3519
- }
3520
-
3521
- // Load project profile for context
3522
- let context = 'ROOT';
3523
- let profile = null;
3524
- const profileFile = path.join(targetDir, '.project-profile.json');
3525
- if (fs.existsSync(profileFile)) {
3526
- try {
3527
- profile = JSON.parse(fs.readFileSync(profileFile, 'utf8'));
3528
- // Use profile type as context (e.g., 'nodejs', 'python', 'knowledge-base')
3529
- context = profile.type.toUpperCase();
3530
- if (profile.framework !== 'none') {
3531
- context += `/${profile.framework.toUpperCase()}`;
3532
- }
3533
- } catch (e) {
3534
- // Fallback to ROOT if profile parse fails
3535
- context = 'ROOT';
3536
- }
3537
- }
3538
-
3539
- // Load executor spec
3540
- const executorSpec = fs.readFileSync(executorFile, 'utf8');
3541
-
3542
- // Load PERSONA.md
3543
- const personaFile = path.join(targetDir, 'PERSONA.md');
3544
- let persona = '';
3545
- if (fs.existsSync(personaFile)) {
3546
- persona = fs.readFileSync(personaFile, 'utf8');
3547
- }
3548
-
3549
- // Reference MAP.md (agents read on-demand)
3550
- const mapFile = path.join(targetDir, 'MAP.md');
3551
- const mapPath = fs.existsSync(mapFile) ? path.relative(process.cwd(), mapFile) : null;
3552
-
3553
- // Load tasks from TASK_CONTEXTS.md (generic - no hardcoded paths)
3554
- let tasksContent = '';
3555
- let taskSource = '';
3556
- const taskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
3557
- if (fs.existsSync(taskContextsFile)) {
3558
- tasksContent = fs.readFileSync(taskContextsFile, 'utf8');
3559
- taskSource = 'atris/TASK_CONTEXTS.md';
3560
- }
3561
-
3562
- // Extract tasks from TASK_CONTEXTS.md (no tag filtering - all tasks available)
3563
- const taskTag = '';
3564
- let filteredTasks = '';
3565
- if (taskTag && tasksContent) {
3566
- const taskLines = tasksContent.split('\n');
3567
- let inTasksSection = false;
3568
- let currentTask = [];
3569
- for (const line of taskLines) {
3570
- if (line.includes('<tasks>')) {
3571
- inTasksSection = true;
3572
- continue;
3573
- }
3574
- if (line.includes('</tasks>')) {
3575
- inTasksSection = false;
3576
- if (currentTask.length > 0) {
3577
- filteredTasks += currentTask.join('\n') + '\n\n';
3578
- }
3579
- currentTask = [];
3580
- continue;
3581
- }
3582
- if (inTasksSection && line.trim()) {
3583
- if (line.includes(taskTag)) {
3584
- currentTask = [line];
3585
- } else if (currentTask.length > 0) {
3586
- currentTask.push(line);
3587
- }
3588
- }
3589
- }
3590
- if (currentTask.length > 0) {
3591
- filteredTasks += currentTask.join('\n') + '\n';
3592
- }
3593
- } else {
3594
- filteredTasks = tasksContent;
3595
- }
3596
-
3597
- // Load TASK_CONTEXTS.md content (using existing taskContextsFile variable)
3598
- let taskContexts = '';
3599
- if (fs.existsSync(taskContextsFile)) {
3600
- taskContexts = fs.readFileSync(taskContextsFile, 'utf8');
3601
- }
3602
-
3603
- // Build super prompt
3604
- console.log('');
3605
- console.log('┌─────────────────────────────────────────────────────────────┐');
3606
- console.log('│ ATRIS Do — Executor Agent Activated │');
3607
- console.log(`│ Context: ${context} │`);
3608
- console.log('└─────────────────────────────────────────────────────────────┘');
3609
- console.log('');
3610
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3611
- console.log('🎯 YOUR TASK: Execute tasks tagged [' + taskTag + '] from ' + taskSource);
3612
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3613
- console.log('');
3614
-
3615
- if (persona) {
3616
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3617
- console.log('👤 PERSONA.md — Communication & Work Style');
3618
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3619
- console.log(persona);
3620
- console.log('');
3621
- }
3622
-
3623
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3624
- console.log('🔧 EXECUTOR SPEC — How to Build');
3625
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3626
- console.log(executorSpec);
3627
- console.log('');
3628
-
3629
- if (filteredTasks) {
3630
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3631
- console.log('📋 TASKS TO EXECUTE — ' + taskSource);
3632
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3633
- console.log(filteredTasks || '(No tasks found matching your tag)');
3634
- console.log('');
3635
- }
3636
-
3637
- if (taskContexts && taskContexts.trim()) {
3638
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3639
- console.log('📝 TASK_CONTEXTS.md (Additional Context)');
3640
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3641
- console.log(taskContexts);
3642
- console.log('');
3643
- }
3644
-
3645
- if (mapPath) {
3646
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3647
- console.log('🗺️ MAP.md: ' + mapPath);
3648
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3649
- console.log(' Read this file for file:line references when navigating the codebase.');
3650
- console.log('');
3651
- }
3652
-
3653
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3654
- console.log('📋 INSTRUCTION PROMPT FOR YOUR CODING AGENT:');
3655
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3656
- console.log('');
3657
- console.log('⚠️ CRITICAL: Execute tasks NOW. Use file tools to edit code, terminal to run commands.');
3658
- console.log('');
3659
- console.log('You are the Executor. Get it done, precisely, following instructions perfectly.');
3660
- console.log('');
3661
- console.log('Execution Modes:');
3662
- console.log(' • Single-shot (default): Complete all tasks automatically');
3663
- console.log(' • Step-by-step: Use "atris do --step" for user-controlled execution');
3664
- console.log('');
3665
- console.log('Your process (EXECUTE these steps):');
3666
- console.log('1. Read tasks from TASK_CONTEXTS.md (shown above)');
3667
- console.log('2. For each task: Show ASCII visualization first (especially complex changes)');
3668
- console.log('3. Execute task: Use file edit tools, terminal commands, etc.');
3669
- console.log('4. After completion: Move task to TASK_CONTEXTS.md <completed> section');
3670
- console.log('5. Follow PERSONA.md for communication style');
3671
- console.log('');
3672
- console.log('DO NOT just describe what you would do - actually edit files and execute commands!');
3673
- console.log('6. Use MAP.md to navigate codebase');
3674
- console.log('');
3675
- console.log('User controls pace: Choose single-shot or step-by-step based on confidence.');
3676
- console.log('');
3677
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3678
- console.log('');
3679
-
3680
- // Check execution mode
3681
- if (executionMode === 'agent') {
3682
- // Agent mode: execute via backend API
3683
- if (!config.agent_id) {
3684
- throw new Error('No agent selected. Run "atris agent" first.');
3685
- }
3686
- const credentials = loadCredentials();
3687
- if (!credentials || !credentials.token) {
3688
- throw new Error('Not logged in. Run "atris login" first.');
3689
- }
3690
-
3691
- // Build system prompt
3692
- let systemPrompt = '';
3693
- if (executorSpec) {
3694
- systemPrompt += executorSpec + '\n\n';
3695
- }
3696
- if (persona) {
3697
- systemPrompt += '## PERSONA.md\n' + persona + '\n\n';
3698
- }
3699
- if (mapPath) {
3700
- systemPrompt += `## MAP.md\nRead this file for file:line references: ${mapPath}\n\n`;
3701
- }
3702
- if (profile) {
3703
- systemPrompt += `## PROJECT CONTEXT\nType: ${context}\nProfile: ${JSON.stringify(profile, null, 2)}\n\n`;
3704
- }
3705
-
3706
- // Build user prompt with context
3707
- let userPrompt = `⚠️ CRITICAL: Execute tasks NOW. Use file tools to edit code, terminal to run commands.\n\n`;
3708
- userPrompt += `You are the Executor. Get it done, precisely, following instructions perfectly.\n\n`;
3709
-
3710
- if (filteredTasks) {
3711
- userPrompt += `## TASKS TO EXECUTE (from ${taskSource}):\n${filteredTasks}\n\n`;
3712
- } else {
3713
- userPrompt += `## TASKS TO EXECUTE:\n(No tasks found - check TASK_CONTEXTS.md)\n\n`;
3714
- }
3715
-
3716
- if (taskContexts) {
3717
- userPrompt += `## TASK_CONTEXTS.md (Additional Context):\n${taskContexts}\n\n`;
3718
- }
3719
-
3720
- userPrompt += `Your process (EXECUTE these steps):\n`;
3721
- userPrompt += `1. Read tasks from TASK_CONTEXTS.md (shown above)\n`;
3722
- userPrompt += `2. For each task: Show ASCII visualization first (especially complex changes)\n`;
3723
- userPrompt += `3. Execute task: Use file edit tools, terminal commands, etc.\n`;
3724
- userPrompt += `4. After completion: Move task to TASK_CONTEXTS.md <completed> section\n`;
3725
- userPrompt += `5. Follow PERSONA.md for communication style\n`;
3726
- userPrompt += `6. Use MAP.md to navigate codebase\n\n`;
3727
- userPrompt += `DO NOT just describe what you would do - actually edit files and execute commands!\n`;
3728
- userPrompt += `Context: ${context}\n`;
3729
- userPrompt += `Start executing tasks now.`;
3730
-
3731
- console.log('');
3732
- console.log('🤖 AGENT MODE: Executing via backend API...');
3733
- console.log('');
3734
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3735
- console.log('');
3736
-
3737
- // Execute via API
3738
- try {
3739
- await executeCodeExecution({
3740
- prompt: userPrompt,
3741
- allowedTools: ['Read', 'Write', 'Edit', 'Bash'], // Executor needs all tools
3742
- permissionMode: 'default',
3743
- maxTurns: 20,
3744
- systemPrompt,
3745
- workingDirectory: process.cwd(),
3746
- agentId: config.agent_id,
3747
- token: credentials.token,
3748
- onMessage: (data) => {
3749
- if (data.type === 'text' && data.content) {
3750
- process.stdout.write(data.content);
3751
- } else if (data.type === 'tool_use') {
3752
- console.log(`\n🛠️ [${data.tool || data.tool_name}] ${JSON.stringify(data.input || data.tool_input || {}).substring(0, 100)}`);
3753
- } else if (data.type === 'tool_result') {
3754
- const result = data.result || data.content || '';
3755
- const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
3756
- const preview = resultStr.substring(0, 200);
3757
- console.log(`\n✅ [Result] ${preview}${resultStr.length > 200 ? '...' : ''}`);
3758
- } else if (data.type === 'error') {
3759
- console.error(`\n❌ Error: ${data.error}`);
3760
- } else if (data.type === 'result') {
3761
- if (data.result) {
3762
- console.log(`\n🎯 [Final] ${data.result}`);
3763
- }
3764
- if (data.duration_ms) {
3765
- console.log(`⏱️ Duration: ${(data.duration_ms / 1000).toFixed(2)}s`);
3766
- }
3767
- if (data.cost_usd) {
3768
- console.log(`💰 Cost: $${data.cost_usd.toFixed(4)}`);
3769
- }
3770
- }
3771
- },
3772
- onError: (error) => {
3773
- console.error(`\n❌ Execution error: ${error.message}`);
3774
- },
3775
- });
3776
-
3777
- console.log('\n');
3778
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3779
- console.log('');
3780
- } catch (error) {
3781
- console.error(`\n✗ Agent execution failed: ${error.message}`);
3782
- throw error;
3783
- }
3784
- }
3785
- // Prompt mode continues with existing output (already logged above)
3786
- }
3787
-
3788
- async function reviewAtris() {
3789
- const { loadConfig } = require('../utils/config');
3790
- const { loadCredentials } = require('../utils/auth');
3791
- const { executeCodeExecution } = require('../utils/claude_sdk');
3792
- const args = process.argv.slice(3);
3793
- const executeFlag = args.includes('--execute');
3794
-
3795
- const config = loadConfig();
3796
- const executionMode = executeFlag ? 'agent' : (config.execution_mode || 'prompt');
3797
-
3798
- const targetDir = path.join(process.cwd(), 'atris');
3799
- const validatorFile = path.join(targetDir, 'agent_team', 'validator.md');
3800
-
3801
- if (!fs.existsSync(validatorFile)) {
3802
- console.log('✗ validator.md not found. Run "atris init" first.');
3803
- process.exit(1);
3804
- }
3805
-
3806
- // Read validator.md
3807
- const validatorSpec = fs.readFileSync(validatorFile, 'utf8');
3808
-
3809
- // Read project-specific testing guide if it exists (optional - projects can add their own)
3810
- // Checks common locations: root, backend/, atris/ directories
3811
- let testingGuide = '';
3812
- const possiblePaths = [
3813
- path.join(process.cwd(), 'AGENT_TESTING_GUIDE.md'),
3814
- path.join(process.cwd(), 'TESTING_GUIDE.md'),
3815
- path.join(process.cwd(), 'atris', 'TESTING_GUIDE.md'),
3816
- ];
3817
- for (const guidePath of possiblePaths) {
3818
- if (fs.existsSync(guidePath)) {
3819
- testingGuide = fs.readFileSync(guidePath, 'utf8');
3820
- break;
3821
- }
3822
- }
3823
-
3824
- // Read TASK_CONTEXTS.md
3825
- const taskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
3826
- let taskContexts = '';
3827
- if (fs.existsSync(taskContextsFile)) {
3828
- taskContexts = fs.readFileSync(taskContextsFile, 'utf8');
3829
- }
3830
-
3831
- // Read journal for timestamp context
3832
- const { logFile, dateFormatted } = getLogPath();
3833
- let journalPath = '';
3834
- if (fs.existsSync(logFile)) {
3835
- journalPath = path.relative(process.cwd(), logFile);
3836
- }
3837
-
3838
- const mapFile = path.join(targetDir, 'MAP.md');
3839
- const mapPath = fs.existsSync(mapFile) ? path.relative(process.cwd(), mapFile) : null;
3840
-
3841
- console.log('');
3842
- console.log('┌─────────────────────────────────────────────────────────────┐');
3843
- console.log('│ ATRIS Review — Validator Agent Activated │');
3844
- console.log('└─────────────────────────────────────────────────────────────┘');
3845
- console.log('');
3846
- console.log('📋 AGENT SPEC:');
3847
- console.log('─────────────────────────────────────────────────────────────');
3848
- console.log(validatorSpec);
3849
- console.log('');
3850
- console.log('─────────────────────────────────────────────────────────────');
3851
- if (testingGuide) {
3852
- console.log('');
3853
- console.log('🧪 BACKEND TESTING GUIDE:');
3854
- console.log('─────────────────────────────────────────────────────────────');
3855
- console.log(testingGuide);
3856
- console.log('');
3857
- console.log('─────────────────────────────────────────────────────────────');
3858
- }
3859
- console.log('');
3860
- console.log('📝 TASK_CONTEXTS.md:');
3861
- console.log('─────────────────────────────────────────────────────────────');
3862
- console.log(taskContexts);
3863
- console.log('');
3864
- console.log('─────────────────────────────────────────────────────────────');
3865
- console.log('');
3866
- console.log('🗺️ MAP.md: ' + (mapPath || 'Not found'));
3867
- console.log(' Read this file for file:line references when navigating the codebase.');
3868
- console.log('');
3869
- console.log('─────────────────────────────────────────────────────────────');
3870
- console.log('');
3871
- console.log('📅 JOURNAL: ' + (journalPath || 'Not found'));
3872
- console.log('');
3873
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3874
- console.log('📋 INSTRUCTION PROMPT FOR YOUR CODING AGENT:');
3875
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3876
- console.log('');
3877
- console.log('You are the Validator. Auto-activated after "atris do" completes.');
3878
- console.log('');
3879
- console.log('Validation Loop:');
3880
- console.log(' 1. Ultrathink (say "ultrathink", think 3 times)');
3881
- console.log(' 2. Check requirements → build → edge cases → errors → integration');
3882
- console.log(' 3. Run tests (unit, integration, linting, type checking)');
3883
- console.log(' 4. If issues found: report → "atris do" fixes → "atris review" again');
3884
- console.log(' 5. Repeat until: "✅ All good. Ready for human testing."');
3885
- console.log('');
3886
- console.log('Your job:');
3887
- console.log(' • Verify everything works');
3888
- console.log(' • Test thoroughly (unless user says no)');
3889
- console.log(' • Update docs if needed');
3890
- console.log(' • Clean TASK_CONTEXTS.md (move completed tasks)');
3891
- console.log(' • Extract learnings for journal');
3892
- console.log(' • Only approve when truly ready for human testing');
3893
- console.log('');
3894
- console.log('The cycle: do → review → [issues] → do → review → ✅ Ready');
3895
- console.log('');
3896
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3897
- console.log('');
3898
-
3899
- // Check execution mode
3900
- if (executionMode === 'agent') {
3901
- // Agent mode: execute via backend API
3902
- if (!config.agent_id) {
3903
- throw new Error('No agent selected. Run "atris agent" first.');
3904
- }
3905
- const credentials = loadCredentials();
3906
- if (!credentials || !credentials.token) {
3907
- throw new Error('Not logged in. Run "atris login" first.');
3908
- }
3909
-
3910
- // Build system prompt
3911
- let systemPrompt = '';
3912
- if (validatorSpec) {
3913
- systemPrompt += validatorSpec + '\n\n';
3914
- }
3915
- if (testingGuide) {
3916
- systemPrompt += '## TESTING GUIDE\n' + testingGuide + '\n\n';
3917
- }
3918
-
3919
- const personaFile = path.join(targetDir, 'PERSONA.md');
3920
- if (fs.existsSync(personaFile)) {
3921
- systemPrompt += '## PERSONA.md\n' + fs.readFileSync(personaFile, 'utf8') + '\n\n';
3922
- }
3923
-
3924
- if (mapPath) {
3925
- systemPrompt += `## MAP.md\nRead this file for file:line references: ${mapPath}\n\n`;
3926
- }
3927
-
3928
- // Build user prompt with context
3929
- let userPrompt = `You are the Validator. Auto-activated after "atris do" completes.\n\n`;
3930
- userPrompt += `Validation Loop:\n`;
3931
- userPrompt += ` 1. Ultrathink (say "ultrathink", think 3 times)\n`;
3932
- userPrompt += ` 2. Check requirements → build → edge cases → errors → integration\n`;
3933
- userPrompt += ` 3. Run tests (unit, integration, linting, type checking)\n`;
3934
- userPrompt += ` 4. If issues found: report → "atris do" fixes → "atris review" again\n`;
3935
- userPrompt += ` 5. Repeat until: "✅ All good. Ready for human testing."\n\n`;
3936
-
3937
- if (taskContexts) {
3938
- userPrompt += `## TASK_CONTEXTS.md:\n${taskContexts}\n\n`;
3939
- }
3940
-
3941
- userPrompt += `Your job:\n`;
3942
- userPrompt += ` • Verify everything works\n`;
3943
- userPrompt += ` • Test thoroughly (unless user says no)\n`;
3944
- userPrompt += ` • Update docs if needed (MAP.md, TASK_CONTEXTS.md)\n`;
3945
- userPrompt += ` • Clean TASK_CONTEXTS.md (move completed tasks to Completed section, then delete)\n`;
3946
- userPrompt += ` • Extract learnings for journal\n`;
3947
- userPrompt += ` • Only approve when truly ready for human testing\n\n`;
3948
- userPrompt += `The cycle: do → review → [issues] → do → review → ✅ Ready\n`;
3949
- userPrompt += `Start validating now. Read files, run tests, verify implementation.`;
3950
-
3951
- console.log('');
3952
- console.log('🤖 AGENT MODE: Executing via backend API...');
3953
- console.log('');
3954
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3955
- console.log('');
3956
-
3957
- // Execute via API
3958
- try {
3959
- await executeCodeExecution({
3960
- prompt: userPrompt,
3961
- allowedTools: ['Read', 'Write', 'Edit', 'Bash'], // Validator needs to read, test, update docs
3962
- permissionMode: 'default',
3963
- maxTurns: 15,
3964
- systemPrompt,
3965
- workingDirectory: process.cwd(),
3966
- agentId: config.agent_id,
3967
- token: credentials.token,
3968
- onMessage: (data) => {
3969
- if (data.type === 'text' && data.content) {
3970
- process.stdout.write(data.content);
3971
- } else if (data.type === 'tool_use') {
3972
- console.log(`\n🛠️ [${data.tool || data.tool_name}] ${JSON.stringify(data.input || data.tool_input || {}).substring(0, 100)}`);
3973
- } else if (data.type === 'tool_result') {
3974
- const result = data.result || data.content || '';
3975
- const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
3976
- const preview = resultStr.substring(0, 200);
3977
- console.log(`\n✅ [Result] ${preview}${resultStr.length > 200 ? '...' : ''}`);
3978
- } else if (data.type === 'error') {
3979
- console.error(`\n❌ Error: ${data.error}`);
3980
- } else if (data.type === 'result') {
3981
- if (data.result) {
3982
- console.log(`\n🎯 [Final] ${data.result}`);
3983
- }
3984
- if (data.duration_ms) {
3985
- console.log(`⏱️ Duration: ${(data.duration_ms / 1000).toFixed(2)}s`);
3986
- }
3987
- if (data.cost_usd) {
3988
- console.log(`💰 Cost: $${data.cost_usd.toFixed(4)}`);
3989
- }
3990
- }
3991
- },
3992
- onError: (error) => {
3993
- console.error(`\n❌ Execution error: ${error.message}`);
3994
- },
3995
- });
3996
-
3997
- console.log('\n');
3998
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
3999
- console.log('');
4000
- } catch (error) {
4001
- console.error(`\n✗ Agent execution failed: ${error.message}`);
4002
- throw error;
4003
- }
4004
- }
4005
- // Prompt mode continues with existing output (already logged above)
4006
- }
4007
-
4008
- function launchAtris() {
4009
- const targetDir = path.join(process.cwd(), 'atris');
4010
- const launcherFile = path.join(targetDir, 'agent_team', 'launcher.md');
4011
-
4012
- if (!fs.existsSync(launcherFile)) {
4013
- console.log('✗ launcher.md not found. Run "atris init" first.');
4014
- process.exit(1);
4015
- }
4016
-
4017
- // Read launcher.md
4018
- const launcherSpec = fs.readFileSync(launcherFile, 'utf8');
4019
-
4020
- // Reference TASK_CONTEXTS.md (agents read on-demand)
4021
- const taskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
4022
-
4023
- // Reference MAP.md (agents read on-demand)
4024
- const mapFile = path.join(targetDir, 'MAP.md');
4025
- const mapPath = fs.existsSync(mapFile) ? path.relative(process.cwd(), mapFile) : null;
2162
+ // Reference MAP.md (agents read on-demand)
2163
+ const mapFile = path.join(targetDir, 'MAP.md');
2164
+ const mapPath = fs.existsSync(mapFile) ? path.relative(process.cwd(), mapFile) : null;
4026
2165
 
4027
2166
  // Reference journal (agents read on-demand)
4028
2167
  const { logFile, dateFormatted } = getLogPath();
@@ -4042,8 +2181,11 @@ function launchAtris() {
4042
2181
  console.log('');
4043
2182
  console.log('─────────────────────────────────────────────────────────────');
4044
2183
  console.log('');
4045
- const taskContextsPath = fs.existsSync(taskContextsFile) ? path.relative(process.cwd(), taskContextsFile) : null;
4046
- console.log('📝 TASK_CONTEXTS.md: ' + (taskContextsPath || 'Not found'));
2184
+ const taskFilePath = fs.existsSync(todoFile)
2185
+ ? todoFile
2186
+ : (fs.existsSync(legacyTaskContextsFile) ? legacyTaskContextsFile : null);
2187
+ const taskContextsPath = taskFilePath ? path.relative(process.cwd(), taskFilePath) : null;
2188
+ console.log('📝 TODO.md: ' + (taskContextsPath || 'Not found'));
4047
2189
  console.log(' Read for completed tasks context (usually small, or reference path if large).');
4048
2190
  console.log('');
4049
2191
  console.log('─────────────────────────────────────────────────────────────');
@@ -4104,152 +2246,6 @@ function launchAtris() {
4104
2246
  console.log('');
4105
2247
  }
4106
2248
 
4107
- function statusAtris(isQuick = false) {
4108
- const targetDir = path.join(process.cwd(), 'atris');
4109
-
4110
- if (!fs.existsSync(targetDir)) {
4111
- console.log('✗ atris/ folder not found. Run "atris init" first.');
4112
- process.exit(1);
4113
- }
4114
-
4115
- // Read TASK_CONTEXTS.md for backlog and in-progress tasks
4116
- const taskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
4117
- let backlogTasks = [];
4118
- let inProgressTasks = [];
4119
- if (fs.existsSync(taskContextsFile)) {
4120
- const taskContent = fs.readFileSync(taskContextsFile, 'utf8');
4121
-
4122
- // Extract Backlog
4123
- const backlogMatch = taskContent.match(/## Backlog\n([\s\S]*?)(?=\n##|$)/);
4124
- if (backlogMatch && backlogMatch[1].trim() && !backlogMatch[1].includes('(No active tasks)')) {
4125
- const tasks = backlogMatch[1].trim().split('\n### ').filter(t => t.trim());
4126
- backlogTasks = tasks.map(t => {
4127
- const match = t.match(/Task:\s*(.+)/);
4128
- return match ? match[1].substring(0, 60) : null;
4129
- }).filter(Boolean);
4130
- }
4131
-
4132
- // Extract In Progress
4133
- const inProgressMatch = taskContent.match(/## In Progress\n([\s\S]*?)(?=\n##|$)/);
4134
- if (inProgressMatch && inProgressMatch[1].trim() && !inProgressMatch[1].includes('Format:')) {
4135
- const tasks = inProgressMatch[1].trim().split('\n### ').filter(t => t.trim() && !t.startsWith('(Tasks') && !t.startsWith('Format:'));
4136
- inProgressTasks = tasks.map(t => {
4137
- const titleMatch = t.match(/Task:\s*(.+)/);
4138
- const claimMatch = t.match(/\*\*Claimed by:\*\*\s*(.+)/);
4139
- if (titleMatch) {
4140
- const title = titleMatch[1].substring(0, 40);
4141
- const claimed = claimMatch ? claimMatch[1].substring(0, 20) : 'Unknown';
4142
- return { title, claimed };
4143
- }
4144
- return null;
4145
- }).filter(Boolean);
4146
- }
4147
- }
4148
-
4149
- // Read journal for inbox and completions
4150
- const { logFile, dateFormatted } = getLogPath();
4151
- let inboxItems = [];
4152
- let completions = [];
4153
-
4154
- if (fs.existsSync(logFile)) {
4155
- const logContent = fs.readFileSync(logFile, 'utf8');
4156
-
4157
- // Extract inbox
4158
- const inboxMatch = logContent.match(/## Inbox\n([\s\S]*?)(?=\n##|$)/);
4159
- if (inboxMatch && inboxMatch[1].trim()) {
4160
- inboxItems = inboxMatch[1]
4161
- .trim()
4162
- .split('\n')
4163
- .filter(l => l.match(/^- \*\*I\d+:/))
4164
- .map(l => {
4165
- const match = l.match(/^- \*\*I(\d+):\s+(.+)$|^- \*\*I(\d+):\*\*\s*(.+)$/);
4166
- return match ? { id: match[1] || match[3], title: match[2] || match[4] } : null;
4167
- })
4168
- .filter(Boolean);
4169
- }
4170
-
4171
- // Extract recent completions
4172
- const completedMatch = logContent.match(/## Completed ✅\n([\s\S]*?)(?=\n##|---)/);
4173
- if (completedMatch && completedMatch[1].trim()) {
4174
- completions = completedMatch[1]
4175
- .trim()
4176
- .split('\n')
4177
- .filter(l => l.match(/^- \*\*C\d+:/))
4178
- .slice(-3)
4179
- .map(l => {
4180
- const match = l.match(/^- \*\*C(\d+):\s+(.+)$|^- \*\*C(\d+):\*\*\s*(.+)$/);
4181
- return match ? { id: match[1] || match[3], title: match[2] || match[4] } : null;
4182
- })
4183
- .filter(Boolean);
4184
- }
4185
- }
4186
-
4187
- // Quick mode: one-line summary
4188
- if (isQuick) {
4189
- console.log(`📥 ${inboxItems.length} | 📋 ${backlogTasks.length} | 🔨 ${inProgressTasks.length} | ✅ ${completions.length}`);
4190
- return;
4191
- }
4192
-
4193
- // Full display status
4194
- console.log('');
4195
- console.log('┌─────────────────────────────────────────────────────────────┐');
4196
- console.log(`│ ATRIS Status — ${dateFormatted}${' '.repeat(39 - dateFormatted.length)}│`);
4197
- console.log('└─────────────────────────────────────────────────────────────┘');
4198
- console.log('');
4199
-
4200
- // Backlog tasks
4201
- console.log(`📋 Backlog (unclaimed): ${backlogTasks.length}`);
4202
- if (backlogTasks.length > 0) {
4203
- backlogTasks.forEach(t => {
4204
- console.log(` • ${t}${t.length > 60 ? '...' : ''}`);
4205
- });
4206
- } else {
4207
- console.log(' (No backlog tasks)');
4208
- }
4209
- console.log('');
4210
-
4211
- // In Progress tasks
4212
- console.log(`🔨 In Progress (claimed): ${inProgressTasks.length}`);
4213
- if (inProgressTasks.length > 0) {
4214
- inProgressTasks.forEach(t => {
4215
- console.log(` • ${t.title}${t.title.length > 40 ? '...' : ''}`);
4216
- console.log(` Claimed by: ${t.claimed}`);
4217
- });
4218
- } else {
4219
- console.log(' (No tasks being worked on)');
4220
- }
4221
- console.log('');
4222
-
4223
- // Inbox
4224
- console.log(`📥 Inbox Items: ${inboxItems.length}`);
4225
- if (inboxItems.length > 0) {
4226
- inboxItems.slice(0, 3).forEach(i => {
4227
- console.log(` • I${i.id}: ${i.title.substring(0, 50)}${i.title.length > 50 ? '...' : ''}`);
4228
- });
4229
- if (inboxItems.length > 3) {
4230
- console.log(` ... and ${inboxItems.length - 3} more`);
4231
- }
4232
- } else {
4233
- console.log(' (No items in inbox)');
4234
- }
4235
- console.log('');
4236
-
4237
- // Recent completions
4238
- console.log(`✅ Recent Completions: ${completions.length}`);
4239
- if (completions.length > 0) {
4240
- completions.forEach(c => {
4241
- console.log(` • C${c.id}: ${c.title.substring(0, 50)}${c.title.length > 50 ? '...' : ''}`);
4242
- });
4243
- } else {
4244
- console.log(' (No completions yet)');
4245
- }
4246
- console.log('');
4247
-
4248
- console.log('─────────────────────────────────────────────────────────────');
4249
- console.log('Next: atris plan → do → review (or atris log to add ideas)');
4250
- console.log('');
4251
- }
4252
-
4253
2249
  function spawnClaudeCodeSession(url, token, body) {
4254
2250
  return new Promise((resolve, reject) => {
4255
2251
  const parsed = new URL(url);
@@ -4401,146 +2397,3 @@ function streamProChat(url, token, body, showTools = false) {
4401
2397
  });
4402
2398
  }
4403
2399
 
4404
- function analyticsAtris() {
4405
- const targetDir = path.join(process.cwd(), 'atris');
4406
-
4407
- if (!fs.existsSync(targetDir)) {
4408
- console.log('✗ atris/ folder not found. Run "atris init" first.');
4409
- process.exit(1);
4410
- }
4411
-
4412
- // Get date range (today + last 7 days)
4413
- const today = new Date();
4414
- const dates = [];
4415
- for (let i = 0; i < 7; i++) {
4416
- const date = new Date(today);
4417
- date.setDate(date.getDate() - i);
4418
- dates.push(date);
4419
- }
4420
-
4421
- // Parse journals and collect data
4422
- let totalCompletions = 0;
4423
- let todayCompletions = 0;
4424
- let todayInbox = 0;
4425
- let oldestInbox = 0;
4426
- const completionsByDay = {};
4427
- const hourCounts = {};
4428
-
4429
- dates.forEach((date, index) => {
4430
- const year = date.getFullYear();
4431
- // Use local timezone, not UTC (fixes timezone bug)
4432
- const month = String(date.getMonth() + 1).padStart(2, '0');
4433
- const day = String(date.getDate()).padStart(2, '0');
4434
- const dateFormatted = `${year}-${month}-${day}`;
4435
- const logPath = path.join(targetDir, 'logs', year.toString(), `${dateFormatted}.md`);
4436
-
4437
- if (!fs.existsSync(logPath)) {
4438
- completionsByDay[dateFormatted] = 0;
4439
- return;
4440
- }
4441
-
4442
- const content = fs.readFileSync(logPath, 'utf8');
4443
-
4444
- // Count completions (C# pattern)
4445
- const completionMatches = content.match(/- \*\*C\d+:/g);
4446
- const completionCount = completionMatches ? completionMatches.length : 0;
4447
- completionsByDay[dateFormatted] = completionCount;
4448
- totalCompletions += completionCount;
4449
-
4450
- if (index === 0) {
4451
- todayCompletions = completionCount;
4452
-
4453
- // Count today's inbox
4454
- const inboxMatch = content.match(/## Inbox\n([\s\S]*?)(?=\n##|---)/);
4455
- if (inboxMatch && inboxMatch[1].trim()) {
4456
- const inboxMatches = inboxMatch[1].match(/- \*\*I\d+:/g);
4457
- todayInbox = inboxMatches ? inboxMatches.length : 0;
4458
- }
4459
- }
4460
-
4461
- if (index === 6) {
4462
- // Count oldest day's inbox for trend
4463
- const inboxMatch = content.match(/## Inbox\n([\s\S]*?)(?=\n##|---)/);
4464
- if (inboxMatch && inboxMatch[1].trim()) {
4465
- const inboxMatches = inboxMatch[1].match(/- \*\*I\d+:/g);
4466
- oldestInbox = inboxMatches ? inboxMatches.length : 0;
4467
- }
4468
- }
4469
-
4470
- // Parse timestamps for productivity hours
4471
- const timestampMatches = content.match(/\*\*(\d{2}):(\d{2}):(\d{2})\*\*/g);
4472
- if (timestampMatches) {
4473
- timestampMatches.forEach(ts => {
4474
- const hour = parseInt(ts.match(/\d{2}/)[0]);
4475
- hourCounts[hour] = (hourCounts[hour] || 0) + 1;
4476
- });
4477
- }
4478
- });
4479
-
4480
- // Calculate metrics
4481
- const velocity = (totalCompletions / 7).toFixed(1);
4482
- const inboxTrend = todayInbox > oldestInbox ? 'Growing ⬆' :
4483
- todayInbox < oldestInbox ? 'Shrinking ⬇' :
4484
- 'Stable →';
4485
-
4486
- // Find most productive hour
4487
- let mostProductiveHour = null;
4488
- let maxCount = 0;
4489
- Object.keys(hourCounts).forEach(hour => {
4490
- if (hourCounts[hour] > maxCount) {
4491
- maxCount = hourCounts[hour];
4492
- mostProductiveHour = hour;
4493
- }
4494
- });
4495
-
4496
- const productiveHours = mostProductiveHour !== null ?
4497
- `${mostProductiveHour}:00 - ${(parseInt(mostProductiveHour) + 1) % 24}:00` :
4498
- 'No data';
4499
-
4500
- // Display analytics
4501
- // Use local timezone, not UTC (fixes timezone bug)
4502
- const year = today.getFullYear();
4503
- const month = String(today.getMonth() + 1).padStart(2, '0');
4504
- const day = String(today.getDate()).padStart(2, '0');
4505
- const dateFormatted = `${year}-${month}-${day}`;
4506
- console.log('');
4507
- console.log('┌─────────────────────────────────────────────────────────────┐');
4508
- console.log(`│ ATRIS Analytics — ${dateFormatted}${' '.repeat(33 - dateFormatted.length)}│`);
4509
- console.log('└─────────────────────────────────────────────────────────────┘');
4510
- console.log('');
4511
-
4512
- // Today's performance
4513
- console.log(`📊 Today's Performance`);
4514
- console.log(` Completions: ${todayCompletions}`);
4515
- console.log(` Inbox items: ${todayInbox}`);
4516
- console.log('');
4517
-
4518
- // Weekly trends
4519
- console.log(`📈 Weekly Trends (Last 7 Days)`);
4520
- console.log(` Total completions: ${totalCompletions}`);
4521
- console.log(` Average velocity: ${velocity} completions/day`);
4522
- console.log(` Inbox trend: ${inboxTrend}`);
4523
- console.log('');
4524
-
4525
- // Productivity patterns
4526
- console.log(`⏰ Productivity Patterns`);
4527
- console.log(` Most active hour: ${productiveHours}`);
4528
- console.log(` Activity count: ${maxCount} timestamps`);
4529
- console.log('');
4530
-
4531
- // Daily breakdown
4532
- console.log(`📅 Daily Breakdown`);
4533
- const sortedDates = Object.keys(completionsByDay).sort().reverse();
4534
- sortedDates.forEach((date, index) => {
4535
- const count = completionsByDay[date];
4536
- const bar = '█'.repeat(count);
4537
- const label = index === 0 ? ' (today)' : '';
4538
- console.log(` ${date}: ${bar} ${count}${label}`);
4539
- });
4540
- console.log('');
4541
-
4542
- console.log('─────────────────────────────────────────────────────────────');
4543
- console.log('💡 Insight: This data syncs to backend via "atris log sync"');
4544
- console.log('');
4545
- }
4546
-