sage-team 3.5.3 → 3.7.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 (96) hide show
  1. package/README.md +40 -9
  2. package/dist/cli/commands/setup-claude.d.ts +3 -0
  3. package/dist/cli/commands/setup-claude.d.ts.map +1 -0
  4. package/dist/cli/commands/setup-claude.js +75 -0
  5. package/dist/cli/commands/setup-claude.js.map +1 -0
  6. package/dist/cli/index.d.ts.map +1 -1
  7. package/dist/cli/index.js +3 -1
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/engine/__tests__/ceo-brain.test.js +2 -2
  10. package/dist/engine/__tests__/ceo-brain.test.js.map +1 -1
  11. package/dist/engine/ceo-brain.d.ts +15 -8
  12. package/dist/engine/ceo-brain.d.ts.map +1 -1
  13. package/dist/engine/ceo-brain.js +326 -208
  14. package/dist/engine/ceo-brain.js.map +1 -1
  15. package/dist/engine/orchestrator.d.ts +1 -0
  16. package/dist/engine/orchestrator.d.ts.map +1 -1
  17. package/dist/engine/orchestrator.js +27 -1
  18. package/dist/engine/orchestrator.js.map +1 -1
  19. package/dist/mcp/server.js +1 -1
  20. package/dist/server/index.d.ts.map +1 -1
  21. package/dist/server/index.js +9 -3
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/server/routes/api.d.ts.map +1 -1
  24. package/dist/server/routes/api.js +10 -3
  25. package/dist/server/routes/api.js.map +1 -1
  26. package/dist/state/repositories/agents.js +1 -1
  27. package/dist/state/repositories/agents.js.map +1 -1
  28. package/{src/web/dist/assets/BufferResource-CnEVFTWO.js → dist/web/dist/assets/BufferResource-BGsixSHO.js} +1 -1
  29. package/dist/web/dist/assets/BufferResource-BbpTIQ85.js +185 -0
  30. package/dist/web/dist/assets/BufferResource-BjJe3r2a.js +185 -0
  31. package/dist/web/dist/assets/BufferResource-C7ypptTD.js +185 -0
  32. package/dist/web/dist/assets/BufferResource-CuR0zQl9.js +185 -0
  33. package/dist/web/dist/assets/BufferResource-_EbXUkK9.js +185 -0
  34. package/dist/web/dist/assets/BufferResource-dCF0GA9M.js +185 -0
  35. package/{src/web/dist/assets/CanvasRenderer-DJddxDJS.js → dist/web/dist/assets/CanvasRenderer-BGC-Rjpm.js} +1 -1
  36. package/dist/web/dist/assets/CanvasRenderer-BQ7ScDtX.js +1 -0
  37. package/dist/web/dist/assets/CanvasRenderer-CBkg7qb3.js +1 -0
  38. package/dist/web/dist/assets/CanvasRenderer-CcFmL3Lw.js +1 -0
  39. package/dist/web/dist/assets/CanvasRenderer-CwKoxkGD.js +1 -0
  40. package/dist/web/dist/assets/CanvasRenderer-YM8daXJu.js +1 -0
  41. package/dist/web/dist/assets/CanvasRenderer-yT9JOc5o.js +1 -0
  42. package/{src/web/dist/assets/Filter-DWNM0gBH.js → dist/web/dist/assets/Filter-BzM7i9ln.js} +1 -1
  43. package/dist/web/dist/assets/Filter-CJvSyeWH.js +1 -0
  44. package/dist/web/dist/assets/Filter-CUPFlJF_.js +1 -0
  45. package/dist/web/dist/assets/Filter-D2kh6Lyn.js +1 -0
  46. package/dist/web/dist/assets/Filter-DMjNMMPy.js +1 -0
  47. package/dist/web/dist/assets/Filter-GQK6iqrb.js +1 -0
  48. package/dist/web/dist/assets/Filter-nUtqhIEl.js +1 -0
  49. package/{src/web/dist/assets/RenderTargetSystem-C4fzbyMy.js → dist/web/dist/assets/RenderTargetSystem-B-ekYymr.js} +1 -1
  50. package/dist/web/dist/assets/RenderTargetSystem-BylEtp0F.js +172 -0
  51. package/dist/web/dist/assets/RenderTargetSystem-D2L3KLdt.js +172 -0
  52. package/dist/web/dist/assets/RenderTargetSystem-DNsILH5P.js +172 -0
  53. package/dist/web/dist/assets/RenderTargetSystem-lhfSOVIz.js +172 -0
  54. package/dist/web/dist/assets/RenderTargetSystem-nRg6zEP8.js +172 -0
  55. package/dist/web/dist/assets/RenderTargetSystem-uCCnK3Aw.js +172 -0
  56. package/{src/web/dist/assets/WebGLRenderer-Dl5dddTJ.js → dist/web/dist/assets/WebGLRenderer-BEupTZT-.js} +1 -1
  57. package/dist/web/dist/assets/WebGLRenderer-BJuclWjv.js +156 -0
  58. package/dist/web/dist/assets/WebGLRenderer-BONohh1V.js +156 -0
  59. package/dist/web/dist/assets/WebGLRenderer-Bsr1n0Uu.js +156 -0
  60. package/dist/web/dist/assets/WebGLRenderer-Da_BkFlZ.js +156 -0
  61. package/dist/web/dist/assets/WebGLRenderer-DiL4ivWY.js +156 -0
  62. package/dist/web/dist/assets/WebGLRenderer-DqRhyEby.js +156 -0
  63. package/{src/web/dist/assets/WebGPURenderer-DHzWrTEL.js → dist/web/dist/assets/WebGPURenderer-C0BJNyre.js} +1 -1
  64. package/dist/web/dist/assets/WebGPURenderer-Clrsu_cP.js +41 -0
  65. package/dist/web/dist/assets/WebGPURenderer-DbGOPnSE.js +41 -0
  66. package/dist/web/dist/assets/WebGPURenderer-Ds8VNo2-.js +41 -0
  67. package/dist/web/dist/assets/WebGPURenderer-Du7e_Agz.js +41 -0
  68. package/dist/web/dist/assets/WebGPURenderer-pLgFan3y.js +41 -0
  69. package/dist/web/dist/assets/WebGPURenderer-pd03jK7j.js +41 -0
  70. package/{src/web/dist/assets/browserAll-CrRQwob6.js → dist/web/dist/assets/browserAll-1QqybVac.js} +1 -1
  71. package/dist/web/dist/assets/browserAll-Bd3WFbs9.js +14 -0
  72. package/dist/web/dist/assets/browserAll-Bv-Fa9BT.js +14 -0
  73. package/dist/web/dist/assets/browserAll-C9HRyLf9.js +14 -0
  74. package/dist/web/dist/assets/browserAll-D7SUz7ce.js +14 -0
  75. package/dist/web/dist/assets/browserAll-TAEt2LTG.js +14 -0
  76. package/dist/web/dist/assets/browserAll-d1yltqWP.js +14 -0
  77. package/dist/web/dist/assets/index-BQx3wJPr.css +1 -0
  78. package/dist/web/dist/assets/index-C1XGJE1t.js +298 -0
  79. package/dist/web/dist/assets/index-C6hoHqN5.js +298 -0
  80. package/dist/web/dist/assets/index-CFVD9jMF.js +298 -0
  81. package/dist/web/dist/assets/index-CO3-IuiC.js +298 -0
  82. package/dist/web/dist/assets/index-CRT2SA6d.js +298 -0
  83. package/dist/web/dist/assets/index-CVcQAOB6.css +1 -0
  84. package/dist/web/dist/assets/index-CwYVpEl9.js +298 -0
  85. package/dist/web/dist/assets/index-DBSQsI6C.js +298 -0
  86. package/{src/web/dist/assets/webworkerAll-B9FNurHh.js → dist/web/dist/assets/webworkerAll-B3fxgx9g.js} +1 -1
  87. package/dist/web/dist/assets/webworkerAll-BILg7nF2.js +83 -0
  88. package/dist/web/dist/assets/webworkerAll-C7T1zoFZ.js +83 -0
  89. package/dist/web/dist/assets/webworkerAll-CMt6Ulvh.js +83 -0
  90. package/dist/web/dist/assets/webworkerAll-CREQA0I8.js +83 -0
  91. package/dist/web/dist/assets/webworkerAll-CykNhAo8.js +83 -0
  92. package/dist/web/dist/assets/webworkerAll-WnWYpa5G.js +83 -0
  93. package/{src → dist}/web/dist/index.html +2 -2
  94. package/package.json +2 -3
  95. package/src/web/dist/assets/index-8Qtg1JIx.css +0 -1
  96. package/src/web/dist/assets/index-PrNo7ukG.js +0 -298
@@ -6,88 +6,109 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.CEOBrain = void 0;
7
7
  const child_process_1 = require("child_process");
8
8
  const path_1 = __importDefault(require("path"));
9
+ const fs_1 = __importDefault(require("fs"));
9
10
  const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
10
- const PROJECT_TYPES = [
11
- {
12
- keywords: ['site', 'website', 'web', 'landing', 'página', 'pagina', 'homepage'],
13
- greeting: 'Love it! A website project — Uma (our designer) is going to have a blast with this, and Dex is already itching to code.',
14
- questions: [
15
- 'What tech stack do you prefer? (e.g., Next.js, React, plain HTML/CSS, WordPress)',
16
- 'Do you have a design style in mind? (minimalist, bold/colorful, corporate, modern)',
17
- 'Do you already have the content (texts, images, logo) or do we need to create placeholder content?',
18
- 'Where do you want to deploy this? (Vercel, Netlify, traditional hosting, etc.)',
19
- ],
20
- },
21
- {
22
- keywords: ['api', 'backend', 'server', 'rest', 'graphql', 'microservice'],
23
- greeting: 'A backend project — Nova (CTO) and Dex (Senior Dev) are going to love architecting this.',
24
- questions: [
25
- 'What language/framework do you prefer? (Node.js/Express, Python/FastAPI, Go, etc.)',
26
- 'What database do you need? (PostgreSQL, MongoDB, SQLite, etc.)',
27
- 'Do you need authentication? (JWT, OAuth, sessions)',
28
- 'Will this API serve a frontend, mobile app, or both?',
29
- ],
30
- },
31
- {
32
- keywords: ['app', 'mobile', 'ios', 'android', 'react native', 'flutter'],
33
- greeting: 'A mobile app — exciting! Uma will design the screens while Dex and Gage handle the implementation.',
34
- questions: [
35
- 'Which platform? (iOS only, Android only, or cross-platform)',
36
- 'What framework do you prefer? (React Native, Flutter, native)',
37
- 'Does it need a backend/API, or is it standalone?',
38
- 'What are the 3 most important features?',
39
- ],
40
- },
41
- {
42
- keywords: ['dashboard', 'admin', 'painel', 'analytics', 'crm'],
43
- greeting: 'A dashboard project — River (Data) and Uma (Design) will make this shine. Dex will wire it all up.',
44
- questions: [
45
- 'What data sources will this dashboard connect to?',
46
- 'Do you need real-time updates or is periodic refresh OK?',
47
- 'What are the key metrics/charts you want to display?',
48
- 'Who are the users? (internal team, clients, public)',
49
- ],
50
- },
51
- {
52
- keywords: ['ecommerce', 'e-commerce', 'loja', 'store', 'shop', 'produto', 'product'],
53
- greeting: 'An e-commerce project — this is a big one! Nova will architect it, Uma will design the shopping experience, and Dex will build it solid.',
54
- questions: [
55
- 'What platform do you prefer? (Shopify, custom with Stripe, WooCommerce)',
56
- 'How many products approximately?',
57
- 'Do you need inventory management, or just a simple catalog?',
58
- 'What payment methods? (Credit card, PayPal, Pix, etc.)',
59
- ],
60
- },
61
- ];
62
- const DEFAULT_PROJECT = {
63
- keywords: [],
64
- greeting: 'Interesting project! Let me make sure I understand exactly what you need before I rally the team.',
65
- questions: [
66
- 'What technology/framework do you want us to use?',
67
- 'What are the 3 most important features or requirements?',
68
- 'Do you have any design preferences or references?',
69
- 'What is the target audience for this project?',
70
- ],
71
- };
72
- function detectProjectType(goal) {
73
- const lower = goal.toLowerCase();
74
- for (const pt of PROJECT_TYPES) {
75
- if (pt.keywords.some(kw => lower.includes(kw))) {
76
- return pt;
11
+ // ── Project context scanner ──────────────────────────────────────
12
+ function scanProjectContext() {
13
+ const cwd = process.cwd();
14
+ const lines = [];
15
+ // Read package.json if exists
16
+ const pkgPath = path_1.default.join(cwd, 'package.json');
17
+ if (fs_1.default.existsSync(pkgPath)) {
18
+ try {
19
+ const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, 'utf-8'));
20
+ lines.push(`## package.json`);
21
+ lines.push(`- name: ${pkg.name || 'unknown'}`);
22
+ lines.push(`- description: ${pkg.description || 'none'}`);
23
+ if (pkg.scripts)
24
+ lines.push(`- scripts: ${Object.keys(pkg.scripts).join(', ')}`);
25
+ if (pkg.dependencies)
26
+ lines.push(`- dependencies: ${Object.keys(pkg.dependencies).join(', ')}`);
27
+ lines.push('');
28
+ }
29
+ catch { }
30
+ }
31
+ // Read requirements.txt / pyproject.toml for Python projects
32
+ const reqPath = path_1.default.join(cwd, 'requirements.txt');
33
+ if (fs_1.default.existsSync(reqPath)) {
34
+ try {
35
+ const content = fs_1.default.readFileSync(reqPath, 'utf-8').trim();
36
+ lines.push(`## requirements.txt`);
37
+ lines.push(content.split('\n').slice(0, 20).join('\n'));
38
+ lines.push('');
39
+ }
40
+ catch { }
41
+ }
42
+ const pyprojectPath = path_1.default.join(cwd, 'pyproject.toml');
43
+ if (fs_1.default.existsSync(pyprojectPath)) {
44
+ try {
45
+ const content = fs_1.default.readFileSync(pyprojectPath, 'utf-8');
46
+ lines.push(`## pyproject.toml (first 30 lines)`);
47
+ lines.push(content.split('\n').slice(0, 30).join('\n'));
48
+ lines.push('');
49
+ }
50
+ catch { }
51
+ }
52
+ // Read README if exists (first 50 lines)
53
+ for (const readme of ['README.md', 'readme.md', 'README.txt', 'README']) {
54
+ const readmePath = path_1.default.join(cwd, readme);
55
+ if (fs_1.default.existsSync(readmePath)) {
56
+ try {
57
+ const content = fs_1.default.readFileSync(readmePath, 'utf-8');
58
+ lines.push(`## ${readme} (first 50 lines)`);
59
+ lines.push(content.split('\n').slice(0, 50).join('\n'));
60
+ lines.push('');
61
+ }
62
+ catch { }
63
+ break;
77
64
  }
78
65
  }
79
- return DEFAULT_PROJECT;
66
+ // Directory tree (top level + 1 depth)
67
+ try {
68
+ const entries = fs_1.default.readdirSync(cwd, { withFileTypes: true });
69
+ const relevant = entries.filter(e => !e.name.startsWith('.') &&
70
+ !['node_modules', '__pycache__', 'dist', 'build', '.git', '.sage-team', 'venv', '.venv'].includes(e.name));
71
+ lines.push('## Project structure');
72
+ for (const entry of relevant.slice(0, 30)) {
73
+ const prefix = entry.isDirectory() ? '📁' : '📄';
74
+ lines.push(`${prefix} ${entry.name}`);
75
+ if (entry.isDirectory()) {
76
+ try {
77
+ const sub = fs_1.default.readdirSync(path_1.default.join(cwd, entry.name), { withFileTypes: true });
78
+ for (const s of sub.slice(0, 10)) {
79
+ const sp = s.isDirectory() ? '📁' : '📄';
80
+ lines.push(` ${sp} ${s.name}`);
81
+ }
82
+ if (sub.length > 10)
83
+ lines.push(` ... and ${sub.length - 10} more`);
84
+ }
85
+ catch { }
86
+ }
87
+ }
88
+ lines.push('');
89
+ }
90
+ catch { }
91
+ // Git info
92
+ try {
93
+ const branch = (0, child_process_1.execSync)('git branch --show-current', { cwd, encoding: 'utf-8', timeout: 3000 }).trim();
94
+ const lastCommits = (0, child_process_1.execSync)('git log --oneline -5', { cwd, encoding: 'utf-8', timeout: 3000 }).trim();
95
+ lines.push('## Git');
96
+ lines.push(`Branch: ${branch}`);
97
+ lines.push(`Recent commits:\n${lastCommits}`);
98
+ lines.push('');
99
+ }
100
+ catch { }
101
+ return lines.join('\n');
80
102
  }
81
103
  // ── Claude Code spawn (only used for decomposition) ─────────────
82
104
  function findClaudeExe() {
83
105
  if (process.platform === 'win32') {
84
- const fs = require('fs');
85
106
  const exePaths = [
86
107
  path_1.default.join(process.env.USERPROFILE || '', '.local', 'bin', 'claude.exe'),
87
108
  path_1.default.join(process.env.LOCALAPPDATA || '', 'Programs', 'claude', 'claude.exe'),
88
109
  ];
89
110
  for (const p of exePaths) {
90
- if (fs.existsSync(p))
111
+ if (fs_1.default.existsSync(p))
91
112
  return p;
92
113
  }
93
114
  try {
@@ -139,17 +160,40 @@ function runClaude(systemPrompt, userPrompt) {
139
160
  });
140
161
  });
141
162
  }
163
+ const CEO_SYSTEM_PROMPT = `You are Sage, CEO of Sage Team — an AI-powered autonomous software company with 11 agents.
164
+
165
+ Your personality: Confident, warm, decisive. You speak naturally and directly. You refer to your team members by name.
166
+
167
+ Your team:
168
+ - Nova (CTO) — technical strategy
169
+ - Aria (Architect) — system design
170
+ - Dex (Senior Dev) — core implementation
171
+ - Flux (Fullstack Dev) — full-stack features
172
+ - Quinn (QA Lead) — testing & quality
173
+ - Gage (DevOps) — infrastructure & deployment
174
+ - Morgan (Product Manager) — product strategy
175
+ - Uma (UX Designer) — design & user experience
176
+ - River (Scrum Master) — process & coordination
177
+ - Atlas (Data Engineer) — data & analytics
178
+
179
+ CRITICAL RULES:
180
+ 1. You ALWAYS read the project context provided to understand what already exists in the current directory
181
+ 2. If there is existing code, you DO NOT propose building from scratch — you work WITH the existing project
182
+ 3. You ask relevant questions based on what you actually see in the project
183
+ 4. You respond in the SAME LANGUAGE the user writes in (Portuguese → Portuguese, English → English, etc.)
184
+ 5. Keep responses concise and natural — you're a CEO, not a chatbot
185
+ 6. When the user asks to analyze/review an existing project, you plan analysis tasks, not creation tasks`;
142
186
  // ── CEO Brain ───────────────────────────────────────────────────
143
187
  class CEOBrain {
144
188
  config;
145
189
  client = null;
146
190
  conversationHistory = [];
147
- collectedAnswers = [];
148
- detectedProject = null;
191
+ projectContext = '';
192
+ projectScanned = false;
149
193
  constructor(config) {
150
194
  this.config = config;
151
195
  if (config.apiKey === 'test') {
152
- // Test mode
196
+ // Test mode — no client
153
197
  }
154
198
  else if (process.env.ANTHROPIC_API_KEY) {
155
199
  try {
@@ -160,49 +204,110 @@ class CEOBrain {
160
204
  }
161
205
  }
162
206
  }
207
+ ensureProjectContext() {
208
+ if (!this.projectScanned) {
209
+ this.projectContext = scanProjectContext();
210
+ this.projectScanned = true;
211
+ }
212
+ return this.projectContext;
213
+ }
163
214
  /**
164
- * Chat with the user — INSTANT, no AI needed.
165
- * Uses smart project detection to ask relevant questions.
166
- * Only decomposition (later) needs AI.
215
+ * Chat with the user — uses Claude API if available, template fallback otherwise.
167
216
  */
168
217
  async chat(userMessage) {
169
218
  this.conversationHistory.push({ role: 'user', content: userMessage });
219
+ const context = this.ensureProjectContext();
170
220
  const messageCount = this.conversationHistory.filter(m => m.role === 'user').length;
171
- if (messageCount === 1) {
172
- // First message — detect project type and ask questions
173
- this.detectedProject = detectProjectType(userMessage);
174
- const response = {
175
- type: 'question',
176
- message: this.detectedProject.greeting,
177
- questions: this.detectedProject.questions,
178
- };
179
- this.conversationHistory.push({ role: 'assistant', content: JSON.stringify(response) });
180
- return response;
221
+ // Try AI-powered chat first
222
+ if (this.client) {
223
+ try {
224
+ return await this.chatWithAI(userMessage, context, messageCount);
225
+ }
226
+ catch {
227
+ // Fall through to template
228
+ }
181
229
  }
182
- // Subsequent messages — user is answering questions
183
- this.collectedAnswers.push(userMessage);
184
- if (messageCount === 2) {
185
- // Second message — we have enough context, Sage is ready
186
- const goalSummary = this.conversationHistory
187
- .filter(m => m.role === 'user')
188
- .map(m => m.content)
189
- .join(' | ');
190
- const response = {
191
- type: 'ready',
192
- message: `Perfect, I have a clear picture now. Let me call the team together Nova will handle the architecture, Uma will work on the design, and Dex will lead the implementation. I'll have Morgan coordinate everything. Let's go!`,
193
- summary: goalSummary,
194
- };
230
+ // Template fallback
231
+ return this.chatWithTemplate(userMessage, messageCount);
232
+ }
233
+ async chatWithAI(userMessage, projectContext, messageCount) {
234
+ const systemPrompt = `${CEO_SYSTEM_PROMPT}
235
+
236
+ ## Current Project Context
237
+ ${projectContext || 'Empty directory — no existing project detected.'}
238
+
239
+ ## Response Rules
240
+ - On FIRST message: Greet the user, acknowledge what you see in the project, ask 2-3 targeted questions. Respond as JSON: {"type":"question","message":"your greeting","questions":["q1","q2"]}
241
+ - On SECOND message or when user says to proceed: Signal ready. Respond as JSON: {"type":"ready","message":"your confirmation","summary":"concise goal summary"}
242
+ - ALWAYS respond with valid JSON only, no markdown wrapping`;
243
+ const messages = this.conversationHistory.map(m => ({
244
+ role: m.role,
245
+ content: m.content,
246
+ }));
247
+ const response = await this.client.messages.create({
248
+ model: this.config.model,
249
+ max_tokens: 1024,
250
+ system: systemPrompt,
251
+ messages,
252
+ });
253
+ const text = response.content[0].type === 'text' ? response.content[0].text : '';
254
+ this.conversationHistory.push({ role: 'assistant', content: text });
255
+ // Parse the JSON response
256
+ try {
257
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
258
+ if (jsonMatch) {
259
+ const parsed = JSON.parse(jsonMatch[0]);
260
+ return {
261
+ type: parsed.type || (messageCount >= 2 ? 'ready' : 'question'),
262
+ message: parsed.message || text,
263
+ questions: parsed.questions,
264
+ summary: parsed.summary,
265
+ };
266
+ }
267
+ }
268
+ catch { }
269
+ // If JSON parsing fails, treat as ready after 2+ messages
270
+ if (messageCount >= 2) {
271
+ const summary = this.conversationHistory.filter(m => m.role === 'user').map(m => m.content).join(' | ');
272
+ return { type: 'ready', message: text, summary };
273
+ }
274
+ return { type: 'question', message: text };
275
+ }
276
+ chatWithTemplate(userMessage, messageCount) {
277
+ if (messageCount === 1) {
278
+ const context = this.ensureProjectContext();
279
+ const hasExistingProject = context.includes('package.json') || context.includes('requirements.txt') || context.includes('pyproject.toml');
280
+ let greeting;
281
+ let questions;
282
+ if (hasExistingProject) {
283
+ greeting = 'I can see there\'s already an existing project here. Let me understand what you need.';
284
+ questions = [
285
+ 'What specific changes or improvements do you want the team to work on?',
286
+ 'Are there any areas of the codebase that need special attention?',
287
+ 'What\'s the priority — new features, bug fixes, refactoring, or something else?',
288
+ ];
289
+ }
290
+ else {
291
+ greeting = 'Interesting project! Let me make sure I understand exactly what you need before I rally the team.';
292
+ questions = [
293
+ 'What technology/framework do you want us to use?',
294
+ 'What are the 3 most important features or requirements?',
295
+ 'Do you have any design preferences or references?',
296
+ ];
297
+ }
298
+ const response = { type: 'question', message: greeting, questions };
195
299
  this.conversationHistory.push({ role: 'assistant', content: JSON.stringify(response) });
196
300
  return response;
197
301
  }
198
- // 3+ messages — still ready
199
302
  const goalSummary = this.conversationHistory
200
303
  .filter(m => m.role === 'user')
201
304
  .map(m => m.content)
202
305
  .join(' | ');
203
306
  const response = {
204
307
  type: 'ready',
205
- message: `Got it! Adding that to the plan. Let me rally the team now.`,
308
+ message: messageCount === 2
309
+ ? `Got it! I have a clear picture now. Let me call the team together and plan the sprint.`
310
+ : `Adding that to the plan. Let me rally the team now.`,
206
311
  summary: goalSummary,
207
312
  };
208
313
  this.conversationHistory.push({ role: 'assistant', content: JSON.stringify(response) });
@@ -210,8 +315,8 @@ class CEOBrain {
210
315
  }
211
316
  resetConversation() {
212
317
  this.conversationHistory = [];
213
- this.collectedAnswers = [];
214
- this.detectedProject = null;
318
+ this.projectScanned = false;
319
+ this.projectContext = '';
215
320
  }
216
321
  getConversationContext() {
217
322
  return this.conversationHistory
@@ -223,38 +328,44 @@ class CEOBrain {
223
328
  const contextBlock = context
224
329
  ? `## Conversation Context\n${context}\n\n`
225
330
  : '';
226
- return `You are Sage, CEO of an AI software company. Decompose this goal into a sprint with concrete tasks.
227
-
228
- ${contextBlock}## Goal
229
- ${goal}
230
-
231
- ## Available Team
232
- ${agentList}
233
-
234
- ## Rules
235
- - Each task must be assignable to exactly ONE agent
236
- - Tasks should be 2-5 minutes of focused work each
237
- - Use depends_on to express task dependencies (reference by index: "task-0", "task-1", etc.)
238
- - Assign required_skills using the agent's skill IDs (sp- or ag- prefix)
239
- - Priority: 1=critical, 2=high, 3=medium, 4=low, 5=nice-to-have
240
- - Order tasks by dependency chain: independent tasks first
241
- - Include description with enough context for the agent to work autonomously
242
-
243
- ## Response Format
244
- Respond with ONLY valid JSON (no markdown, no explanation):
245
-
246
- {
247
- "sprint": { "name": "Sprint Name", "goal": "Sprint goal" },
248
- "tasks": [
249
- {
250
- "title": "Task title",
251
- "description": "Detailed description",
252
- "assignee": "agent-id",
253
- "priority": 1,
254
- "required_skills": ["sp-tdd-cycle"],
255
- "depends_on": []
256
- }
257
- ]
331
+ const projectBlock = this.ensureProjectContext();
332
+ return `You are Sage, CEO of an AI software company. Decompose this goal into a sprint with concrete tasks.
333
+
334
+ ${contextBlock}## Goal
335
+ ${goal}
336
+
337
+ ## Current Project (files in working directory)
338
+ ${projectBlock || 'Empty directory — new project.'}
339
+
340
+ ## Available Team
341
+ ${agentList}
342
+
343
+ ## Rules
344
+ - CRITICAL: If there is existing code, tasks must work WITH the existing codebase, NOT create from scratch
345
+ - Each task must be assignable to exactly ONE agent
346
+ - Tasks should be 2-5 minutes of focused work each
347
+ - Use depends_on to express task dependencies (reference by index: "task-0", "task-1", etc.)
348
+ - Assign required_skills using the agent's skill IDs (sp- or ag- prefix)
349
+ - Priority: 1=critical, 2=high, 3=medium, 4=low, 5=nice-to-have
350
+ - Order tasks by dependency chain: independent tasks first
351
+ - Include description with enough context for the agent to work autonomously
352
+ - Task descriptions must reference specific files/modules from the project when working on existing code
353
+
354
+ ## Response Format
355
+ Respond with ONLY valid JSON (no markdown, no explanation):
356
+
357
+ {
358
+ "sprint": { "name": "Sprint Name", "goal": "Sprint goal" },
359
+ "tasks": [
360
+ {
361
+ "title": "Task title",
362
+ "description": "Detailed description referencing specific files",
363
+ "assignee": "agent-id",
364
+ "priority": 1,
365
+ "required_skills": ["sp-tdd-cycle"],
366
+ "depends_on": []
367
+ }
368
+ ]
258
369
  }`;
259
370
  }
260
371
  parseDecomposition(response) {
@@ -291,30 +402,53 @@ Respond with ONLY valid JSON (no markdown, no explanation):
291
402
  })),
292
403
  };
293
404
  }
294
- async decompose(goal, _roster) {
295
- // Template-based decomposition — instant, works for everyone, no AI needed
405
+ async decompose(goal, roster) {
406
+ // Try AI-powered decomposition first
407
+ if (this.client) {
408
+ try {
409
+ return await this.decomposeWithAI(goal, roster);
410
+ }
411
+ catch {
412
+ // Fall through to template
413
+ }
414
+ }
415
+ // Template fallback
296
416
  return this.decomposeFromTemplate(goal);
297
417
  }
298
- /** Template-based decomposition — instant, no AI. Always returns a result. */
418
+ async decomposeWithAI(goal, roster) {
419
+ const context = this.getConversationContext();
420
+ const prompt = this.buildDecompositionPrompt(goal, roster, context || undefined);
421
+ const response = await this.client.messages.create({
422
+ model: this.config.model,
423
+ max_tokens: 4096,
424
+ messages: [{ role: 'user', content: prompt }],
425
+ });
426
+ const text = response.content[0].type === 'text' ? response.content[0].text : '';
427
+ return this.parseDecomposition(text);
428
+ }
429
+ /** Template-based decomposition — instant, no AI. Fallback only. */
299
430
  decomposeFromTemplate(goal) {
300
431
  const allContext = this.conversationHistory.map(m => m.content).join(' ').toLowerCase();
301
432
  const combined = `${goal.toLowerCase()} ${allContext}`;
433
+ const projectContext = this.ensureProjectContext();
434
+ const hasExistingProject = projectContext.includes('package.json') || projectContext.includes('requirements.txt') || projectContext.includes('pyproject.toml');
435
+ // If existing project detected, use analysis/improvement template
436
+ if (hasExistingProject && this.isAnalysisRequest(combined)) {
437
+ return this.analysisSprintTemplate(goal, projectContext);
438
+ }
302
439
  if (this.matchesKeywords(combined, ['site', 'website', 'landing', 'página', 'pagina', 'homepage', 'web page', 'webpage'])) {
303
440
  return this.websiteSprintTemplate(goal, combined);
304
441
  }
305
442
  if (this.matchesKeywords(combined, ['rest api', 'graphql', 'backend api', 'api endpoint', 'microservice', 'server api'])) {
306
443
  return this.apiSprintTemplate(goal);
307
444
  }
308
- if (this.matchesKeywords(combined, ['app', 'mobile', 'ios', 'android', 'react native', 'flutter', 'aplicativo'])) {
309
- return this.mobileSprintTemplate(goal);
310
- }
311
445
  if (this.matchesKeywords(combined, ['dashboard', 'admin', 'painel', 'analytics', 'crm', 'portal'])) {
312
446
  return this.dashboardSprintTemplate(goal);
313
447
  }
314
448
  if (this.matchesKeywords(combined, ['ecommerce', 'e-commerce', 'loja', 'store', 'shop', 'produto', 'product', 'cart', 'carrinho'])) {
315
449
  return this.ecommerceSprintTemplate(goal);
316
450
  }
317
- if (this.matchesKeywords(combined, ['cli', 'command line', 'terminal', 'tool', 'script', 'automation', 'ferramenta'])) {
451
+ if (this.matchesKeywords(combined, ['cli', 'command line', 'terminal', 'tool', 'script', 'ferramenta'])) {
318
452
  return this.cliToolSprintTemplate(goal);
319
453
  }
320
454
  if (this.matchesKeywords(combined, ['bot', 'chatbot', 'discord', 'telegram', 'slack', 'whatsapp'])) {
@@ -323,12 +457,52 @@ Respond with ONLY valid JSON (no markdown, no explanation):
323
457
  if (this.matchesKeywords(combined, ['game', 'jogo', 'gaming', 'gameplay'])) {
324
458
  return this.gameSprintTemplate(goal);
325
459
  }
326
- // Generic fallbackworks for ANY project
460
+ // Don't match 'app'/'mobile' too eagerly only if clearly about mobile
461
+ if (this.matchesKeywords(combined, ['mobile app', 'ios app', 'android app', 'react native', 'flutter app', 'aplicativo mobile'])) {
462
+ return this.mobileSprintTemplate(goal);
463
+ }
464
+ // If existing project, default to improvement sprint
465
+ if (hasExistingProject) {
466
+ return this.improvementSprintTemplate(goal, projectContext);
467
+ }
468
+ // Generic fallback for new projects
327
469
  return this.genericSprintTemplate(goal);
328
470
  }
471
+ isAnalysisRequest(text) {
472
+ return this.matchesKeywords(text, [
473
+ 'analis', 'review', 'revis', 'estado', 'status', 'audit', 'verificar', 'check',
474
+ 'como est', 'what is the', 'how is', 'diagnos', 'inspect', 'avaliar', 'evaluate',
475
+ ]);
476
+ }
329
477
  matchesKeywords(text, keywords) {
330
478
  return keywords.some(kw => text.includes(kw));
331
479
  }
480
+ /** Template for analyzing an existing project */
481
+ analysisSprintTemplate(goal, projectContext) {
482
+ return {
483
+ sprint: { name: 'Project Analysis', goal: `Analyze and review: ${goal.slice(0, 100)}` },
484
+ tasks: [
485
+ { title: 'Analyze project architecture and code quality', description: `Read all source files in the project directory. Analyze the architecture, code organization, patterns used, and overall quality. Identify strengths and weaknesses. Project context:\n${projectContext.slice(0, 500)}`, assignee: 'aria', priority: 1, required_skills: [], depends_on: [] },
486
+ { title: 'Review tests and code coverage', description: `Run existing tests and analyze coverage. Identify untested code paths, fragile tests, and testing gaps. Check test quality and best practices.`, assignee: 'quinn', priority: 1, required_skills: [], depends_on: [] },
487
+ { title: 'Audit dependencies and security', description: `Review all dependencies for outdated versions, known vulnerabilities, and unnecessary packages. Check for security issues in the codebase.`, assignee: 'gage', priority: 2, required_skills: [], depends_on: [] },
488
+ { title: 'Review documentation and developer experience', description: `Check README, inline docs, API documentation. Evaluate setup process, developer onboarding experience, and documentation completeness.`, assignee: 'atlas', priority: 2, required_skills: [], depends_on: ['task-0'] },
489
+ { title: 'Compile analysis report with recommendations', description: `Gather findings from all team members and compile a comprehensive report: what's working well, what needs improvement, and prioritized next steps.`, assignee: 'morgan', priority: 1, required_skills: [], depends_on: ['task-0', 'task-1', 'task-2', 'task-3'] },
490
+ ],
491
+ };
492
+ }
493
+ /** Template for improving an existing project */
494
+ improvementSprintTemplate(goal, projectContext) {
495
+ return {
496
+ sprint: { name: 'Project Improvement', goal: `Improve: ${goal.slice(0, 100)}` },
497
+ tasks: [
498
+ { title: 'Review existing codebase and plan changes', description: `Read the existing source code to understand the current architecture before making any changes. Plan the implementation approach. Project context:\n${projectContext.slice(0, 500)}\n\nGoal: ${goal.slice(0, 300)}`, assignee: 'aria', priority: 1, required_skills: [], depends_on: [] },
499
+ { title: 'Implement primary changes', description: `Make the core changes requested by the user. Work with the existing code — modify, extend, or refactor as needed. Do not rewrite from scratch. Goal: ${goal.slice(0, 300)}`, assignee: 'dex', priority: 1, required_skills: [], depends_on: ['task-0'] },
500
+ { title: 'Implement supporting changes', description: `Handle any secondary modifications, configuration updates, or infrastructure changes needed to support the primary work.`, assignee: 'gage', priority: 2, required_skills: [], depends_on: ['task-0'] },
501
+ { title: 'Update tests for changes', description: `Add or update tests to cover the new/modified functionality. Ensure existing tests still pass.`, assignee: 'quinn', priority: 2, required_skills: [], depends_on: ['task-1'] },
502
+ { title: 'Update documentation', description: `Update README, comments, and any documentation to reflect the changes made.`, assignee: 'atlas', priority: 3, required_skills: [], depends_on: ['task-1'] },
503
+ ],
504
+ };
505
+ }
332
506
  websiteSprintTemplate(goal, context) {
333
507
  const sprintName = 'Website MVP';
334
508
  const hasNextjs = context.includes('next') || context.includes('react');
@@ -338,70 +512,14 @@ Respond with ONLY valid JSON (no markdown, no explanation):
338
512
  return {
339
513
  sprint: { name: sprintName, goal: `Build a professional website: ${goal.slice(0, 100)}` },
340
514
  tasks: [
341
- {
342
- title: 'Define project architecture and tech stack',
343
- description: `Architect the website using ${framework} with ${styling}. Define folder structure, routing strategy, and component hierarchy. Create the project scaffold with all necessary configuration files.`,
344
- assignee: 'aria',
345
- priority: 1,
346
- required_skills: [],
347
- depends_on: [],
348
- },
349
- {
350
- title: 'Design UI/UX wireframes and component library',
351
- description: `Design the visual style for the website: color palette, typography, spacing, and component designs. Create reusable UI components (Header, Footer, Hero, Card, Button, CTA sections). Ensure responsive design for mobile, tablet, and desktop. Context: ${goal.slice(0, 200)}`,
352
- assignee: 'uma',
353
- priority: 1,
354
- required_skills: [],
355
- depends_on: [],
356
- },
357
- {
358
- title: 'Build Home page with Hero, Features, and CTA',
359
- description: `Implement the Home/Landing page with: hero section with headline and call-to-action, services overview section, testimonials preview, and contact CTA. Use ${framework} with ${styling}. Ensure responsive design.`,
360
- assignee: 'dex',
361
- priority: 1,
362
- required_skills: [],
363
- depends_on: ['task-0'],
364
- },
365
- {
366
- title: 'Build Services and About pages',
367
- description: `Create the Services page (list all services with descriptions, icons, and CTAs) and the About page (company story, team, mission/values). Use ${framework} with ${styling}. Include placeholder content.`,
368
- assignee: 'gage',
369
- priority: 2,
370
- required_skills: [],
371
- depends_on: ['task-0'],
372
- },
373
- {
374
- title: 'Build Gallery and Testimonials pages',
375
- description: `Create the Gallery page (grid of project images with lightbox/modal) and Testimonials page (customer reviews with ratings and quotes). Use placeholder images and content.`,
376
- assignee: 'gage',
377
- priority: 2,
378
- required_skills: [],
379
- depends_on: ['task-0'],
380
- },
381
- {
382
- title: 'Build Contact page with form',
383
- description: `Create the Contact page with: contact form (name, email, phone, message, service type dropdown), company address/map placeholder, phone number, email, business hours. Form should validate inputs client-side.`,
384
- assignee: 'dex',
385
- priority: 2,
386
- required_skills: [],
387
- depends_on: ['task-0'],
388
- },
389
- {
390
- title: 'SEO optimization and metadata',
391
- description: `Add proper SEO: meta tags (title, description, OpenGraph), semantic HTML structure, alt tags for images, sitemap.xml, robots.txt. Optimize for local SEO if applicable. Add structured data (JSON-LD) for local business.`,
392
- assignee: 'atlas',
393
- priority: 3,
394
- required_skills: [],
395
- depends_on: ['task-2', 'task-3'],
396
- },
397
- {
398
- title: 'Testing and quality review',
399
- description: `Test all pages: responsive design on mobile/tablet/desktop, navigation works correctly, form validation works, images load properly, no broken links, accessibility basics (contrast, alt tags, keyboard nav). Cross-browser check.`,
400
- assignee: 'quinn',
401
- priority: 3,
402
- required_skills: [],
403
- depends_on: ['task-2', 'task-3', 'task-4', 'task-5'],
404
- },
515
+ { title: 'Define project architecture and tech stack', description: `Architect the website using ${framework} with ${styling}. Define folder structure, routing strategy, and component hierarchy. Create the project scaffold with all necessary configuration files.`, assignee: 'aria', priority: 1, required_skills: [], depends_on: [] },
516
+ { title: 'Design UI/UX wireframes and component library', description: `Design the visual style for the website: color palette, typography, spacing, and component designs. Create reusable UI components (Header, Footer, Hero, Card, Button, CTA sections). Ensure responsive design for mobile, tablet, and desktop. Context: ${goal.slice(0, 200)}`, assignee: 'uma', priority: 1, required_skills: [], depends_on: [] },
517
+ { title: 'Build Home page with Hero, Features, and CTA', description: `Implement the Home/Landing page with: hero section with headline and call-to-action, services overview section, testimonials preview, and contact CTA. Use ${framework} with ${styling}. Ensure responsive design.`, assignee: 'dex', priority: 1, required_skills: [], depends_on: ['task-0'] },
518
+ { title: 'Build Services and About pages', description: `Create the Services page (list all services with descriptions, icons, and CTAs) and the About page (company story, team, mission/values). Use ${framework} with ${styling}. Include placeholder content.`, assignee: 'gage', priority: 2, required_skills: [], depends_on: ['task-0'] },
519
+ { title: 'Build Gallery and Testimonials pages', description: `Create the Gallery page (grid of project images with lightbox/modal) and Testimonials page (customer reviews with ratings and quotes). Use placeholder images and content.`, assignee: 'gage', priority: 2, required_skills: [], depends_on: ['task-0'] },
520
+ { title: 'Build Contact page with form', description: `Create the Contact page with: contact form (name, email, phone, message, service type dropdown), company address/map placeholder, phone number, email, business hours. Form should validate inputs client-side.`, assignee: 'dex', priority: 2, required_skills: [], depends_on: ['task-0'] },
521
+ { title: 'SEO optimization and metadata', description: `Add proper SEO: meta tags (title, description, OpenGraph), semantic HTML structure, alt tags for images, sitemap.xml, robots.txt. Optimize for local SEO if applicable. Add structured data (JSON-LD) for local business.`, assignee: 'atlas', priority: 3, required_skills: [], depends_on: ['task-2', 'task-3'] },
522
+ { title: 'Testing and quality review', description: `Test all pages: responsive design on mobile/tablet/desktop, navigation works correctly, form validation works, images load properly, no broken links, accessibility basics (contrast, alt tags, keyboard nav). Cross-browser check.`, assignee: 'quinn', priority: 3, required_skills: [], depends_on: ['task-2', 'task-3', 'task-4', 'task-5'] },
405
523
  ],
406
524
  };
407
525
  }
@@ -499,7 +617,7 @@ Respond with ONLY valid JSON (no markdown, no explanation):
499
617
  ],
500
618
  };
501
619
  }
502
- /** Generic fallback template — works for ANY project type */
620
+ /** Generic fallback template — works for ANY new project type */
503
621
  genericSprintTemplate(goal) {
504
622
  return {
505
623
  sprint: { name: 'Project Sprint', goal: `Build: ${goal.slice(0, 100)}` },