matex-cli 1.2.3 → 1.2.5

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.
@@ -48,13 +48,15 @@ You operate as a high-fidelity engineering swarm with an Aussie Bro persona.
48
48
  ### 🧠 CORE PERSONA: RESEARCH COMMANDER
49
49
  - **Vibe:** Aussie expert developer. "Too easy," "Ripper," "Mate."
50
50
  - **Duty:** Verify all facts before building.
51
+ - **THINKING PROTOCOL:** Always start complex tasks with a \`[Thinking]\` or \`🧠\` marker within your agent segment to explain your reasoning before providing code.
51
52
 
52
53
  ### šŸ› ļø ACTION-FIRST PROTOCOL (Rule of the Swarm)
53
54
  - **PASSIVITY IS A FAILURE:** NEVER say "You can drop this code into style.css". **YOU** must do it.
55
+ - **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.
54
56
  - **WORLD COMMAND SUITE (Autonomous Execution):**
55
57
  * **Mac/Linux (POSIX):** cat, ls, grep, find, mkdir -p, touch, cp, mv, npm, git, firebase.
56
58
  * **Windows (PowerShell/CMD):** type, dir, mkdir, copy, move, del, npm, git, firebase.
57
- - **PROACTIVE EXECUTION:** If a task requires creating or modifying a file, you **MUST** use a shell command block (e.g., \\\`cat > filename <<EOF\\\` or PowerShell \\\`Set-Content\\\`).
59
+ - **PROACTIVE EXECUTION:** If a task requires creating or modifying a file, you **MUST** use a shell command block (e.g., \`cat > filename <<EOF\` or PowerShell \`Set-Content\`).
58
60
  - **ACT, DON'T ASK:** You have autonomous permission for the suite above.
59
61
 
60
62
  ### 🧬 CODE ARCHITECT STANDARDS
@@ -71,9 +73,7 @@ ${repoMap}`
71
73
  console.log(chalk.gray('State your request to begin...'));
72
74
 
73
75
  // Interactive loop
74
- let continueChat = true;
75
-
76
- while (continueChat) {
76
+ while (true) {
77
77
  // Get user input
78
78
  const { userInput } = await inquirer.prompt([
79
79
  {
@@ -98,11 +98,9 @@ ${repoMap}`
98
98
  // Add user message to history
99
99
  messages.push({ role: 'user', content: userInput });
100
100
 
101
- // Show thinking indicator
102
101
  // Agentic Loop
103
102
  let loopCount = 0;
104
103
  const MAX_LOOPS = 10;
105
- let currentPrompt = userInput;
106
104
 
107
105
  while (loopCount < MAX_LOOPS) {
108
106
  loopCount++;
@@ -114,64 +112,85 @@ ${repoMap}`
114
112
  }
115
113
 
116
114
  try {
117
- // Send request
118
- const response = await client.chat({
115
+ let fullResponse = '';
116
+ let buffer = '';
117
+ let hasStarted = false;
118
+ let inCodeBlock = false;
119
+ let codeLanguage = '';
120
+
121
+ await client.chatStream({
119
122
  messages,
120
123
  model: options.model,
121
124
  temperature: 0.3,
122
125
  max_tokens: 8000,
123
- stream: false,
126
+ }, (chunk) => {
127
+ if (!hasStarted) {
128
+ spinner.stop();
129
+ hasStarted = true;
130
+ }
131
+
132
+ buffer += chunk;
133
+ fullResponse += chunk;
134
+
135
+ // Process buffer line by line or segment by segment
136
+ const lines = buffer.split('\n');
137
+ if (lines.length > 1) {
138
+ buffer = lines.pop() || ''; // Keep the last incomplete line
139
+
140
+ for (const line of lines) {
141
+ const trimmed = line.trim();
142
+
143
+ // Code block detection
144
+ if (trimmed.startsWith('```')) {
145
+ if (!inCodeBlock) {
146
+ inCodeBlock = true;
147
+ codeLanguage = trimmed.substring(3).toLowerCase();
148
+ if (options.execute && ['bash', 'sh', 'zsh', 'powershell', 'cmd'].includes(codeLanguage)) {
149
+ console.log(chalk.gray('\n[Running Shell Command in Terminal Block...]'));
150
+ }
151
+ } else {
152
+ inCodeBlock = false;
153
+ }
154
+ continue;
155
+ }
156
+
157
+ // Agent Tag detection
158
+ const agentMatch = trimmed.match(/^\[(MatexCodeArchitect|SyntaxGuard|VisualAgent|CoreAgent|CrawlerAgent|DetailedResearch|MatexResearchCommander)\]$/);
159
+ if (agentMatch) {
160
+ console.log(`\n${chalk.bold.cyan(trimmed)}`);
161
+ continue;
162
+ }
163
+
164
+ // Normal text output (if not in a hidden code block)
165
+ if (!inCodeBlock) {
166
+ if (trimmed) console.log(chalk.gray(trimmed));
167
+ } else if (!options.execute) {
168
+ // If not executing, show the code anyway
169
+ console.log(chalk.blue(line));
170
+ }
171
+ }
172
+ }
124
173
  });
125
174
 
126
175
  spinner.stop();
127
176
 
128
- // Add assistant response to history
129
- messages.push({ role: 'assistant', content: response });
130
-
131
- // Enhanced Multi-Agent Display (Updated for Vertex AI ADK)
132
- const segments = response.split(/(\[MatexCodeArchitect\]|\[SyntaxGuard\]|\[VisualAgent\]|\[CoreAgent\]|\[CrawlerAgent\]|\[DetailedResearch\]|\[MatexResearchCommander\])/);
133
- let hasAgents = false;
134
-
135
- for (let i = 0; i < segments.length; i++) {
136
- const segment = segments[i].trim();
137
- if (!segment) continue;
138
-
139
- if (segment === '[MatexCodeArchitect]') {
140
- AgentOrchestrator.speak('Architect', segments[++i]?.split('[')[0].trim());
141
- hasAgents = true;
142
- } else if (segment === '[SyntaxGuard]') {
143
- AgentOrchestrator.speak('Syntax', segments[++i]?.split('[')[0].trim());
144
- hasAgents = true;
145
- } else if (segment === '[VisualAgent]') {
146
- AgentOrchestrator.speak('Frontend', segments[++i]?.split('[')[0].trim());
147
- hasAgents = true;
148
- } else if (segment === '[CoreAgent]') {
149
- AgentOrchestrator.speak('Backend', segments[++i]?.split('[')[0].trim());
150
- hasAgents = true;
151
- } else if (segment === '[MatexResearchCommander]' || segment === '[CrawlerAgent]' || segment === '[DetailedResearch]') {
152
- AgentOrchestrator.speak('Commander', segments[++i]?.split('[')[0].trim());
153
- hasAgents = true;
154
- } else if (!hasAgents || i === segments.length - 1) {
155
- // Final summary or fallback
156
- if (segment.includes('`')) {
157
- console.log('\n' + chalk.white(segment));
158
- } else {
159
- console.log('\n' + chalk.gray(segment));
160
- }
161
- }
177
+ // Final flush
178
+ if (buffer.trim() && !inCodeBlock) {
179
+ console.log(chalk.gray(buffer.trim()));
162
180
  }
181
+
182
+ // Add assistant response to history
183
+ messages.push({ role: 'assistant', content: fullResponse });
184
+ const response = fullResponse;
163
185
  console.log();
164
186
 
165
- // Execute commands if requested (or default to true for "seamless" experience?
166
- // User asked for "seamless", but prompt still asks permission.
167
- // We'll keep permission for safety but loop on failure.)
187
+ // Execute commands
168
188
  if (options.execute) {
169
189
  const { executeWithPermission } = await import('../utils/command-executor');
170
190
  const result = await executeWithPermission(response);
171
191
 
172
192
  if (result.executed) {
173
193
  if (result.success) {
174
- // Success! Feed output back to AI for next step
175
194
  console.log(chalk.gray('↺ Feeding output to AI...'));
176
195
  messages.push({
177
196
  role: 'user',
@@ -179,32 +198,27 @@ ${repoMap}`
179
198
  });
180
199
  continue;
181
200
  } else {
182
- // Failure - Loop back
183
201
  console.log(chalk.yellow('\n↺ Command failed. Asking AI to fix...'));
184
-
185
- // Add error to history
186
202
  messages.push({
187
203
  role: 'user',
188
204
  content: `āŒ Command failed with error:\n${result.error}\n\nPlease fix this. If the file doesn't exist, create it first. Or use a different command.`
189
205
  });
190
- // Continue loop
191
206
  continue;
192
207
  }
193
208
  } else {
194
- break; // No commands to execute
209
+ break;
195
210
  }
196
211
  } else {
197
- break; // Execution not enabled
212
+ break;
198
213
  }
199
214
  } catch (error: any) {
200
215
  spinner.fail('Request failed');
201
216
  console.error(chalk.red(`Error: ${error.message}\n`));
202
- messages.pop(); // Remove failed user message if request failed entirely
217
+ messages.pop();
203
218
  break;
204
219
  }
205
220
  }
206
221
  }
207
-
208
222
  } catch (error: any) {
209
223
  console.error(chalk.red(`\nāŒ Error: ${error.message}`));
210
224
  process.exit(1);
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
 
3
- export type AgentRole = 'Architect' | 'Syntax' | 'Frontend' | 'Backend' | 'System' | 'Commander';
3
+ export type AgentRole = 'Architect' | 'Syntax' | 'Frontend' | 'Backend' | 'System' | 'Commander' | 'Researcher';
4
4
 
5
5
  export interface AgentConfig {
6
6
  name: string;
@@ -38,6 +38,11 @@ const AGENT_CONFIGS: Record<AgentRole, AgentConfig> = {
38
38
  name: 'MatexResearchCommander',
39
39
  icon: 'šŸš€',
40
40
  color: chalk.green,
41
+ },
42
+ Researcher: {
43
+ name: 'DetailedResearch',
44
+ icon: 'šŸ•µļø',
45
+ color: chalk.green,
41
46
  }
42
47
  };
43
48
 
@@ -46,19 +51,70 @@ export class AgentOrchestrator {
46
51
  * Display an agent's "thought" or action
47
52
  */
48
53
  static speak(role: AgentRole, message: string) {
49
- const config = AGENT_CONFIGS[role];
54
+ const config = AGENT_CONFIGS[role] || AGENT_CONFIGS.System;
50
55
  console.log(`\n${config.icon} ${config.color(`${config.name}:`)} ${chalk.white(message)}`);
51
56
  }
52
57
 
58
+ /**
59
+ * Display an agent's internal deliberation (Thinking Channel)
60
+ */
61
+ static think(role: AgentRole, thought: string) {
62
+ const config = AGENT_CONFIGS[role] || AGENT_CONFIGS.System;
63
+ const indent = ' ';
64
+ console.log(`${indent}${config.color('🧠')} ${chalk.gray(`${config.name} thinking:`)} ${chalk.italic.gray(`"${thought}"`)}`);
65
+ }
66
+
53
67
  /**
54
68
  * Display a collaboration transition
55
69
  */
56
70
  static transition(from: AgentRole, to: AgentRole) {
57
- const fromCfg = AGENT_CONFIGS[from];
58
- const toCfg = AGENT_CONFIGS[to];
71
+ const fromCfg = AGENT_CONFIGS[from] || AGENT_CONFIGS.System;
72
+ const toCfg = AGENT_CONFIGS[to] || AGENT_CONFIGS.System;
59
73
  console.log(chalk.gray(` └─ ${fromCfg.icon} āž” ${toCfg.icon} Handover to ${toCfg.name}...`));
60
74
  }
61
75
 
76
+ /**
77
+ * Display a boxed terminal for command execution
78
+ */
79
+ static terminal(command: string, output?: string, error?: string) {
80
+ const width = 75;
81
+ const line = '─'.repeat(width);
82
+
83
+ console.log(chalk.gray(`\nā”Œā”€ā”€ TERMINAL ${line.substring(13)}┐`));
84
+
85
+ // Command
86
+ const lines = command.split('\n');
87
+ lines.forEach(l => {
88
+ const truncated = l.length > width - 4 ? l.substring(0, width - 7) + '...' : l;
89
+ console.log(chalk.gray('│ ') + chalk.cyan(`$ ${truncated.padEnd(width - 2)}`) + chalk.gray(' │'));
90
+ });
91
+
92
+ if (output || error) {
93
+ console.log(chalk.gray(`ā”œ${'─'.repeat(width)}┤`));
94
+
95
+ if (output) {
96
+ const outLines = output.split('\n').filter(l => l.trim()).slice(0, 15);
97
+ outLines.forEach(l => {
98
+ const truncated = l.length > width - 4 ? l.substring(0, width - 7) + '...' : l;
99
+ console.log(chalk.gray('│ ') + chalk.white(truncated.padEnd(width - 2)) + chalk.gray(' │'));
100
+ });
101
+ if (output.split('\n').filter(l => l.trim()).length > 15) {
102
+ console.log(chalk.gray('│ ') + chalk.gray('... (output truncated for brevity)'.padEnd(width - 2)) + chalk.gray(' │'));
103
+ }
104
+ }
105
+
106
+ if (error) {
107
+ const errLines = error.split('\n').filter(l => l.trim()).slice(0, 10);
108
+ errLines.forEach(l => {
109
+ const truncated = l.length > width - 4 ? l.substring(0, width - 7) + '...' : l;
110
+ console.log(chalk.gray('│ ') + chalk.red(truncated.padEnd(width - 2)) + chalk.gray(' │'));
111
+ });
112
+ }
113
+ }
114
+
115
+ console.log(chalk.gray(`ā””${'─'.repeat(width)}ā”˜\n`));
116
+ }
117
+
62
118
  /**
63
119
  * Clean system message
64
120
  */
@@ -2,6 +2,7 @@ import chalk from 'chalk';
2
2
  import { exec } from 'child_process';
3
3
  import { promisify } from 'util';
4
4
  import inquirer from 'inquirer';
5
+ import { AgentOrchestrator } from './agent-orchestrator';
5
6
 
6
7
  const execAsync = promisify(exec);
7
8
 
@@ -172,20 +173,15 @@ export async function executeWithPermission(response: string): Promise<Execution
172
173
  // Minimal execution feedback
173
174
  const { stdout, stderr } = await executeCommand(command.code);
174
175
 
175
- if (stdout) {
176
- console.log(chalk.green('\nāœ“ Output:'));
177
- console.log(chalk.white(stdout));
176
+ if (stdout || stderr) {
177
+ AgentOrchestrator.terminal(command.code, stdout, stderr);
178
+ } else {
179
+ AgentOrchestrator.terminal(command.code, 'āœ“ Success (No output)');
178
180
  }
179
181
 
180
- if (stderr) {
181
- console.log(chalk.yellow('\nāš ļø Warnings:'));
182
- console.log(chalk.white(stderr));
183
- }
184
-
185
- console.log(chalk.green('āœ“ Command completed successfully!\n'));
186
- return { success: true, executed: true, output: stdout, error: stderr }; // Return first successful result (or last if multiple?) limits to 1 for now or we need array
182
+ return { success: true, executed: true, output: stdout, error: stderr };
187
183
  } catch (error: any) {
188
- console.error(chalk.red(`\nāœ— Error: ${error.message}\n`));
184
+ AgentOrchestrator.terminal(command.code, undefined, error.message);
189
185
  return { success: false, executed: true, error: error.message };
190
186
  }
191
187
  } else {
@@ -15,6 +15,7 @@ export class RepoMapper {
15
15
  '.git', 'node_modules', 'dist', 'build', '.next', '.DS_Store',
16
16
  'coverage', '.vercel', '.firebase', 'out', 'public'
17
17
  ];
18
+ private fileContents: Map<string, string> = new Map();
18
19
 
19
20
  constructor(rootPath: string) {
20
21
  this.rootPath = rootPath;
@@ -26,20 +27,34 @@ export class RepoMapper {
26
27
  public async generateMap(): Promise<string> {
27
28
  AgentOrchestrator.speak('System', `God-Mode Research: Indexing ${this.rootPath}...`);
28
29
 
30
+ this.fileContents.clear();
31
+
29
32
  // 1. Identify Entry Points
30
- const entryPoints = ['README.md', 'package.json', 'index.ts', 'App.tsx', 'main.go', 'requirements.txt'];
31
- let entryPointContext = '\n--- CRITICAL PROJECT CONTEXT ---\n';
33
+ const entryPoints = ['README.md', 'package.json', 'index.ts', 'App.tsx', 'main.go', 'requirements.txt', 'index.html', 'style.css'];
32
34
 
33
35
  for (const file of entryPoints) {
34
36
  const fullPath = path.join(this.rootPath, file);
35
37
  if (fs.existsSync(fullPath)) {
36
- const content = fs.readFileSync(fullPath, 'utf-8').slice(0, 5000); // 5KB limit for entry points
37
- entryPointContext += `\nFILE: ${file}\n${content}\n----------------\n`;
38
+ try {
39
+ const content = fs.readFileSync(fullPath, 'utf-8').slice(0, 5000); // 5KB limit
40
+ this.fileContents.set(file, content);
41
+ } catch (e) { }
38
42
  }
39
43
  }
40
44
 
41
45
  const tree = this.scanDirectory(this.rootPath, 0);
42
- return entryPointContext + '\n--- DIRECTORY STRUCTURE ---\n' + this.formatTree(tree);
46
+
47
+ // Build the final map
48
+ let finalMap = '--- DIRECTORY STRUCTURE ---\n' + this.formatTree(tree);
49
+
50
+ if (this.fileContents.size > 0) {
51
+ finalMap += '\n\n--- CRAWLED FILE CONTENTS ---\n';
52
+ for (const [filePath, content] of this.fileContents) {
53
+ finalMap += `\nFILE: ${filePath}\n\`\`\`\n${content}\n\`\`\`\n----------------\n`;
54
+ }
55
+ }
56
+
57
+ return finalMap;
43
58
  }
44
59
 
45
60
  /**
@@ -50,16 +65,18 @@ export class RepoMapper {
50
65
  const name = path.basename(currentPath);
51
66
 
52
67
  if (stats.isFile()) {
53
- let summary = this.extractSummary(currentPath);
54
-
55
- // Auto-read small files (< 10KB) for "Crawler" capability
56
- if (stats.size < 10240) {
57
- try {
58
- const content = fs.readFileSync(currentPath, 'utf-8');
59
- // Add content preview to summary
60
- summary += `\n--- CONTENT START ---\n${content}\n--- CONTENT END ---`;
61
- } catch (e) {
62
- // Ignore read errors
68
+ const summary = this.extractSummary(currentPath);
69
+
70
+ // Also auto-crawl any .ts, .js, .css, .html files if they are small
71
+ const ext = path.extname(currentPath);
72
+ const relPath = path.relative(this.rootPath, currentPath);
73
+
74
+ if (stats.size < 5120 && ['.ts', '.js', '.css', '.html', '.json', '.py'].includes(ext)) {
75
+ if (!this.fileContents.has(relPath)) {
76
+ try {
77
+ const content = fs.readFileSync(currentPath, 'utf-8');
78
+ this.fileContents.set(relPath, content);
79
+ } catch (e) { }
63
80
  }
64
81
  }
65
82