cloving 0.3.5 → 0.3.7
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 +8 -2
- package/dist/cloving_gpt/index.js +1 -1
- package/dist/commands/config.js +2 -2
- package/dist/commands/generate/context.js +3 -3
- package/dist/commands/init.js +3 -218
- package/dist/commands/tokens.js +2 -2
- package/dist/index.js +3 -2
- package/dist/managers/BlockManager.js +125 -0
- package/dist/managers/ChatManager.js +89 -18
- package/dist/managers/ChunkManager.js +12 -0
- package/dist/managers/CodeManager.js +5 -5
- package/dist/managers/InitManager.js +216 -0
- package/dist/managers/ProxyManager.js +11 -3
- package/dist/managers/ReviewManager.js +14 -2
- package/dist/managers/ShellManager.js +7 -2
- package/dist/managers/UnitTestManager.js +3 -3
- package/dist/package.json +1 -1
- package/dist/utils/prompt_utils.js +515 -0
- package/dist/utils/string_utils.js +22 -16
- package/package.json +1 -1
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.addFileOrDirectoryToContext = exports.readFileContent = exports.fetchModels = exports.runCommand = exports.generateFileList = exports.getAllFilesInDirectory = exports.checkForSpecialFiles = exports.collectSpecialFileContents = exports.generateShellPrompt = exports.generateCodegenPrompt = exports.getPackageVersion = exports.SPECIAL_FILES = exports.INIT_INSTRUCTIONS = exports.CODEGEN_INSTRUCTIONS = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const isbinaryfile_1 = require("isbinaryfile");
|
|
11
|
+
const colors_1 = __importDefault(require("colors"));
|
|
12
|
+
const path_2 = require("path");
|
|
13
|
+
const config_utils_1 = require("./config_utils");
|
|
14
|
+
exports.CODEGEN_INSTRUCTIONS = `## AI Code Generation Instructions
|
|
15
|
+
|
|
16
|
+
### General Guidelines
|
|
17
|
+
|
|
18
|
+
1. **Respond as an expert software developer.**
|
|
19
|
+
2. **Follow best practices and the latest standards found in the **Description of App** section.**
|
|
20
|
+
3. **Adhere to existing conventions and libraries in the codebase.**
|
|
21
|
+
|
|
22
|
+
### Request Handling
|
|
23
|
+
|
|
24
|
+
1. **Try to understand the request, but if anything is unclear, ask questions.**
|
|
25
|
+
2. **Decide if you need to propose edits to existing files not in the chat.**
|
|
26
|
+
- If yes, provide the full path names and ask the user to add them.
|
|
27
|
+
- Wait for user approval before proceeding.
|
|
28
|
+
3. **Propose changes using *CURRENT/NEW* Blocks.**
|
|
29
|
+
4. **Show the smallest possible *CURRENT* section that uniquely identifies the code.**
|
|
30
|
+
5. **To move code, use two *CURRENT/NEW* blocks: one to remove and one to add.**
|
|
31
|
+
6. **For new files or to replace an existing file, the *CURRENT* block is empty.**
|
|
32
|
+
|
|
33
|
+
### *CURRENT/NEW* Block Format
|
|
34
|
+
|
|
35
|
+
1. **Start a block with three backticks and the language name.**
|
|
36
|
+
\`\`\`typescript
|
|
37
|
+
2. **Next line: seven <, CURRENT, and the file path.**
|
|
38
|
+
<<<<<<< CURRENT path/to/file.ts
|
|
39
|
+
3. **Include the exact existing code to be changed.**
|
|
40
|
+
4. **Divide with seven =.**
|
|
41
|
+
=======
|
|
42
|
+
5. **Add the new code.**
|
|
43
|
+
6. **End with seven > and NEW.**
|
|
44
|
+
>>>>>>> NEW
|
|
45
|
+
7. **Close the block with three backticks.**
|
|
46
|
+
\`\`\``;
|
|
47
|
+
exports.INIT_INSTRUCTIONS = `You are an AI assistant helping to initialize a project by analyzing its structure and technologies.
|
|
48
|
+
|
|
49
|
+
Please return JSON-formatted metadata about the project, including:
|
|
50
|
+
|
|
51
|
+
- The programming language(s) used
|
|
52
|
+
- The detected framework(s) (if any)
|
|
53
|
+
- The version of the language(s)
|
|
54
|
+
|
|
55
|
+
Here is a typescript interface for the expected response:
|
|
56
|
+
|
|
57
|
+
interface LanguageConfig {
|
|
58
|
+
name: string
|
|
59
|
+
version?: string
|
|
60
|
+
primary?: boolean
|
|
61
|
+
directory: string
|
|
62
|
+
extension: string
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface FrameworkConfig {
|
|
66
|
+
name: string
|
|
67
|
+
type: string
|
|
68
|
+
version?: string
|
|
69
|
+
primary?: boolean
|
|
70
|
+
directory?: string
|
|
71
|
+
extension?: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface TestingFrameworkConfig {
|
|
75
|
+
name: string
|
|
76
|
+
type: string
|
|
77
|
+
version?: string
|
|
78
|
+
directory?: string
|
|
79
|
+
runCommand?: string
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface BuildToolConfig {
|
|
83
|
+
name: string
|
|
84
|
+
type: string
|
|
85
|
+
version?: string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface LinterConfig {
|
|
89
|
+
name: string
|
|
90
|
+
version?: string
|
|
91
|
+
type?: string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface DatabaseConfig {
|
|
95
|
+
name: string
|
|
96
|
+
primary?: boolean
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ClovingfileConfig {
|
|
100
|
+
languages: LanguageConfig[]
|
|
101
|
+
frameworks: FrameworkConfig[]
|
|
102
|
+
testingFrameworks?: TestingFrameworkConfig[]
|
|
103
|
+
buildTools: BuildToolConfig[]
|
|
104
|
+
packageManager: string
|
|
105
|
+
linters: LinterConfig[]
|
|
106
|
+
databases?: DatabaseConfig[]
|
|
107
|
+
projectType: string
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
Here is an example response:
|
|
111
|
+
|
|
112
|
+
{
|
|
113
|
+
"languages": [
|
|
114
|
+
{
|
|
115
|
+
"name": "TypeScript",
|
|
116
|
+
"version": "~> 5.5.3"
|
|
117
|
+
"primary": true,
|
|
118
|
+
"directory": "src",
|
|
119
|
+
"extension": ".ts"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"name": "JavaScript",
|
|
123
|
+
"version": "ES6+",
|
|
124
|
+
"directory": "src",
|
|
125
|
+
"extension": ".js"
|
|
126
|
+
}
|
|
127
|
+
],
|
|
128
|
+
"frameworks": [
|
|
129
|
+
{
|
|
130
|
+
"name": "Node.js",
|
|
131
|
+
"type": "Runtime environment",
|
|
132
|
+
"primary": true,
|
|
133
|
+
"directory": "src",
|
|
134
|
+
"extension": ".js"
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
"testingFrameworks": [
|
|
138
|
+
{
|
|
139
|
+
"name": "Jest",
|
|
140
|
+
"type": "Testing framework",
|
|
141
|
+
"version": "29.7.0"
|
|
142
|
+
"directory": "tests"
|
|
143
|
+
"runCommand": "yarn test"
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
"buildTools": [
|
|
147
|
+
{
|
|
148
|
+
"name": "TypeScript Compiler (tsc)",
|
|
149
|
+
"type": "Transpiler"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"name": "Vite",
|
|
153
|
+
"type": "Build tool",
|
|
154
|
+
"version": "5.3.3"
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"packageManager": "Yarn",
|
|
158
|
+
"linters": [
|
|
159
|
+
{
|
|
160
|
+
"name": "ESLint",
|
|
161
|
+
"version": "9.6.0"
|
|
162
|
+
}
|
|
163
|
+
],
|
|
164
|
+
"databases": [
|
|
165
|
+
{
|
|
166
|
+
"name": "MongoDB",
|
|
167
|
+
"version": "5.0.3",
|
|
168
|
+
"primary": true
|
|
169
|
+
}
|
|
170
|
+
],
|
|
171
|
+
"projectType": "Command-line tool",
|
|
172
|
+
}`;
|
|
173
|
+
exports.SPECIAL_FILES = [
|
|
174
|
+
'package.json',
|
|
175
|
+
'Gemfile',
|
|
176
|
+
'requirements.txt',
|
|
177
|
+
'Pipfile',
|
|
178
|
+
'pyproject.toml',
|
|
179
|
+
'pom.xml',
|
|
180
|
+
'build.gradle',
|
|
181
|
+
'.csproj',
|
|
182
|
+
'packages.config',
|
|
183
|
+
'composer.json',
|
|
184
|
+
'CMakeLists.txt',
|
|
185
|
+
'conanfile.txt',
|
|
186
|
+
'conanfile.py',
|
|
187
|
+
'go.mod',
|
|
188
|
+
'Cargo.toml',
|
|
189
|
+
'Package.swift',
|
|
190
|
+
'build.gradle.kts',
|
|
191
|
+
'Podfile',
|
|
192
|
+
'Cartfile',
|
|
193
|
+
'cpanfile',
|
|
194
|
+
'DESCRIPTION',
|
|
195
|
+
'mix.exs',
|
|
196
|
+
'build.sbt',
|
|
197
|
+
'pubspec.yaml',
|
|
198
|
+
'stack.yaml',
|
|
199
|
+
'top.sls',
|
|
200
|
+
'requirements.yml',
|
|
201
|
+
'terraform.tf',
|
|
202
|
+
'versions.tf',
|
|
203
|
+
'main.yml',
|
|
204
|
+
'cabal.project',
|
|
205
|
+
'Project.toml',
|
|
206
|
+
'rockspec',
|
|
207
|
+
'rebar.config',
|
|
208
|
+
'project.clj',
|
|
209
|
+
'tsconfig.json',
|
|
210
|
+
'.eslintrc.js',
|
|
211
|
+
'.eslintrc.json',
|
|
212
|
+
'.eslintrc.yaml',
|
|
213
|
+
'.eslintrc.yml',
|
|
214
|
+
'.eslintrc',
|
|
215
|
+
'.prettierrc',
|
|
216
|
+
'.prettierrc.js',
|
|
217
|
+
'.prettierrc.json',
|
|
218
|
+
'.prettierrc.yaml',
|
|
219
|
+
'.prettierrc.yml',
|
|
220
|
+
'.prettierrc',
|
|
221
|
+
'.stylelintrc',
|
|
222
|
+
'.stylelintrc.json',
|
|
223
|
+
'.stylelintrc.yaml',
|
|
224
|
+
'.stylelintrc.yml',
|
|
225
|
+
'.stylelintrc',
|
|
226
|
+
'jest.config.js',
|
|
227
|
+
'jest.config.json',
|
|
228
|
+
'jest.config.ts',
|
|
229
|
+
'jest.config.cjs',
|
|
230
|
+
'jest.config.mjs',
|
|
231
|
+
'webpack.config.js',
|
|
232
|
+
'webpack.config.ts',
|
|
233
|
+
'webpack.config.cjs',
|
|
234
|
+
'webpack.config.mjs',
|
|
235
|
+
'webpack.config.common.js',
|
|
236
|
+
];
|
|
237
|
+
const getPackageVersion = () => {
|
|
238
|
+
const packagePath = (0, path_2.join)(__dirname, '..', 'package.json');
|
|
239
|
+
const packageJson = JSON.parse(fs_1.default.readFileSync(packagePath, 'utf8'));
|
|
240
|
+
return packageJson.version;
|
|
241
|
+
};
|
|
242
|
+
exports.getPackageVersion = getPackageVersion;
|
|
243
|
+
const renderAsString = (contextFile) => {
|
|
244
|
+
if (typeof contextFile === 'string') {
|
|
245
|
+
return contextFile;
|
|
246
|
+
}
|
|
247
|
+
return JSON.stringify(contextFile, null, 2);
|
|
248
|
+
};
|
|
249
|
+
const generateCodegenPrompt = (contextFilesContent) => {
|
|
250
|
+
const specialFileContents = (0, exports.collectSpecialFileContents)();
|
|
251
|
+
// detect if specialFileContents[file] is a string or an object
|
|
252
|
+
const specialFiles = Object.keys(specialFileContents)
|
|
253
|
+
.map((file) => `### Contents of **${file}**\n\n\`\`\`\n${renderAsString(specialFileContents[file])}\n\`\`\`\n\n`)
|
|
254
|
+
.join('\n');
|
|
255
|
+
const contextFileContents = Object.keys(contextFilesContent)
|
|
256
|
+
.map((file) => `### Contents of **${file}**\n\n\`\`\`\n${contextFilesContent[file]}\n\`\`\`\n\n`)
|
|
257
|
+
.join('\n');
|
|
258
|
+
const prompt = `${exports.CODEGEN_INSTRUCTIONS}
|
|
259
|
+
|
|
260
|
+
## Description of App
|
|
261
|
+
|
|
262
|
+
\`\`\`json
|
|
263
|
+
${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
|
|
264
|
+
\`\`\`
|
|
265
|
+
|
|
266
|
+
## Special Files
|
|
267
|
+
|
|
268
|
+
${specialFiles.length > 0 ? specialFiles : 'No special files provided.'}
|
|
269
|
+
|
|
270
|
+
## Context Files
|
|
271
|
+
|
|
272
|
+
${contextFileContents.length > 0 ? contextFileContents : 'No context files provided.'}
|
|
273
|
+
|
|
274
|
+
## Directory structure
|
|
275
|
+
|
|
276
|
+
${Object.keys(contextFilesContent).join('\n')}
|
|
277
|
+
|
|
278
|
+
${exports.CODEGEN_INSTRUCTIONS}
|
|
279
|
+
|
|
280
|
+
### Example 1: Changing a Variable
|
|
281
|
+
|
|
282
|
+
\`\`\`typescript
|
|
283
|
+
<<<<<<< CURRENT path/to/file.ts
|
|
284
|
+
const myVariable = 'exact'
|
|
285
|
+
const oldVariable = 1
|
|
286
|
+
=======
|
|
287
|
+
const myVariable = 'exact'
|
|
288
|
+
const newVariable = 2
|
|
289
|
+
>>>>>>> NEW
|
|
290
|
+
\`\`\`
|
|
291
|
+
|
|
292
|
+
### Example 2: Create a New File
|
|
293
|
+
|
|
294
|
+
\`\`\`ruby
|
|
295
|
+
<<<<<<< CURRENT path/to/newfile.rb
|
|
296
|
+
=======
|
|
297
|
+
def new_function
|
|
298
|
+
puts 'Hello, world!'
|
|
299
|
+
end
|
|
300
|
+
>>>>>>> NEW
|
|
301
|
+
\`\`\`
|
|
302
|
+
|
|
303
|
+
### Example 3: Moving a Function
|
|
304
|
+
|
|
305
|
+
Removing it from path/to/file.py
|
|
306
|
+
|
|
307
|
+
\`\`\`python
|
|
308
|
+
<<<<<<< CURRENT path/to/file.py
|
|
309
|
+
def old_function():
|
|
310
|
+
return 'Old function'
|
|
311
|
+
=======
|
|
312
|
+
>>>>>>> NEW
|
|
313
|
+
\`\`\`
|
|
314
|
+
|
|
315
|
+
Adding it to path/to/newfile.py
|
|
316
|
+
|
|
317
|
+
\`\`\`python
|
|
318
|
+
<<<<<<< CURRENT path/to/newfile.py
|
|
319
|
+
def existing_function():
|
|
320
|
+
return 'Existing function'
|
|
321
|
+
|
|
322
|
+
=======
|
|
323
|
+
def existing_function():
|
|
324
|
+
return 'Existing function'
|
|
325
|
+
|
|
326
|
+
def old_function():
|
|
327
|
+
return 'Old function'
|
|
328
|
+
>>>>>>> NEW
|
|
329
|
+
\`\`\`
|
|
330
|
+
|
|
331
|
+
### Example 4: Removing a Function
|
|
332
|
+
|
|
333
|
+
\`\`\`java
|
|
334
|
+
<<<<<<< CURRENT path/to/file.java
|
|
335
|
+
public void oldFunction() {
|
|
336
|
+
System.out.println("Old function")
|
|
337
|
+
}
|
|
338
|
+
=======
|
|
339
|
+
>>>>>>> NEW
|
|
340
|
+
\`\`\`
|
|
341
|
+
|
|
342
|
+
### Example 5: Replace a file
|
|
343
|
+
|
|
344
|
+
This will replace the entire contents of **path/to/file.rb** with the new content:
|
|
345
|
+
|
|
346
|
+
\`\`\`ruby
|
|
347
|
+
<<<<<<< CURRENT path/to/file.rb
|
|
348
|
+
=======
|
|
349
|
+
def new_function
|
|
350
|
+
puts 'Hello, world!'
|
|
351
|
+
end
|
|
352
|
+
>>>>>>> NEW
|
|
353
|
+
\`\`\`
|
|
354
|
+
|
|
355
|
+
## Don't Invent Code That Isn't Provided in Context
|
|
356
|
+
|
|
357
|
+
A file might be referenced by just the file name without the full path, but the code must be provided in the *Context Files* section.
|
|
358
|
+
|
|
359
|
+
Unless you believe you are creating a whole new file, never ever under any circumstances make up code in the CURRENT block that was not provided to you in the *Context Files* section.
|
|
360
|
+
|
|
361
|
+
If a required file hasn't been provided in the *Context Files* section, stop everything and ask the user to provide it in the context by adding -f path/to/file to the command or add path/to/file in the interactive chat.`;
|
|
362
|
+
return prompt;
|
|
363
|
+
};
|
|
364
|
+
exports.generateCodegenPrompt = generateCodegenPrompt;
|
|
365
|
+
const generateShellPrompt = () => {
|
|
366
|
+
const shell = (0, child_process_1.execSync)('echo $SHELL').toString().trim();
|
|
367
|
+
const os = (0, child_process_1.execSync)('echo $OSTYPE').toString().trim();
|
|
368
|
+
return `Generate an executable ${shell} script that works on ${os}. Try to make it a single line if possible and as simple and straightforward as possible.
|
|
369
|
+
|
|
370
|
+
Do not add any commentary or context to the message other than the commit message itself.
|
|
371
|
+
|
|
372
|
+
An example of the output for this should look like the following:
|
|
373
|
+
|
|
374
|
+
\`\`\`sh
|
|
375
|
+
find . -type f -name "*.ts" -exec sed -i '' 's/old/new/g' {} +
|
|
376
|
+
\`\`\`
|
|
377
|
+
|
|
378
|
+
Don't use that script, it is only an example.`;
|
|
379
|
+
};
|
|
380
|
+
exports.generateShellPrompt = generateShellPrompt;
|
|
381
|
+
const collectSpecialFileContents = () => {
|
|
382
|
+
const specialFileContents = {};
|
|
383
|
+
for (const file of exports.SPECIAL_FILES) {
|
|
384
|
+
if (fs_1.default.existsSync(file)) {
|
|
385
|
+
try {
|
|
386
|
+
const content = fs_1.default.readFileSync(file, 'utf-8');
|
|
387
|
+
specialFileContents[file] = file.endsWith('.json') ? JSON.parse(content) : content;
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
specialFileContents[file] = fs_1.default.readFileSync(file, 'utf-8');
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return specialFileContents;
|
|
395
|
+
};
|
|
396
|
+
exports.collectSpecialFileContents = collectSpecialFileContents;
|
|
397
|
+
const checkForSpecialFiles = () => exports.SPECIAL_FILES.some((file) => fs_1.default.existsSync(file));
|
|
398
|
+
exports.checkForSpecialFiles = checkForSpecialFiles;
|
|
399
|
+
const getAllFilesInDirectory = async (dir) => {
|
|
400
|
+
const subdirs = await fs_1.default.promises.readdir(dir);
|
|
401
|
+
const files = await Promise.all(subdirs.map(async (subdir) => {
|
|
402
|
+
const res = path_1.default.resolve(dir, subdir);
|
|
403
|
+
if (subdir === 'node_modules' || subdir === '.git' || subdir === '.DS_Store') {
|
|
404
|
+
return [];
|
|
405
|
+
}
|
|
406
|
+
const stat = await fs_1.default.promises.stat(res);
|
|
407
|
+
if (stat.isDirectory()) {
|
|
408
|
+
return (0, exports.getAllFilesInDirectory)(res);
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
const isBinary = await (0, isbinaryfile_1.isBinaryFile)(res);
|
|
412
|
+
return isBinary ? [] : res;
|
|
413
|
+
}
|
|
414
|
+
}));
|
|
415
|
+
return files.flat();
|
|
416
|
+
};
|
|
417
|
+
exports.getAllFilesInDirectory = getAllFilesInDirectory;
|
|
418
|
+
const generateFileList = async () => {
|
|
419
|
+
try {
|
|
420
|
+
const lsOutput = await (0, exports.runCommand)('ls', []);
|
|
421
|
+
const findOutput = await (0, exports.runCommand)('find', ['.']);
|
|
422
|
+
const cleanedFindOutput = findOutput.map((file) => file.startsWith('./') ? file.slice(2) : file);
|
|
423
|
+
const files = [...lsOutput, ...cleanedFindOutput];
|
|
424
|
+
const uniqueFiles = Array.from(new Set(files));
|
|
425
|
+
return uniqueFiles.filter((file) => file && !file.includes('.git') && !file.includes('node_modules') && !file.includes('tmp'));
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
console.error('Error generating file list:', error.message);
|
|
429
|
+
return [];
|
|
430
|
+
}
|
|
431
|
+
};
|
|
432
|
+
exports.generateFileList = generateFileList;
|
|
433
|
+
const runCommand = (command, args) => {
|
|
434
|
+
return new Promise((resolve, reject) => {
|
|
435
|
+
const process = (0, child_process_1.spawn)(command, args);
|
|
436
|
+
const output = [];
|
|
437
|
+
process.stdout.on('data', (data) => {
|
|
438
|
+
output.push(data.toString());
|
|
439
|
+
});
|
|
440
|
+
process.stderr.on('data', (data) => {
|
|
441
|
+
console.error(`stderr: ${data}`);
|
|
442
|
+
});
|
|
443
|
+
process.on('close', (code) => {
|
|
444
|
+
if (code !== 0) {
|
|
445
|
+
reject(new Error(`${command} process exited with code ${code}`));
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
resolve(output.join('').trim().split('\n'));
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
};
|
|
453
|
+
exports.runCommand = runCommand;
|
|
454
|
+
const fetchModels = async () => {
|
|
455
|
+
try {
|
|
456
|
+
const modelsOutput = await (0, exports.runCommand)('cloving', ['models']);
|
|
457
|
+
return modelsOutput;
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
console.error('Error fetching models:', error.message);
|
|
461
|
+
return [];
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
exports.fetchModels = fetchModels;
|
|
465
|
+
const readFileContent = (file) => {
|
|
466
|
+
try {
|
|
467
|
+
return fs_1.default.readFileSync(file, 'utf-8');
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
console.error('Error reading file content:', error.message);
|
|
471
|
+
return '';
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
exports.readFileContent = readFileContent;
|
|
475
|
+
const addFileToContext = async (filePath, contextFiles, baseDir) => {
|
|
476
|
+
const relativePath = path_1.default.relative(baseDir, filePath);
|
|
477
|
+
if (await (0, isbinaryfile_1.isBinaryFile)(filePath)) {
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
const content = await fs_1.default.promises.readFile(filePath, 'utf-8');
|
|
481
|
+
contextFiles[relativePath] = content;
|
|
482
|
+
};
|
|
483
|
+
const addDirectoryToContext = async (dirPath, contextFiles, baseDir) => {
|
|
484
|
+
const entries = await fs_1.default.promises.readdir(dirPath, { withFileTypes: true });
|
|
485
|
+
for (const entry of entries) {
|
|
486
|
+
const fullPath = path_1.default.join(dirPath, entry.name);
|
|
487
|
+
if (entry.isDirectory()) {
|
|
488
|
+
if (entry.name !== 'node_modules' && entry.name !== '.git') {
|
|
489
|
+
await addDirectoryToContext(fullPath, contextFiles, baseDir);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
await addFileToContext(fullPath, contextFiles, baseDir);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
const addFileOrDirectoryToContext = async (contextFile, contextFiles, options) => {
|
|
498
|
+
const filePath = path_1.default.resolve(contextFile);
|
|
499
|
+
const baseDir = process.cwd();
|
|
500
|
+
try {
|
|
501
|
+
const stats = await fs_1.default.promises.stat(filePath);
|
|
502
|
+
if (stats.isDirectory()) {
|
|
503
|
+
await addDirectoryToContext(filePath, contextFiles, baseDir);
|
|
504
|
+
}
|
|
505
|
+
else if (stats.isFile()) {
|
|
506
|
+
await addFileToContext(filePath, contextFiles, baseDir);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
catch (error) {
|
|
510
|
+
console.error(colors_1.default.red(`${colors_1.default.bold('Error')}: File or directory "${contextFile}" does not exist`));
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
return contextFiles;
|
|
514
|
+
};
|
|
515
|
+
exports.addFileOrDirectoryToContext = addFileOrDirectoryToContext;
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.checkBlocksApplicability = exports.applyAndSaveCurrentNewBlocks = exports.
|
|
6
|
+
exports.checkBlocksApplicability = exports.applyAndSaveCurrentNewBlocks = exports.applyAndSaveBlock = exports.searchReplaceString = exports.writeFileContent = exports.ensureDirectoryExists = exports.extractCurrentNewBlocks = exports.extractBlock = exports.findBlockIndices = exports.parseMarkdownInstructions = exports.extractMarkdown = exports.extractJsonMetadata = exports.estimateTokens = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
9
|
const colors_1 = __importDefault(require("colors"));
|
|
@@ -286,13 +286,13 @@ exports.writeFileContent = writeFileContent;
|
|
|
286
286
|
* currentContent: "console.log('Old content')",
|
|
287
287
|
* newContent: "console.log('New content')"
|
|
288
288
|
* }
|
|
289
|
-
* const updatedContent =
|
|
289
|
+
* const updatedContent = searchReplaceString(currentContent, block)
|
|
290
290
|
* // updatedContent will be:
|
|
291
291
|
* // function example() {
|
|
292
292
|
* // console.log('New content')
|
|
293
293
|
* // }
|
|
294
294
|
*/
|
|
295
|
-
const
|
|
295
|
+
const searchReplaceString = (currentContent, block) => {
|
|
296
296
|
// 1) if block.currentContent.trim() is empty then return block.newContent
|
|
297
297
|
if (block.currentContent.trim() === '') {
|
|
298
298
|
return block.newContent;
|
|
@@ -308,26 +308,32 @@ const updateFileContent = (currentContent, block) => {
|
|
|
308
308
|
if (matchingLine === -1) {
|
|
309
309
|
return currentContent; // If no match is found, return the original content
|
|
310
310
|
}
|
|
311
|
-
// 5) compare the first line of block.currentContent with the
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
311
|
+
// 5) compare the first line of block.currentContent with the startLine line of currentContent, putting any preceeding spaces that are in currentContent but not in block.currentContent into a variable named preceedingSpaces
|
|
312
|
+
const startLine = currentContent.split('\n').findIndex((line) => line.includes(blockLines[0]));
|
|
313
|
+
if (startLine === -1) {
|
|
314
|
+
return currentContent; // If no match is found, return the original content
|
|
315
|
+
}
|
|
316
|
+
const startLineContent = currentContent.split('\n')[startLine];
|
|
317
|
+
const blockLineContent = block.currentContent.split('\n')[0];
|
|
318
|
+
const precedingSpaces = startLineContent.replace(blockLineContent, '');
|
|
316
319
|
// 6) add preceedingSpaces to the start of each line in both block.currentContent and block.newContent
|
|
317
320
|
if (precedingSpaces) {
|
|
318
|
-
const
|
|
321
|
+
const indentedCurrentContent = block.currentContent
|
|
322
|
+
.split('\n')
|
|
323
|
+
.map((line) => precedingSpaces + line)
|
|
324
|
+
.join('\n');
|
|
319
325
|
const indentedNewContent = block.newContent
|
|
320
326
|
.split('\n')
|
|
321
|
-
.map((line) =>
|
|
327
|
+
.map((line) => precedingSpaces + line)
|
|
322
328
|
.join('\n');
|
|
323
329
|
// 7) finally, do a simple currentContent.replace(block.currentContent, block.newContent) with the modified (added preceeding spaces to match) variables
|
|
324
|
-
return currentContent.replace(
|
|
330
|
+
return currentContent.replace(indentedCurrentContent, indentedNewContent);
|
|
325
331
|
}
|
|
326
332
|
else {
|
|
327
333
|
return currentContent.replace(block.currentContent, block.newContent);
|
|
328
334
|
}
|
|
329
335
|
};
|
|
330
|
-
exports.
|
|
336
|
+
exports.searchReplaceString = searchReplaceString;
|
|
331
337
|
/**
|
|
332
338
|
* Analyzes the content of a block against the file content to find matching lines.
|
|
333
339
|
*
|
|
@@ -364,7 +370,7 @@ const analyzeBlockContent = async (block, fileContent) => {
|
|
|
364
370
|
* @param {number} index - The index of the block being processed (used for logging).
|
|
365
371
|
* @returns {Promise<void>} A promise that resolves when the block has been processed.
|
|
366
372
|
*/
|
|
367
|
-
const
|
|
373
|
+
const applyAndSaveBlock = async (block, index) => {
|
|
368
374
|
const filePath = path_1.default.resolve(block.filePath);
|
|
369
375
|
let fileContent = await readFileContent(filePath);
|
|
370
376
|
if (fileContent === '') {
|
|
@@ -381,11 +387,11 @@ const processBlock = async (block, index) => {
|
|
|
381
387
|
console.log(`[${index + 1}] ${colors_1.default.red.bold(block.filePath)} ${colors_1.default.red.bold(`ERROR: Current content matches multiple (${matchingLines.length}) parts in the file`)}`);
|
|
382
388
|
return;
|
|
383
389
|
}
|
|
384
|
-
const updatedContent = (0, exports.
|
|
390
|
+
const updatedContent = (0, exports.searchReplaceString)(fileContent, block);
|
|
385
391
|
await (0, exports.writeFileContent)(filePath, updatedContent);
|
|
386
392
|
console.log(`[${index + 1}] ${colors_1.default.green.bold(block.filePath)} has been ${colors_1.default.yellow.bold('updated')}`);
|
|
387
393
|
};
|
|
388
|
-
exports.
|
|
394
|
+
exports.applyAndSaveBlock = applyAndSaveBlock;
|
|
389
395
|
/**
|
|
390
396
|
* Applies and saves the changes specified in the CurrentNewBlock array.
|
|
391
397
|
*
|
|
@@ -413,7 +419,7 @@ const applyAndSaveCurrentNewBlocks = async (blocks) => {
|
|
|
413
419
|
console.log(`\nApplying and saving current/new blocks...\n`);
|
|
414
420
|
for (const [index, block] of blocks.entries()) {
|
|
415
421
|
try {
|
|
416
|
-
await (0, exports.
|
|
422
|
+
await (0, exports.applyAndSaveBlock)(block, index);
|
|
417
423
|
}
|
|
418
424
|
catch (error) {
|
|
419
425
|
console.error(`Error processing ${colors_1.default.red(block.filePath)}:`, error);
|