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/GETTING_STARTED.md +10 -7
- package/PERSONA.md +2 -2
- package/README.md +6 -7
- package/atris/GETTING_STARTED.md +37 -32
- package/atris/PERSONA.md +2 -2
- package/atris/agent_team/navigator.md +4 -4
- package/atris/agent_team/validator.md +1 -1
- package/atris/atris.md +84 -17
- package/atris.md +81 -14
- package/bin/atris.js +359 -2506
- package/commands/analytics.js +150 -0
- package/commands/brainstorm.js +1291 -0
- package/commands/init.js +33 -6
- package/commands/status.js +159 -0
- package/commands/sync.js +8 -0
- package/commands/visualize.js +74 -0
- package/commands/workflow.js +834 -0
- package/lib/journal.js +35 -0
- package/lib/state-detection.js +10 -6
- package/package.json +1 -1
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('
|
|
112
|
-
console.log('
|
|
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 -
|
|
119
|
+
console.log(' analytics - Show recent productivity from journals');
|
|
116
120
|
console.log('');
|
|
117
|
-
console.log('
|
|
118
|
-
console.log('
|
|
119
|
-
console.log('
|
|
120
|
-
console.log('
|
|
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('
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
.
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
430
|
+
statusCmd(isQuick);
|
|
233
431
|
} else if (command === 'analytics') {
|
|
234
|
-
|
|
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
|
|
1819
|
-
|
|
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
|
|
1822
|
-
if (!fs.existsSync(
|
|
1823
|
-
console.log('
|
|
1824
|
-
|
|
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
|
-
|
|
1828
|
-
const
|
|
1829
|
-
|
|
1830
|
-
|
|
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
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
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
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
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
|
|
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
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
2061
|
+
// Show existing features
|
|
2062
|
+
if (existingFeatures.length > 0) {
|
|
2063
|
+
console.log('📦 Features: ' + existingFeatures.join(', '));
|
|
2064
|
+
console.log('');
|
|
1888
2065
|
}
|
|
1889
2066
|
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
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
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
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
|
-
//
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
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
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
2106
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
2107
|
+
console.log('🤖 atrisDev Protocol — Navigator Agent');
|
|
2108
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
2109
|
+
console.log('');
|
|
1951
2110
|
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
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
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
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
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
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
|
-
|
|
1998
|
-
|
|
2150
|
+
if (!fs.existsSync(launcherFile)) {
|
|
2151
|
+
console.log('✗ launcher.md not found. Run "atris init" first.');
|
|
2152
|
+
process.exit(1);
|
|
2153
|
+
}
|
|
1999
2154
|
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
let inboxItems = parseInboxItems(initialContent);
|
|
2155
|
+
// Read launcher.md
|
|
2156
|
+
const launcherSpec = fs.readFileSync(launcherFile, 'utf8');
|
|
2003
2157
|
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
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
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
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
|
|
4046
|
-
|
|
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
|
-
|