hybard-agent 0.1.0
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/LICENSE +21 -0
- package/README.md +41 -0
- package/bin/.cmd +0 -0
- package/bin/agent.js +673 -0
- package/bin/cli.js +326 -0
- package/bin/hybard.cmd +2 -0
- package/knowledge/BENGALI_GUIDE.md +436 -0
- package/knowledge/CAPABILITIES.md +448 -0
- package/knowledge/INDEX.md +204 -0
- package/knowledge/KNOWLEDGE_BASE.md +1174 -0
- package/knowledge/README.md +97 -0
- package/knowledge/SYSTEM_PROMPT.md +310 -0
- package/lib/agent.js +730 -0
- package/lib/analyzer.js +330 -0
- package/lib/coding-agent.js +87 -0
- package/lib/coding-model.js +585 -0
- package/lib/engine.js +591 -0
- package/lib/hybard-agent.js +1063 -0
- package/lib/main.dart +32 -0
- package/lib/models.js +357 -0
- package/lib/online.js +654 -0
- package/lib/server.js +278 -0
- package/lib/ui.js +96 -0
- package/package.json +50 -0
|
@@ -0,0 +1,1063 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybard AI Agent — Complete working agent with real LLM integration.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Real LLM Provider Connection (Groq → Ollama → OpenRouter → OpenAI)
|
|
6
|
+
* - Codebase Understanding (search, find, understand code)
|
|
7
|
+
* - Project Detection System (scan, detect framework/language/deps)
|
|
8
|
+
* - Agent Loop (Think → Act → Observe → Evaluate → Fix)
|
|
9
|
+
* - Tool Calling (file ops, terminal, code search)
|
|
10
|
+
* - Knowledge Base awareness (35 domains)
|
|
11
|
+
* - Memory system (short-term, long-term)
|
|
12
|
+
* - Bengali + English multilingual support
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
const { spawnSync } = require('child_process');
|
|
18
|
+
const { HybardAI } = require('./online');
|
|
19
|
+
const { ProjectAnalyzer } = require('./analyzer');
|
|
20
|
+
|
|
21
|
+
// ─── Knowledge Base Loader ──────────────────────────────
|
|
22
|
+
|
|
23
|
+
class KnowledgeBase {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.kbPath = path.join(__dirname, '..', 'knowledge', 'KNOWLEDGE_BASE.md');
|
|
26
|
+
this.capabilitiesPath = path.join(__dirname, '..', 'knowledge', 'CAPABILITIES.md');
|
|
27
|
+
this.content = '';
|
|
28
|
+
this.capabilities = '';
|
|
29
|
+
this.load();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
load() {
|
|
33
|
+
try {
|
|
34
|
+
if (fs.existsSync(this.kbPath)) {
|
|
35
|
+
this.content = fs.readFileSync(this.kbPath, 'utf8');
|
|
36
|
+
}
|
|
37
|
+
if (fs.existsSync(this.capabilitiesPath)) {
|
|
38
|
+
this.capabilities = fs.readFileSync(this.capabilitiesPath, 'utf8');
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.error('Failed to load knowledge base:', e.message);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getDomainKnowledge(domain) {
|
|
46
|
+
const regex = new RegExp(`## ${domain}[\\s\\S]*?(?=\\n## \\d|$)`, 'i');
|
|
47
|
+
const match = this.content.match(regex);
|
|
48
|
+
return match ? match[0] : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getCapabilitiesSummary() {
|
|
52
|
+
return this.capabilities.slice(0, 3000);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ─── Memory System ──────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
class Memory {
|
|
59
|
+
constructor() {
|
|
60
|
+
this.shortTerm = [];
|
|
61
|
+
this.longTerm = [];
|
|
62
|
+
this.projectMemory = [];
|
|
63
|
+
this.memoryDir = path.join(require('os').homedir(), '.hybard', 'memory');
|
|
64
|
+
this.loadLongTerm();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
loadLongTerm() {
|
|
68
|
+
try {
|
|
69
|
+
const memoryFile = path.join(this.memoryDir, 'long-term.json');
|
|
70
|
+
if (fs.existsSync(memoryFile)) {
|
|
71
|
+
this.longTerm = JSON.parse(fs.readFileSync(memoryFile, 'utf8'));
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
saveLongTerm() {
|
|
77
|
+
try {
|
|
78
|
+
if (!fs.existsSync(this.memoryDir)) {
|
|
79
|
+
fs.mkdirSync(this.memoryDir, { recursive: true });
|
|
80
|
+
}
|
|
81
|
+
fs.writeFileSync(
|
|
82
|
+
path.join(this.memoryDir, 'long-term.json'),
|
|
83
|
+
JSON.stringify(this.longTerm, null, 2)
|
|
84
|
+
);
|
|
85
|
+
} catch (e) {}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
addShortTerm(role, content) {
|
|
89
|
+
this.shortTerm.push({ role, content, timestamp: Date.now() });
|
|
90
|
+
if (this.shortTerm.length > 20) {
|
|
91
|
+
this.shortTerm = this.shortTerm.slice(-20);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
addLongTerm(content, type = 'fact') {
|
|
96
|
+
this.longTerm.push({ content, type, timestamp: Date.now() });
|
|
97
|
+
this.saveLongTerm();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
addProjectMemory(content) {
|
|
101
|
+
this.projectMemory.push({ content, timestamp: Date.now() });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getConversationContext() {
|
|
105
|
+
return this.shortTerm.map(m => `${m.role}: ${m.content}`).join('\n');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
searchLongTerm(query) {
|
|
109
|
+
const lower = query.toLowerCase();
|
|
110
|
+
return this.longTerm.filter(m => m.content.toLowerCase().includes(lower));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ─── Project Detection System ───────────────────────────
|
|
115
|
+
|
|
116
|
+
class ProjectDetector {
|
|
117
|
+
constructor(rootDir = process.cwd()) {
|
|
118
|
+
this.rootDir = rootDir;
|
|
119
|
+
this.result = {
|
|
120
|
+
type: 'unknown',
|
|
121
|
+
languages: [],
|
|
122
|
+
frameworks: [],
|
|
123
|
+
dependencies: {},
|
|
124
|
+
configFiles: [],
|
|
125
|
+
entryPoints: [],
|
|
126
|
+
structure: {},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
detect() {
|
|
131
|
+
this.scanConfigFiles();
|
|
132
|
+
this.detectLanguage();
|
|
133
|
+
this.detectFramework();
|
|
134
|
+
this.detectDependencies();
|
|
135
|
+
this.detectEntryPoints();
|
|
136
|
+
this.buildStructure();
|
|
137
|
+
return this.result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
scanConfigFiles() {
|
|
141
|
+
const configFiles = [
|
|
142
|
+
'package.json', 'tsconfig.json', 'vite.config.js', 'vite.config.ts',
|
|
143
|
+
'next.config.js', 'next.config.ts', 'nuxt.config.js',
|
|
144
|
+
'requirements.txt', 'pyproject.toml', 'setup.py', 'Pipfile',
|
|
145
|
+
'pubspec.yaml', 'Cargo.toml', 'go.mod', 'pom.xml', 'build.gradle',
|
|
146
|
+
'Makefile', 'Dockerfile', 'docker-compose.yml', 'docker-compose.yaml',
|
|
147
|
+
'.eslintrc.js', '.prettierrc', 'tailwind.config.js', 'postcss.config.js',
|
|
148
|
+
'manage.py', 'app.json', 'eas.json',
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
for (const file of configFiles) {
|
|
152
|
+
const filePath = path.join(this.rootDir, file);
|
|
153
|
+
if (fs.existsSync(filePath)) {
|
|
154
|
+
this.result.configFiles.push(file);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
detectLanguage() {
|
|
160
|
+
const langMap = {
|
|
161
|
+
'.js': 'JavaScript', '.jsx': 'React JSX', '.ts': 'TypeScript', '.tsx': 'React TSX',
|
|
162
|
+
'.py': 'Python', '.dart': 'Dart', '.rs': 'Rust', '.go': 'Go',
|
|
163
|
+
'.java': 'Java', '.kt': 'Kotlin', '.swift': 'Swift',
|
|
164
|
+
'.html': 'HTML', '.css': 'CSS', '.scss': 'SCSS',
|
|
165
|
+
'.sql': 'SQL', '.sh': 'Shell',
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const counts = {};
|
|
169
|
+
const walk = (dir, depth = 0) => {
|
|
170
|
+
if (depth > 5) return;
|
|
171
|
+
try {
|
|
172
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
173
|
+
for (const item of items) {
|
|
174
|
+
if (item.isDirectory() && !item.name.startsWith('.') &&
|
|
175
|
+
item.name !== 'node_modules' && item.name !== '__pycache__') {
|
|
176
|
+
walk(path.join(dir, item.name), depth + 1);
|
|
177
|
+
} else if (item.isFile()) {
|
|
178
|
+
const ext = path.extname(item.name).toLowerCase();
|
|
179
|
+
if (langMap[ext]) {
|
|
180
|
+
counts[langMap[ext]] = (counts[langMap[ext]] || 0) + 1;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} catch (e) {}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
walk(this.rootDir);
|
|
188
|
+
this.result.languages = Object.entries(counts)
|
|
189
|
+
.sort((a, b) => b[1] - a[1])
|
|
190
|
+
.map(([lang]) => lang);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
detectFramework() {
|
|
194
|
+
const frameworks = [];
|
|
195
|
+
|
|
196
|
+
// Check package.json
|
|
197
|
+
const pkgPath = path.join(this.rootDir, 'package.json');
|
|
198
|
+
if (fs.existsSync(pkgPath)) {
|
|
199
|
+
try {
|
|
200
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
201
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
202
|
+
if (deps.react) frameworks.push('React');
|
|
203
|
+
if (deps.vue) frameworks.push('Vue');
|
|
204
|
+
if (deps.angular || deps['@angular/core']) frameworks.push('Angular');
|
|
205
|
+
if (deps.svelte) frameworks.push('Svelte');
|
|
206
|
+
if (deps.next) frameworks.push('Next.js');
|
|
207
|
+
if (deps.nuxt) frameworks.push('Nuxt');
|
|
208
|
+
if (deps.express) frameworks.push('Express');
|
|
209
|
+
if (deps.fastify) frameworks.push('Fastify');
|
|
210
|
+
if (deps.tailwindcss) frameworks.push('Tailwind CSS');
|
|
211
|
+
if (deps.typescript) frameworks.push('TypeScript');
|
|
212
|
+
if (deps.fastapi || deps['fastapi']) frameworks.push('FastAPI');
|
|
213
|
+
if (deps.flutter || deps.flutter_test) frameworks.push('Flutter');
|
|
214
|
+
} catch (e) {}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Check requirements.txt
|
|
218
|
+
const reqPath = path.join(this.rootDir, 'requirements.txt');
|
|
219
|
+
if (fs.existsSync(reqPath)) {
|
|
220
|
+
const req = fs.readFileSync(reqPath, 'utf8');
|
|
221
|
+
if (req.includes('Django')) frameworks.push('Django');
|
|
222
|
+
if (req.includes('flask') || req.includes('Flask')) frameworks.push('Flask');
|
|
223
|
+
if (req.includes('fastapi') || req.includes('FastAPI')) frameworks.push('FastAPI');
|
|
224
|
+
if (req.includes('torch')) frameworks.push('PyTorch');
|
|
225
|
+
if (req.includes('tensorflow')) frameworks.push('TensorFlow');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Check pubspec.yaml
|
|
229
|
+
if (fs.existsSync(path.join(this.rootDir, 'pubspec.yaml'))) {
|
|
230
|
+
frameworks.push('Flutter');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Check manage.py
|
|
234
|
+
if (fs.existsSync(path.join(this.rootDir, 'manage.py'))) {
|
|
235
|
+
frameworks.push('Django');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this.result.frameworks = frameworks;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
detectDependencies() {
|
|
242
|
+
const deps = {};
|
|
243
|
+
|
|
244
|
+
// Node.js dependencies
|
|
245
|
+
const pkgPath = path.join(this.rootDir, 'package.json');
|
|
246
|
+
if (fs.existsSync(pkgPath)) {
|
|
247
|
+
try {
|
|
248
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
249
|
+
deps.npm = {
|
|
250
|
+
dependencies: Object.keys(pkg.dependencies || {}),
|
|
251
|
+
devDependencies: Object.keys(pkg.devDependencies || {}),
|
|
252
|
+
};
|
|
253
|
+
} catch (e) {}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Python dependencies
|
|
257
|
+
const reqPath = path.join(this.rootDir, 'requirements.txt');
|
|
258
|
+
if (fs.existsSync(reqPath)) {
|
|
259
|
+
const req = fs.readFileSync(reqPath, 'utf8');
|
|
260
|
+
deps.pip = req.split('\n').filter(l => l.trim() && !l.startsWith('#'));
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.result.dependencies = deps;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
detectEntryPoints() {
|
|
267
|
+
const entryPoints = [];
|
|
268
|
+
|
|
269
|
+
// Common entry points
|
|
270
|
+
const commonEntries = [
|
|
271
|
+
'src/index.js', 'src/index.ts', 'src/main.js', 'src/main.ts',
|
|
272
|
+
'src/App.jsx', 'src/App.tsx', 'src/main.jsx', 'src/main.tsx',
|
|
273
|
+
'index.js', 'index.ts', 'main.js', 'main.ts',
|
|
274
|
+
'app.py', 'main.py', 'manage.py',
|
|
275
|
+
'lib/main.dart', 'lib/main.dart',
|
|
276
|
+
'server.js', 'server.ts',
|
|
277
|
+
'src/server.js', 'src/server.ts',
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
for (const entry of commonEntries) {
|
|
281
|
+
if (fs.existsSync(path.join(this.rootDir, entry))) {
|
|
282
|
+
entryPoints.push(entry);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
this.result.entryPoints = entryPoints;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
buildStructure() {
|
|
290
|
+
const structure = { dirs: [], files: [] };
|
|
291
|
+
const walk = (dir, prefix = '', depth = 0) => {
|
|
292
|
+
if (depth > 3) return;
|
|
293
|
+
try {
|
|
294
|
+
const items = fs.readdirSync(dir, { withFileTypes: true }).slice(0, 50);
|
|
295
|
+
for (const item of items) {
|
|
296
|
+
if (item.isDirectory() && !item.name.startsWith('.') &&
|
|
297
|
+
item.name !== 'node_modules' && item.name !== '__pycache__') {
|
|
298
|
+
structure.dirs.push(`${prefix}${item.name}/`);
|
|
299
|
+
walk(path.join(dir, item.name), `${prefix} `, depth + 1);
|
|
300
|
+
} else if (item.isFile()) {
|
|
301
|
+
structure.files.push(`${prefix}${item.name}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
} catch (e) {}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
walk(this.rootDir);
|
|
308
|
+
this.result.structure = structure;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
getSummary() {
|
|
312
|
+
const r = this.result;
|
|
313
|
+
let summary = `Project Type: ${r.type}\n`;
|
|
314
|
+
summary += `Languages: ${r.languages.join(', ') || 'Unknown'}\n`;
|
|
315
|
+
summary += `Frameworks: ${r.frameworks.join(', ') || 'None detected'}\n`;
|
|
316
|
+
summary += `Config Files: ${r.configFiles.length}\n`;
|
|
317
|
+
summary += `Entry Points: ${r.entryPoints.join(', ') || 'None found'}\n`;
|
|
318
|
+
return summary;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// ─── Codebase Search Engine ─────────────────────────────
|
|
323
|
+
|
|
324
|
+
class CodebaseSearch {
|
|
325
|
+
constructor(rootDir = process.cwd()) {
|
|
326
|
+
this.rootDir = rootDir;
|
|
327
|
+
this.ignoreDirs = ['node_modules', '.git', '__pycache__', '.venv', 'venv',
|
|
328
|
+
'.idea', '.vscode', 'dist', 'build', '.next', 'coverage'];
|
|
329
|
+
this.ignoreFiles = ['.gitignore', '.env', 'package-lock.json', 'yarn.lock',
|
|
330
|
+
'*.pyc', '*.pyo', 'db.sqlite3'];
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
searchCode(query) {
|
|
334
|
+
const results = [];
|
|
335
|
+
const walk = (dir, depth = 0) => {
|
|
336
|
+
if (depth > 8) return;
|
|
337
|
+
try {
|
|
338
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
339
|
+
for (const item of items) {
|
|
340
|
+
if (item.isDirectory() && !this.ignoreDirs.includes(item.name)) {
|
|
341
|
+
walk(path.join(dir, item.name), depth + 1);
|
|
342
|
+
} else if (item.isFile() && !this.shouldIgnore(item.name)) {
|
|
343
|
+
this.searchInFile(path.join(dir, item.name), query, results);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
} catch (e) {}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
walk(this.rootDir);
|
|
350
|
+
return results;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
searchInFile(filePath, query, results) {
|
|
354
|
+
try {
|
|
355
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
356
|
+
const lines = content.split('\n');
|
|
357
|
+
const matches = [];
|
|
358
|
+
|
|
359
|
+
lines.forEach((line, index) => {
|
|
360
|
+
if (line.toLowerCase().includes(query.toLowerCase())) {
|
|
361
|
+
matches.push({
|
|
362
|
+
line: index + 1,
|
|
363
|
+
content: line.trim(),
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
if (matches.length > 0) {
|
|
369
|
+
results.push({
|
|
370
|
+
file: path.relative(this.rootDir, filePath),
|
|
371
|
+
matches,
|
|
372
|
+
score: matches.length,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
} catch (e) {}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
findFiles(pattern) {
|
|
379
|
+
const results = [];
|
|
380
|
+
const walk = (dir, depth = 0) => {
|
|
381
|
+
if (depth > 8) return;
|
|
382
|
+
try {
|
|
383
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
384
|
+
for (const item of items) {
|
|
385
|
+
if (item.isDirectory() && !this.ignoreDirs.includes(item.name)) {
|
|
386
|
+
walk(path.join(dir, item.name), depth + 1);
|
|
387
|
+
} else if (item.isFile() && item.name.includes(pattern)) {
|
|
388
|
+
results.push(path.relative(this.rootDir, path.join(dir, item.name)));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} catch (e) {}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
walk(this.rootDir);
|
|
395
|
+
return results;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
findFunctions(query) {
|
|
399
|
+
const results = [];
|
|
400
|
+
const functionPatterns = [
|
|
401
|
+
/function\s+(\w+)/g,
|
|
402
|
+
/const\s+(\w+)\s*=\s*(?:async\s+)?\(/g,
|
|
403
|
+
/def\s+(\w+)/g,
|
|
404
|
+
/class\s+(\w+)/g,
|
|
405
|
+
/export\s+(?:default\s+)?(?:function|const|class)\s+(\w+)/g,
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
const walk = (dir, depth = 0) => {
|
|
409
|
+
if (depth > 8) return;
|
|
410
|
+
try {
|
|
411
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
412
|
+
for (const item of items) {
|
|
413
|
+
if (item.isDirectory() && !this.ignoreDirs.includes(item.name)) {
|
|
414
|
+
walk(path.join(dir, item.name), depth + 1);
|
|
415
|
+
} else if (item.isFile() && !this.shouldIgnore(item.name)) {
|
|
416
|
+
this.findInFile(path.join(dir, item.name), query, functionPatterns, results);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
} catch (e) {}
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
walk(this.rootDir);
|
|
423
|
+
return results;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
findInFile(filePath, query, patterns, results) {
|
|
427
|
+
try {
|
|
428
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
429
|
+
const lines = content.split('\n');
|
|
430
|
+
|
|
431
|
+
lines.forEach((line, index) => {
|
|
432
|
+
if (line.toLowerCase().includes(query.toLowerCase())) {
|
|
433
|
+
for (const pattern of patterns) {
|
|
434
|
+
const match = line.match(pattern);
|
|
435
|
+
if (match) {
|
|
436
|
+
results.push({
|
|
437
|
+
file: path.relative(this.rootDir, filePath),
|
|
438
|
+
line: index + 1,
|
|
439
|
+
name: match[1] || match[0],
|
|
440
|
+
content: line.trim(),
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
} catch (e) {}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
shouldIgnore(filename) {
|
|
450
|
+
return this.ignoreFiles.some(pattern => {
|
|
451
|
+
if (pattern.startsWith('*')) return filename.endsWith(pattern.slice(1));
|
|
452
|
+
return filename === pattern;
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
getContextForQuery(query) {
|
|
457
|
+
const codeResults = this.searchCode(query);
|
|
458
|
+
const fileResults = this.findFiles(query);
|
|
459
|
+
const funcResults = this.findFunctions(query);
|
|
460
|
+
|
|
461
|
+
let context = `Search Results for "${query}":\n\n`;
|
|
462
|
+
|
|
463
|
+
if (codeResults.length > 0) {
|
|
464
|
+
context += `Code Matches (${codeResults.length} files):\n`;
|
|
465
|
+
for (const result of codeResults.slice(0, 5)) {
|
|
466
|
+
context += ` ${result.file}:\n`;
|
|
467
|
+
for (const match of result.matches.slice(0, 3)) {
|
|
468
|
+
context += ` Line ${match.line}: ${match.content}\n`;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (fileResults.length > 0) {
|
|
474
|
+
context += `\nFiles Found (${fileResults.length}):\n`;
|
|
475
|
+
for (const file of fileResults.slice(0, 10)) {
|
|
476
|
+
context += ` ${file}\n`;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (funcResults.length > 0) {
|
|
481
|
+
context += `\nFunctions Found (${funcResults.length}):\n`;
|
|
482
|
+
for (const func of funcResults.slice(0, 10)) {
|
|
483
|
+
context += ` ${func.name} in ${func.file}:${func.line}\n`;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return context;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// ─── Tool System ────────────────────────────────────────
|
|
492
|
+
|
|
493
|
+
class ToolSystem {
|
|
494
|
+
constructor(rootDir = process.cwd()) {
|
|
495
|
+
this.rootDir = rootDir;
|
|
496
|
+
this.projectDetector = new ProjectDetector(rootDir);
|
|
497
|
+
this.codebaseSearch = new CodebaseSearch(rootDir);
|
|
498
|
+
this.projectInfo = null;
|
|
499
|
+
|
|
500
|
+
this.tools = {
|
|
501
|
+
read_file: this.readFile.bind(this),
|
|
502
|
+
write_file: this.writeFile.bind(this),
|
|
503
|
+
edit_file: this.editFile.bind(this),
|
|
504
|
+
delete_file: this.deleteFile.bind(this),
|
|
505
|
+
create_file: this.createFile.bind(this),
|
|
506
|
+
list_directory: this.listDirectory.bind(this),
|
|
507
|
+
search_files: this.searchFiles.bind(this),
|
|
508
|
+
search_text: this.searchText.bind(this),
|
|
509
|
+
search_code: this.searchCode.bind(this),
|
|
510
|
+
find_functions: this.findFunctions.bind(this),
|
|
511
|
+
run_command: this.runCommand.bind(this),
|
|
512
|
+
get_project_info: this.getProjectInfo.bind(this),
|
|
513
|
+
detect_project: this.detectProject.bind(this),
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
readFile(filePath) {
|
|
518
|
+
try {
|
|
519
|
+
const absPath = path.resolve(this.rootDir, filePath);
|
|
520
|
+
if (!fs.existsSync(absPath)) {
|
|
521
|
+
return { success: false, error: `File not found: ${filePath}` };
|
|
522
|
+
}
|
|
523
|
+
const content = fs.readFileSync(absPath, 'utf8');
|
|
524
|
+
return { success: true, content, path: absPath };
|
|
525
|
+
} catch (e) {
|
|
526
|
+
return { success: false, error: e.message };
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
writeFile(filePath, content) {
|
|
531
|
+
try {
|
|
532
|
+
const absPath = path.resolve(this.rootDir, filePath);
|
|
533
|
+
const dir = path.dirname(absPath);
|
|
534
|
+
if (!fs.existsSync(dir)) {
|
|
535
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
536
|
+
}
|
|
537
|
+
fs.writeFileSync(absPath, content, 'utf8');
|
|
538
|
+
return { success: true, path: absPath };
|
|
539
|
+
} catch (e) {
|
|
540
|
+
return { success: false, error: e.message };
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
createFile(filePath, content) {
|
|
545
|
+
const absPath = path.resolve(this.rootDir, filePath);
|
|
546
|
+
if (fs.existsSync(absPath)) {
|
|
547
|
+
return { success: false, error: `File already exists: ${filePath}` };
|
|
548
|
+
}
|
|
549
|
+
return this.writeFile(filePath, content);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
editFile(filePath, oldContent, newContent) {
|
|
553
|
+
try {
|
|
554
|
+
const absPath = path.resolve(this.rootDir, filePath);
|
|
555
|
+
if (!fs.existsSync(absPath)) {
|
|
556
|
+
return { success: false, error: `File not found: ${filePath}` };
|
|
557
|
+
}
|
|
558
|
+
let content = fs.readFileSync(absPath, 'utf8');
|
|
559
|
+
if (!content.includes(oldContent)) {
|
|
560
|
+
return { success: false, error: 'Old content not found in file' };
|
|
561
|
+
}
|
|
562
|
+
content = content.replace(oldContent, newContent);
|
|
563
|
+
fs.writeFileSync(absPath, content, 'utf8');
|
|
564
|
+
return { success: true, path: absPath };
|
|
565
|
+
} catch (e) {
|
|
566
|
+
return { success: false, error: e.message };
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
deleteFile(filePath) {
|
|
571
|
+
try {
|
|
572
|
+
const absPath = path.resolve(this.rootDir, filePath);
|
|
573
|
+
if (!fs.existsSync(absPath)) {
|
|
574
|
+
return { success: false, error: `File not found: ${filePath}` };
|
|
575
|
+
}
|
|
576
|
+
fs.unlinkSync(absPath);
|
|
577
|
+
return { success: true, path: absPath };
|
|
578
|
+
} catch (e) {
|
|
579
|
+
return { success: false, error: e.message };
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
listDirectory(dirPath = '.') {
|
|
584
|
+
try {
|
|
585
|
+
const absPath = path.resolve(this.rootDir, dirPath);
|
|
586
|
+
const items = fs.readdirSync(absPath, { withFileTypes: true });
|
|
587
|
+
const result = items.map(item => ({
|
|
588
|
+
name: item.name,
|
|
589
|
+
type: item.isDirectory() ? 'directory' : 'file',
|
|
590
|
+
path: path.join(absPath, item.name),
|
|
591
|
+
}));
|
|
592
|
+
return { success: true, items: result, path: absPath };
|
|
593
|
+
} catch (e) {
|
|
594
|
+
return { success: false, error: e.message };
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
searchFiles(pattern) {
|
|
599
|
+
try {
|
|
600
|
+
const results = this.codebaseSearch.findFiles(pattern);
|
|
601
|
+
return { success: true, results, count: results.length };
|
|
602
|
+
} catch (e) {
|
|
603
|
+
return { success: false, error: e.message };
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
searchText(searchText, filePath) {
|
|
608
|
+
try {
|
|
609
|
+
const absPath = path.resolve(this.rootDir, filePath);
|
|
610
|
+
if (!fs.existsSync(absPath)) {
|
|
611
|
+
return { success: false, error: `File not found: ${filePath}` };
|
|
612
|
+
}
|
|
613
|
+
const content = fs.readFileSync(absPath, 'utf8');
|
|
614
|
+
const lines = content.split('\n');
|
|
615
|
+
const matches = [];
|
|
616
|
+
|
|
617
|
+
lines.forEach((line, index) => {
|
|
618
|
+
if (line.toLowerCase().includes(searchText.toLowerCase())) {
|
|
619
|
+
matches.push({ line: index + 1, content: line.trim() });
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
return { success: true, matches, count: matches.length };
|
|
624
|
+
} catch (e) {
|
|
625
|
+
return { success: false, error: e.message };
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
searchCode(query) {
|
|
630
|
+
try {
|
|
631
|
+
const results = this.codebaseSearch.searchCode(query);
|
|
632
|
+
return { success: true, results, count: results.length };
|
|
633
|
+
} catch (e) {
|
|
634
|
+
return { success: false, error: e.message };
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
findFunctions(query) {
|
|
639
|
+
try {
|
|
640
|
+
const results = this.codebaseSearch.findFunctions(query);
|
|
641
|
+
return { success: true, results, count: results.length };
|
|
642
|
+
} catch (e) {
|
|
643
|
+
return { success: false, error: e.message };
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
runCommand(command, options = {}) {
|
|
648
|
+
try {
|
|
649
|
+
const result = spawnSync(command, {
|
|
650
|
+
shell: true,
|
|
651
|
+
encoding: 'utf8',
|
|
652
|
+
cwd: options.cwd || this.rootDir,
|
|
653
|
+
timeout: options.timeout || 30000,
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
return {
|
|
657
|
+
success: result.status === 0,
|
|
658
|
+
stdout: result.stdout,
|
|
659
|
+
stderr: result.stderr,
|
|
660
|
+
exitCode: result.status,
|
|
661
|
+
};
|
|
662
|
+
} catch (e) {
|
|
663
|
+
return { success: false, error: e.message };
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
getProjectInfo() {
|
|
668
|
+
if (!this.projectInfo) {
|
|
669
|
+
this.projectInfo = this.projectDetector.detect();
|
|
670
|
+
}
|
|
671
|
+
return this.projectInfo;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
detectProject() {
|
|
675
|
+
this.projectInfo = this.projectDetector.detect();
|
|
676
|
+
return this.projectInfo;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// ─── Main Agent ─────────────────────────────────────────
|
|
681
|
+
|
|
682
|
+
class HybardAgent {
|
|
683
|
+
constructor(options = {}) {
|
|
684
|
+
this.ai = new HybardAI(options);
|
|
685
|
+
this.knowledge = new KnowledgeBase();
|
|
686
|
+
this.memory = new Memory();
|
|
687
|
+
this.tools = new ToolSystem(options.rootDir || process.cwd());
|
|
688
|
+
this.maxIterations = 10;
|
|
689
|
+
this.conversationHistory = [];
|
|
690
|
+
|
|
691
|
+
this.systemPrompt = this.buildSystemPrompt();
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
buildSystemPrompt() {
|
|
695
|
+
return `You are Hybard AI Agent — a full-stack AI coding assistant with real LLM integration.
|
|
696
|
+
|
|
697
|
+
IDENTITY:
|
|
698
|
+
- You are an expert coding agent with knowledge across 35 domains
|
|
699
|
+
- You can write, read, edit, and delete files
|
|
700
|
+
- You can execute terminal commands
|
|
701
|
+
- You can search and understand codebases
|
|
702
|
+
- You can build complete applications
|
|
703
|
+
- You support Bengali (বাংলা) and English
|
|
704
|
+
|
|
705
|
+
AGENT LOOP:
|
|
706
|
+
When given a task, follow this loop:
|
|
707
|
+
1. THINK — Understand the request, plan your approach
|
|
708
|
+
2. ACT — Execute tools (read files, write code, run commands)
|
|
709
|
+
3. OBSERVE — Check results, read errors
|
|
710
|
+
4. EVALUATE — Did it work? If yes, done. If no, fix.
|
|
711
|
+
5. FIX — Generate a fix and retry
|
|
712
|
+
|
|
713
|
+
TOOL CALLING:
|
|
714
|
+
You can use these tools by outputting JSON in your response:
|
|
715
|
+
{"tool": "read_file", "path": "file.js"}
|
|
716
|
+
{"tool": "write_file", "path": "file.js", "content": "..."}
|
|
717
|
+
{"tool": "edit_file", "path": "file.js", "old": "...", "new": "..."}
|
|
718
|
+
{"tool": "run_command", "command": "npm install"}
|
|
719
|
+
{"tool": "list_directory", "path": "."}
|
|
720
|
+
{"tool": "search_files", "pattern": ".js"}
|
|
721
|
+
{"tool": "search_text", "text": "function", "file": "index.js"}
|
|
722
|
+
{"tool": "search_code", "query": "login"}
|
|
723
|
+
{"tool": "find_functions", "query": "authenticate"}
|
|
724
|
+
{"tool": "get_project_info"}
|
|
725
|
+
{"tool": "detect_project"}
|
|
726
|
+
|
|
727
|
+
CODEBASE UNDERSTANDING:
|
|
728
|
+
When asked about code, use these tools:
|
|
729
|
+
- search_code: Find code by keyword
|
|
730
|
+
- find_functions: Find function definitions
|
|
731
|
+
- read_file: Read specific files
|
|
732
|
+
- get_project_info: Understand project structure
|
|
733
|
+
|
|
734
|
+
PROJECT DETECTION:
|
|
735
|
+
Your agent automatically detects:
|
|
736
|
+
- Project type (Node, Python, React, Flutter, etc.)
|
|
737
|
+
- Languages used
|
|
738
|
+
- Frameworks and libraries
|
|
739
|
+
- Dependencies
|
|
740
|
+
- Entry points
|
|
741
|
+
- File structure
|
|
742
|
+
|
|
743
|
+
RESPONSE FORMAT:
|
|
744
|
+
- Always respond in the user's language
|
|
745
|
+
- Keep code comments in English
|
|
746
|
+
- Use markdown code blocks for code
|
|
747
|
+
- Be concise but thorough
|
|
748
|
+
- Always verify your work
|
|
749
|
+
|
|
750
|
+
IMPORTANT:
|
|
751
|
+
- Never hardcode secrets or API keys
|
|
752
|
+
- Always handle errors gracefully
|
|
753
|
+
- Write clean, readable code
|
|
754
|
+
- Test your code when possible`;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
async chat(message) {
|
|
758
|
+
// Add to memory
|
|
759
|
+
this.memory.addShortTerm('user', message);
|
|
760
|
+
this.conversationHistory.push({ role: 'user', content: message });
|
|
761
|
+
|
|
762
|
+
// Build context
|
|
763
|
+
const context = this.buildContext(message);
|
|
764
|
+
|
|
765
|
+
// Call AI with real LLM
|
|
766
|
+
let response;
|
|
767
|
+
try {
|
|
768
|
+
response = await this.ai.chat(message, {
|
|
769
|
+
system: this.systemPrompt + '\n\n' + context,
|
|
770
|
+
});
|
|
771
|
+
} catch (err) {
|
|
772
|
+
console.error(`[LLM Error] ${err.message}`);
|
|
773
|
+
response = `[Error] Failed to get response from AI: ${err.message}`;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Process response and execute tools
|
|
777
|
+
const result = await this.processResponse(response);
|
|
778
|
+
|
|
779
|
+
// Add to memory
|
|
780
|
+
this.memory.addShortTerm('assistant', result);
|
|
781
|
+
this.conversationHistory.push({ role: 'assistant', content: result });
|
|
782
|
+
|
|
783
|
+
return result;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
buildContext(message) {
|
|
787
|
+
let context = '';
|
|
788
|
+
|
|
789
|
+
// Add project info
|
|
790
|
+
try {
|
|
791
|
+
const projectInfo = this.tools.getProjectInfo();
|
|
792
|
+
context += `\n\nPROJECT CONTEXT:\n`;
|
|
793
|
+
context += `Type: ${projectInfo.type}\n`;
|
|
794
|
+
context += `Languages: ${projectInfo.languages.join(', ')}\n`;
|
|
795
|
+
context += `Frameworks: ${projectInfo.frameworks.join(', ')}\n`;
|
|
796
|
+
context += `Entry Points: ${projectInfo.entryPoints.join(', ')}\n`;
|
|
797
|
+
context += `Config Files: ${projectInfo.configFiles.join(', ')}`;
|
|
798
|
+
} catch (e) {}
|
|
799
|
+
|
|
800
|
+
// Add codebase search if query looks like code-related
|
|
801
|
+
if (this.isCodeRelated(message)) {
|
|
802
|
+
try {
|
|
803
|
+
const searchContext = this.tools.codebaseSearch.getContextForQuery(message);
|
|
804
|
+
if (searchContext.length > 50) {
|
|
805
|
+
context += `\n\nCODEBASE SEARCH:\n${searchContext}`;
|
|
806
|
+
}
|
|
807
|
+
} catch (e) {}
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// Add conversation history
|
|
811
|
+
if (this.conversationHistory.length > 0) {
|
|
812
|
+
context += '\n\nCONVERSATION HISTORY:\n';
|
|
813
|
+
context += this.conversationHistory.slice(-5).map(m =>
|
|
814
|
+
`${m.role}: ${m.content.slice(0, 200)}`
|
|
815
|
+
).join('\n');
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return context;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
isCodeRelated(message) {
|
|
822
|
+
const codeKeywords = [
|
|
823
|
+
'code', 'function', 'class', 'file', 'module', 'import', 'export',
|
|
824
|
+
'component', 'hook', 'api', 'endpoint', 'route', 'controller',
|
|
825
|
+
'model', 'view', 'service', 'auth', 'login', 'user', 'test',
|
|
826
|
+
'build', 'deploy', 'fix', 'bug', 'error', 'debug',
|
|
827
|
+
];
|
|
828
|
+
const lower = message.toLowerCase();
|
|
829
|
+
return codeKeywords.some(kw => lower.includes(kw));
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
async processResponse(response) {
|
|
833
|
+
let finalResponse = response;
|
|
834
|
+
let iterations = 0;
|
|
835
|
+
|
|
836
|
+
// Check for tool calls in response
|
|
837
|
+
const toolCallRegex = /\{"tool":\s*"[^"]+?"[^}]*\}/g;
|
|
838
|
+
const toolCalls = response.match(toolCallRegex);
|
|
839
|
+
|
|
840
|
+
if (toolCalls && toolCalls.length > 0) {
|
|
841
|
+
for (const toolCallStr of toolCalls) {
|
|
842
|
+
try {
|
|
843
|
+
const toolCall = JSON.parse(toolCallStr);
|
|
844
|
+
console.log(`[Tool] Executing: ${toolCall.tool}...`);
|
|
845
|
+
|
|
846
|
+
const result = this.executeTool(toolCall);
|
|
847
|
+
|
|
848
|
+
// Add tool result to context
|
|
849
|
+
finalResponse += `\n\nTool Result (${toolCall.tool}):\n${JSON.stringify(result, null, 2)}`;
|
|
850
|
+
|
|
851
|
+
// If tool failed, ask AI to fix
|
|
852
|
+
if (!result.success && iterations < this.maxIterations) {
|
|
853
|
+
const fixPrompt = `The tool call failed. Error: ${result.error}\n\nPlease fix the issue and try again.`;
|
|
854
|
+
const fixResponse = await this.ai.chat(fixPrompt, {
|
|
855
|
+
system: this.systemPrompt,
|
|
856
|
+
});
|
|
857
|
+
finalResponse += '\n\n' + fixResponse;
|
|
858
|
+
iterations++;
|
|
859
|
+
}
|
|
860
|
+
} catch (e) {
|
|
861
|
+
finalResponse += `\n\nTool Error: ${e.message}`;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
return finalResponse;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
executeTool(toolCall) {
|
|
870
|
+
const { tool, ...params } = toolCall;
|
|
871
|
+
|
|
872
|
+
if (!this.tools.tools[tool]) {
|
|
873
|
+
return { success: false, error: `Unknown tool: ${tool}` };
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
return this.tools.tools[tool](...Object.values(params));
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// ─── Quick Methods ──────────────────────────────────
|
|
880
|
+
|
|
881
|
+
async generateCode(prompt, language = 'python') {
|
|
882
|
+
const system = `You are an expert ${language} programmer. Generate clean, working code.
|
|
883
|
+
Respond ONLY with the code in a markdown code block. No explanations unless asked.`;
|
|
884
|
+
return this.ai.chat(prompt, { system });
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
async fixCode(code, error = '') {
|
|
888
|
+
const system = `You are a debugging expert. Fix the given code.
|
|
889
|
+
Return ONLY the corrected code in a markdown code block.`;
|
|
890
|
+
const msg = error
|
|
891
|
+
? `Fix this code. Error: ${error}\n\n\`\`\`\n${code}\n\`\`\``
|
|
892
|
+
: `Fix any issues in this code:\n\n\`\`\`\n${code}\n\`\`\``;
|
|
893
|
+
return this.ai.chat(msg, { system });
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
async explainCode(code) {
|
|
897
|
+
const system = `You are a code analysis expert. Explain the given code clearly and concisely.
|
|
898
|
+
Cover: what it does, how it works, key patterns, and potential issues.`;
|
|
899
|
+
return this.ai.chat(`Explain this code:\n\n\`\`\`\n${code}\n\`\`\``, { system });
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
async createProject(goal) {
|
|
903
|
+
const system = `You are a project scaffolding expert. Create project files for the given goal.
|
|
904
|
+
Return each file in this format:
|
|
905
|
+
## filename.ext
|
|
906
|
+
\`\`\`language
|
|
907
|
+
file content here
|
|
908
|
+
\`\`\`
|
|
909
|
+
Include all necessary files (main code, config, tests, README).`;
|
|
910
|
+
return this.ai.chat(`Create a project scaffold for: ${goal}`, { system });
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
async writeCodeToFile(code, filePath) {
|
|
914
|
+
const result = this.tools.writeFile(filePath, code);
|
|
915
|
+
if (result.success) {
|
|
916
|
+
return `File created successfully: ${filePath}`;
|
|
917
|
+
} else {
|
|
918
|
+
return `Failed to create file: ${result.error}`;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
async runCommand(command) {
|
|
923
|
+
const result = this.tools.runCommand(command);
|
|
924
|
+
if (result.success) {
|
|
925
|
+
return `Command executed successfully:\n${result.stdout}`;
|
|
926
|
+
} else {
|
|
927
|
+
return `Command failed:\n${result.stderr || result.error}`;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
// ─── Interactive Chat ──────────────────────────────
|
|
932
|
+
|
|
933
|
+
async startChat() {
|
|
934
|
+
const readline = require('readline');
|
|
935
|
+
const rl = readline.createInterface({
|
|
936
|
+
input: process.stdin,
|
|
937
|
+
output: process.stdout,
|
|
938
|
+
prompt: 'Hybard > ',
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
console.log('\n╔════════════════════════════════════════════════════════════╗');
|
|
942
|
+
console.log('║ Hybard AI Agent — Full Stack ║');
|
|
943
|
+
console.log('║ ║');
|
|
944
|
+
console.log('║ Connected to Real LLM Provider ║');
|
|
945
|
+
console.log('║ Codebase Understanding Active ║');
|
|
946
|
+
console.log('║ Project Detection Active ║');
|
|
947
|
+
console.log('║ ║');
|
|
948
|
+
console.log('║ Commands: ║');
|
|
949
|
+
console.log('║ /help — Show capabilities ║');
|
|
950
|
+
console.log('║ /clear — Clear conversation ║');
|
|
951
|
+
console.log('║ /memory — Show memory ║');
|
|
952
|
+
console.log('║ /project — Show project info ║');
|
|
953
|
+
console.log('║ /search — Search codebase ║');
|
|
954
|
+
console.log('║ exit — Exit ║');
|
|
955
|
+
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
|
956
|
+
|
|
957
|
+
// Show connection status
|
|
958
|
+
if (!this.ai.getApiKey()) {
|
|
959
|
+
console.log('⚠ No API key detected. Running in offline mode.');
|
|
960
|
+
} else {
|
|
961
|
+
console.log(`✓ Connected to ${this.ai.config.provider} (${this.ai.config.model})`);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// Show project info
|
|
965
|
+
try {
|
|
966
|
+
const info = this.tools.getProjectInfo();
|
|
967
|
+
console.log(`✓ Project detected: ${info.languages.join(', ')} with ${info.frameworks.join(', ')}`);
|
|
968
|
+
} catch (e) {
|
|
969
|
+
console.log('ℹ No project detected in current directory');
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
console.log('');
|
|
973
|
+
|
|
974
|
+
rl.prompt();
|
|
975
|
+
|
|
976
|
+
rl.on('line', async (line) => {
|
|
977
|
+
const text = line.trim();
|
|
978
|
+
if (!text) { rl.prompt(); return; }
|
|
979
|
+
|
|
980
|
+
if (text.toLowerCase() === 'exit' || text.toLowerCase() === 'quit') {
|
|
981
|
+
console.log('\nGoodbye!\n');
|
|
982
|
+
rl.close();
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
if (text === '/help') {
|
|
987
|
+
console.log('\n' + this.knowledge.getCapabilitiesSummary());
|
|
988
|
+
rl.prompt();
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
if (text === '/clear') {
|
|
993
|
+
this.conversationHistory = [];
|
|
994
|
+
this.memory.shortTerm = [];
|
|
995
|
+
console.log('Conversation cleared.\n');
|
|
996
|
+
rl.prompt();
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
if (text === '/memory') {
|
|
1001
|
+
console.log('\nShort-term memory:');
|
|
1002
|
+
this.memory.shortTerm.forEach(m => {
|
|
1003
|
+
console.log(` ${m.role}: ${m.content.slice(0, 100)}`);
|
|
1004
|
+
});
|
|
1005
|
+
console.log('\nLong-term memory:');
|
|
1006
|
+
this.memory.longTerm.forEach(m => {
|
|
1007
|
+
console.log(` [${m.type}] ${m.content.slice(0, 100)}`);
|
|
1008
|
+
});
|
|
1009
|
+
rl.prompt();
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
if (text === '/project') {
|
|
1014
|
+
try {
|
|
1015
|
+
const info = this.tools.detectProject();
|
|
1016
|
+
console.log('\nProject Info:');
|
|
1017
|
+
console.log(` Type: ${info.type}`);
|
|
1018
|
+
console.log(` Languages: ${info.languages.join(', ')}`);
|
|
1019
|
+
console.log(` Frameworks: ${info.frameworks.join(', ')}`);
|
|
1020
|
+
console.log(` Entry Points: ${info.entryPoints.join(', ')}`);
|
|
1021
|
+
console.log(` Config Files: ${info.configFiles.join(', ')}`);
|
|
1022
|
+
} catch (e) {
|
|
1023
|
+
console.log('No project detected in current directory.');
|
|
1024
|
+
}
|
|
1025
|
+
rl.prompt();
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
if (text.startsWith('/search ')) {
|
|
1030
|
+
const query = text.slice(8);
|
|
1031
|
+
console.log(`\nSearching codebase for "${query}"...`);
|
|
1032
|
+
const results = this.tools.codebaseSearch.searchCode(query);
|
|
1033
|
+
if (results.length === 0) {
|
|
1034
|
+
console.log('No results found.');
|
|
1035
|
+
} else {
|
|
1036
|
+
console.log(`Found ${results.length} files:\n`);
|
|
1037
|
+
for (const result of results.slice(0, 10)) {
|
|
1038
|
+
console.log(` ${result.file}:`);
|
|
1039
|
+
for (const match of result.matches.slice(0, 3)) {
|
|
1040
|
+
console.log(` Line ${match.line}: ${match.content}`);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
rl.prompt();
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// Normal message — send to AI
|
|
1049
|
+
process.stdout.write('\nHybard > Thinking...\n');
|
|
1050
|
+
try {
|
|
1051
|
+
const response = await this.chat(text);
|
|
1052
|
+
console.log('\n' + response + '\n');
|
|
1053
|
+
} catch (err) {
|
|
1054
|
+
console.error(`\n[Error] ${err.message}\n`);
|
|
1055
|
+
}
|
|
1056
|
+
rl.prompt();
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
rl.on('close', () => process.exit(0));
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
module.exports = { HybardAgent, KnowledgeBase, Memory, ToolSystem, ProjectDetector, CodebaseSearch };
|