matex-cli 1.2.37 → 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.
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,6 +121,12 @@ ${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;
124
132
  const streamStartTime = Date.now();
@@ -127,13 +135,12 @@ ${context}`
127
135
  // 🏁 GRACE PERIOD: Ignore aborts in the first 200ms to prevent stray newlines
128
136
  if (Date.now() - streamStartTime < 200) return;
129
137
 
130
- if (!isAborted && (data.includes(10) || data.includes(13) || data.includes(3))) {
138
+ if (!isAborted && (data[0] === 13 || data[0] === 10 || data[0] === 27 || data[0] === 3)) {
131
139
  isAborted = true;
132
140
  abortController.abort();
133
141
  }
134
142
  };
135
143
 
136
-
137
144
  const isRaw = process.stdin.isRaw;
138
145
  process.stdin.resume();
139
146
  if (process.stdin.setRawMode) process.stdin.setRawMode(true);
@@ -144,13 +151,94 @@ ${context}`
144
151
  messages,
145
152
  model: options.model,
146
153
  }, (chunk) => {
147
- spinner.stop();
148
- process.stdout.write(chunk);
154
+ if (!hasStarted) {
155
+ spinner.stop();
156
+ hasStarted = true;
157
+ }
149
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
+ }
150
237
  }, abortController.signal);
151
238
  } catch (streamErr: any) {
152
239
  if (isAborted || streamErr.name === 'CanceledError' || streamErr.message === 'canceled') {
153
240
  console.log(chalk.gray('\n\n [🛑] Swarm stopped by brother (Enter pressed).'));
241
+ if (!hasStarted) spinner.stop();
154
242
  } else {
155
243
  throw streamErr;
156
244
  }
@@ -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
@@ -80,10 +80,10 @@ export class TUI {
80
80
  if (width <= 0 || height <= 0) return;
81
81
 
82
82
  const leftTag = chalk.bgHex('#1E1E1E').hex('#D97757').bold(' ⚡ MATEX AI ');
83
- const modelTag = chalk.bgHex('#333333').white(` 🤖 \${model} `);
84
- const cwdTag = chalk.bgHex('#1E1E1E').gray(` 📂 \${path.basename(process.cwd())} `);
83
+ const modelTag = chalk.bgHex('#333333').white(` 🤖 ${model} `);
84
+ const cwdTag = chalk.bgHex('#1E1E1E').gray(` 📂 ${path.basename(process.cwd())} `);
85
85
 
86
- const mainMessage = chalk.bgHex('#1E1E1E').white(` \${message} `);
86
+ const mainMessage = chalk.bgHex('#1E1E1E').white(` ${message} `);
87
87
  const remainingWidth = width - (12 + modelTag.length + cwdTag.length + mainMessage.length);
88
88
  const spacer = chalk.bgHex('#1E1E1E')(' '.repeat(Math.max(0, remainingWidth)));
89
89