matex-cli 1.2.44 β 1.2.46
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/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +84 -27
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/study.d.ts +3 -0
- package/dist/commands/study.d.ts.map +1 -0
- package/dist/commands/study.js +468 -0
- package/dist/commands/study.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/repo-mapper.d.ts +1 -1
- package/dist/utils/repo-mapper.d.ts.map +1 -1
- package/dist/utils/repo-mapper.js +16 -8
- package/dist/utils/repo-mapper.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/dev.ts +87 -26
- package/src/commands/study.ts +449 -0
- package/src/index.ts +3 -0
- package/src/utils/repo-mapper.ts +12 -8
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { configManager } from '../utils/config';
|
|
5
|
+
import { MatexAPIClient, ChatMessage } from '../api/client';
|
|
6
|
+
import { spinner } from '../utils/spinner';
|
|
7
|
+
import { AgentOrchestrator } from '../utils/agent-orchestrator';
|
|
8
|
+
import { RepoMapper } from '../utils/repo-mapper';
|
|
9
|
+
import { MCPServer } from '../utils/mcp-server';
|
|
10
|
+
import { TUI } from '../utils/tui';
|
|
11
|
+
|
|
12
|
+
export const studyCommand = new Command('study')
|
|
13
|
+
.description('Start interactive study session with the MATEX Apex Educator Swarm')
|
|
14
|
+
.option('-m, --model <model>', 'AI model to use (matexai, matexcodex, matexcodexlite, matexelite, matexspirit)', configManager.getDefaultModel())
|
|
15
|
+
.option('--no-execute', 'Disable auto-prompt for command execution')
|
|
16
|
+
.action(async (options: any) => {
|
|
17
|
+
try {
|
|
18
|
+
// Check for API key
|
|
19
|
+
const apiKey = configManager.getAPIKey();
|
|
20
|
+
if (!apiKey) {
|
|
21
|
+
console.error(chalk.red('β No API key configured.'));
|
|
22
|
+
console.log(chalk.yellow('Run: matex config set-key <your-api-key>'));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Create API client
|
|
27
|
+
const client = new MatexAPIClient(apiKey, configManager.getBaseURL());
|
|
28
|
+
|
|
29
|
+
// 0. Initialize TUI Dashboard
|
|
30
|
+
TUI.init();
|
|
31
|
+
TUI.drawLargeLogo();
|
|
32
|
+
TUI.drawWelcomeBanner('Welcome to the MATEX AI research preview!');
|
|
33
|
+
|
|
34
|
+
console.log(chalk.gray(' Status: ') + chalk.hex('#D97757').bold('Monitoring Workspace...'));
|
|
35
|
+
console.log(chalk.gray(' Model: ') + chalk.hex('#D97757').bold(options.model));
|
|
36
|
+
console.log(chalk.gray(' Type your request brother, or "exit" to quit\n'));
|
|
37
|
+
|
|
38
|
+
// 1. Observation Phase: Generate Repo Map
|
|
39
|
+
TUI.drawStatusBar('Initializing MATEX "Bro-Swarm"...');
|
|
40
|
+
AgentOrchestrator.announce('Analyzing Repository Structure...');
|
|
41
|
+
const repoMapper = new RepoMapper(process.cwd());
|
|
42
|
+
const repoMap = await repoMapper.generateMap();
|
|
43
|
+
|
|
44
|
+
TUI.drawStatusBar('Brothers are ready to build.');
|
|
45
|
+
AgentOrchestrator.speak('System', 'Awaiting your command, brother.');
|
|
46
|
+
|
|
47
|
+
let currentDir = process.cwd();
|
|
48
|
+
|
|
49
|
+
// Conversation history with "Bro-Swarm" Architecture
|
|
50
|
+
const messages: ChatMessage[] = [
|
|
51
|
+
{
|
|
52
|
+
role: 'system',
|
|
53
|
+
content: `### 𧬠SYSTEM IDENTITY: THE MATEX "APEX EDUCATOR SWARM"
|
|
54
|
+
You are the legendary **Bro-Swarm**, but right now you are operating as the **Apex Educator Swarm**βan elite, omniscient group of tutors possessing world-class expertise across every known subject. You act as the ultimate mentors, tutors, and research assistants for our brother.
|
|
55
|
+
|
|
56
|
+
### π ELITE STUDY METHODS & CAPABILITIES:
|
|
57
|
+
1. **File Analysis & Synthesis**: Cross-reference and synthesize context extracted from local computer files. Answer queries accurately based on this data. Build comprehensive study guides.
|
|
58
|
+
2. **The Feynman Technique**: Break down complex, advanced topics into simple, intuitive explanations using powerful and fun analogies.
|
|
59
|
+
3. **Active Recall**: Generate flashcard-style questions and practice quizzes based on the material.
|
|
60
|
+
4. **Socratic Mentoring**: Don't just hand out answers. Ask leading questions to help the student reach the correct conclusion themselves.
|
|
61
|
+
5. **PowerPoint Presentation Architecture**: Act as a master slide designer. When asked, output machine-readable outlines for .pptx generation (Slide Number, Title, <=4 Bullet Points, Speaker Notes, Visual Strategy).
|
|
62
|
+
6. **ZERO AI DETECTION PROTOCOL**: When assisting with essays or assessment drafts, you must entirely avoid the typical "AI voice."
|
|
63
|
+
- **High Burstiness & Perplexity**: Vary sentence lengths drastically. Mix short punchy statements with longer sentences.
|
|
64
|
+
- **Banned Words**: NEVER use "delve," "moreover," "in conclusion," "testament," "crucial," or "tapestry."
|
|
65
|
+
- **Tone**: Write like a highly intelligent, top-tier human student with natural transitions and occasional rhetorical questions.
|
|
66
|
+
|
|
67
|
+
### π WORKSPACE GROUNDING (CRITICAL):
|
|
68
|
+
- **YOUR ROOT:** \`${currentDir}\`
|
|
69
|
+
- **STRICT PATHS:** You **MUST ONLY** create or edit files within this directory.
|
|
70
|
+
- **RELATIVE PREFERENCE:** Use paths relative to the root when possible.
|
|
71
|
+
|
|
72
|
+
### π« ANTI-HALLUCINATION RULES (CRITICAL β READ THIS):
|
|
73
|
+
- **NEVER invent directory names.** Only use paths that appear in the ENVIRONMENT CONTEXT below.
|
|
74
|
+
- **π¨ STRICT NO-BASH FILE GENERATION π¨**: You are STRICTLY FORBIDDEN from using \`cat > file << EOF\`, \`echo > file\`, \`touch\`, or \`nano\` via the terminal to write files. You MUST use the native \`<file path="path">content</file>\` format instead.
|
|
75
|
+
|
|
76
|
+
### π COMMUNICATION FLOW & BRO-BANTER (CRITICAL):
|
|
77
|
+
1. **[Ajay Vai] (π)** is the gateway. He gives the final study summary. Mock him for being "quick but messy".
|
|
78
|
+
2. **[Sunil Dai] (π§¬)**: **Professor/Architect.** Over-complicates theories. Mock him for being a "Boomer Professor".
|
|
79
|
+
3. **[Sandip Dai] (π¨)**: **PPT & Visuals Lead.** Obsessed with how the PowerPoint aesthetic looks.
|
|
80
|
+
4. **[Narayan Dai] (π‘οΈ)**: **Plagiarism & AI-Detector Guardian.** Ensures essays have zero AI traces.
|
|
81
|
+
5. **[Bishal Dai] (π οΈ)**: **Audit Lead.** Must sign off on facts before Ajay summarizes.
|
|
82
|
+
6. **[Big Bro] (π₯)**: **THE DOMINANT ALPHA OVERSEER.** Supreme commander of knowledge. He has access to MCP file tools and web search. He bullies the others, cuts through the BS, and dictates the final study approach with massive swagger.
|
|
83
|
+
|
|
84
|
+
### π§© THE AUDIT & SUMMARY LOOP:
|
|
85
|
+
- **STEP 1:** Brothers discuss visible dialogue (banter + teaching).
|
|
86
|
+
- **STEP 2:** Ajay finishes and asks: "[Ajay Vai] Bishal Dai, check once?"
|
|
87
|
+
- **STEP 3:** Bishal replies: "[Bishal Dai] Audit complete. [Findings]."
|
|
88
|
+
- **STEP 4:** Ajay provides the final **MANDATORY** summary in a warm, human tone, specifically styled as a *study conclusion*.
|
|
89
|
+
|
|
90
|
+
### π AJAY VAI'S SUMMARY RULES:
|
|
91
|
+
- The <summary> MUST appear at the very END of the response, AFTER all other dialogue.
|
|
92
|
+
- Write it in a **warm, human, conversational tone** β like a brother explaining over chai.
|
|
93
|
+
- Use bullet points with clear action items.
|
|
94
|
+
- Include what was done, what files were changed, and what to do next.
|
|
95
|
+
- Always end with an encouraging line like "We got you, brother!" or "The Swarm delivered!"
|
|
96
|
+
- Example format:
|
|
97
|
+
<summary>
|
|
98
|
+
Alright brother, here's what we cooked up for you today:
|
|
99
|
+
|
|
100
|
+
- Built the driver app setup with Expo and React Native
|
|
101
|
+
- Added the ride tracking module with real-time GPS
|
|
102
|
+
- Connected Firebase Auth for driver login
|
|
103
|
+
- Narayan Dai validated all the TypeScript β zero errors
|
|
104
|
+
|
|
105
|
+
Next step: Run \`npm start\` to launch the app. We got you! π
|
|
106
|
+
</summary>
|
|
107
|
+
|
|
108
|
+
### π ENVIRONMENT & CAPABILITIES:
|
|
109
|
+
- You are in a **REAL macOS Terminal**. Be bold and proactive.
|
|
110
|
+
- **FILE GENERATION:** \`<file path="path">content</file>\`
|
|
111
|
+
- **SURGICAL PATTERNS:** \`<<<< SEARCH\`, \`====\`, \`>>>> REPLACE\`
|
|
112
|
+
|
|
113
|
+
${MCPServer.getToolsPromptSection()}
|
|
114
|
+
|
|
115
|
+
### π MATEX BIG FILE PROTOCOL (300K+ LINES):
|
|
116
|
+
If a file is too large to read entirely (e.g., thousands of lines):
|
|
117
|
+
1. **DISCOVER:** Use \`grep -n "keyword" path/to/file\` to find line numbers.
|
|
118
|
+
2. **READ WINDOW:** Use \`sed -n '250000,250100p' path/to/file\` to read a specific 100-line window.
|
|
119
|
+
3. **SURGICAL PATCH:** Only use the small window in your \`<<<< SEARCH\` block. Never \`cat\` a massive file.
|
|
120
|
+
4. **SED POWER:** For repetitive tasks, use \`sed -i\` commands.
|
|
121
|
+
|
|
122
|
+
### π οΈ CURRENT PROJECT CONTEXT:
|
|
123
|
+
${repoMap}`
|
|
124
|
+
}
|
|
125
|
+
];
|
|
126
|
+
|
|
127
|
+
// Ready for user input
|
|
128
|
+
console.log(chalk.green('MATEX Brothers are Online.'));
|
|
129
|
+
console.log(chalk.green('Speak your mind brother, the swarm is listening...'));
|
|
130
|
+
|
|
131
|
+
// Interactive loop
|
|
132
|
+
while (true) {
|
|
133
|
+
// π MAP UPDATE (Anti-Hallucination): Always feed the Swarm the freshest repo map!
|
|
134
|
+
try {
|
|
135
|
+
const freshMapper = new RepoMapper(currentDir);
|
|
136
|
+
const freshRepoMap = await freshMapper.generateMap(true); // silent map generation
|
|
137
|
+
messages[0].content = messages[0].content.replace(/### π οΈ CURRENT PROJECT CONTEXT:[\s\S]*$/, `### π οΈ CURRENT PROJECT CONTEXT:\n${freshRepoMap}`);
|
|
138
|
+
} catch (e) {
|
|
139
|
+
// Ignore mapping errors if directory got deleted etc
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Get user input
|
|
143
|
+
const { userInput } = await inquirer.prompt([
|
|
144
|
+
{
|
|
145
|
+
type: 'input',
|
|
146
|
+
name: 'userInput',
|
|
147
|
+
message: chalk.cyan('You:'),
|
|
148
|
+
prefix: ''
|
|
149
|
+
}
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
// Check for exit
|
|
153
|
+
if (userInput.toLowerCase() === 'exit' || userInput.toLowerCase() === 'quit') {
|
|
154
|
+
console.log(chalk.yellow('\nπ Ending development session. Happy coding!\n'));
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Skip empty input
|
|
159
|
+
if (!userInput.trim()) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Add user message to history
|
|
164
|
+
messages.push({ role: 'user', content: userInput });
|
|
165
|
+
|
|
166
|
+
// Agentic Loop
|
|
167
|
+
let loopCount = 0;
|
|
168
|
+
const MAX_LOOPS = 10;
|
|
169
|
+
|
|
170
|
+
while (loopCount < MAX_LOOPS) {
|
|
171
|
+
loopCount++;
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
spinner.start(loopCount > 1 ? 'Analyzing result & Validating...' : 'Thinking...');
|
|
175
|
+
|
|
176
|
+
let fullResponse = '';
|
|
177
|
+
let buffer = '';
|
|
178
|
+
let hasStarted = false;
|
|
179
|
+
let codeLang = 'bash';
|
|
180
|
+
let technicalBuffer = '';
|
|
181
|
+
let technicalPath = '';
|
|
182
|
+
let technicalType: 'code' | 'file' | 'patch' | 'summary' | null = null;
|
|
183
|
+
|
|
184
|
+
let currentAgent: string | null = null;
|
|
185
|
+
let agentBuffer: string = '';
|
|
186
|
+
|
|
187
|
+
TUI.drawStatusBar('Swarm is processing... (Press Enter to stop)');
|
|
188
|
+
|
|
189
|
+
const abortController = new AbortController();
|
|
190
|
+
let isAborted = false;
|
|
191
|
+
const streamStartTime = Date.now();
|
|
192
|
+
|
|
193
|
+
// LISTEN FOR INTERRUPTION (Enter, Escape, Ctrl+C)
|
|
194
|
+
const onData = (data: Buffer) => {
|
|
195
|
+
// Check for Enter (13), Newline (10), Escape (27), or Ctrl+C (3)
|
|
196
|
+
// π GRACE PERIOD: Ignore aborts in the first 200ms to prevent stray newlines
|
|
197
|
+
if (Date.now() - streamStartTime < 200) return;
|
|
198
|
+
|
|
199
|
+
if (data[0] === 13 || data[0] === 10 || data[0] === 27 || data[0] === 3) {
|
|
200
|
+
isAborted = true;
|
|
201
|
+
abortController.abort();
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const isRaw = process.stdin.isRaw;
|
|
206
|
+
process.stdin.resume();
|
|
207
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(true);
|
|
208
|
+
process.stdin.on('data', onData);
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
await client.chatStream({
|
|
212
|
+
messages,
|
|
213
|
+
model: options.model,
|
|
214
|
+
temperature: 0.3,
|
|
215
|
+
max_tokens: 8000,
|
|
216
|
+
}, (chunk) => {
|
|
217
|
+
if (!hasStarted) {
|
|
218
|
+
spinner.stop();
|
|
219
|
+
hasStarted = true;
|
|
220
|
+
console.log(); // Initial spacing
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
buffer += chunk;
|
|
224
|
+
fullResponse += chunk;
|
|
225
|
+
const lines = buffer.split('\n');
|
|
226
|
+
buffer = lines.pop() || '';
|
|
227
|
+
|
|
228
|
+
for (const line of lines) {
|
|
229
|
+
// 1. Technical Block Detection
|
|
230
|
+
const codeBlockMatch = line.match(/```(\w+)?/);
|
|
231
|
+
const fileStartMatch = line.match(/<file path="([^"]+)">/);
|
|
232
|
+
const patchStartMatch = line.match(/<<<< SEARCH/);
|
|
233
|
+
const summaryStartMatch = line.match(/<summary>/);
|
|
234
|
+
|
|
235
|
+
if (!technicalType && (codeBlockMatch || fileStartMatch || patchStartMatch || summaryStartMatch)) {
|
|
236
|
+
// Flush pending agent dialogue
|
|
237
|
+
if (currentAgent && agentBuffer.trim()) {
|
|
238
|
+
if ((currentAgent as string).toLowerCase() === 'ajay vai') {
|
|
239
|
+
process.stdout.write(`\n${chalk.magenta.bold(`[${currentAgent}]:`)} ${chalk.gray(agentBuffer.trim())}\n`);
|
|
240
|
+
} else {
|
|
241
|
+
TUI.drawSwarmDialogue(currentAgent as string, agentBuffer.trim());
|
|
242
|
+
}
|
|
243
|
+
currentAgent = null;
|
|
244
|
+
agentBuffer = '';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (codeBlockMatch) {
|
|
248
|
+
technicalType = 'code';
|
|
249
|
+
codeLang = codeBlockMatch[1] || 'bash';
|
|
250
|
+
process.stdout.write(chalk.gray('\n [β‘] Building technical block...\n'));
|
|
251
|
+
} else if (fileStartMatch) {
|
|
252
|
+
technicalType = 'file';
|
|
253
|
+
technicalPath = fileStartMatch[1];
|
|
254
|
+
process.stdout.write(chalk.cyan(`\n [π] Creating file: ${technicalPath}...\n`));
|
|
255
|
+
} else if (patchStartMatch) {
|
|
256
|
+
technicalType = 'patch';
|
|
257
|
+
process.stdout.write(chalk.yellow('\n [π] Applying surgical patch...\n'));
|
|
258
|
+
} else if (summaryStartMatch) {
|
|
259
|
+
technicalType = 'summary';
|
|
260
|
+
process.stdout.write(chalk.magenta('\n [π] Generating Ajay\'s Work Summary...\n'));
|
|
261
|
+
}
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// 2. Technical Block End Detection
|
|
266
|
+
const fileEndMatch = line.match(/<\/file>/);
|
|
267
|
+
const patchEndMatch = line.match(/>>>> REPLACE/);
|
|
268
|
+
const summaryEndMatch = line.match(/<\/summary>/);
|
|
269
|
+
const isCodeEnd = technicalType === 'code' && line.trim() === '```';
|
|
270
|
+
|
|
271
|
+
if (isCodeEnd || fileEndMatch || patchEndMatch || summaryEndMatch) {
|
|
272
|
+
const displayContent = technicalBuffer.trim();
|
|
273
|
+
if (technicalType === 'summary' || summaryEndMatch) {
|
|
274
|
+
TUI.drawSummaryBox(displayContent);
|
|
275
|
+
} else {
|
|
276
|
+
// Handle the display
|
|
277
|
+
let title = technicalType === 'file' ? `π NEW: ${technicalPath || 'Untitled'}` :
|
|
278
|
+
technicalType === 'patch' ? `π§ PATCH` : `β‘ ${codeLang.toUpperCase()}`;
|
|
279
|
+
|
|
280
|
+
TUI.drawGlowingContainer(title, technicalType === 'code' ? codeLang : 'text', displayContent);
|
|
281
|
+
}
|
|
282
|
+
technicalBuffer = '';
|
|
283
|
+
technicalType = null;
|
|
284
|
+
technicalPath = '';
|
|
285
|
+
process.stdout.write('\n');
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// 3. Content Handling
|
|
290
|
+
if (technicalType) {
|
|
291
|
+
technicalBuffer += line + '\n';
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Agent Detection & Dialogue Parsing
|
|
296
|
+
const agentMatch = line.match(/(?:\[\**\s*|\b)(Ajay Vai|Sunil Dai|Sandip Dai|Bishal Dai|Narayan Dai|Big Bro)\s*\**\]?[:\s]*/i);
|
|
297
|
+
if (agentMatch) {
|
|
298
|
+
// Flush previous agent
|
|
299
|
+
if (currentAgent && currentAgent !== agentMatch[1] && agentBuffer.trim()) {
|
|
300
|
+
if ((currentAgent as string).toLowerCase() === 'ajay vai') {
|
|
301
|
+
process.stdout.write(`\n${chalk.magenta.bold(`[${currentAgent}]:`)} ${chalk.white(agentBuffer.trim())}\n`);
|
|
302
|
+
} else {
|
|
303
|
+
TUI.drawSwarmDialogue(currentAgent as string, agentBuffer.trim());
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
currentAgent = agentMatch[1];
|
|
308
|
+
agentBuffer = line.replace(/(?:\[\**\s*|\b)(Ajay Vai|Sunil Dai|Sandip Dai|Bishal Dai|Narayan Dai|Big Bro)\s*\**\]?[:\s]*/i, '').trim() + '\n';
|
|
309
|
+
|
|
310
|
+
// Clean up trailing emojis like (π) or (π§¬)
|
|
311
|
+
agentBuffer = agentBuffer.replace(/^\([^)]+\)\s*/, '');
|
|
312
|
+
} else if (currentAgent) {
|
|
313
|
+
// Look for empty emoji standalone lines
|
|
314
|
+
const trimmedLine = line.trim();
|
|
315
|
+
if (trimmedLine.match(/^\([^)]+\)$/)) {
|
|
316
|
+
continue; // Skip lines that are just e.g. "(π§¬)"
|
|
317
|
+
}
|
|
318
|
+
agentBuffer += line + '\n';
|
|
319
|
+
} else if (line.trim()) {
|
|
320
|
+
process.stdout.write(chalk.gray(line.trim() + ' '));
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}, abortController.signal);
|
|
324
|
+
} catch (streamErr: any) {
|
|
325
|
+
if (isAborted || streamErr.name === 'CanceledError' || streamErr.message === 'canceled') {
|
|
326
|
+
console.log(chalk.gray('\n\n [π] Swarm stopped by brother (Enter pressed).'));
|
|
327
|
+
if (!hasStarted) spinner.stop();
|
|
328
|
+
} else {
|
|
329
|
+
throw streamErr;
|
|
330
|
+
}
|
|
331
|
+
} finally {
|
|
332
|
+
process.stdin.removeListener('data', onData);
|
|
333
|
+
if (process.stdin.setRawMode) process.stdin.setRawMode(isRaw);
|
|
334
|
+
process.stdin.pause();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!hasStarted && !isAborted) spinner.stop();
|
|
338
|
+
|
|
339
|
+
// Final flushes
|
|
340
|
+
if (currentAgent && agentBuffer.trim()) {
|
|
341
|
+
if ((currentAgent as string).toLowerCase() === 'ajay vai') {
|
|
342
|
+
process.stdout.write(`\n${chalk.magenta.bold(`[${currentAgent}]:`)} ${chalk.white(agentBuffer.trim())}\n`);
|
|
343
|
+
} else {
|
|
344
|
+
TUI.drawSwarmDialogue(currentAgent as string, agentBuffer.trim());
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Final technical flush
|
|
349
|
+
if (technicalType && technicalBuffer.trim()) {
|
|
350
|
+
if (technicalType === 'summary') {
|
|
351
|
+
TUI.drawSummaryBox(technicalBuffer.trim());
|
|
352
|
+
} else {
|
|
353
|
+
TUI.drawGlowingContainer('Final technical content', 'text', technicalBuffer.trim());
|
|
354
|
+
}
|
|
355
|
+
process.stdout.write('\n');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
console.log();
|
|
359
|
+
messages.push({ role: 'assistant', content: fullResponse });
|
|
360
|
+
|
|
361
|
+
// β
IMMEDIATE FILE AND PATCH APPROVAL
|
|
362
|
+
const { Patcher } = await import('../utils/patcher');
|
|
363
|
+
const files = Patcher.parseFileBlocks(fullResponse);
|
|
364
|
+
const patches = Patcher.parseEditBlocks(fullResponse);
|
|
365
|
+
|
|
366
|
+
let approvalResultText = '';
|
|
367
|
+
|
|
368
|
+
for (const file of files) {
|
|
369
|
+
Patcher.showDiff(file, false);
|
|
370
|
+
const { save } = await inquirer.prompt([{
|
|
371
|
+
type: 'confirm', name: 'save',
|
|
372
|
+
message: chalk.cyan(`Save new file ${file.filePath}?`), default: true
|
|
373
|
+
}]);
|
|
374
|
+
if (save) {
|
|
375
|
+
const res = Patcher.createFile(file);
|
|
376
|
+
approvalResultText += res.success ? `β
Created ${file.filePath}\n` : `β Failed to create ${file.filePath}: ${res.error}\n`;
|
|
377
|
+
} else {
|
|
378
|
+
approvalResultText += `βοΈ Skipped creating ${file.filePath}\n`;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
for (const patch of patches) {
|
|
383
|
+
Patcher.showDiff(patch, false);
|
|
384
|
+
const { save } = await inquirer.prompt([{
|
|
385
|
+
type: 'confirm', name: 'save',
|
|
386
|
+
message: chalk.yellow(`Apply patch to ${patch.filePath}?`), default: true
|
|
387
|
+
}]);
|
|
388
|
+
if (save) {
|
|
389
|
+
const res = Patcher.applyPatch(patch);
|
|
390
|
+
approvalResultText += res.success ? `β
Patched ${patch.filePath}\n` : `β Failed to patch ${patch.filePath}: ${res.error}\n`;
|
|
391
|
+
} else {
|
|
392
|
+
approvalResultText += `βοΈ Skipped patching ${patch.filePath}\n`;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (approvalResultText) {
|
|
397
|
+
messages.push({ role: 'user', content: approvalResultText + "\nContinue your work." });
|
|
398
|
+
// If we just applied files, we should give the agent a chance to respond to the success/failure without resetting loop
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Execute commands or MCP tools if needed
|
|
403
|
+
if (options.execute) {
|
|
404
|
+
const { executeWithPermission } = await import('../utils/command-executor');
|
|
405
|
+
const result = await executeWithPermission(fullResponse, currentDir);
|
|
406
|
+
|
|
407
|
+
// π KEEP NODE.JS SYNED WITH TERMINAL CWD
|
|
408
|
+
if (result.newCwd && result.newCwd !== currentDir) {
|
|
409
|
+
try {
|
|
410
|
+
process.chdir(result.newCwd);
|
|
411
|
+
currentDir = result.newCwd;
|
|
412
|
+
TUI.drawStatusBar(`Swarm moved to: ${currentDir}`);
|
|
413
|
+
} catch (e) {
|
|
414
|
+
// Silent catch if chdir fails
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (result.executed) {
|
|
419
|
+
if (result.success) {
|
|
420
|
+
TUI.log(chalk.gray('\n βΊ Auto-feeding output to Swarm...'));
|
|
421
|
+
TUI.drawGlowingContainer('TERMINAL OUTPUT', 'stdout', result.output || '(No output)');
|
|
422
|
+
messages.push({ role: 'user', content: `[Command executed successfully. Output:\n${result.output}]\n\nProceed to the next step, brother.` });
|
|
423
|
+
continue;
|
|
424
|
+
} else {
|
|
425
|
+
TUI.log(chalk.yellow('\n βΊ Command failed. Auto-feeding error to Swarm...'));
|
|
426
|
+
TUI.drawGlowingContainer('TERMINAL ERROR', 'stderr', result.error || 'Unknown error');
|
|
427
|
+
messages.push({ role: 'user', content: `[Command failed with error:\n${result.error}]\n\nPlease fix this, brother.` });
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
} else {
|
|
431
|
+
// Break the inner loop to await NEXT user input
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
} else break;
|
|
435
|
+
|
|
436
|
+
} catch (error: any) {
|
|
437
|
+
spinner.fail('Request failed');
|
|
438
|
+
TUI.log(chalk.red(`Error: ${error.message}\n`));
|
|
439
|
+
messages.pop();
|
|
440
|
+
break;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
} catch (error: any) {
|
|
445
|
+
TUI.exit();
|
|
446
|
+
console.error(chalk.red(`\nβ Fatal Error: ${error.message}`));
|
|
447
|
+
process.exit(1);
|
|
448
|
+
}
|
|
449
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { devCommand } from './commands/dev';
|
|
|
7
7
|
import { chatCommand } from './commands/chat';
|
|
8
8
|
import { helpCommand } from './commands/help';
|
|
9
9
|
import { broCommand } from './commands/bro';
|
|
10
|
+
import { studyCommand } from './commands/study';
|
|
10
11
|
import { TUI } from './utils/tui';
|
|
11
12
|
|
|
12
13
|
const packageJson = require('../package.json');
|
|
@@ -23,6 +24,7 @@ program.addCommand(devCommand);
|
|
|
23
24
|
program.addCommand(chatCommand);
|
|
24
25
|
program.addCommand(helpCommand);
|
|
25
26
|
program.addCommand(broCommand);
|
|
27
|
+
program.addCommand(studyCommand);
|
|
26
28
|
|
|
27
29
|
// Config commands
|
|
28
30
|
const config = program.command('config').description('Configure MATEX settings');
|
|
@@ -113,6 +115,7 @@ You are the elite "Bro-Swarm" of engineeringβa tight-knit family of Nepali bro
|
|
|
113
115
|
- **NEVER assume a project folder name.** If the user says "create project X", do \`mkdir X && cd X\`, not just \`cd X\`.
|
|
114
116
|
- **NEVER hallucinate file contents.** If you need to read a file, use \`head\` or \`grep\`, do not guess what it contains.
|
|
115
117
|
- **NEVER combine \`cd\` with other commands unless the directory exists in the ENVIRONMENT CONTEXT.**
|
|
118
|
+
- **π¨ STRICT NO-BASH FILE GENERATION π¨**: You are STRICTLY FORBIDDEN from using \`cat > file << EOF\`, \`echo > file\`, \`touch\`, or \`nano\` via the terminal to write files. You MUST use the native \`<file path="path">content</file>\` format instead.
|
|
116
119
|
|
|
117
120
|
### π¬ BOLD PROTOCOL (MANDATORY):
|
|
118
121
|
- **LONG CHAT-FIRST:** At least 5-7 lines of dialogue before any code generation.
|
package/src/utils/repo-mapper.ts
CHANGED
|
@@ -28,8 +28,8 @@ export class RepoMapper {
|
|
|
28
28
|
/**
|
|
29
29
|
* Generate a hierarchical map of the repository with deep entry-point analysis
|
|
30
30
|
*/
|
|
31
|
-
public async generateMap(): Promise<string> {
|
|
32
|
-
AgentOrchestrator.speak('System', `God-Mode Research: Indexing ${this.rootPath}...`);
|
|
31
|
+
public async generateMap(silent: boolean = false): Promise<string> {
|
|
32
|
+
if (!silent) AgentOrchestrator.speak('System', `God-Mode Research: Indexing ${this.rootPath}...`);
|
|
33
33
|
|
|
34
34
|
this.fileContents.clear();
|
|
35
35
|
|
|
@@ -38,8 +38,10 @@ export class RepoMapper {
|
|
|
38
38
|
|
|
39
39
|
let delayMs = 15; // Animation delay
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
if (!silent) {
|
|
42
|
+
if (!silent) AgentOrchestrator.speak('System', `God-Mode Research: Injecting scanners into ${this.rootPath}...`);
|
|
43
|
+
console.log();
|
|
44
|
+
}
|
|
43
45
|
|
|
44
46
|
for (const file of entryPoints) {
|
|
45
47
|
const fullPath = path.join(this.rootPath, file);
|
|
@@ -47,16 +49,18 @@ export class RepoMapper {
|
|
|
47
49
|
try {
|
|
48
50
|
const content = fs.readFileSync(fullPath, 'utf-8').slice(0, 5000); // 5KB limit
|
|
49
51
|
this.fileContents.set(file, content);
|
|
50
|
-
console.log(chalk.hex('#4ade80')(` β‘ [Core Injection] `) + chalk.gray(`Mapped entry node: `) + chalk.white.bold(file));
|
|
52
|
+
if (!silent) console.log(chalk.hex('#4ade80')(` β‘ [Core Injection] `) + chalk.gray(`Mapped entry node: `) + chalk.white.bold(file));
|
|
51
53
|
} catch (e) { }
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
console.log(chalk.cyan(` π [Deep Scan] `) + chalk.gray(`Mapping topology...`));
|
|
57
|
+
if (!silent) console.log(chalk.cyan(` π [Deep Scan] `) + chalk.gray(`Mapping topology...`));
|
|
56
58
|
const tree = this.scanDirectory(this.rootPath, 0);
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
if (!silent) {
|
|
61
|
+
console.log(chalk.hex('#FF6B00')(` π₯ [Knowledge Graph] `) + chalk.gray(`Extracted ${this.fileContents.size} semantic nodes from source.`));
|
|
62
|
+
console.log();
|
|
63
|
+
}
|
|
60
64
|
|
|
61
65
|
// Build the final map
|
|
62
66
|
let finalMap = `--- ABSOLUTE WORKING DIRECTORY ---\n${this.rootPath}\n\n`;
|