vibe-weaver 1.0.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/README.md +229 -0
- package/bin/vibe-weaver.js +8 -0
- package/dist/bin/vibe-weaver.js +8 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2018 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
- package/src/agent/deploy.ts +171 -0
- package/src/agent/swarm.ts +167 -0
- package/src/agent/tasks.ts +205 -0
- package/src/auth/index.ts +273 -0
- package/src/config/index.ts +86 -0
- package/src/index.ts +797 -0
- package/src/lib/code-parser.ts +150 -0
- package/src/lib/vibe-api.ts +197 -0
- package/src/mcp/index.ts +273 -0
- package/src/tools/files.ts +279 -0
- package/src/tools/git.ts +498 -0
- package/src/tools/shell.ts +248 -0
- package/tsconfig.json +25 -0
- package/tsup.config.ts +34 -0
- package/vibe-weaver-1.0.0.tgz +0 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Tools for Vibe-Weaver CLI
|
|
3
|
+
* Real file operations against local filesystem
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs/promises';
|
|
7
|
+
import path from 'path';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { generateDiff } from '../lib/code-parser.js';
|
|
10
|
+
|
|
11
|
+
export interface FileOperationResult {
|
|
12
|
+
success: boolean;
|
|
13
|
+
path: string;
|
|
14
|
+
content?: string;
|
|
15
|
+
error?: string;
|
|
16
|
+
diff?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Read a file from the local filesystem
|
|
21
|
+
*/
|
|
22
|
+
export async function readFile(filePath: string, baseDir: string = process.cwd()): Promise<FileOperationResult> {
|
|
23
|
+
try {
|
|
24
|
+
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(baseDir, filePath);
|
|
25
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
26
|
+
return { success: true, path: filePath, content };
|
|
27
|
+
} catch (error) {
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
path: filePath,
|
|
31
|
+
error: error instanceof Error ? error.message : String(error)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Write content to a file
|
|
38
|
+
*/
|
|
39
|
+
export async function writeFile(
|
|
40
|
+
filePath: string,
|
|
41
|
+
content: string,
|
|
42
|
+
baseDir: string = process.cwd(),
|
|
43
|
+
options: { dryRun?: boolean; showDiff?: boolean } = {}
|
|
44
|
+
): Promise<FileOperationResult> {
|
|
45
|
+
try {
|
|
46
|
+
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(baseDir, filePath);
|
|
47
|
+
const dir = path.dirname(fullPath);
|
|
48
|
+
|
|
49
|
+
// Check if file exists for diff
|
|
50
|
+
let oldContent = '';
|
|
51
|
+
try {
|
|
52
|
+
oldContent = await fs.readFile(fullPath, 'utf-8');
|
|
53
|
+
} catch {
|
|
54
|
+
// File doesn't exist, that's fine
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Generate diff if requested and file exists
|
|
58
|
+
const diff = options.showDiff && oldContent ? generateDiff(oldContent, content, filePath) : undefined;
|
|
59
|
+
|
|
60
|
+
if (options.dryRun) {
|
|
61
|
+
return { success: true, path: filePath, diff };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Create directory if needed
|
|
65
|
+
await fs.mkdir(dir, { recursive: true });
|
|
66
|
+
|
|
67
|
+
// Write file
|
|
68
|
+
await fs.writeFile(fullPath, content, 'utf-8');
|
|
69
|
+
|
|
70
|
+
return { success: true, path: filePath, diff };
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return {
|
|
73
|
+
success: false,
|
|
74
|
+
path: filePath,
|
|
75
|
+
error: error instanceof Error ? error.message : String(error)
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Edit a file using search/replace
|
|
82
|
+
*/
|
|
83
|
+
export async function editFile(
|
|
84
|
+
filePath: string,
|
|
85
|
+
search: string,
|
|
86
|
+
replace: string,
|
|
87
|
+
baseDir: string = process.cwd(),
|
|
88
|
+
options: { dryRun?: boolean; showDiff?: boolean } = {}
|
|
89
|
+
): Promise<FileOperationResult> {
|
|
90
|
+
try {
|
|
91
|
+
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(baseDir, filePath);
|
|
92
|
+
|
|
93
|
+
// Read current content
|
|
94
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
95
|
+
|
|
96
|
+
// Check if search string exists
|
|
97
|
+
if (!content.includes(search)) {
|
|
98
|
+
return {
|
|
99
|
+
success: false,
|
|
100
|
+
path: filePath,
|
|
101
|
+
error: `Search string not found in file`
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Generate diff
|
|
106
|
+
const newContent = content.replace(search, replace);
|
|
107
|
+
const diff = options.showDiff ? generateDiff(content, newContent, filePath) : undefined;
|
|
108
|
+
|
|
109
|
+
if (options.dryRun) {
|
|
110
|
+
return { success: true, path: filePath, diff };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Write updated content
|
|
114
|
+
await fs.writeFile(fullPath, newContent, 'utf-8');
|
|
115
|
+
|
|
116
|
+
return { success: true, path: filePath, diff };
|
|
117
|
+
} catch (error) {
|
|
118
|
+
return {
|
|
119
|
+
success: false,
|
|
120
|
+
path: filePath,
|
|
121
|
+
error: error instanceof Error ? error.message : String(error)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* List files in a directory
|
|
128
|
+
*/
|
|
129
|
+
export async function listDir(dirPath: string = '.', baseDir: string = process.cwd()): Promise<{ path: string; isDirectory: boolean }[]> {
|
|
130
|
+
const fullPath = path.isAbsolute(dirPath) ? dirPath : path.join(baseDir, dirPath);
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const entries = await fs.readdir(fullPath, { withFileTypes: true });
|
|
134
|
+
return entries.map(entry => ({
|
|
135
|
+
path: entry.name,
|
|
136
|
+
isDirectory: entry.isDirectory()
|
|
137
|
+
}));
|
|
138
|
+
} catch (error) {
|
|
139
|
+
throw new Error(`Failed to list directory: ${error instanceof Error ? error.message : String(error)}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Search for a pattern in files
|
|
145
|
+
*/
|
|
146
|
+
export async function grep(
|
|
147
|
+
pattern: string,
|
|
148
|
+
baseDir: string = process.cwd(),
|
|
149
|
+
options: { filePattern?: string; recursive?: boolean } = {}
|
|
150
|
+
): Promise<{ file: string; line: number; content: string }[]> {
|
|
151
|
+
const results: { file: string; line: number; content: string }[] = [];
|
|
152
|
+
const fs = await import('fs/promises');
|
|
153
|
+
|
|
154
|
+
async function searchDir(dir: string) {
|
|
155
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
156
|
+
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
const fullPath = path.join(dir, entry.name);
|
|
159
|
+
|
|
160
|
+
// Skip node_modules, .git, etc.
|
|
161
|
+
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name.startsWith('.')) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (entry.isDirectory() && options.recursive !== false) {
|
|
166
|
+
await searchDir(fullPath);
|
|
167
|
+
} else if (entry.isFile()) {
|
|
168
|
+
// Check file pattern
|
|
169
|
+
if (options.filePattern && !new RegExp(options.filePattern).test(entry.name)) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Search in file
|
|
174
|
+
try {
|
|
175
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
176
|
+
const lines = content.split('\n');
|
|
177
|
+
|
|
178
|
+
for (let i = 0; i < lines.length; i++) {
|
|
179
|
+
if (lines[i].includes(pattern)) {
|
|
180
|
+
results.push({
|
|
181
|
+
file: path.relative(baseDir, fullPath),
|
|
182
|
+
line: i + 1,
|
|
183
|
+
content: lines[i].trim()
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} catch {
|
|
188
|
+
// Skip binary files or unreadable files
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
await searchDir(baseDir);
|
|
195
|
+
return results;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Find files matching a glob pattern
|
|
200
|
+
*/
|
|
201
|
+
export async function glob(
|
|
202
|
+
pattern: string,
|
|
203
|
+
baseDir: string = process.cwd()
|
|
204
|
+
): Promise<string[]> {
|
|
205
|
+
const results: string[] = [];
|
|
206
|
+
const patternParts = pattern.split('/');
|
|
207
|
+
const regexPattern = patternParts.map(part => {
|
|
208
|
+
if (part === '**') return '.*';
|
|
209
|
+
if (part === '*') return '[^/]*';
|
|
210
|
+
return part.replace(/\./g, '\\.').replace(/\*/g, '.*');
|
|
211
|
+
}).join('/');
|
|
212
|
+
|
|
213
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
214
|
+
|
|
215
|
+
async function searchDir(dir: string) {
|
|
216
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
217
|
+
|
|
218
|
+
for (const entry of entries) {
|
|
219
|
+
const fullPath = path.join(dir, entry.name);
|
|
220
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
221
|
+
|
|
222
|
+
// Skip node_modules, .git, etc.
|
|
223
|
+
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name.startsWith('.')) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (entry.isDirectory()) {
|
|
228
|
+
await searchDir(fullPath);
|
|
229
|
+
} else if (entry.isFile()) {
|
|
230
|
+
if (regex.test(relativePath) || regex.test(entry.name)) {
|
|
231
|
+
results.push(relativePath);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
await searchDir(baseDir);
|
|
238
|
+
return results;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Check if a file exists
|
|
243
|
+
*/
|
|
244
|
+
export async function fileExists(filePath: string, baseDir: string = process.cwd()): Promise<boolean> {
|
|
245
|
+
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(baseDir, filePath);
|
|
246
|
+
try {
|
|
247
|
+
await fs.access(fullPath);
|
|
248
|
+
return true;
|
|
249
|
+
} catch {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Get file stats
|
|
256
|
+
*/
|
|
257
|
+
export async function getFileStats(filePath: string, baseDir: string = process.cwd()): Promise<{ size: number; modified: Date } | null> {
|
|
258
|
+
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(baseDir, filePath);
|
|
259
|
+
try {
|
|
260
|
+
const stats = await fs.stat(fullPath);
|
|
261
|
+
return {
|
|
262
|
+
size: stats.size,
|
|
263
|
+
modified: stats.mtime
|
|
264
|
+
};
|
|
265
|
+
} catch {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export default {
|
|
271
|
+
readFile,
|
|
272
|
+
writeFile,
|
|
273
|
+
editFile,
|
|
274
|
+
listDir,
|
|
275
|
+
grep,
|
|
276
|
+
glob,
|
|
277
|
+
fileExists,
|
|
278
|
+
getFileStats
|
|
279
|
+
};
|