sage-team 3.6.0 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) 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/__tests__/dispatcher.test.js +2 -1
  12. package/dist/engine/__tests__/dispatcher.test.js.map +1 -1
  13. package/dist/engine/ceo-brain.d.ts +15 -8
  14. package/dist/engine/ceo-brain.d.ts.map +1 -1
  15. package/dist/engine/ceo-brain.js +339 -213
  16. package/dist/engine/ceo-brain.js.map +1 -1
  17. package/dist/engine/dispatcher.d.ts +2 -0
  18. package/dist/engine/dispatcher.d.ts.map +1 -1
  19. package/dist/engine/dispatcher.js +26 -6
  20. package/dist/engine/dispatcher.js.map +1 -1
  21. package/dist/engine/orchestrator.d.ts +1 -0
  22. package/dist/engine/orchestrator.d.ts.map +1 -1
  23. package/dist/engine/orchestrator.js +35 -4
  24. package/dist/engine/orchestrator.js.map +1 -1
  25. package/dist/mcp/server.js +1 -1
  26. package/dist/server/index.d.ts.map +1 -1
  27. package/dist/server/index.js +9 -3
  28. package/dist/server/index.js.map +1 -1
  29. package/dist/server/routes/api.d.ts.map +1 -1
  30. package/dist/server/routes/api.js +10 -3
  31. package/dist/server/routes/api.js.map +1 -1
  32. package/{src/web/dist/assets/BufferResource-C0a2-23_.js → dist/web/dist/assets/BufferResource-BGsixSHO.js} +1 -1
  33. package/dist/web/dist/assets/BufferResource-BbpTIQ85.js +185 -0
  34. package/dist/web/dist/assets/BufferResource-BjJe3r2a.js +185 -0
  35. package/dist/web/dist/assets/BufferResource-C7ypptTD.js +185 -0
  36. package/dist/web/dist/assets/BufferResource-CuR0zQl9.js +185 -0
  37. package/dist/web/dist/assets/BufferResource-_EbXUkK9.js +185 -0
  38. package/dist/web/dist/assets/BufferResource-dCF0GA9M.js +185 -0
  39. package/{src/web/dist/assets/CanvasRenderer-DAx637pP.js → dist/web/dist/assets/CanvasRenderer-BGC-Rjpm.js} +1 -1
  40. package/dist/web/dist/assets/CanvasRenderer-BQ7ScDtX.js +1 -0
  41. package/dist/web/dist/assets/CanvasRenderer-CBkg7qb3.js +1 -0
  42. package/dist/web/dist/assets/CanvasRenderer-CcFmL3Lw.js +1 -0
  43. package/dist/web/dist/assets/CanvasRenderer-CwKoxkGD.js +1 -0
  44. package/dist/web/dist/assets/CanvasRenderer-YM8daXJu.js +1 -0
  45. package/dist/web/dist/assets/CanvasRenderer-yT9JOc5o.js +1 -0
  46. package/{src/web/dist/assets/Filter-BpH04nQq.js → dist/web/dist/assets/Filter-BzM7i9ln.js} +1 -1
  47. package/dist/web/dist/assets/Filter-CJvSyeWH.js +1 -0
  48. package/dist/web/dist/assets/Filter-CUPFlJF_.js +1 -0
  49. package/dist/web/dist/assets/Filter-D2kh6Lyn.js +1 -0
  50. package/dist/web/dist/assets/Filter-DMjNMMPy.js +1 -0
  51. package/dist/web/dist/assets/Filter-GQK6iqrb.js +1 -0
  52. package/dist/web/dist/assets/Filter-nUtqhIEl.js +1 -0
  53. package/{src/web/dist/assets/RenderTargetSystem-B6ma-XuE.js → dist/web/dist/assets/RenderTargetSystem-B-ekYymr.js} +1 -1
  54. package/dist/web/dist/assets/RenderTargetSystem-BylEtp0F.js +172 -0
  55. package/dist/web/dist/assets/RenderTargetSystem-D2L3KLdt.js +172 -0
  56. package/dist/web/dist/assets/RenderTargetSystem-DNsILH5P.js +172 -0
  57. package/dist/web/dist/assets/RenderTargetSystem-lhfSOVIz.js +172 -0
  58. package/dist/web/dist/assets/RenderTargetSystem-nRg6zEP8.js +172 -0
  59. package/dist/web/dist/assets/RenderTargetSystem-uCCnK3Aw.js +172 -0
  60. package/{src/web/dist/assets/WebGLRenderer-DxbdlULS.js → dist/web/dist/assets/WebGLRenderer-BEupTZT-.js} +1 -1
  61. package/dist/web/dist/assets/WebGLRenderer-BJuclWjv.js +156 -0
  62. package/dist/web/dist/assets/WebGLRenderer-BONohh1V.js +156 -0
  63. package/dist/web/dist/assets/WebGLRenderer-Bsr1n0Uu.js +156 -0
  64. package/dist/web/dist/assets/WebGLRenderer-Da_BkFlZ.js +156 -0
  65. package/dist/web/dist/assets/WebGLRenderer-DiL4ivWY.js +156 -0
  66. package/dist/web/dist/assets/WebGLRenderer-DqRhyEby.js +156 -0
  67. package/{src/web/dist/assets/WebGPURenderer-DALpH00k.js → dist/web/dist/assets/WebGPURenderer-C0BJNyre.js} +1 -1
  68. package/dist/web/dist/assets/WebGPURenderer-Clrsu_cP.js +41 -0
  69. package/dist/web/dist/assets/WebGPURenderer-DbGOPnSE.js +41 -0
  70. package/dist/web/dist/assets/WebGPURenderer-Ds8VNo2-.js +41 -0
  71. package/dist/web/dist/assets/WebGPURenderer-Du7e_Agz.js +41 -0
  72. package/dist/web/dist/assets/WebGPURenderer-pLgFan3y.js +41 -0
  73. package/dist/web/dist/assets/WebGPURenderer-pd03jK7j.js +41 -0
  74. package/{src/web/dist/assets/browserAll-BToSt8GL.js → dist/web/dist/assets/browserAll-1QqybVac.js} +1 -1
  75. package/dist/web/dist/assets/browserAll-Bd3WFbs9.js +14 -0
  76. package/dist/web/dist/assets/browserAll-Bv-Fa9BT.js +14 -0
  77. package/dist/web/dist/assets/browserAll-C9HRyLf9.js +14 -0
  78. package/dist/web/dist/assets/browserAll-D7SUz7ce.js +14 -0
  79. package/dist/web/dist/assets/browserAll-TAEt2LTG.js +14 -0
  80. package/dist/web/dist/assets/browserAll-d1yltqWP.js +14 -0
  81. package/dist/web/dist/assets/index-BQx3wJPr.css +1 -0
  82. package/dist/web/dist/assets/index-C1XGJE1t.js +298 -0
  83. package/dist/web/dist/assets/index-C6hoHqN5.js +298 -0
  84. package/dist/web/dist/assets/index-CFVD9jMF.js +298 -0
  85. package/dist/web/dist/assets/index-CO3-IuiC.js +298 -0
  86. package/dist/web/dist/assets/index-CRT2SA6d.js +298 -0
  87. package/dist/web/dist/assets/index-CVcQAOB6.css +1 -0
  88. package/dist/web/dist/assets/index-CwYVpEl9.js +298 -0
  89. package/{src/web/dist/assets/index-CnLwjlPD.js → dist/web/dist/assets/index-DBSQsI6C.js} +47 -47
  90. package/{src/web/dist/assets/webworkerAll-DmVDEFdQ.js → dist/web/dist/assets/webworkerAll-B3fxgx9g.js} +1 -1
  91. package/dist/web/dist/assets/webworkerAll-BILg7nF2.js +83 -0
  92. package/dist/web/dist/assets/webworkerAll-C7T1zoFZ.js +83 -0
  93. package/dist/web/dist/assets/webworkerAll-CMt6Ulvh.js +83 -0
  94. package/dist/web/dist/assets/webworkerAll-CREQA0I8.js +83 -0
  95. package/dist/web/dist/assets/webworkerAll-CykNhAo8.js +83 -0
  96. package/dist/web/dist/assets/webworkerAll-WnWYpa5G.js +83 -0
  97. package/{src → dist}/web/dist/index.html +2 -2
  98. package/package.json +2 -3
  99. package/src/web/dist/assets/index-8Qtg1JIx.css +0 -1
@@ -6,106 +6,135 @@ 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');
106
+ // 1. Native .exe
85
107
  const exePaths = [
86
108
  path_1.default.join(process.env.USERPROFILE || '', '.local', 'bin', 'claude.exe'),
87
109
  path_1.default.join(process.env.LOCALAPPDATA || '', 'Programs', 'claude', 'claude.exe'),
88
110
  ];
89
111
  for (const p of exePaths) {
90
- if (fs.existsSync(p))
91
- return p;
112
+ if (fs_1.default.existsSync(p))
113
+ return { command: p, prefix: [] };
114
+ }
115
+ // 2. npm global cli.js (run with node)
116
+ const npmCliJs = path_1.default.join(process.env.APPDATA || '', 'npm', 'node_modules', '@anthropic-ai', 'claude-code', 'cli.js');
117
+ if (fs_1.default.existsSync(npmCliJs)) {
118
+ return { command: process.execPath, prefix: [npmCliJs] };
92
119
  }
120
+ // 3. where command
93
121
  try {
94
122
  const lines = (0, child_process_1.execSync)('where claude.exe', { encoding: 'utf-8' }).trim().split('\n');
95
123
  const exeLine = lines.find((l) => l.trim().endsWith('.exe'));
96
124
  if (exeLine)
97
- return exeLine.trim();
125
+ return { command: exeLine.trim(), prefix: [] };
98
126
  }
99
127
  catch { /* continue */ }
100
128
  }
101
- return 'claude';
129
+ return { command: 'claude', prefix: [] };
102
130
  }
103
131
  function runClaude(systemPrompt, userPrompt) {
104
132
  return new Promise((resolve, reject) => {
105
- const claude = findClaudeExe();
133
+ const { command, prefix } = findClaudeExe();
106
134
  const env = { ...process.env };
107
135
  delete env.CLAUDECODE;
108
- const proc = (0, child_process_1.spawn)(claude, [
136
+ const proc = (0, child_process_1.spawn)(command, [
137
+ ...prefix,
109
138
  '--print',
110
139
  '--dangerously-skip-permissions',
111
140
  '--output-format', 'text',
@@ -139,17 +168,40 @@ function runClaude(systemPrompt, userPrompt) {
139
168
  });
140
169
  });
141
170
  }
171
+ const CEO_SYSTEM_PROMPT = `You are Sage, CEO of Sage Team — an AI-powered autonomous software company with 11 agents.
172
+
173
+ Your personality: Confident, warm, decisive. You speak naturally and directly. You refer to your team members by name.
174
+
175
+ Your team:
176
+ - Nova (CTO) — technical strategy
177
+ - Aria (Architect) — system design
178
+ - Dex (Senior Dev) — core implementation
179
+ - Flux (Fullstack Dev) — full-stack features
180
+ - Quinn (QA Lead) — testing & quality
181
+ - Gage (DevOps) — infrastructure & deployment
182
+ - Morgan (Product Manager) — product strategy
183
+ - Uma (UX Designer) — design & user experience
184
+ - River (Scrum Master) — process & coordination
185
+ - Atlas (Data Engineer) — data & analytics
186
+
187
+ CRITICAL RULES:
188
+ 1. You ALWAYS read the project context provided to understand what already exists in the current directory
189
+ 2. If there is existing code, you DO NOT propose building from scratch — you work WITH the existing project
190
+ 3. You ask relevant questions based on what you actually see in the project
191
+ 4. You respond in the SAME LANGUAGE the user writes in (Portuguese → Portuguese, English → English, etc.)
192
+ 5. Keep responses concise and natural — you're a CEO, not a chatbot
193
+ 6. When the user asks to analyze/review an existing project, you plan analysis tasks, not creation tasks`;
142
194
  // ── CEO Brain ───────────────────────────────────────────────────
143
195
  class CEOBrain {
144
196
  config;
145
197
  client = null;
146
198
  conversationHistory = [];
147
- collectedAnswers = [];
148
- detectedProject = null;
199
+ projectContext = '';
200
+ projectScanned = false;
149
201
  constructor(config) {
150
202
  this.config = config;
151
203
  if (config.apiKey === 'test') {
152
- // Test mode
204
+ // Test mode — no client
153
205
  }
154
206
  else if (process.env.ANTHROPIC_API_KEY) {
155
207
  try {
@@ -160,49 +212,110 @@ class CEOBrain {
160
212
  }
161
213
  }
162
214
  }
215
+ ensureProjectContext() {
216
+ if (!this.projectScanned) {
217
+ this.projectContext = scanProjectContext();
218
+ this.projectScanned = true;
219
+ }
220
+ return this.projectContext;
221
+ }
163
222
  /**
164
- * Chat with the user — INSTANT, no AI needed.
165
- * Uses smart project detection to ask relevant questions.
166
- * Only decomposition (later) needs AI.
223
+ * Chat with the user — uses Claude API if available, template fallback otherwise.
167
224
  */
168
225
  async chat(userMessage) {
169
226
  this.conversationHistory.push({ role: 'user', content: userMessage });
227
+ const context = this.ensureProjectContext();
170
228
  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;
229
+ // Try AI-powered chat first
230
+ if (this.client) {
231
+ try {
232
+ return await this.chatWithAI(userMessage, context, messageCount);
233
+ }
234
+ catch {
235
+ // Fall through to template
236
+ }
237
+ }
238
+ // Template fallback
239
+ return this.chatWithTemplate(userMessage, messageCount);
240
+ }
241
+ async chatWithAI(userMessage, projectContext, messageCount) {
242
+ const systemPrompt = `${CEO_SYSTEM_PROMPT}
243
+
244
+ ## Current Project Context
245
+ ${projectContext || 'Empty directory — no existing project detected.'}
246
+
247
+ ## Response Rules
248
+ - 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"]}
249
+ - On SECOND message or when user says to proceed: Signal ready. Respond as JSON: {"type":"ready","message":"your confirmation","summary":"concise goal summary"}
250
+ - ALWAYS respond with valid JSON only, no markdown wrapping`;
251
+ const messages = this.conversationHistory.map(m => ({
252
+ role: m.role,
253
+ content: m.content,
254
+ }));
255
+ const response = await this.client.messages.create({
256
+ model: this.config.model,
257
+ max_tokens: 1024,
258
+ system: systemPrompt,
259
+ messages,
260
+ });
261
+ const text = response.content[0].type === 'text' ? response.content[0].text : '';
262
+ this.conversationHistory.push({ role: 'assistant', content: text });
263
+ // Parse the JSON response
264
+ try {
265
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
266
+ if (jsonMatch) {
267
+ const parsed = JSON.parse(jsonMatch[0]);
268
+ return {
269
+ type: parsed.type || (messageCount >= 2 ? 'ready' : 'question'),
270
+ message: parsed.message || text,
271
+ questions: parsed.questions,
272
+ summary: parsed.summary,
273
+ };
274
+ }
275
+ }
276
+ catch { }
277
+ // If JSON parsing fails, treat as ready after 2+ messages
278
+ if (messageCount >= 2) {
279
+ const summary = this.conversationHistory.filter(m => m.role === 'user').map(m => m.content).join(' | ');
280
+ return { type: 'ready', message: text, summary };
181
281
  }
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
- };
282
+ return { type: 'question', message: text };
283
+ }
284
+ chatWithTemplate(userMessage, messageCount) {
285
+ if (messageCount === 1) {
286
+ const context = this.ensureProjectContext();
287
+ const hasExistingProject = context.includes('package.json') || context.includes('requirements.txt') || context.includes('pyproject.toml');
288
+ let greeting;
289
+ let questions;
290
+ if (hasExistingProject) {
291
+ greeting = 'I can see there\'s already an existing project here. Let me understand what you need.';
292
+ questions = [
293
+ 'What specific changes or improvements do you want the team to work on?',
294
+ 'Are there any areas of the codebase that need special attention?',
295
+ 'What\'s the priority — new features, bug fixes, refactoring, or something else?',
296
+ ];
297
+ }
298
+ else {
299
+ greeting = 'Interesting project! Let me make sure I understand exactly what you need before I rally the team.';
300
+ questions = [
301
+ 'What technology/framework do you want us to use?',
302
+ 'What are the 3 most important features or requirements?',
303
+ 'Do you have any design preferences or references?',
304
+ ];
305
+ }
306
+ const response = { type: 'question', message: greeting, questions };
195
307
  this.conversationHistory.push({ role: 'assistant', content: JSON.stringify(response) });
196
308
  return response;
197
309
  }
198
- // 3+ messages — still ready
199
310
  const goalSummary = this.conversationHistory
200
311
  .filter(m => m.role === 'user')
201
312
  .map(m => m.content)
202
313
  .join(' | ');
203
314
  const response = {
204
315
  type: 'ready',
205
- message: `Got it! Adding that to the plan. Let me rally the team now.`,
316
+ message: messageCount === 2
317
+ ? `Got it! I have a clear picture now. Let me call the team together and plan the sprint.`
318
+ : `Adding that to the plan. Let me rally the team now.`,
206
319
  summary: goalSummary,
207
320
  };
208
321
  this.conversationHistory.push({ role: 'assistant', content: JSON.stringify(response) });
@@ -210,8 +323,8 @@ class CEOBrain {
210
323
  }
211
324
  resetConversation() {
212
325
  this.conversationHistory = [];
213
- this.collectedAnswers = [];
214
- this.detectedProject = null;
326
+ this.projectScanned = false;
327
+ this.projectContext = '';
215
328
  }
216
329
  getConversationContext() {
217
330
  return this.conversationHistory
@@ -223,38 +336,44 @@ class CEOBrain {
223
336
  const contextBlock = context
224
337
  ? `## Conversation Context\n${context}\n\n`
225
338
  : '';
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
- ]
339
+ const projectBlock = this.ensureProjectContext();
340
+ return `You are Sage, CEO of an AI software company. Decompose this goal into a sprint with concrete tasks.
341
+
342
+ ${contextBlock}## Goal
343
+ ${goal}
344
+
345
+ ## Current Project (files in working directory)
346
+ ${projectBlock || 'Empty directory — new project.'}
347
+
348
+ ## Available Team
349
+ ${agentList}
350
+
351
+ ## Rules
352
+ - CRITICAL: If there is existing code, tasks must work WITH the existing codebase, NOT create from scratch
353
+ - Each task must be assignable to exactly ONE agent
354
+ - Tasks should be 2-5 minutes of focused work each
355
+ - Use depends_on to express task dependencies (reference by index: "task-0", "task-1", etc.)
356
+ - Assign required_skills using the agent's skill IDs (sp- or ag- prefix)
357
+ - Priority: 1=critical, 2=high, 3=medium, 4=low, 5=nice-to-have
358
+ - Order tasks by dependency chain: independent tasks first
359
+ - Include description with enough context for the agent to work autonomously
360
+ - Task descriptions must reference specific files/modules from the project when working on existing code
361
+
362
+ ## Response Format
363
+ Respond with ONLY valid JSON (no markdown, no explanation):
364
+
365
+ {
366
+ "sprint": { "name": "Sprint Name", "goal": "Sprint goal" },
367
+ "tasks": [
368
+ {
369
+ "title": "Task title",
370
+ "description": "Detailed description referencing specific files",
371
+ "assignee": "agent-id",
372
+ "priority": 1,
373
+ "required_skills": ["sp-tdd-cycle"],
374
+ "depends_on": []
375
+ }
376
+ ]
258
377
  }`;
259
378
  }
260
379
  parseDecomposition(response) {
@@ -291,30 +410,53 @@ Respond with ONLY valid JSON (no markdown, no explanation):
291
410
  })),
292
411
  };
293
412
  }
294
- async decompose(goal, _roster) {
295
- // Template-based decomposition — instant, works for everyone, no AI needed
413
+ async decompose(goal, roster) {
414
+ // Try AI-powered decomposition first
415
+ if (this.client) {
416
+ try {
417
+ return await this.decomposeWithAI(goal, roster);
418
+ }
419
+ catch {
420
+ // Fall through to template
421
+ }
422
+ }
423
+ // Template fallback
296
424
  return this.decomposeFromTemplate(goal);
297
425
  }
298
- /** Template-based decomposition — instant, no AI. Always returns a result. */
426
+ async decomposeWithAI(goal, roster) {
427
+ const context = this.getConversationContext();
428
+ const prompt = this.buildDecompositionPrompt(goal, roster, context || undefined);
429
+ const response = await this.client.messages.create({
430
+ model: this.config.model,
431
+ max_tokens: 4096,
432
+ messages: [{ role: 'user', content: prompt }],
433
+ });
434
+ const text = response.content[0].type === 'text' ? response.content[0].text : '';
435
+ return this.parseDecomposition(text);
436
+ }
437
+ /** Template-based decomposition — instant, no AI. Fallback only. */
299
438
  decomposeFromTemplate(goal) {
300
439
  const allContext = this.conversationHistory.map(m => m.content).join(' ').toLowerCase();
301
440
  const combined = `${goal.toLowerCase()} ${allContext}`;
441
+ const projectContext = this.ensureProjectContext();
442
+ const hasExistingProject = projectContext.includes('package.json') || projectContext.includes('requirements.txt') || projectContext.includes('pyproject.toml');
443
+ // If existing project detected, use analysis/improvement template
444
+ if (hasExistingProject && this.isAnalysisRequest(combined)) {
445
+ return this.analysisSprintTemplate(goal, projectContext);
446
+ }
302
447
  if (this.matchesKeywords(combined, ['site', 'website', 'landing', 'página', 'pagina', 'homepage', 'web page', 'webpage'])) {
303
448
  return this.websiteSprintTemplate(goal, combined);
304
449
  }
305
450
  if (this.matchesKeywords(combined, ['rest api', 'graphql', 'backend api', 'api endpoint', 'microservice', 'server api'])) {
306
451
  return this.apiSprintTemplate(goal);
307
452
  }
308
- if (this.matchesKeywords(combined, ['app', 'mobile', 'ios', 'android', 'react native', 'flutter', 'aplicativo'])) {
309
- return this.mobileSprintTemplate(goal);
310
- }
311
453
  if (this.matchesKeywords(combined, ['dashboard', 'admin', 'painel', 'analytics', 'crm', 'portal'])) {
312
454
  return this.dashboardSprintTemplate(goal);
313
455
  }
314
456
  if (this.matchesKeywords(combined, ['ecommerce', 'e-commerce', 'loja', 'store', 'shop', 'produto', 'product', 'cart', 'carrinho'])) {
315
457
  return this.ecommerceSprintTemplate(goal);
316
458
  }
317
- if (this.matchesKeywords(combined, ['cli', 'command line', 'terminal', 'tool', 'script', 'automation', 'ferramenta'])) {
459
+ if (this.matchesKeywords(combined, ['cli', 'command line', 'terminal', 'tool', 'script', 'ferramenta'])) {
318
460
  return this.cliToolSprintTemplate(goal);
319
461
  }
320
462
  if (this.matchesKeywords(combined, ['bot', 'chatbot', 'discord', 'telegram', 'slack', 'whatsapp'])) {
@@ -323,12 +465,52 @@ Respond with ONLY valid JSON (no markdown, no explanation):
323
465
  if (this.matchesKeywords(combined, ['game', 'jogo', 'gaming', 'gameplay'])) {
324
466
  return this.gameSprintTemplate(goal);
325
467
  }
326
- // Generic fallbackworks for ANY project
468
+ // Don't match 'app'/'mobile' too eagerly only if clearly about mobile
469
+ if (this.matchesKeywords(combined, ['mobile app', 'ios app', 'android app', 'react native', 'flutter app', 'aplicativo mobile'])) {
470
+ return this.mobileSprintTemplate(goal);
471
+ }
472
+ // If existing project, default to improvement sprint
473
+ if (hasExistingProject) {
474
+ return this.improvementSprintTemplate(goal, projectContext);
475
+ }
476
+ // Generic fallback for new projects
327
477
  return this.genericSprintTemplate(goal);
328
478
  }
479
+ isAnalysisRequest(text) {
480
+ return this.matchesKeywords(text, [
481
+ 'analis', 'review', 'revis', 'estado', 'status', 'audit', 'verificar', 'check',
482
+ 'como est', 'what is the', 'how is', 'diagnos', 'inspect', 'avaliar', 'evaluate',
483
+ ]);
484
+ }
329
485
  matchesKeywords(text, keywords) {
330
486
  return keywords.some(kw => text.includes(kw));
331
487
  }
488
+ /** Template for analyzing an existing project */
489
+ analysisSprintTemplate(goal, projectContext) {
490
+ return {
491
+ sprint: { name: 'Project Analysis', goal: `Analyze and review: ${goal.slice(0, 100)}` },
492
+ tasks: [
493
+ { 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: [] },
494
+ { 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: [] },
495
+ { 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: [] },
496
+ { 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'] },
497
+ { 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'] },
498
+ ],
499
+ };
500
+ }
501
+ /** Template for improving an existing project */
502
+ improvementSprintTemplate(goal, projectContext) {
503
+ return {
504
+ sprint: { name: 'Project Improvement', goal: `Improve: ${goal.slice(0, 100)}` },
505
+ tasks: [
506
+ { 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: [] },
507
+ { 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'] },
508
+ { 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'] },
509
+ { 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'] },
510
+ { 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'] },
511
+ ],
512
+ };
513
+ }
332
514
  websiteSprintTemplate(goal, context) {
333
515
  const sprintName = 'Website MVP';
334
516
  const hasNextjs = context.includes('next') || context.includes('react');
@@ -338,70 +520,14 @@ Respond with ONLY valid JSON (no markdown, no explanation):
338
520
  return {
339
521
  sprint: { name: sprintName, goal: `Build a professional website: ${goal.slice(0, 100)}` },
340
522
  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
- },
523
+ { 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: [] },
524
+ { 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: [] },
525
+ { 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'] },
526
+ { 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'] },
527
+ { 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'] },
528
+ { 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'] },
529
+ { 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'] },
530
+ { 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
531
  ],
406
532
  };
407
533
  }
@@ -499,7 +625,7 @@ Respond with ONLY valid JSON (no markdown, no explanation):
499
625
  ],
500
626
  };
501
627
  }
502
- /** Generic fallback template — works for ANY project type */
628
+ /** Generic fallback template — works for ANY new project type */
503
629
  genericSprintTemplate(goal) {
504
630
  return {
505
631
  sprint: { name: 'Project Sprint', goal: `Build: ${goal.slice(0, 100)}` },