atris 2.6.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +124 -34
  2. package/atris/CLAUDE.md +5 -1
  3. package/atris/atris.md +4 -0
  4. package/atris/features/README.md +24 -0
  5. package/atris/skills/autopilot/SKILL.md +74 -75
  6. package/atris/skills/endgame/SKILL.md +179 -0
  7. package/atris/skills/flow/SKILL.md +121 -0
  8. package/atris/skills/improve/SKILL.md +84 -0
  9. package/atris/skills/loop/SKILL.md +72 -0
  10. package/atris/skills/wiki/SKILL.md +61 -0
  11. package/atris/team/executor/MEMBER.md +10 -4
  12. package/atris/team/navigator/MEMBER.md +2 -0
  13. package/atris/team/validator/MEMBER.md +8 -5
  14. package/atris.md +33 -0
  15. package/bin/atris.js +210 -41
  16. package/commands/activate.js +28 -2
  17. package/commands/align.js +720 -0
  18. package/commands/auth.js +75 -2
  19. package/commands/autopilot.js +1213 -270
  20. package/commands/browse.js +100 -0
  21. package/commands/business.js +785 -12
  22. package/commands/clean.js +107 -2
  23. package/commands/computer.js +429 -0
  24. package/commands/context-sync.js +78 -8
  25. package/commands/experiments.js +351 -0
  26. package/commands/feedback.js +150 -0
  27. package/commands/fleet.js +395 -0
  28. package/commands/fork.js +127 -0
  29. package/commands/init.js +50 -1
  30. package/commands/learn.js +407 -0
  31. package/commands/lifecycle.js +94 -0
  32. package/commands/loop.js +114 -0
  33. package/commands/publish.js +129 -0
  34. package/commands/pull.js +434 -48
  35. package/commands/push.js +312 -164
  36. package/commands/review.js +149 -0
  37. package/commands/run.js +76 -43
  38. package/commands/serve.js +360 -0
  39. package/commands/setup.js +1 -1
  40. package/commands/soul.js +381 -0
  41. package/commands/status.js +119 -1
  42. package/commands/sync.js +147 -1
  43. package/commands/terminal.js +201 -0
  44. package/commands/wiki.js +376 -0
  45. package/commands/workflow.js +191 -74
  46. package/commands/workspace-clean.js +3 -3
  47. package/lib/endstate.js +259 -0
  48. package/lib/learnings.js +235 -0
  49. package/lib/manifest.js +1 -0
  50. package/lib/todo.js +9 -5
  51. package/lib/wiki.js +578 -0
  52. package/package.json +2 -2
  53. package/utils/api.js +48 -36
  54. package/utils/auth.js +1 -0
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Atris Review — Run code review with specialist agents
3
+ *
4
+ * atris review — Review staged changes
5
+ * atris review <file> — Review a specific file
6
+ * atris review --diff HEAD~1 — Review last commit
7
+ * atris review --all — Audit all Python services
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const { execSync, spawnSync } = require('child_process');
13
+
14
+ function findReviewEngine() {
15
+ // Look for review_engine.py in common locations
16
+ const candidates = [
17
+ path.join(process.cwd(), 'atris', 'business', 'claude-code-review', 'workspace', 'review_engine.py'),
18
+ path.join(process.cwd(), 'review_engine.py'),
19
+ ];
20
+ // Also check parent dirs
21
+ let dir = process.cwd();
22
+ for (let i = 0; i < 3; i++) {
23
+ candidates.push(path.join(dir, 'atris', 'business', 'claude-code-review', 'workspace', 'review_engine.py'));
24
+ dir = path.dirname(dir);
25
+ }
26
+ for (const p of candidates) {
27
+ if (fs.existsSync(p)) return p;
28
+ }
29
+ return null;
30
+ }
31
+
32
+ function runReview(args) {
33
+ const enginePath = findReviewEngine();
34
+ if (!enginePath) {
35
+ console.error('Review engine not found.');
36
+ console.error('Expected at: atris/business/claude-code-review/workspace/review_engine.py');
37
+ console.error('');
38
+ console.error('The review engine runs 6 specialists:');
39
+ console.error(' Security, Testing, Performance, Maintainability, Database, Async');
40
+ console.error('');
41
+ console.error('Install: copy review_engine.py to your project');
42
+ process.exit(1);
43
+ }
44
+
45
+ // Parse args
46
+ let file = null;
47
+ let diffRef = null;
48
+ let allMode = false;
49
+ let jsonMode = false;
50
+
51
+ for (let i = 0; i < args.length; i++) {
52
+ if (args[i] === '--diff' && args[i + 1]) {
53
+ diffRef = args[i + 1];
54
+ i++;
55
+ } else if (args[i] === '--all') {
56
+ allMode = true;
57
+ } else if (args[i] === '--json') {
58
+ jsonMode = true;
59
+ } else if (!args[i].startsWith('-')) {
60
+ file = args[i];
61
+ }
62
+ }
63
+
64
+ // Build command
65
+ const cmdArgs = ['python3', enginePath];
66
+ if (file) {
67
+ cmdArgs.push('--file', file);
68
+ } else if (diffRef) {
69
+ cmdArgs.push('--diff', diffRef);
70
+ }
71
+ if (jsonMode) {
72
+ cmdArgs.push('--json');
73
+ }
74
+
75
+ if (allMode) {
76
+ // Audit all Python services
77
+ console.log('Auditing all Python services...\n');
78
+ const servicesDir = path.join(process.cwd(), 'backend', 'services');
79
+ if (!fs.existsSync(servicesDir)) {
80
+ console.error('No backend/services/ directory found.');
81
+ process.exit(1);
82
+ }
83
+
84
+ const files = fs.readdirSync(servicesDir).filter(f => f.endsWith('.py'));
85
+ let totalFindings = 0;
86
+ let cleanCount = 0;
87
+ const issues = [];
88
+
89
+ for (const f of files) {
90
+ const filePath = path.join(servicesDir, f);
91
+ try {
92
+ const result = spawnSync('python3', [enginePath, '--file', filePath, '--json'], {
93
+ encoding: 'utf8', timeout: 10000,
94
+ });
95
+ if (result.stdout) {
96
+ const data = JSON.parse(result.stdout);
97
+ const highMed = data.findings.filter(x => x.severity === 'high' || x.severity === 'medium');
98
+ if (highMed.length > 0) {
99
+ issues.push({ file: f, score: data.quality_score, count: highMed.length, top: highMed[0].rule });
100
+ totalFindings += highMed.length;
101
+ } else {
102
+ cleanCount++;
103
+ }
104
+ }
105
+ } catch {}
106
+ }
107
+
108
+ issues.sort((a, b) => a.score - b.score);
109
+
110
+ console.log(`AUDIT: ${files.length} services | ${cleanCount} clean | ${issues.length} with findings\n`);
111
+ if (issues.length > 0) {
112
+ console.log(`${'Service'.padEnd(40)} ${'Score'.padStart(6)} ${'Findings'.padStart(8)} Top Issue`);
113
+ console.log(`${'─'.repeat(40)} ${'─'.repeat(6)} ${'─'.repeat(8)} ${'─'.repeat(15)}`);
114
+ for (const i of issues) {
115
+ console.log(`${i.file.padEnd(40)} ${(i.score + '/10').padStart(6)} ${String(i.count).padStart(8)} ${i.top}`);
116
+ }
117
+ }
118
+ console.log('');
119
+ return;
120
+ }
121
+
122
+ // Run single file or diff review
123
+ const result = spawnSync(cmdArgs[0], cmdArgs.slice(1), {
124
+ encoding: 'utf8',
125
+ stdio: 'inherit',
126
+ timeout: 30000,
127
+ });
128
+
129
+ process.exit(result.status || 0);
130
+ }
131
+
132
+ async function reviewCommand(...args) {
133
+ if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
134
+ console.log('Usage: atris review [file] [options]');
135
+ console.log('');
136
+ console.log(' atris review Review staged changes');
137
+ console.log(' atris review <file.py> Review a specific file');
138
+ console.log(' atris review --diff HEAD~1 Review last commit');
139
+ console.log(' atris review --all Audit all backend services');
140
+ console.log(' atris review --json Machine-readable output');
141
+ console.log('');
142
+ console.log('6 specialists: Security, Testing, Performance, Maintainability, Database, Async');
143
+ return;
144
+ }
145
+
146
+ runReview(args);
147
+ }
148
+
149
+ module.exports = { reviewCommand };
package/commands/run.js CHANGED
@@ -154,7 +154,10 @@ function hasWork(atrisDir) {
154
154
  const content = fs.readFileSync(logFile, 'utf8');
155
155
  const inboxMatch = content.match(/## Inbox\n([\s\S]*?)(?=\n##|$)/);
156
156
  if (inboxMatch && inboxMatch[1].trim()) {
157
- const items = inboxMatch[1].trim().split('\n').filter(l => l.trim().startsWith('-'));
157
+ const items = inboxMatch[1].trim().split('\n').filter(l => {
158
+ const t = l.trim();
159
+ return t.startsWith('- ') && t.length > 2;
160
+ });
158
161
  if (items.length > 0) return true;
159
162
  }
160
163
  }
@@ -225,14 +228,20 @@ async function runAtris(options = {}) {
225
228
  }
226
229
 
227
230
  console.log('');
228
- console.log('┌─────────────────────────────────────────────────────────────┐');
229
- console.log(`│ Atris Run v${pkg.version} — autonomous plan → do → review │`);
230
- console.log('└─────────────────────────────────────────────────────────────┘');
231
- console.log('');
232
- console.log(`Max cycles: ${cycles}`);
233
- console.log(`Phase timeout: ${timeout / 1000}s`);
234
- console.log(`Verbose: ${verbose}`);
235
- console.log('');
231
+ if (verbose) {
232
+ console.log('┌─────────────────────────────────────────────────────────────┐');
233
+ console.log(`│ Atris Run v${pkg.version} — autonomous plan → do → review │`);
234
+ console.log('└─────────────────────────────────────────────────────────────┘');
235
+ console.log('');
236
+ console.log(`Max cycles: ${cycles}`);
237
+ console.log(`Phase timeout: ${timeout / 1000}s`);
238
+ console.log(`Verbose: ${verbose}`);
239
+ console.log('');
240
+ } else {
241
+ console.log(`atris run v${pkg.version} — plan, do, review, repeat.`);
242
+ console.log(`i'll run up to ${cycles} cycle${cycles === 1 ? '' : 's'}, ${timeout / 1000}s per phase. next i'll check the backlog.`);
243
+ console.log('');
244
+ }
236
245
 
237
246
  // Build context paths
238
247
  const context = {
@@ -255,13 +264,19 @@ async function runAtris(options = {}) {
255
264
  let completedCycles = 0;
256
265
 
257
266
  for (let cycle = 1; cycle <= cycles; cycle++) {
258
- console.log(`\n${'━'.repeat(60)}`);
259
- console.log(`CYCLE ${cycle}/${cycles}`);
260
- console.log(`${'━'.repeat(60)}`);
267
+ if (verbose) {
268
+ console.log(`\n${'━'.repeat(60)}`);
269
+ console.log(`CYCLE ${cycle}/${cycles}`);
270
+ console.log(`${'━'.repeat(60)}`);
271
+ } else {
272
+ console.log(`\ncycle ${cycle} of ${cycles}.`);
273
+ }
261
274
 
262
275
  // Check if there's work
263
276
  if (!hasWork(atrisDir)) {
264
- console.log('\nInbox empty. Backlog empty. Nothing to do.');
277
+ console.log(verbose
278
+ ? '\nInbox empty. Backlog empty. Nothing to do.'
279
+ : 'i checked the inbox and backlog. both empty. nothing to do.');
265
280
  break;
266
281
  }
267
282
 
@@ -269,67 +284,81 @@ async function runAtris(options = {}) {
269
284
 
270
285
  try {
271
286
  // PLAN
272
- console.log('\n[1/3] PLAN — reading inbox, creating tasks...');
287
+ console.log(verbose
288
+ ? '\n[1/3] PLAN — reading inbox, creating tasks...'
289
+ : 'planning… reading inbox, turning ideas into tasks.');
273
290
  let phaseStart = Date.now();
274
291
  const planOutput = executePhase('plan', context, { verbose, timeout });
275
292
  timing.plan = Date.now() - phaseStart;
276
293
 
277
294
  if (planOutput.includes('[NOTHING_TO_DO]')) {
278
- console.log('Nothing to do. Stopping.');
295
+ console.log(verbose ? 'Nothing to do. Stopping.' : 'navigator says nothing to do. stopping.');
279
296
  break;
280
297
  }
281
- console.log(`✓ Plan complete (${Math.round(timing.plan / 1000)}s)`);
298
+ console.log(verbose
299
+ ? `✓ Plan complete (${Math.round(timing.plan / 1000)}s)`
300
+ : `planned in ${Math.round(timing.plan / 1000)}s. next i'll pick the top backlog task and build it.`);
282
301
 
283
302
  // Check if plan created tasks
284
303
  if (!hasWork(atrisDir)) {
285
- console.log('No tasks created. Stopping.');
304
+ console.log(verbose ? 'No tasks created. Stopping.' : 'no tasks got created. stopping.');
286
305
  break;
287
306
  }
288
307
 
289
308
  // DO
290
- console.log('\n[2/3] DO — building task...');
309
+ console.log(verbose ? '\n[2/3] DO — building task...' : 'building the top task now.');
291
310
  phaseStart = Date.now();
292
311
  executePhase('do', context, { verbose, timeout });
293
312
  timing.do = Date.now() - phaseStart;
294
- console.log(`✓ Build complete (${Math.round(timing.do / 1000)}s)`);
313
+ console.log(verbose
314
+ ? `✓ Build complete (${Math.round(timing.do / 1000)}s)`
315
+ : `built in ${Math.round(timing.do / 1000)}s. next i'll review it.`);
295
316
 
296
317
  // REVIEW
297
- console.log('\n[3/3] REVIEW — validating...');
318
+ console.log(verbose ? '\n[3/3] REVIEW — validating...' : 'reviewing the change against tests and validate.md.');
298
319
  phaseStart = Date.now();
299
320
  const reviewOutput = executePhase('review', context, { verbose, timeout });
300
321
  timing.review = Date.now() - phaseStart;
301
322
 
302
323
  if (reviewOutput.includes('[REVIEW_FAILED]')) {
303
- console.log('⚠ Review found issues. Stopping for manual check.');
324
+ console.log(verbose
325
+ ? '⚠ Review found issues. Stopping for manual check.'
326
+ : 'review found issues. stopping so a human can look.');
304
327
  cycleTimings.push(timing);
305
328
  completedCycles++;
306
329
  break;
307
330
  }
308
- console.log(`✓ Review complete (${Math.round(timing.review / 1000)}s)`);
331
+ console.log(verbose
332
+ ? `✓ Review complete (${Math.round(timing.review / 1000)}s)`
333
+ : `review passed in ${Math.round(timing.review / 1000)}s.`);
309
334
 
310
335
  cycleTimings.push(timing);
311
336
  completedCycles++;
312
337
 
313
338
  // Self-heal MAP.md refs after each cycle
314
- console.log('\n[+] CLEAN — healing MAP.md refs...');
339
+ console.log(verbose
340
+ ? '\n[+] CLEAN — healing MAP.md refs...'
341
+ : 'cleaning up drifted MAP.md refs.');
315
342
  try {
316
343
  cleanAtris({ dryRun: false });
317
344
  } catch (cleanErr) {
318
- console.log(`⚠ Clean failed: ${cleanErr.message}`);
345
+ console.log(`${verbose ? '⚠ Clean failed: ' : 'clean failed: '}${cleanErr.message}`);
319
346
  }
320
347
 
321
348
  // Auto-push if not disabled
322
349
  if (push) {
323
- console.log('\n[+] PUSH — pushing to remote...');
350
+ console.log(verbose ? '\n[+] PUSH — pushing to remote...' : 'pushing to remote.');
324
351
  try {
325
352
  execSync('git push', { cwd: process.cwd(), encoding: 'utf8', stdio: 'pipe' });
326
- console.log('✓ Pushed to remote');
353
+ console.log(verbose ? '✓ Pushed to remote' : 'pushed.');
327
354
  } catch (pushErr) {
328
- console.log(`⚠ Push failed: ${pushErr.message.split('\n')[0]}`);
355
+ console.log(`${verbose ? '⚠ Push failed: ' : 'push failed: '}${pushErr.message.split('\n')[0]}`);
329
356
  }
330
357
  }
331
358
 
332
- console.log(`\n✓ Cycle ${cycle} done`);
359
+ console.log(verbose
360
+ ? `\n✓ Cycle ${cycle} done`
361
+ : `cycle ${cycle} done. next cycle.`);
333
362
 
334
363
  } catch (err) {
335
364
  console.error(`\n✗ Cycle ${cycle} failed: ${err.message}`);
@@ -343,24 +372,28 @@ async function runAtris(options = {}) {
343
372
  logRunCompletion(completedCycles, startTime, cycleTimings);
344
373
 
345
374
  console.log('');
346
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
347
- console.log(`Run complete. ${elapsed}s elapsed.`);
375
+ if (verbose) {
376
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
377
+ console.log(`Run complete. ${elapsed}s elapsed.`);
378
+
379
+ if (cycleTimings.length > 0) {
380
+ console.log('');
381
+ console.log(' Cycle │ Plan │ Do │ Review');
382
+ console.log(' ───────┼─────────┼─────────┼────────');
383
+ cycleTimings.forEach((t, i) => {
384
+ const p = `${Math.round(t.plan / 1000)}s`.padStart(5);
385
+ const d = `${Math.round(t.do / 1000)}s`.padStart(5);
386
+ const r = `${Math.round(t.review / 1000)}s`.padStart(5);
387
+ console.log(` ${String(i + 1).padStart(2)} │ ${p} │ ${d} │ ${r}`);
388
+ });
389
+ }
348
390
 
349
- // Print phase duration summary table
350
- if (cycleTimings.length > 0) {
391
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
392
+ console.log('');
393
+ } else {
394
+ console.log(`run complete. ${completedCycles} cycle${completedCycles === 1 ? '' : 's'} in ${elapsed}s. logged to today's journal.`);
351
395
  console.log('');
352
- console.log(' Cycle │ Plan │ Do │ Review');
353
- console.log(' ───────┼─────────┼─────────┼────────');
354
- cycleTimings.forEach((t, i) => {
355
- const p = `${Math.round(t.plan / 1000)}s`.padStart(5);
356
- const d = `${Math.round(t.do / 1000)}s`.padStart(5);
357
- const r = `${Math.round(t.review / 1000)}s`.padStart(5);
358
- console.log(` ${String(i + 1).padStart(2)} │ ${p} │ ${d} │ ${r}`);
359
- });
360
396
  }
361
-
362
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
363
- console.log('');
364
397
  }
365
398
 
366
399
  module.exports = { runAtris };