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.
- package/README.md +40 -9
- package/dist/cli/commands/setup-claude.d.ts +3 -0
- package/dist/cli/commands/setup-claude.d.ts.map +1 -0
- package/dist/cli/commands/setup-claude.js +75 -0
- package/dist/cli/commands/setup-claude.js.map +1 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +3 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/engine/__tests__/ceo-brain.test.js +2 -2
- package/dist/engine/__tests__/ceo-brain.test.js.map +1 -1
- package/dist/engine/ceo-brain.d.ts +15 -8
- package/dist/engine/ceo-brain.d.ts.map +1 -1
- package/dist/engine/ceo-brain.js +326 -208
- package/dist/engine/ceo-brain.js.map +1 -1
- package/dist/engine/orchestrator.d.ts +1 -0
- package/dist/engine/orchestrator.d.ts.map +1 -1
- package/dist/engine/orchestrator.js +27 -1
- package/dist/engine/orchestrator.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +9 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/routes/api.d.ts.map +1 -1
- package/dist/server/routes/api.js +10 -3
- package/dist/server/routes/api.js.map +1 -1
- package/dist/state/repositories/agents.js +1 -1
- package/dist/state/repositories/agents.js.map +1 -1
- package/{src/web/dist/assets/BufferResource-CnEVFTWO.js → dist/web/dist/assets/BufferResource-BGsixSHO.js} +1 -1
- package/dist/web/dist/assets/BufferResource-BbpTIQ85.js +185 -0
- package/dist/web/dist/assets/BufferResource-BjJe3r2a.js +185 -0
- package/dist/web/dist/assets/BufferResource-C7ypptTD.js +185 -0
- package/dist/web/dist/assets/BufferResource-CuR0zQl9.js +185 -0
- package/dist/web/dist/assets/BufferResource-_EbXUkK9.js +185 -0
- package/dist/web/dist/assets/BufferResource-dCF0GA9M.js +185 -0
- package/{src/web/dist/assets/CanvasRenderer-DJddxDJS.js → dist/web/dist/assets/CanvasRenderer-BGC-Rjpm.js} +1 -1
- package/dist/web/dist/assets/CanvasRenderer-BQ7ScDtX.js +1 -0
- package/dist/web/dist/assets/CanvasRenderer-CBkg7qb3.js +1 -0
- package/dist/web/dist/assets/CanvasRenderer-CcFmL3Lw.js +1 -0
- package/dist/web/dist/assets/CanvasRenderer-CwKoxkGD.js +1 -0
- package/dist/web/dist/assets/CanvasRenderer-YM8daXJu.js +1 -0
- package/dist/web/dist/assets/CanvasRenderer-yT9JOc5o.js +1 -0
- package/{src/web/dist/assets/Filter-DWNM0gBH.js → dist/web/dist/assets/Filter-BzM7i9ln.js} +1 -1
- package/dist/web/dist/assets/Filter-CJvSyeWH.js +1 -0
- package/dist/web/dist/assets/Filter-CUPFlJF_.js +1 -0
- package/dist/web/dist/assets/Filter-D2kh6Lyn.js +1 -0
- package/dist/web/dist/assets/Filter-DMjNMMPy.js +1 -0
- package/dist/web/dist/assets/Filter-GQK6iqrb.js +1 -0
- package/dist/web/dist/assets/Filter-nUtqhIEl.js +1 -0
- package/{src/web/dist/assets/RenderTargetSystem-C4fzbyMy.js → dist/web/dist/assets/RenderTargetSystem-B-ekYymr.js} +1 -1
- package/dist/web/dist/assets/RenderTargetSystem-BylEtp0F.js +172 -0
- package/dist/web/dist/assets/RenderTargetSystem-D2L3KLdt.js +172 -0
- package/dist/web/dist/assets/RenderTargetSystem-DNsILH5P.js +172 -0
- package/dist/web/dist/assets/RenderTargetSystem-lhfSOVIz.js +172 -0
- package/dist/web/dist/assets/RenderTargetSystem-nRg6zEP8.js +172 -0
- package/dist/web/dist/assets/RenderTargetSystem-uCCnK3Aw.js +172 -0
- package/{src/web/dist/assets/WebGLRenderer-Dl5dddTJ.js → dist/web/dist/assets/WebGLRenderer-BEupTZT-.js} +1 -1
- package/dist/web/dist/assets/WebGLRenderer-BJuclWjv.js +156 -0
- package/dist/web/dist/assets/WebGLRenderer-BONohh1V.js +156 -0
- package/dist/web/dist/assets/WebGLRenderer-Bsr1n0Uu.js +156 -0
- package/dist/web/dist/assets/WebGLRenderer-Da_BkFlZ.js +156 -0
- package/dist/web/dist/assets/WebGLRenderer-DiL4ivWY.js +156 -0
- package/dist/web/dist/assets/WebGLRenderer-DqRhyEby.js +156 -0
- package/{src/web/dist/assets/WebGPURenderer-DHzWrTEL.js → dist/web/dist/assets/WebGPURenderer-C0BJNyre.js} +1 -1
- package/dist/web/dist/assets/WebGPURenderer-Clrsu_cP.js +41 -0
- package/dist/web/dist/assets/WebGPURenderer-DbGOPnSE.js +41 -0
- package/dist/web/dist/assets/WebGPURenderer-Ds8VNo2-.js +41 -0
- package/dist/web/dist/assets/WebGPURenderer-Du7e_Agz.js +41 -0
- package/dist/web/dist/assets/WebGPURenderer-pLgFan3y.js +41 -0
- package/dist/web/dist/assets/WebGPURenderer-pd03jK7j.js +41 -0
- package/{src/web/dist/assets/browserAll-CrRQwob6.js → dist/web/dist/assets/browserAll-1QqybVac.js} +1 -1
- package/dist/web/dist/assets/browserAll-Bd3WFbs9.js +14 -0
- package/dist/web/dist/assets/browserAll-Bv-Fa9BT.js +14 -0
- package/dist/web/dist/assets/browserAll-C9HRyLf9.js +14 -0
- package/dist/web/dist/assets/browserAll-D7SUz7ce.js +14 -0
- package/dist/web/dist/assets/browserAll-TAEt2LTG.js +14 -0
- package/dist/web/dist/assets/browserAll-d1yltqWP.js +14 -0
- package/dist/web/dist/assets/index-BQx3wJPr.css +1 -0
- package/dist/web/dist/assets/index-C1XGJE1t.js +298 -0
- package/dist/web/dist/assets/index-C6hoHqN5.js +298 -0
- package/dist/web/dist/assets/index-CFVD9jMF.js +298 -0
- package/dist/web/dist/assets/index-CO3-IuiC.js +298 -0
- package/dist/web/dist/assets/index-CRT2SA6d.js +298 -0
- package/dist/web/dist/assets/index-CVcQAOB6.css +1 -0
- package/dist/web/dist/assets/index-CwYVpEl9.js +298 -0
- package/dist/web/dist/assets/index-DBSQsI6C.js +298 -0
- package/{src/web/dist/assets/webworkerAll-B9FNurHh.js → dist/web/dist/assets/webworkerAll-B3fxgx9g.js} +1 -1
- package/dist/web/dist/assets/webworkerAll-BILg7nF2.js +83 -0
- package/dist/web/dist/assets/webworkerAll-C7T1zoFZ.js +83 -0
- package/dist/web/dist/assets/webworkerAll-CMt6Ulvh.js +83 -0
- package/dist/web/dist/assets/webworkerAll-CREQA0I8.js +83 -0
- package/dist/web/dist/assets/webworkerAll-CykNhAo8.js +83 -0
- package/dist/web/dist/assets/webworkerAll-WnWYpa5G.js +83 -0
- package/{src → dist}/web/dist/index.html +2 -2
- package/package.json +2 -3
- package/src/web/dist/assets/index-8Qtg1JIx.css +0 -1
- package/src/web/dist/assets/index-PrNo7ukG.js +0 -298
package/dist/engine/ceo-brain.js
CHANGED
|
@@ -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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
148
|
-
|
|
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 —
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
//
|
|
183
|
-
this.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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:
|
|
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.
|
|
214
|
-
this.
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
${
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
-
|
|
240
|
-
-
|
|
241
|
-
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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,
|
|
295
|
-
//
|
|
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
|
-
|
|
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', '
|
|
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
|
-
//
|
|
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
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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)}` },
|