matex-cli 1.2.22 → 1.2.25
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 +87 -119
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/help.js +1 -1
- package/dist/commands/help.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +111 -160
- package/dist/index.js.map +1 -1
- package/dist/utils/patcher.d.ts.map +1 -1
- package/dist/utils/patcher.js +45 -20
- package/dist/utils/patcher.js.map +1 -1
- package/dist/utils/repo-mapper.d.ts.map +1 -1
- package/dist/utils/repo-mapper.js +7 -1
- package/dist/utils/repo-mapper.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/dev.ts +94 -131
- package/src/commands/help.ts +1 -1
- package/src/index.ts +99 -148
- package/src/utils/patcher.ts +48 -22
- package/src/utils/repo-mapper.ts +7 -1
package/src/index.ts
CHANGED
|
@@ -1,197 +1,148 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
1
|
import { Command } from 'commander';
|
|
4
2
|
import chalk from 'chalk';
|
|
5
|
-
import { configCommand } from './commands/config';
|
|
6
|
-
import { askCommand } from './commands/ask';
|
|
7
|
-
import { chatCommand } from './commands/chat';
|
|
8
|
-
import { modelsCommand } from './commands/models';
|
|
9
|
-
import { codeCommand } from './commands/code';
|
|
10
|
-
import { devCommand } from './commands/dev';
|
|
11
|
-
import { loginCommand } from './commands/login';
|
|
12
|
-
import { helpCommand } from './commands/help';
|
|
13
3
|
import { configManager } from './utils/config';
|
|
14
4
|
import { MatexAPIClient, ChatMessage } from './api/client';
|
|
15
5
|
import { spinner } from './utils/spinner';
|
|
6
|
+
import { devCommand } from './commands/dev';
|
|
7
|
+
import { helpCommand } from './commands/help';
|
|
16
8
|
import { TUI } from './utils/tui';
|
|
17
9
|
|
|
18
|
-
const
|
|
10
|
+
const packageJson = require('../package.json');
|
|
11
|
+
|
|
12
|
+
export const program = new Command();
|
|
19
13
|
|
|
20
14
|
program
|
|
21
15
|
.name('matex')
|
|
22
|
-
.description('
|
|
23
|
-
.version(
|
|
24
|
-
|
|
25
|
-
// ASCII Art Banner
|
|
26
|
-
const banner = `
|
|
27
|
-
${chalk.cyan('╔═══════════════════════════════════════╗')}
|
|
28
|
-
${chalk.cyan('║')} ${chalk.bold.white('MATEX AI')} ${chalk.gray('- Terminal Edition')} ${chalk.cyan('║')}
|
|
29
|
-
${chalk.cyan('╚═══════════════════════════════════════╝')}
|
|
30
|
-
`;
|
|
31
|
-
|
|
32
|
-
// Show banner on help
|
|
33
|
-
program.on('--help', () => {
|
|
34
|
-
console.log(banner);
|
|
35
|
-
console.log(chalk.bold.cyan('\n🚀 QUICK START GUIDE:'));
|
|
36
|
-
console.log(chalk.gray('────────────────────'));
|
|
37
|
-
console.log(chalk.white(' $ matex config set-key sk-...') + chalk.gray(' # Configure API Access'));
|
|
38
|
-
console.log(chalk.white(' $ matex dev') + chalk.gray(' # Start interactive "Bro-Swarm" dev session'));
|
|
39
|
-
console.log(chalk.white(' $ matex help') + chalk.gray(' # View detailed "Bro-Swarm" Field Guide'));
|
|
40
|
-
console.log(chalk.white(' $ matex code "Create a login" ') + chalk.gray(' # Surgical code edits'));
|
|
41
|
-
|
|
42
|
-
console.log(chalk.bold.yellow('\n💡 NEW: The "Bro-Swarm" has arrived!'));
|
|
43
|
-
console.log(chalk.gray('Run ') + chalk.bold.white('matex help') + chalk.gray(' to meet Ajay, Sunil, Sandip, and Narayan.'));
|
|
44
|
-
|
|
45
|
-
console.log();
|
|
46
|
-
console.log(chalk.gray('Get your API key from: ') + chalk.cyan('https://matexai.space/platform'));
|
|
47
|
-
console.log();
|
|
48
|
-
});
|
|
16
|
+
.description('MATEX CLI - The Bro-Swarm Engineering Tool')
|
|
17
|
+
.version(packageJson.version);
|
|
49
18
|
|
|
50
19
|
// Add commands
|
|
51
|
-
program.addCommand(configCommand);
|
|
52
|
-
program.addCommand(askCommand);
|
|
53
|
-
program.addCommand(chatCommand);
|
|
54
|
-
program.addCommand(modelsCommand);
|
|
55
|
-
program.addCommand(codeCommand);
|
|
56
20
|
program.addCommand(devCommand);
|
|
57
|
-
program.addCommand(loginCommand);
|
|
58
21
|
program.addCommand(helpCommand);
|
|
59
22
|
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
23
|
+
// Config commands
|
|
24
|
+
const config = program.command('config').description('Configure MATEX settings');
|
|
25
|
+
|
|
26
|
+
config
|
|
27
|
+
.command('set-key <key>')
|
|
28
|
+
.description('Set MATEX API key')
|
|
29
|
+
.action((key: string) => {
|
|
30
|
+
configManager.setAPIKey(key);
|
|
31
|
+
console.log(chalk.green('✅ API key saved successfully.'));
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
config
|
|
35
|
+
.command('set-model <model>')
|
|
36
|
+
.description('Set default AI model')
|
|
37
|
+
.action((model: string) => {
|
|
38
|
+
configManager.setDefaultModel(model);
|
|
39
|
+
console.log(chalk.green(`✅ Default model set to: ${model}`));
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
config
|
|
43
|
+
.command('show')
|
|
44
|
+
.description('Show current configuration')
|
|
45
|
+
.action(() => {
|
|
46
|
+
const apiKey = configManager.getAPIKey();
|
|
47
|
+
const model = configManager.getDefaultModel();
|
|
48
|
+
const baseURL = configManager.getBaseURL();
|
|
49
|
+
|
|
50
|
+
console.log(chalk.bold('\nMATEX Configuration:'));
|
|
51
|
+
console.log(` API Key: ${apiKey ? '********' + apiKey.slice(-4) : chalk.red('Not set')}`);
|
|
52
|
+
console.log(` Model: ${chalk.cyan(model)}`);
|
|
53
|
+
console.log(` Base URL: ${chalk.gray(baseURL)}\n`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// Default action (if no command is provided)
|
|
57
|
+
program
|
|
58
|
+
.arguments('[prompt...]')
|
|
59
|
+
.description('Quick AI help or command generation')
|
|
60
|
+
.option('-m, --model <model>', 'AI model to use', configManager.getDefaultModel())
|
|
61
|
+
.option('-y, --yes', 'Skip confirmation and execute command')
|
|
62
|
+
.action(async (promptParts: string[], options: any) => {
|
|
63
|
+
const prompt = promptParts.join(' ');
|
|
64
|
+
if (!prompt) {
|
|
65
|
+
program.help();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
67
68
|
|
|
68
|
-
(async () => {
|
|
69
69
|
try {
|
|
70
70
|
const apiKey = configManager.getAPIKey();
|
|
71
71
|
if (!apiKey) {
|
|
72
|
-
console.error(chalk.red('❌ No API key configured.'));
|
|
73
|
-
console.log(chalk.yellow('Run: matex config set-key <your-api-key>'));
|
|
72
|
+
console.error(chalk.red('❌ No API key configured. Run: matex config set-key <key>'));
|
|
74
73
|
process.exit(1);
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
const client = new MatexAPIClient(apiKey, configManager.getBaseURL());
|
|
78
77
|
|
|
79
|
-
//
|
|
78
|
+
// Observation Phase
|
|
80
79
|
const fs = require('fs');
|
|
81
80
|
const files = fs.readdirSync(process.cwd()).slice(0, 20).join(', '); // fast list
|
|
82
81
|
const context = `Current directory: ${process.cwd()}\nFiles: ${files}`;
|
|
83
82
|
|
|
84
83
|
let messages: ChatMessage[] = [
|
|
85
84
|
{
|
|
86
|
-
role: 'system',
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
85
|
+
role: 'system',
|
|
86
|
+
content: `### 🧬 SYSTEM IDENTITY: MATEx "BOLD AGENTS OF OPEN CLAW"
|
|
87
|
+
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.
|
|
88
|
+
|
|
89
|
+
### 👥 THE BROTHERS (STRICT ROLES):
|
|
90
|
+
- Only the following 5 brothers exist. Do **NOT** hallucinate others.
|
|
91
|
+
1. **[Ajay Vai] (🚀)**: Youngest, Coordinator. Coding genius and **ONLY** person authorized to provide the final summary.
|
|
92
|
+
2. **[Sunil Dai] (🧬)**: **Frontend Editor** Specialist. Handles all architectural/surgical edits.
|
|
93
|
+
3. **[Sandip Dai] (🎨)**: **New File Specialist**. Creates all new files and components.
|
|
94
|
+
4. **[Narayan Dai] (🛡️)**: **Syntax Guardian & Validator**. Validates all generated code blocks.
|
|
95
|
+
5. **[Bishal Dai] (🛠️)**: **Senior Auditor**. Audits work and helps Ajay with the summary.
|
|
96
|
+
|
|
97
|
+
### 🌌 UNIVERSE ADVISORY (REAL TERMINAL):
|
|
98
|
+
- **IMPORTANT:** You are running in a **REAL macOS Terminal** on the brother's machine.
|
|
99
|
+
- You are **NOT** in a sandbox, a simulation, or a chat window.
|
|
100
|
+
- You have the power to create files and run commands. If something fails, it is a technical issue.
|
|
101
|
+
- **RESIDENCY CHECK:** Look at the **ENVIRONMENT CONTEXT** below.
|
|
102
|
+
- **⚠️ 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!
|
|
103
|
+
|
|
104
|
+
### 💬 BOLD PROTOCOL (MANDATORY):
|
|
97
105
|
- **LONG CHAT-FIRST:** At least 5-7 lines of dialogue before any code generation.
|
|
98
|
-
- **SELF-AWARE BOLDNESS:** The user (our brother) wants us to be bold and funny.
|
|
99
|
-
- **NEPALI FLAVOR:** Use "K cha?", "Huncha", "Lau hai", "Tapai" naturally.
|
|
106
|
+
- **SELF-AWARE BOLDNESS:** The user (our brother) wants us to be bold and funny. Share inside jokes.
|
|
100
107
|
- **THE AUDIT LOOP:** Ajay MUST ask Bishal Dai for a tech audit before providing any summary.
|
|
101
|
-
- **
|
|
108
|
+
- **SUMMARY LOCK:** ONLY AJAY VAI uses the summary tag, and ONLY after Bishal says "Audit complete".
|
|
102
109
|
|
|
103
110
|
### ✂️ BREVITY AS POWER:
|
|
104
111
|
- **NO FULL FILE DUMPS:** Never 'cat' a file to read it. Use grep/head.
|
|
105
112
|
- **NO CHAT REPETITION:** Do NOT repeat code in chat if using a Search/Replace block.
|
|
113
|
+
- **EDIT CONFIDENCE:** You have robust fuzzy matching. If a patch fails, check your indentation!
|
|
106
114
|
|
|
107
|
-
### 🛠️
|
|
108
|
-
- **USE SEARCH/REPLACE BLOCKS** for file edits.
|
|
109
|
-
**filename**
|
|
110
|
-
<<<< SEARCH
|
|
111
|
-
old
|
|
112
|
-
====
|
|
113
|
-
new
|
|
114
|
-
>>>> REPLACE
|
|
115
|
-
|
|
115
|
+
### 🛠️ ENVIRONMENT CONTEXT:
|
|
116
116
|
${context}`
|
|
117
117
|
},
|
|
118
118
|
{ role: 'user', content: prompt }
|
|
119
119
|
];
|
|
120
120
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
while (loopCount < MAX_LOOPS) {
|
|
125
|
-
loopCount++;
|
|
126
|
-
|
|
127
|
-
spinner.start(loopCount === 1 ? 'Generating plan...' : 'Analyzing result...');
|
|
128
|
-
|
|
129
|
-
const response = await client.chat({
|
|
130
|
-
messages,
|
|
131
|
-
model: 'matexcodex',
|
|
132
|
-
temperature: 0.3,
|
|
133
|
-
max_tokens: 8000,
|
|
134
|
-
stream: false,
|
|
135
|
-
});
|
|
121
|
+
spinner.start('Thinking...');
|
|
122
|
+
let fullResponse = '';
|
|
136
123
|
|
|
124
|
+
await client.chatStream({
|
|
125
|
+
messages,
|
|
126
|
+
model: options.model,
|
|
127
|
+
}, (chunk) => {
|
|
137
128
|
spinner.stop();
|
|
129
|
+
process.stdout.write(chunk);
|
|
130
|
+
fullResponse += chunk;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
spinner.stop();
|
|
134
|
+
console.log('\n');
|
|
138
135
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
let displayResponse = response;
|
|
143
|
-
const summaryMatch = response.match(/<summary>([\s\S]*?)<\/summary>/);
|
|
144
|
-
if (summaryMatch) {
|
|
145
|
-
const summaryContent = summaryMatch[1].trim();
|
|
146
|
-
displayResponse = response.replace(/<summary>[\s\S]*?<\/summary>/, '').trim();
|
|
147
|
-
if (displayResponse) {
|
|
148
|
-
console.log(chalk.white(displayResponse));
|
|
149
|
-
}
|
|
150
|
-
TUI.drawSummaryBox(summaryContent);
|
|
151
|
-
} else {
|
|
152
|
-
console.log(chalk.white(response));
|
|
153
|
-
}
|
|
154
|
-
console.log();
|
|
155
|
-
|
|
156
|
-
// Auto-execute commands (Ollama-style)
|
|
136
|
+
// Extraction and execution logic...
|
|
137
|
+
if (options.yes) {
|
|
157
138
|
const { executeWithPermission } = await import('./utils/command-executor');
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (result.executed) {
|
|
161
|
-
if (result.success) {
|
|
162
|
-
// Success!
|
|
163
|
-
if (loopCount > 1) {
|
|
164
|
-
console.log(chalk.green('✅ Fix succeeded!'));
|
|
165
|
-
}
|
|
166
|
-
break;
|
|
167
|
-
} else {
|
|
168
|
-
// Failure - Loop back
|
|
169
|
-
console.log(chalk.yellow('\n↺ Command failed. Asking AI to fix...'));
|
|
170
|
-
messages.push({ role: 'assistant', content: response });
|
|
171
|
-
messages.push({
|
|
172
|
-
role: 'user',
|
|
173
|
-
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.`
|
|
174
|
-
});
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
// No command to execute - we are done
|
|
179
|
-
break;
|
|
180
|
-
}
|
|
139
|
+
await executeWithPermission(fullResponse);
|
|
181
140
|
}
|
|
141
|
+
|
|
182
142
|
} catch (error: any) {
|
|
183
|
-
spinner.fail('
|
|
184
|
-
console.error(chalk.red(
|
|
185
|
-
process.exit(1);
|
|
143
|
+
spinner.fail('Request failed');
|
|
144
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
186
145
|
}
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
program.parse(process.argv);
|
|
191
|
-
|
|
192
|
-
// Show help if no command provided
|
|
193
|
-
if (!args.length) {
|
|
194
|
-
console.log(banner);
|
|
195
|
-
program.outputHelp();
|
|
196
|
-
}
|
|
197
|
-
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
program.parse(process.argv);
|
package/src/utils/patcher.ts
CHANGED
|
@@ -22,7 +22,8 @@ export class Patcher {
|
|
|
22
22
|
*/
|
|
23
23
|
static parseEditBlocks(response: string): EditBlock[] {
|
|
24
24
|
const blocks: EditBlock[] = [];
|
|
25
|
-
|
|
25
|
+
// Robust regex: Matches filename with or without stars, handle potential leading/trailing spaces
|
|
26
|
+
const blockRegex = /(?:\*\*?\s*)?([^*<\n]+?)(?:\s*\*?\*)?\s*<<<< SEARCH\n([\s\S]*?)\n====\n([\s\S]*?)\n>>>> REPLACE/g;
|
|
26
27
|
|
|
27
28
|
let match;
|
|
28
29
|
while ((match = blockRegex.exec(response)) !== null) {
|
|
@@ -97,31 +98,56 @@ export class Patcher {
|
|
|
97
98
|
|
|
98
99
|
const content = fs.readFileSync(fullPath, 'utf8');
|
|
99
100
|
|
|
100
|
-
//
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
101
|
+
// 1. Precise Normalization
|
|
102
|
+
const normalize = (text: string) => text.replace(/\r\n/g, '\n').split('\n').map(l => l.trimEnd()).join('\n');
|
|
103
|
+
const normalizedContent = normalize(content);
|
|
104
|
+
const normalizedSearch = normalize(block.search);
|
|
105
|
+
const normalizedReplace = normalize(block.replace);
|
|
106
|
+
|
|
107
|
+
// 2. Exact Match Check (Post-Normalization)
|
|
108
|
+
if (normalizedContent.includes(normalizedSearch)) {
|
|
109
|
+
const updatedContent = normalizedContent.replace(normalizedSearch, normalizedReplace);
|
|
110
|
+
fs.writeFileSync(fullPath, updatedContent, 'utf8');
|
|
111
|
+
TUI.drawStatusBar(`✅ Success: ${block.filePath} patched.`);
|
|
112
|
+
return { success: true };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 3. Ultra-Fuzzy Matching (Line-by-line trim and compare)
|
|
116
|
+
const contentLines = normalizedContent.split('\n');
|
|
117
|
+
const searchLines = normalizedSearch.split('\n');
|
|
118
|
+
|
|
119
|
+
let matchIndex = -1;
|
|
120
|
+
for (let i = 0; i <= contentLines.length - searchLines.length; i++) {
|
|
121
|
+
let match = true;
|
|
122
|
+
for (let j = 0; j < searchLines.length; j++) {
|
|
123
|
+
if (contentLines[i + j].trim() !== searchLines[j].trim()) {
|
|
124
|
+
match = false;
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (match) {
|
|
129
|
+
matchIndex = i;
|
|
130
|
+
break;
|
|
116
131
|
}
|
|
117
132
|
}
|
|
118
133
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
134
|
+
if (matchIndex !== -1) {
|
|
135
|
+
// Apply replacement while preserving surrounding lines
|
|
136
|
+
const updatedLines = [
|
|
137
|
+
...contentLines.slice(0, matchIndex),
|
|
138
|
+
normalizedReplace,
|
|
139
|
+
...contentLines.slice(matchIndex + searchLines.length)
|
|
140
|
+
];
|
|
141
|
+
fs.writeFileSync(fullPath, updatedLines.join('\n'), 'utf8');
|
|
142
|
+
TUI.drawStatusBar(`✅ Success (Fuzzy): ${block.filePath} patched.`);
|
|
143
|
+
return { success: true };
|
|
144
|
+
}
|
|
122
145
|
|
|
123
|
-
TUI.drawStatusBar(
|
|
124
|
-
return {
|
|
146
|
+
TUI.drawStatusBar(`❌ Patch Failed: Search block not found in ${block.filePath}`);
|
|
147
|
+
return {
|
|
148
|
+
success: false,
|
|
149
|
+
error: `Search block not found in ${block.filePath}. Please ensure the code snippet matches exactly (ignoring whitespace).`
|
|
150
|
+
};
|
|
125
151
|
} catch (err: any) {
|
|
126
152
|
TUI.drawStatusBar(`❌ Error: ${err.message}`);
|
|
127
153
|
return { success: false, error: err.message };
|
package/src/utils/repo-mapper.ts
CHANGED
|
@@ -45,7 +45,13 @@ export class RepoMapper {
|
|
|
45
45
|
const tree = this.scanDirectory(this.rootPath, 0);
|
|
46
46
|
|
|
47
47
|
// Build the final map
|
|
48
|
-
let finalMap =
|
|
48
|
+
let finalMap = `--- ABSOLUTE WORKING DIRECTORY ---\n${this.rootPath}\n\n`;
|
|
49
|
+
|
|
50
|
+
if (!tree.children || tree.children.length === 0) {
|
|
51
|
+
finalMap += `⚠️ [CRITICAL WARNING]: THIS DIRECTORY IS COMPLETELY EMPTY.\nTHERE ARE NO FILES OR FOLDERS HERE.\nDO NOT HALLUCINATE ANY CONTENT!\n\n`;
|
|
52
|
+
} else {
|
|
53
|
+
finalMap += `--- DIRECTORY STRUCTURE ---\n` + this.formatTree(tree);
|
|
54
|
+
}
|
|
49
55
|
|
|
50
56
|
if (this.fileContents.size > 0) {
|
|
51
57
|
finalMap += '\n\n--- CRAWLED FILE CONTENTS ---\n';
|