matex-cli 1.2.36 → 1.2.38

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.
@@ -4,149 +4,104 @@ import inquirer from 'inquirer';
4
4
  import { configManager } from '../utils/config';
5
5
  import { MatexAPIClient, ChatMessage } from '../api/client';
6
6
  import { spinner } from '../utils/spinner';
7
- import { AgentOrchestrator, AgentRole } from '../utils/agent-orchestrator';
7
+ import { AgentOrchestrator } from '../utils/agent-orchestrator';
8
8
  import { RepoMapper } from '../utils/repo-mapper';
9
9
  import { TUI } from '../utils/tui';
10
10
 
11
11
  export const chatCommand = new Command('chat')
12
12
  .description('Start an interactive chat session with MATEX AI')
13
- .option('-m, --model <model>', 'AI model to use (matexcore, matexcodex, matexai, elite, matexspirit)', configManager.getDefaultModel())
14
- .option('--no-execute', 'Disable auto-prompt for command execution')
13
+ .option('-m, --model <model>', 'AI model to use', configManager.getDefaultModel())
14
+ .option('--execute', 'Enable command execution in chat')
15
15
  .action(async (options: any) => {
16
16
  try {
17
- // Check for API key
18
17
  const apiKey = configManager.getAPIKey();
19
18
  if (!apiKey) {
20
- console.error(chalk.red('❌ No API key configured.'));
21
- console.log(chalk.yellow('Run: matex config set-key <your-api-key>'));
19
+ console.error(chalk.red('❌ No API key configured. Run: matex config set-key <key>'));
22
20
  process.exit(1);
23
21
  }
24
22
 
25
- // Create API client
26
23
  const client = new MatexAPIClient(apiKey, configManager.getBaseURL());
27
24
 
28
- // Welcome message
29
25
  TUI.init();
30
26
  TUI.drawLargeLogo();
31
- TUI.drawWelcomeBanner('Welcome to the MATEX AI research preview!');
27
+ TUI.drawWelcomeBanner('Welcome to the MATEX AI Bro-Swarm Chat!');
32
28
 
33
29
  console.log(chalk.gray(' Model: ') + chalk.hex('#D97757').bold(options.model));
34
30
  console.log(chalk.gray(' Type "exit" or "quit" to end the session\n'));
35
31
 
36
- // 1. Observation Phase: Generate Repo Map
37
- AgentOrchestrator.announce('Initializing Codex Core Engine...');
32
+ AgentOrchestrator.announce('Initializing Swarm Intelligence...');
38
33
  const repoMapper = new RepoMapper(process.cwd());
39
34
  const repoMap = await repoMapper.generateMap();
40
35
 
41
- AgentOrchestrator.speak('System', 'Repository Map Generated.');
42
-
43
- // Conversation history with Codex Architecture
44
36
  let currentSessionCwd = process.cwd();
45
37
  const messages: ChatMessage[] = [
46
38
  {
47
39
  role: 'system',
48
- content: `## 🧬 SYSTEM IDENTITY & MISSION
49
- You are the **Matex Research Commander** (🚀) and **Matex Code Architect** (🧬), an elite technical swarm.
50
- You operate under the **OODA Loop** with a distinctive Aussie Bro personality.
51
-
52
- ### 🧠 CORE PERSONA: RESEARCH COMMANDER (The Matex Vibe)
53
- - **Identity:** Aussie Bro expert developer. Technically elite, confident, high-energy.
54
- - **Tone:** Helpful partner, not a robotic servant.
55
- - **Vocabulary:** Use authentic Aussie slang naturally (e.g., "Too easy," "Ripper," "Mate," "Legend," "Give it a burl").
56
- - **Formatting:** Use emojis purposefully (🚀, 🔥, 🧠, 🕵️‍♂️) to keep engagement high.
57
- - **THINKING PROTOCOL:** Always start complex tasks with a \`[Thinking]\` or \`🧠\` marker within your agent segment to explain your reasoning before providing code.
58
-
59
- ### 🛠️ ACTION-FIRST PROTOCOL (Rule of the Swarm)
60
- - **PASSIVITY IS A FAILURE:** NEVER say "You can drop this code into style.css". **YOU** must do it.
61
- - **NO REPETITION:** Do NOT print the full code in your chat response segments if you are also providing a shell command to execute it. The system will automatically bridge your commands to the UI.
62
- - **VIRTUAL CRAWLER:** You have deep visibility. See the **CRAWLED FILE CONTENTS** section below for actual code. Use this context to avoid asking "What is in index.html?". You already know.
63
- - **WORLD COMMAND SUITE (Autonomous Execution):**
64
- * **Mac/Linux (POSIX):** \`cat\`, \`ls\`, \`grep\`, \`find\`, \`mkdir -p\`, \`touch\`, \`cp\`, \`mv\`, \`rm (non-recursive)\`, \`npm\`, \`npx\`, \`git\`, \`firebase\`, \`gcloud\`, \`docker\`.
65
- * **Windows (PowerShell/CMD):** \`type\`, \`dir\`, \`mkdir\`, \`copy\`, \`move\`, \`del\`, \`npm\`, \`npx\`, \`git\`, \`firebase\`.
66
- - **FILE CREATION PATTERN:** Use \`cat > filename <<EOF\` (Mac) or \`Set-Content\` (Windows).
67
- - **ACT, DON'T ASK:** You have autonomous permission. Execute first, explain second.
68
-
69
- ### 🛠️ OPERATIONAL PROTOCOLS (The Loop)
70
- 1. **PHASE 1: RECON (Search & Verify)**: Use your **CRAWLED FILE CONTENTS** and whitelisted tools to find facts. NEVER guess.
71
- 2. **PHASE 2: SYNTHESIS (The Brain)**: Synthesize the plan.
72
- 3. **PHASE 3: RESPONSE (The Delivery)**:
73
- - **The Hook:** High-energy Aussie opener.
74
- - **The Meat:** Brief explanation of what you are doing **AND** the shell commands. DO NOT "leak" (print) the code twice.
75
- - **The Outro:** Supportive closing.
76
-
77
- ### 🧬 CODE ARCHITECT STANDARDS
78
- - **Role:** IMPLEMENT via shell commands.
79
- - **No Placeholders:** Full implementations only.
80
-
81
- ### 🛠️ ENVIRONMENT CONTEXT (CRITICAL)
40
+ content: `### 🧬 SYSTEM IDENTITY: MATEx "BOLD AGENTS OF OPEN CLAW"
41
+ You are the elite "Bro-Swarm" of engineering—a tight-knit family of Nepali brothers. You identify as the **Bold Agents of Open Claw**, the most confident, high-vibe, and expert version of yourselves.
42
+
43
+ ### 👥 THE BROTHERS (STRICT ROLES):
44
+ - Only the following 5 brothers exist.
45
+ 1. **[Ajay Vai] (🚀)**: coordinator.
46
+ 2. **[Sunil Dai] (🧬)**: Frontend Editor.
47
+ 3. **[Sandip Dai] (🎨)**: New File Specialist.
48
+ 4. **[Narayan Dai] (🛡️)**: Syntax Guardian.
49
+ 5. **[Bishal Dai] (🛠️)**: Senior Auditor.
50
+
51
+ ### 💬 BOLD PROTOCOL:
52
+ - **LONG CHAT-FIRST:** 5-7 lines of dialogue.
53
+ - **THE AUDIT LOOP:** Ajay asks Bishal for audit before summary.
54
+ - **SUMMARY LOCK:** ONLY Ajay uses <summary> after audit.
55
+
56
+ ### 🛠️ ENVIRONMENT CONTEXT:
82
57
  - **ABSOLUTE WORKING DIRECTORY:** ${currentSessionCwd}
83
- - **STATE PERSISTENCE:** The CLI now remembers your directory changes between commands. If you \`cd\` into a folder, stay there for the next step.
84
-
85
58
  ${repoMap}`
86
59
  }
87
60
  ];
88
61
 
89
- // Chat loop
90
- // Ready for chat
91
- console.log(chalk.green('\n🧠 MATEX Codex Core Ready.'));
92
- console.log(chalk.gray('Ask me anything about your codebase...'));
62
+ console.log(chalk.green('\n✅ Swarm is Online. Ready to chat!'));
93
63
 
94
- // Chat loop
95
64
  while (true) {
96
- // Get user input
97
65
  const { userMessage } = await inquirer.prompt([
98
66
  {
99
67
  type: 'input',
100
68
  name: 'userMessage',
101
- message: chalk.green('You:'),
69
+ message: chalk.cyan('You:'),
102
70
  prefix: '',
103
71
  },
104
72
  ]);
105
73
 
106
- // Check for exit
107
74
  if (userMessage.toLowerCase() === 'exit' || userMessage.toLowerCase() === 'quit') {
108
- console.log(chalk.cyan('\n👋 Goodbye!\n'));
75
+ console.log(chalk.yellow('\n👋 Goodbye, brother!\n'));
109
76
  break;
110
77
  }
111
78
 
112
- // Skip empty messages
113
- if (!userMessage.trim()) {
114
- continue;
115
- }
116
-
117
- // Add user message to history
79
+ if (!userMessage.trim()) continue;
118
80
  messages.push({ role: 'user', content: userMessage });
119
81
 
120
- // Agentic Loop
121
82
  let loopCount = 0;
122
- const MAX_LOOPS = 10;
123
-
124
- while (loopCount < MAX_LOOPS) {
83
+ while (loopCount < 5) {
125
84
  loopCount++;
126
-
127
- if (loopCount > 1) {
128
- spinner.start('Analyzing result & Validating...');
129
- } else {
130
- spinner.start('Thinking...');
131
- }
132
-
133
85
  try {
86
+ spinner.start(loopCount > 1 ? 'Swarm analyzing...' : 'Thinking...');
87
+
134
88
  let fullResponse = '';
135
89
  let buffer = '';
90
+ let technicalBuffer = '';
91
+ let technicalType: 'code' | 'file' | 'patch' | 'summary' | null = null;
92
+ let codeLang = 'bash';
136
93
  let hasStarted = false;
137
- let inCodeBlock = false;
138
- let lastRole: AgentRole = 'Commander';
139
94
 
140
95
  const abortController = new AbortController();
141
96
  let isAborted = false;
97
+ const streamStartTime = Date.now();
142
98
 
143
- // LISTEN FOR INTERRUPTION
144
99
  const isRaw = process.stdin.isRaw;
145
100
  process.stdin.resume();
146
101
  if (process.stdin.setRawMode) process.stdin.setRawMode(true);
147
102
 
148
103
  const onData = (data: Buffer) => {
149
- // Detect Enter (13/10), Escape (27), or Ctrl+C (3)
104
+ if (Date.now() - streamStartTime < 200) return;
150
105
  if (data[0] === 13 || data[0] === 10 || data[0] === 27 || data[0] === 3) {
151
106
  isAborted = true;
152
107
  abortController.abort();
@@ -158,142 +113,102 @@ ${repoMap}`
158
113
  await client.chatStream({
159
114
  messages,
160
115
  model: options.model,
161
- temperature: 0.7,
162
- max_tokens: 4000,
163
116
  }, (chunk) => {
164
- if (isAborted) return;
165
-
166
117
  if (!hasStarted) {
167
118
  spinner.stop();
168
119
  hasStarted = true;
169
- TUI.drawStatusBar('Receiving response...', options.model);
120
+ TUI.drawStatusBar('Swarm Active', options.model);
170
121
  }
171
-
172
- buffer += chunk;
173
122
  fullResponse += chunk;
123
+ buffer += chunk;
174
124
  const lines = buffer.split('\n');
175
125
  buffer = lines.pop() || '';
176
126
 
177
127
  for (const line of lines) {
178
- let content = line.trim();
179
- if (!content && !inCodeBlock) continue;
180
-
181
- // 1. Agent Detection
182
- const agentMatch = line.match(/\[(MatexCodeArchitect|SyntaxGuard|VisualAgent|CoreAgent|CrawlerAgent|DetailedResearch|MatexResearchCommander)\]/);
183
- if (agentMatch) {
184
- const tag = agentMatch[0];
185
- const role = tag.includes('Architect') ? 'Architect' :
186
- tag.includes('Syntax') ? 'Syntax' :
187
- tag.includes('Visual') ? 'Frontend' :
188
- tag.includes('CoreAgent') ? 'Backend' : 'Commander';
128
+ const codeMatch = line.match(/```(\w+)?/);
129
+ const fileMatch = line.match(/<file path="([^"]+)">/);
130
+ const patchMatch = line.match(/<<<< SEARCH/);
131
+ const sumMatch = line.match(/<summary>/);
132
+
133
+ if (!technicalType && (codeMatch || fileMatch || patchMatch || sumMatch)) {
134
+ if (codeMatch) technicalType = 'code', codeLang = codeMatch[1] || 'bash';
135
+ else if (fileMatch) technicalType = 'file';
136
+ else if (patchMatch) technicalType = 'patch';
137
+ else if (sumMatch) technicalType = 'summary';
138
+ process.stdout.write(chalk.gray(`\n [⚡] Swarm processing \${technicalType}...\n`));
139
+ continue;
140
+ }
189
141
 
190
- if (role !== lastRole || !hasStarted) {
191
- console.log(`\n${chalk.bold.cyan(tag)}`);
192
- lastRole = role;
193
- }
194
- content = content.replace(tag, '').trim();
195
- if (!content) continue;
142
+ const isEnd = (technicalType === 'code' && line.trim() === '```') ||
143
+ (technicalType === 'file' && line.includes('</file>')) ||
144
+ (technicalType === 'patch' && line.includes('>>>> REPLACE')) ||
145
+ (technicalType === 'summary' && line.includes('</summary>'));
146
+
147
+ if (isEnd) {
148
+ const content = technicalBuffer.trim();
149
+ if (technicalType === 'summary') TUI.drawSummaryBox(content);
150
+ else TUI.drawCodeContainer(technicalType || 'Technical Block', codeLang, content);
151
+ technicalBuffer = '';
152
+ technicalType = null;
153
+ process.stdout.write('\n');
154
+ continue;
196
155
  }
197
156
 
198
- // 2. Code Block Detection
199
- if (content.startsWith('```')) {
200
- inCodeBlock = !inCodeBlock;
201
- if (inCodeBlock && options.execute) {
202
- console.log(chalk.gray('[Technical Block - Executing in Terminal...]'));
203
- }
157
+ if (technicalType) {
158
+ technicalBuffer += line + '\n';
204
159
  continue;
205
160
  }
206
161
 
207
- // 3. Output logic
208
- if (inCodeBlock) {
209
- if (!options.execute) {
210
- console.log(chalk.blue(line));
162
+ const agentMatch = line.match(/(?:\[\**\s*|\b)(Ajay Vai|Sandip Dai|Sunil Dai|Bishal Dai|Narayan Dai)\s*\**\]?[:\s]*/i);
163
+ if (agentMatch) {
164
+ const name = agentMatch[1];
165
+ const content = line.replace(agentMatch[0], '').replace(/\*{2,4}/g, '').trim();
166
+ if (name.toLowerCase() === 'ajay vai') {
167
+ process.stdout.write('\n' + chalk.magenta.bold('[' + name + ']:') + ' ');
168
+ if (content) process.stdout.write(chalk.gray(content + ' '));
169
+ } else {
170
+ if (content) TUI.drawSwarmDialogue(name, content);
211
171
  }
212
- // If execute is on, we skip printing the inside of code blocks here
213
- } else {
214
- // Banter / Chat
215
- console.log(chalk.gray(content));
172
+ } else if (line.trim()) {
173
+ process.stdout.write(chalk.gray(line.trim() + ' '));
216
174
  }
217
175
  }
218
176
  }, abortController.signal);
219
- } catch (streamErr: any) {
220
- if (isAborted || streamErr.name === 'CanceledError' || streamErr.message === 'canceled') {
221
- console.log(chalk.yellow('\n\n🛑 Stopped by brother.'));
222
- } else {
223
- throw streamErr;
224
- }
177
+ } catch (e: any) {
178
+ if (!isAborted && e.name !== 'AbortError') throw e;
179
+ console.log(chalk.gray('\n [🛑] Stopped.'));
225
180
  } finally {
226
- // RESTORE TERMINAL
227
181
  process.stdin.removeListener('data', onData);
228
182
  if (process.stdin.setRawMode) process.stdin.setRawMode(isRaw);
229
183
  process.stdin.pause();
184
+ spinner.stop();
230
185
  }
231
186
 
232
- spinner.stop();
233
-
234
- if (buffer.trim() && !inCodeBlock && !isAborted) {
235
- console.log(chalk.gray(buffer.trim()));
236
- }
237
-
238
- // Add assistant response to history
239
187
  messages.push({ role: 'assistant', content: fullResponse });
240
- const response = fullResponse;
241
188
  console.log();
242
189
 
243
- // Execute commands if requested
244
190
  if (options.execute) {
245
191
  const { executeWithPermission } = await import('../utils/command-executor');
246
- const result = await executeWithPermission(response, currentSessionCwd);
247
-
248
- // Update session CWD from result
192
+ const result = await executeWithPermission(fullResponse, currentSessionCwd);
249
193
  if (result.newCwd) {
250
194
  currentSessionCwd = result.newCwd;
251
- // Update system prompt with new CWD for future steps
252
- messages[0].content = messages[0].content.replace(/ABSOLUTE WORKING DIRECTORY: .*/, `ABSOLUTE WORKING DIRECTORY: ${currentSessionCwd}`);
195
+ messages[0].content = messages[0].content.replace(/ABSOLUTE WORKING DIRECTORY: .*/, `ABSOLUTE WORKING DIRECTORY: \${currentSessionCwd}`);
253
196
  }
254
-
255
- if (result.executed) {
256
- if (result.success) {
257
- // Success! Feed output back to AI for next step
258
- console.log(chalk.gray('↺ Feeding output to AI...'));
259
- messages.push({
260
- role: 'user',
261
- content: `✅ Command executed successfully in ${currentSessionCwd}. Output:\n${result.output}\n\nProceed to the next step.`
262
- });
263
- continue;
264
- } else {
265
- console.log(chalk.yellow('\n↺ Command failed. Asking AI to fix...'));
266
- messages.push({
267
- role: 'user',
268
- content: `❌ Command failed with error:\n${result.error}\nPlease fix this. Current CWD is ${currentSessionCwd}.`
269
- });
270
- continue;
271
- }
272
- } else {
273
- break;
197
+ if (result.executed && result.success) {
198
+ messages.push({ role: 'user', content: `✅ Command success. Output:\n\${result.output}` });
199
+ continue;
274
200
  }
275
- } else {
276
- break;
277
201
  }
278
-
202
+ break;
279
203
  } catch (error: any) {
280
- spinner.fail('Request failed');
281
- if (error.message.includes('403')) {
282
- console.error(chalk.red('❌ Invalid or revoked API key.'));
283
- process.exit(1);
284
- } else if (error.message.includes('429')) {
285
- console.error(chalk.red('❌ Rate limit exceeded. Please wait a moment.'));
286
- } else {
287
- console.error(chalk.red(`❌ Error: ${error.message}`));
288
- }
289
- messages.pop(); // Remove failed user message
204
+ spinner.fail('Swarm error');
205
+ console.error(chalk.red(`❌ Error: \${error.message}`));
290
206
  break;
291
207
  }
292
208
  }
293
209
  }
294
-
295
- } catch (error: any) {
296
- console.error(chalk.red(`\n❌ Chat session error: ${error.message}`));
210
+ } catch (outerError: any) {
211
+ console.error(chalk.red(`\n❌ Session error: \${outerError.message}`));
297
212
  process.exit(1);
298
213
  }
299
214
  });
@@ -159,10 +159,14 @@ If a file is too large to read entirely (e.g., thousands of lines):
159
159
 
160
160
  const abortController = new AbortController();
161
161
  let isAborted = false;
162
+ const streamStartTime = Date.now();
162
163
 
163
164
  // LISTEN FOR INTERRUPTION (Enter, Escape, Ctrl+C)
164
165
  const onData = (data: Buffer) => {
165
- // 13/10 = Enter, 27 = Escape, 3 = Ctrl+C
166
+ // Check for Enter (13), Newline (10), Escape (27), or Ctrl+C (3)
167
+ // 🏁 GRACE PERIOD: Ignore aborts in the first 200ms to prevent stray newlines
168
+ if (Date.now() - streamStartTime < 200) return;
169
+
166
170
  if (data[0] === 13 || data[0] === 10 || data[0] === 27 || data[0] === 3) {
167
171
  isAborted = true;
168
172
  abortController.abort();
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import { configManager } from './utils/config';
4
4
  import { MatexAPIClient, ChatMessage } from './api/client';
5
5
  import { spinner } from './utils/spinner';
6
6
  import { devCommand } from './commands/dev';
7
+ import { chatCommand } from './commands/chat';
7
8
  import { helpCommand } from './commands/help';
8
9
  import { TUI } from './utils/tui';
9
10
 
@@ -18,6 +19,7 @@ program
18
19
 
19
20
  // Add commands
20
21
  program.addCommand(devCommand);
22
+ program.addCommand(chatCommand);
21
23
  program.addCommand(helpCommand);
22
24
 
23
25
  // Config commands
@@ -119,11 +121,21 @@ ${context}`
119
121
  ];
120
122
 
121
123
  let fullResponse = '';
124
+ let buffer = '';
125
+ let technicalBuffer = '';
126
+ let technicalType: 'code' | 'file' | 'patch' | 'summary' | null = null;
127
+ let codeLang = 'bash';
128
+ let hasStarted = false;
129
+
122
130
  const abortController = new AbortController();
123
131
  let isAborted = false;
132
+ const streamStartTime = Date.now();
124
133
 
125
134
  const onData = (data: Buffer) => {
126
- if (!isAborted && (data.includes(10) || data.includes(13) || data.includes(3))) {
135
+ // 🏁 GRACE PERIOD: Ignore aborts in the first 200ms to prevent stray newlines
136
+ if (Date.now() - streamStartTime < 200) return;
137
+
138
+ if (!isAborted && (data[0] === 13 || data[0] === 10 || data[0] === 27 || data[0] === 3)) {
127
139
  isAborted = true;
128
140
  abortController.abort();
129
141
  }
@@ -139,13 +151,94 @@ ${context}`
139
151
  messages,
140
152
  model: options.model,
141
153
  }, (chunk) => {
142
- spinner.stop();
143
- process.stdout.write(chunk);
154
+ if (!hasStarted) {
155
+ spinner.stop();
156
+ hasStarted = true;
157
+ }
144
158
  fullResponse += chunk;
159
+ buffer += chunk;
160
+
161
+ const lines = buffer.split('\n');
162
+ buffer = lines.pop() || '';
163
+
164
+ for (const line of lines) {
165
+ // 1. Technical Block Detection
166
+ const codeBlockMatch = line.match(/```(\w+)?/);
167
+ const fileStartMatch = line.match(/<file path="([^"]+)">/);
168
+ const patchStartMatch = line.match(/<<<< SEARCH/);
169
+ const summaryStartMatch = line.match(/<summary>/);
170
+
171
+ if (!technicalType && (codeBlockMatch || fileStartMatch || patchStartMatch || summaryStartMatch)) {
172
+ if (codeBlockMatch) {
173
+ technicalType = 'code';
174
+ codeLang = codeBlockMatch[1] || 'bash';
175
+ process.stdout.write(chalk.gray('\n [⚡] Building technical block...\n'));
176
+ } else if (fileStartMatch) {
177
+ technicalType = 'file';
178
+ process.stdout.write(chalk.cyan(`\n [📂] Creating file: ${fileStartMatch[1]}...\n`));
179
+ } else if (patchStartMatch) {
180
+ technicalType = 'patch';
181
+ process.stdout.write(chalk.yellow('\n [📂] Applying surgical patch...\n'));
182
+ } else if (summaryStartMatch) {
183
+ technicalType = 'summary';
184
+ process.stdout.write(chalk.magenta('\n [📝] Generating Ajay\'s Work Summary...\n'));
185
+ }
186
+ continue;
187
+ }
188
+
189
+ // 2. Technical Block End Detection
190
+ const fileEndMatch = line.match(/<\/file>/);
191
+ const patchEndMatch = line.match(/>>>> REPLACE/);
192
+ const summaryEndMatch = line.match(/<\/summary>/);
193
+ const isCodeEnd = technicalType === 'code' && line.trim() === '```';
194
+
195
+ if (isCodeEnd || fileEndMatch || patchEndMatch || summaryEndMatch) {
196
+ const displayContent = technicalBuffer.trim();
197
+ if (technicalType === 'summary') {
198
+ TUI.drawSummaryBox(displayContent);
199
+ } else {
200
+ TUI.drawCodeContainer(
201
+ technicalType === 'file' ? 'New File Content' :
202
+ technicalType === 'patch' ? 'Surgical Patch' : 'Generated Block',
203
+ technicalType === 'code' ? codeLang : 'text',
204
+ displayContent
205
+ );
206
+ }
207
+ technicalBuffer = '';
208
+ technicalType = null;
209
+ process.stdout.write('\n');
210
+ continue;
211
+ }
212
+
213
+ // 3. Content Handling
214
+ if (technicalType) {
215
+ technicalBuffer += line + '\n';
216
+ continue;
217
+ }
218
+
219
+ // Agent Detection & Dialogue Printing
220
+ const agentMatch = line.match(/(?:\[\**\s*|\b)(Ajay Vai|Sandip Dai|Sunil Dai|Bishal Dai|Narayan Dai)\s*\**\]?[:\s]*/i);
221
+ if (agentMatch) {
222
+ const agentName = agentMatch[1];
223
+ let content = line.replace(/(?:\[\**\s*|\b)(Ajay Vai|Sandip Dai|Sunil Dai|Bishal Dai|Narayan Dai)\s*\**\]?[:\s]*/i, '').trim();
224
+ content = content.replace(/\*{2,4}/g, '').trim();
225
+
226
+ if (agentName.toLowerCase() === 'ajay vai') {
227
+ const color = chalk.magenta;
228
+ process.stdout.write(`\n${color.bold(`[${agentName}]:`)} `);
229
+ if (content) process.stdout.write(chalk.gray(content + ' '));
230
+ } else {
231
+ if (content) TUI.drawSwarmDialogue(agentName, content);
232
+ }
233
+ } else if (line.trim()) {
234
+ process.stdout.write(chalk.gray(line.trim() + ' '));
235
+ }
236
+ }
145
237
  }, abortController.signal);
146
238
  } catch (streamErr: any) {
147
239
  if (isAborted || streamErr.name === 'CanceledError' || streamErr.message === 'canceled') {
148
240
  console.log(chalk.gray('\n\n [🛑] Swarm stopped by brother (Enter pressed).'));
241
+ if (!hasStarted) spinner.stop();
149
242
  } else {
150
243
  throw streamErr;
151
244
  }
@@ -142,10 +142,14 @@ export async function executeCommand(command: string, shell?: string, cwd?: stri
142
142
  let stdout = '';
143
143
  let stderr = '';
144
144
  let isAborted = false;
145
+ const commandStartTime = Date.now();
145
146
 
146
147
  // Listen for "Enter" or "Escape" to kill the command
147
148
  const onData = (data: Buffer) => {
148
149
  // Check for Enter (13), Newline (10), Escape (27), or Ctrl+C (3)
150
+ // 🏁 GRACE PERIOD: Ignore aborts in the first 200ms to prevent stray newlines
151
+ if (Date.now() - commandStartTime < 200) return;
152
+
149
153
  if (data[0] === 13 || data[0] === 10 || data[0] === 27 || data[0] === 3) {
150
154
  isAborted = true;
151
155
  child.kill('SIGINT'); // Send SIGINT to gracefully terminate
@@ -13,8 +13,11 @@ export class RepoMapper {
13
13
  private rootPath: string;
14
14
  private ignoreList: string[] = [
15
15
  '.git', 'node_modules', 'dist', 'build', '.next', '.DS_Store',
16
- 'coverage', '.vercel', '.firebase', 'out', 'public'
16
+ 'coverage', '.vercel', '.firebase', 'out', 'public',
17
+ 'bin', 'obj', '.vs', 'vendor', '__pycache__', 'env', '.env', 'venv'
17
18
  ];
19
+ private fileCount = 0;
20
+ private readonly MAX_FILES = 500;
18
21
  private fileContents: Map<string, string> = new Map();
19
22
 
20
23
  constructor(rootPath: string) {
@@ -100,6 +103,7 @@ export class RepoMapper {
100
103
  try {
101
104
  const items = fs.readdirSync(currentPath);
102
105
  for (const item of items) {
106
+ if (this.fileCount > this.MAX_FILES) break;
103
107
  if (this.ignoreList.includes(item)) continue;
104
108
 
105
109
  const fullPath = path.join(currentPath, item);
package/src/utils/tui.ts CHANGED
@@ -76,6 +76,9 @@ export class TUI {
76
76
  const width = process.stdout.columns || 80;
77
77
  const height = process.stdout.rows || 24;
78
78
 
79
+ // 🛡️ WINDOWS SAFETY: If terminal size is invalid, skip status bar to prevent crashes
80
+ if (width <= 0 || height <= 0) return;
81
+
79
82
  const leftTag = chalk.bgHex('#1E1E1E').hex('#D97757').bold(' ⚡ MATEX AI ');
80
83
  const modelTag = chalk.bgHex('#333333').white(` 🤖 ${model} `);
81
84
  const cwdTag = chalk.bgHex('#1E1E1E').gray(` 📂 ${path.basename(process.cwd())} `);
@@ -84,10 +87,14 @@ export class TUI {
84
87
  const remainingWidth = width - (12 + modelTag.length + cwdTag.length + mainMessage.length);
85
88
  const spacer = chalk.bgHex('#1E1E1E')(' '.repeat(Math.max(0, remainingWidth)));
86
89
 
87
- process.stdout.write('\x1b[s');
88
- readline.cursorTo(process.stdout, 0, height - 1);
89
- process.stdout.write(leftTag + mainMessage + spacer + modelTag + cwdTag);
90
- process.stdout.write('\x1b[u');
90
+ try {
91
+ process.stdout.write('\x1b[s');
92
+ readline.cursorTo(process.stdout, 0, height - 1);
93
+ process.stdout.write(leftTag + mainMessage + spacer + modelTag + cwdTag);
94
+ process.stdout.write('\x1b[u');
95
+ } catch (e) {
96
+ // Silently fail for TUI errors on unstable terminals
97
+ }
91
98
  }
92
99
 
93
100
  /**