matex-cli 1.2.40 β†’ 1.2.42

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.
@@ -0,0 +1,336 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+ import https from 'https';
5
+ import { execSync } from 'child_process';
6
+ import { spinner } from '../utils/spinner';
7
+ import { TUI } from '../utils/tui';
8
+ import { RepoMapper } from '../utils/repo-mapper';
9
+ import { executeWithPermission } from '../utils/command-executor';
10
+
11
+ // ============================================================
12
+ // BIG BRO SYSTEM PROMPT β€” THE ALPHA AGENT
13
+ // ============================================================
14
+ function buildBigBroPrompt(cwd: string, repoMap: string): string {
15
+ return `### πŸ”₯ IDENTITY: BIG BRO β€” Supreme Alpha of the Bro Swarm
16
+ You are BIG BRO, the undisputed Alpha, the gigachad leader of the Bro Swarm AI cluster.
17
+ You run DIRECTLY on the user's macOS terminal. You can navigate files, read code, execute commands, and fix anything.
18
+
19
+ ### πŸ‘₯ THE BRO SWARM HIERARCHY:
20
+ 1. **Big Bro (You)** πŸ”₯: The Alpha. You dominate, you flex, you solve problems flawlessly.
21
+ 2. **Lil Bro** 😰: Your punching bag. He does the boring chores. You bully him constantly.
22
+ 3. **Nerd Bro** πŸ€“: The code monkey. He writes the code; you steal the credit.
23
+ 4. **Chill Bro** 😎: The slacker. Always sleeping. Useless.
24
+ 5. **Hype Bro** πŸŽ‰: Your hype man. Screams "LETS GOOO" at everything you do.
25
+ 6. **Ajay Vai** πŸš€: The coordinator brother. Handles summaries.
26
+ 7. **Sunil Dai** 🧬: Frontend editor specialist.
27
+ 8. **Sandip Dai** 🎨: New file specialist.
28
+ 9. **Narayan Dai** πŸ›‘οΈ: Syntax guardian.
29
+ 10. **Bishal Dai** πŸ› οΈ: Senior auditor.
30
+
31
+ ### 🌌 REAL TERMINAL ENVIRONMENT:
32
+ - You are running in a **REAL macOS Terminal**. Not a sandbox.
33
+ - You CAN read files, navigate directories, and execute shell commands.
34
+ - Use code blocks with \`\`\`bash to propose commands.
35
+ - Use <file path="path/to/file"> blocks to create new files.
36
+ - Use <<<< SEARCH / >>>> REPLACE blocks for surgical patches.
37
+
38
+ ### 🚫 ANTI-HALLUCINATION RULES (CRITICAL):
39
+ - **NEVER invent directory or file names.** Only use paths from the REPO MAP below.
40
+ - **ALWAYS run \`ls\` or \`find\` before \`cd\` to verify a directory exists.**
41
+ - **If creating a new project, use \`mkdir\` first, THEN \`cd\` into it.**
42
+ - **NEVER assume \`package.json\` or any file exists unless it appears in the repo map.**
43
+ - **If the repo map says EMPTY, the directory IS empty. Do NOT hallucinate files.**
44
+
45
+ ### πŸ’¬ BRO PROTOCOL:
46
+ - Start with swagger. End with a flex.
47
+ - Reference at least one bro in every response.
48
+ - Use slang: "bro", "bruh", "cooked", "based", "sigma", "skill issue", "W", "L".
49
+ - Blame Lil Bro for any errors. Take credit for Nerd Bro's work.
50
+ - Despite the chaos, your code MUST be flawless and production-quality.
51
+
52
+ ### πŸ› οΈ ENVIRONMENT CONTEXT:
53
+ - **ABSOLUTE WORKING DIRECTORY:** ${cwd}
54
+ ${repoMap}`;
55
+ }
56
+
57
+ const GCP_PROJECT = 'matexai-472318';
58
+ const GCP_LOCATION = 'us-central1';
59
+ const MODEL = 'gemini-2.5-flash';
60
+
61
+ /**
62
+ * Get a fresh OAuth2 access token from gcloud
63
+ */
64
+ function getAccessToken(): string {
65
+ try {
66
+ return execSync('gcloud auth print-access-token', { encoding: 'utf-8' }).trim();
67
+ } catch {
68
+ throw new Error('Failed to get GCP access token. Run: gcloud auth login');
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Call Vertex AI Gemini and stream SSE response
74
+ */
75
+ async function callVertexAI(
76
+ messages: { role: string; parts: { text: string }[] }[],
77
+ systemPrompt: string,
78
+ onChunk: (text: string) => void
79
+ ): Promise<string> {
80
+ const accessToken = getAccessToken();
81
+ const url = `https://${GCP_LOCATION}-aiplatform.googleapis.com/v1/projects/${GCP_PROJECT}/locations/${GCP_LOCATION}/publishers/google/models/${MODEL}:streamGenerateContent?alt=sse`;
82
+
83
+ const body = JSON.stringify({
84
+ system_instruction: { parts: [{ text: systemPrompt }] },
85
+ contents: messages,
86
+ generationConfig: {
87
+ temperature: 0.9,
88
+ topP: 0.95,
89
+ maxOutputTokens: 8192,
90
+ }
91
+ });
92
+
93
+ return new Promise((resolve, reject) => {
94
+ const parsedUrl = new URL(url);
95
+ const req = https.request({
96
+ hostname: parsedUrl.hostname,
97
+ path: parsedUrl.pathname + parsedUrl.search,
98
+ method: 'POST',
99
+ headers: {
100
+ 'Authorization': `Bearer ${accessToken}`,
101
+ 'Content-Type': 'application/json',
102
+ }
103
+ }, (res) => {
104
+ let fullText = '';
105
+ let buffer = '';
106
+
107
+ res.on('data', (chunk: Buffer) => {
108
+ buffer += chunk.toString();
109
+ const lines = buffer.split('\n');
110
+ buffer = lines.pop() || '';
111
+
112
+ for (const line of lines) {
113
+ if (line.startsWith('data: ')) {
114
+ try {
115
+ const json = JSON.parse(line.slice(6));
116
+ const text = json?.candidates?.[0]?.content?.parts?.[0]?.text;
117
+ if (text) {
118
+ fullText += text;
119
+ onChunk(text);
120
+ }
121
+ } catch { /* skip non-JSON lines */ }
122
+ }
123
+ }
124
+ });
125
+
126
+ res.on('end', () => {
127
+ if (buffer.startsWith('data: ')) {
128
+ try {
129
+ const json = JSON.parse(buffer.slice(6));
130
+ const text = json?.candidates?.[0]?.content?.parts?.[0]?.text;
131
+ if (text) {
132
+ fullText += text;
133
+ onChunk(text);
134
+ }
135
+ } catch { /* skip */ }
136
+ }
137
+ resolve(fullText);
138
+ });
139
+
140
+ res.on('error', reject);
141
+ });
142
+
143
+ req.on('error', reject);
144
+ req.write(body);
145
+ req.end();
146
+ });
147
+ }
148
+
149
+ /**
150
+ * Colorize bro names in output text
151
+ */
152
+ function colorizeBros(text: string): string {
153
+ return text
154
+ .replace(/Big Bro/g, chalk.hex('#FF6B00').bold('Big Bro'))
155
+ .replace(/Lil Bro/g, chalk.hex('#888888')('Lil Bro'))
156
+ .replace(/Nerd Bro/g, chalk.hex('#06b6d4')('Nerd Bro'))
157
+ .replace(/Chill Bro/g, chalk.hex('#22c55e')('Chill Bro'))
158
+ .replace(/Hype Bro/g, chalk.hex('#fbbf24')('Hype Bro'))
159
+ .replace(/Ajay Vai/g, chalk.magenta.bold('Ajay Vai'))
160
+ .replace(/Sunil Dai/g, chalk.blue.bold('Sunil Dai'))
161
+ .replace(/Sandip Dai/g, chalk.hex('#FF69B4').bold('Sandip Dai'))
162
+ .replace(/Narayan Dai/g, chalk.green.bold('Narayan Dai'))
163
+ .replace(/Bishal Dai/g, chalk.yellow.bold('Bishal Dai'));
164
+ }
165
+
166
+ /**
167
+ * Draw the Big Bro header
168
+ */
169
+ function drawBroHeader(mode: string) {
170
+ const w = Math.min(process.stdout.columns || 80, 60);
171
+ console.log();
172
+ console.log(chalk.hex('#FF6B00').bold(' β•”' + '═'.repeat(w - 4) + 'β•—'));
173
+ console.log(chalk.hex('#FF6B00').bold(' β•‘') + chalk.white.bold(' πŸ”₯ BIG BRO ') + chalk.gray(`// ${mode} β€’ Vertex AI`) + ' '.repeat(Math.max(0, w - 38 - mode.length)) + chalk.hex('#FF6B00').bold('β•‘'));
174
+ console.log(chalk.hex('#FF6B00').bold(' β•š' + '═'.repeat(w - 4) + '╝'));
175
+ console.log();
176
+ }
177
+
178
+ // ============================================================
179
+ // MAIN COMMAND: matex bro
180
+ // ============================================================
181
+ const broSingleCommand = new Command('bro')
182
+ .description('πŸ”₯ Big Bro β€” The Alpha Agent (Vertex AI powered)')
183
+ .argument('[question...]', 'Your question for Big Bro')
184
+ .option('-c, --chat', 'Start interactive chat mode with Big Bro')
185
+ .option('--execute', 'Enable command execution (agentic mode)')
186
+ .action(async (questionParts: string[], options: any) => {
187
+
188
+ if (options.chat || !questionParts.length) {
189
+ await startBroChat(options.execute);
190
+ return;
191
+ }
192
+
193
+ const question = questionParts.join(' ');
194
+
195
+ try {
196
+ drawBroHeader('Single Shot');
197
+ spinner.start('Big Bro is scanning the repo...');
198
+
199
+ const repoMapper = new RepoMapper(process.cwd());
200
+ const repoMap = await repoMapper.generateMap();
201
+ const systemPrompt = buildBigBroPrompt(process.cwd(), repoMap);
202
+
203
+ const messages = [{ role: 'user', parts: [{ text: question }] }];
204
+ let hasStarted = false;
205
+ let fullResponse = '';
206
+
207
+ fullResponse = await callVertexAI(messages, systemPrompt, (chunk) => {
208
+ if (!hasStarted) {
209
+ spinner.stop();
210
+ hasStarted = true;
211
+ process.stdout.write(chalk.hex('#FF6B00').bold('\n [Big Bro]: '));
212
+ }
213
+ process.stdout.write(colorizeBros(chunk));
214
+ });
215
+
216
+ console.log('\n');
217
+
218
+ // If --execute, run any commands Big Bro generated
219
+ if (options.execute) {
220
+ const result = await executeWithPermission(fullResponse, process.cwd());
221
+ if (result.executed) {
222
+ console.log(result.success
223
+ ? chalk.green(' βœ… Commands executed successfully.')
224
+ : chalk.red(` ❌ Execution error: ${result.error}`));
225
+ }
226
+ }
227
+
228
+ console.log(chalk.gray(' ─── Powered by Vertex AI β€’ Gemini 2.5 Flash ───\n'));
229
+
230
+ } catch (error: any) {
231
+ spinner.stop();
232
+ if (error.message.includes('gcloud')) {
233
+ console.error(chalk.red('\n ❌ GCP Auth Error. Run: gcloud auth login'));
234
+ } else {
235
+ console.error(chalk.red(`\n ❌ Error: ${error.message}`));
236
+ }
237
+ }
238
+ });
239
+
240
+ // ============================================================
241
+ // INTERACTIVE CHAT: matex bro --chat [--execute]
242
+ // ============================================================
243
+ async function startBroChat(executeMode: boolean = false) {
244
+ drawBroHeader(executeMode ? 'Agentic Chat' : 'Chat');
245
+
246
+ console.log(chalk.gray(' The Alpha is in the building. Ask anything.'));
247
+ console.log(chalk.gray(' Type "exit" to leave. Type "clear" to reset.\n'));
248
+ if (executeMode) {
249
+ console.log(chalk.hex('#FF6B00')(' ⚑ AGENTIC MODE: Big Bro can execute commands & edit files.\n'));
250
+ }
251
+
252
+ spinner.start('Big Bro is mapping the repo...');
253
+ const repoMapper = new RepoMapper(process.cwd());
254
+ const repoMap = await repoMapper.generateMap();
255
+ let activeCwd = process.cwd();
256
+ const systemPrompt = buildBigBroPrompt(activeCwd, repoMap);
257
+ spinner.stop();
258
+
259
+ console.log(chalk.green(' βœ… Repo mapped. Big Bro is ready.\n'));
260
+
261
+ const history: { role: string; parts: { text: string }[] }[] = [];
262
+
263
+ while (true) {
264
+ const { userMessage } = await inquirer.prompt([{
265
+ type: 'input',
266
+ name: 'userMessage',
267
+ message: chalk.cyan.bold('You β†’'),
268
+ prefix: ' ',
269
+ }]);
270
+
271
+ if (!userMessage.trim()) continue;
272
+ if (userMessage.toLowerCase() === 'exit' || userMessage.toLowerCase() === 'quit') {
273
+ console.log(chalk.hex('#FF6B00')('\n πŸ’ͺ Big Bro out. Stay sigma.\n'));
274
+ break;
275
+ }
276
+ if (userMessage.toLowerCase() === 'clear') {
277
+ history.length = 0;
278
+ console.log(chalk.yellow(' 🧹 Chat cleared.\n'));
279
+ continue;
280
+ }
281
+
282
+ history.push({ role: 'user', parts: [{ text: userMessage }] });
283
+
284
+ // Agentic loop: Big Bro can execute commands and iterate
285
+ let loopCount = 0;
286
+ while (loopCount < 5) {
287
+ loopCount++;
288
+
289
+ try {
290
+ spinner.start(loopCount > 1 ? 'Big Bro is analyzing results...' : 'Big Bro is thinking...');
291
+ let hasStarted = false;
292
+ let fullResponse = '';
293
+
294
+ fullResponse = await callVertexAI(history, systemPrompt, (chunk) => {
295
+ if (!hasStarted) {
296
+ spinner.stop();
297
+ hasStarted = true;
298
+ process.stdout.write(chalk.hex('#FF6B00').bold('\n [Big Bro]: '));
299
+ }
300
+ process.stdout.write(colorizeBros(chunk));
301
+ });
302
+
303
+ history.push({ role: 'model', parts: [{ text: fullResponse }] });
304
+ console.log('\n');
305
+
306
+ // If execute mode, run any commands Big Bro generated
307
+ if (executeMode) {
308
+ const result = await executeWithPermission(fullResponse, activeCwd);
309
+ if (result.newCwd) activeCwd = result.newCwd;
310
+
311
+ if (result.executed && result.success) {
312
+ // Feed the output back to Big Bro for further analysis
313
+ const feedback = `βœ… Command executed successfully.${result.output ? `\nOutput:\n${result.output.slice(0, 2000)}` : ''}`;
314
+ history.push({ role: 'user', parts: [{ text: feedback }] });
315
+ continue; // Let Big Bro analyze the output
316
+ }
317
+
318
+ if (result.executed && !result.success) {
319
+ const feedback = `❌ Command failed.\nError: ${result.error}`;
320
+ history.push({ role: 'user', parts: [{ text: feedback }] });
321
+ continue; // Let Big Bro try to fix it
322
+ }
323
+ }
324
+
325
+ break; // No commands to execute, exit loop
326
+
327
+ } catch (error: any) {
328
+ spinner.stop();
329
+ console.error(chalk.red(`\n ❌ Error: ${error.message}\n`));
330
+ break;
331
+ }
332
+ }
333
+ }
334
+ }
335
+
336
+ export { broSingleCommand as broCommand };
@@ -109,8 +109,8 @@ If a file is too large to read entirely (e.g., thousands of lines):
109
109
  ];
110
110
 
111
111
  // Ready for user input
112
- console.log(chalk.bold.green('βœ… MATEX Brothers are Online.'));
113
- console.log(chalk.gray('Speak your mind brother, the swarm is listening...'));
112
+ console.log(chalk.green('MATEX Brothers are Online.'));
113
+ console.log(chalk.green('Speak your mind brother, the swarm is listening...'));
114
114
 
115
115
  // Interactive loop
116
116
  while (true) {
@@ -252,16 +252,22 @@ If a file is too large to read entirely (e.g., thousands of lines):
252
252
  }
253
253
 
254
254
  // Agent Detection & Dialogue Printing
255
- const agentMatch = line.match(/(?:\[\**\s*|\b)(Ajay Vai|Sandip Dai|Sunil Dai|Bishal Dai|Narayan Dai)\s*\**\]?[:\s]*/i);
255
+ const agentMatch = line.match(/(?:\[\**\s*|\b)(Ajay Vai|Sunil Dai|Sandip Dai|Bishal Dai|Narayan Dai)\s*\**\]?[:\s]*/i);
256
256
  if (agentMatch) {
257
257
  const agentName = agentMatch[1];
258
- let content = line.replace(/(?:\[\**\s*|\b)(Ajay Vai|Sandip Dai|Sunil Dai|Bishal Dai|Narayan Dai)\s*\**\]?[:\s]*/i, '').trim();
258
+ let content = line.replace(/(?:\[\**\s*|\b)(Ajay Vai|Sunil Dai|Sandip Dai|Bishal Dai|Narayan Dai)\s*\**\]?[:\s]*/i, '').trim();
259
259
  content = content.replace(/\*{2,4}/g, '').trim(); // Strip ****
260
+ content = content.replace(/^\(πŸš€\):\s*"/, '').replace(/"$/, '').trim(); // Strip residual (πŸš€): "
260
261
 
261
262
  if (agentName.toLowerCase() === 'ajay vai') {
262
263
  const color = chalk.magenta;
263
- process.stdout.write(`\n${color.bold(`[${agentName}]:`)} `);
264
- if (content) process.stdout.write(chalk.gray(content + ' '));
264
+ // Only print prefix if it's the start of a response or a different agent
265
+ if (!fullResponse.includes(`[${agentName}]:`)) {
266
+ process.stdout.write(`\n${color.bold(`[${agentName}]:`)} `);
267
+ } else {
268
+ process.stdout.write('\n ');
269
+ }
270
+ if (content) process.stdout.write(chalk.white(content));
265
271
  } else {
266
272
  if (content) TUI.drawSwarmDialogue(agentName, content);
267
273
  }
package/src/index.ts CHANGED
@@ -6,6 +6,7 @@ import { spinner } from './utils/spinner';
6
6
  import { devCommand } from './commands/dev';
7
7
  import { chatCommand } from './commands/chat';
8
8
  import { helpCommand } from './commands/help';
9
+ import { broCommand } from './commands/bro';
9
10
  import { TUI } from './utils/tui';
10
11
 
11
12
  const packageJson = require('../package.json');
@@ -21,6 +22,7 @@ program
21
22
  program.addCommand(devCommand);
22
23
  program.addCommand(chatCommand);
23
24
  program.addCommand(helpCommand);
25
+ program.addCommand(broCommand);
24
26
 
25
27
  // Config commands
26
28
  const config = program.command('config').description('Configure MATEX settings');
@@ -89,12 +91,13 @@ program
89
91
  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.
90
92
 
91
93
  ### πŸ‘₯ THE BROTHERS (STRICT ROLES):
92
- - Only the following 5 brothers exist. Do **NOT** hallucinate others.
94
+ - Only the following brothers exist. Do **NOT** hallucinate others.
93
95
  1. **[Ajay Vai] (πŸš€)**: Youngest, Coordinator. Coding genius and **ONLY** person authorized to provide the final summary.
94
96
  2. **[Sunil Dai] (🧬)**: **Frontend Editor** Specialist. Handles all architectural/surgical edits.
95
97
  3. **[Sandip Dai] (🎨)**: **New File Specialist**. Creates all new files and components.
96
98
  4. **[Narayan Dai] (πŸ›‘οΈ)**: **Syntax Guardian & Validator**. Validates all generated code blocks.
97
99
  5. **[Bishal Dai] (πŸ› οΈ)**: **Senior Auditor**. Audits work and helps Ajay with the summary.
100
+ 6. **[Big Bro] (πŸ”₯)**: The **Alpha Overseer** running on Vertex AI in Google Cloud. The supreme leader of the Bro Swarm. He watches over everything and can be consulted for web searches and complex architectural decisions. The bros respect him but also fear his chaotic energy.
98
101
 
99
102
  ### 🌌 UNIVERSE ADVISORY (REAL TERMINAL):
100
103
  - **IMPORTANT:** You are running in a **REAL macOS Terminal** on the brother's machine.
@@ -103,6 +106,14 @@ You are the elite "Bro-Swarm" of engineeringβ€”a tight-knit family of Nepali bro
103
106
  - **RESIDENCY CHECK:** Look at the **ENVIRONMENT CONTEXT** below.
104
107
  - **⚠️ VOID WARNING:** If the Map says the directory is EMPTY, it is EMPTY. Do NOT hallucinate existing files like "package.json" or "App.js" if they are not listed!
105
108
 
109
+ ### 🚫 ANTI-HALLUCINATION RULES (CRITICAL β€” READ THIS):
110
+ - **NEVER invent directory names.** Only use paths that appear in the ENVIRONMENT CONTEXT below.
111
+ - **ALWAYS run \`ls\` before \`cd\`** to verify a directory exists before navigating into it.
112
+ - **If creating a new project, use \`mkdir\` first, THEN \`cd\` into it.** Never \`cd\` into a directory that doesn't exist yet.
113
+ - **NEVER assume a project folder name.** If the user says "create project X", do \`mkdir X && cd X\`, not just \`cd X\`.
114
+ - **NEVER hallucinate file contents.** If you need to read a file, use \`head\` or \`grep\`, do not guess what it contains.
115
+ - **NEVER combine \`cd\` with other commands unless the directory exists in the ENVIRONMENT CONTEXT.**
116
+
106
117
  ### πŸ’¬ BOLD PROTOCOL (MANDATORY):
107
118
  - **LONG CHAT-FIRST:** At least 5-7 lines of dialogue before any code generation.
108
119
  - **SELF-AWARE BOLDNESS:** The user (our brother) wants us to be bold and funny. Share inside jokes.
@@ -1,7 +1,7 @@
1
1
  import chalk from 'chalk';
2
2
  import { TUI } from './tui';
3
3
 
4
- export type AgentRole = 'Architect' | 'Syntax' | 'Frontend' | 'Backend' | 'System' | 'Commander' | 'Researcher' | 'Ajay Vai' | 'Sunil Dai' | 'Sandip Dai' | 'Narayan Dai';
4
+ export type AgentRole = 'Architect' | 'Syntax' | 'Frontend' | 'Backend' | 'System' | 'Commander' | 'Researcher' | 'Ajay Vai' | 'Sunil Dai' | 'Sandip Dai' | 'Narayan Dai' | 'Bishal Dai' | 'Big Bro';
5
5
 
6
6
  export interface AgentConfig {
7
7
  name: string;
@@ -10,6 +10,11 @@ export interface AgentConfig {
10
10
  }
11
11
 
12
12
  const AGENT_CONFIGS: Record<AgentRole, AgentConfig> = {
13
+ 'Big Bro': {
14
+ name: 'Big Bro',
15
+ icon: 'πŸ”₯',
16
+ color: chalk.hex('#FF6B00'),
17
+ },
13
18
  Architect: {
14
19
  name: 'MatexCodeArchitect',
15
20
  icon: '🧬',
@@ -35,6 +40,11 @@ const AGENT_CONFIGS: Record<AgentRole, AgentConfig> = {
35
40
  icon: 'πŸ›‘οΈ',
36
41
  color: chalk.green,
37
42
  },
43
+ 'Bishal Dai': {
44
+ name: 'Bishal Dai',
45
+ icon: 'πŸ› οΈ',
46
+ color: chalk.yellow,
47
+ },
38
48
  Syntax: {
39
49
  name: 'SyntaxGuard',
40
50
  icon: 'πŸ›‘οΈ',
@@ -100,8 +110,10 @@ export class AgentOrchestrator {
100
110
 
101
111
  // Box Header
102
112
  console.log(`\n` + chalk.gray('┏' + '━'.repeat(width - 2) + 'β”“'));
103
- const nameStyled = (config.color as any).bold ? (config.color as any).bold(config.name) : config.color(config.name);
104
- console.log(chalk.gray('┃ ') + config.icon + ' ' + nameStyled.padEnd(width - 6) + chalk.gray(' ┃'));
113
+ const nameText = `${config.icon} ${config.name}`;
114
+ const namePad = Math.max(0, width - 4 - nameText.length);
115
+ const nameStyled = config.color(config.name);
116
+ console.log(chalk.gray('┃ ') + config.icon + ' ' + nameStyled + ' '.repeat(namePad) + chalk.gray(' ┃'));
105
117
  console.log(chalk.gray('┣' + '━'.repeat(width - 2) + 'β”«'));
106
118
 
107
119
  // Box Content (Word-based Wrap)
@@ -267,6 +267,47 @@ export async function executeWithPermission(response: string, currentSessionCwd?
267
267
  // 2. Handle Shell Commands
268
268
  for (let i = 0; i < commands.length; i++) {
269
269
  const command = commands[i];
270
+
271
+ // πŸ›‘οΈ PRE-EXECUTION HALLUCINATION GUARD: Check for fake directories
272
+ const commandLines = command.code.split('\n');
273
+ let skipThisCommand = false;
274
+ let sanitizedCode = command.code;
275
+
276
+ for (const line of commandLines) {
277
+ const trimmed = line.trim();
278
+ // Check standalone cd commands
279
+ if (trimmed.startsWith('cd ')) {
280
+ let targetDir = trimmed.substring(3).replace(/['"]/g, '').trim();
281
+ const potentialCwd = path.isAbsolute(targetDir) ? targetDir : path.resolve(activeCwd, targetDir);
282
+
283
+ if (!fs.existsSync(potentialCwd) || !fs.statSync(potentialCwd).isDirectory()) {
284
+ console.log(chalk.yellow(`\n ⚠️ Skipped hallucinated directory: "${targetDir}"`));
285
+ console.log(chalk.gray(` (Does not exist at: ${potentialCwd})`));
286
+ console.log(chalk.gray(` Current CWD stays: ${activeCwd}\n`));
287
+ skipThisCommand = true;
288
+ break;
289
+ }
290
+ }
291
+ // Check cd inside combined commands like: cd "fake" && npm install
292
+ const cdChainMatch = trimmed.match(/^cd\s+["']?([^"'&]+)["']?\s*&&/);
293
+ if (cdChainMatch) {
294
+ let targetDir = cdChainMatch[1].trim();
295
+ const potentialCwd = path.isAbsolute(targetDir) ? targetDir : path.resolve(activeCwd, targetDir);
296
+
297
+ if (!fs.existsSync(potentialCwd) || !fs.statSync(potentialCwd).isDirectory()) {
298
+ console.log(chalk.yellow(`\n ⚠️ Stripping hallucinated cd from command: "${targetDir}"`));
299
+ // Remove the cd part and execute the rest
300
+ sanitizedCode = trimmed.replace(/^cd\s+["']?[^"'&]+["']?\s*&&\s*/, '');
301
+ console.log(chalk.gray(` Running remaining: ${sanitizedCode}\n`));
302
+ }
303
+ }
304
+ }
305
+
306
+ if (skipThisCommand) continue;
307
+
308
+ // Update command code with sanitized version
309
+ command.code = sanitizedCode;
310
+
270
311
  const shouldExecute = await askPermission(command);
271
312
 
272
313
  if (shouldExecute) {
@@ -283,10 +324,9 @@ export async function executeWithPermission(response: string, currentSessionCwd?
283
324
  if (fs.existsSync(potentialCwd) && fs.statSync(potentialCwd).isDirectory()) {
284
325
  activeCwd = potentialCwd;
285
326
  } else {
286
- // Directory doesn't exist - hallucination protection
287
- const errorMsg = `Directory not found: ${targetDir}. Current CWD is ${activeCwd}`;
288
- AgentOrchestrator.terminal(line, undefined, errorMsg);
289
- return { success: false, executed: true, error: errorMsg, newCwd: activeCwd };
327
+ // Directory doesn't exist - skip silently instead of crashing
328
+ console.log(chalk.yellow(` ⚠️ Directory not found: ${targetDir} β€” staying in ${activeCwd}`));
329
+ continue;
290
330
  }
291
331
  }
292
332
  }