cloving 0.3.5 → 0.3.6

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.
@@ -8,7 +8,7 @@ const child_process_1 = require("child_process");
8
8
  const cli_highlight_1 = __importDefault(require("cli-highlight"));
9
9
  const prompts_1 = require("@inquirer/prompts");
10
10
  const git_utils_1 = require("../utils/git_utils");
11
- const command_utils_1 = require("../utils/command_utils");
11
+ const prompt_utils_1 = require("../utils/prompt_utils");
12
12
  const config_utils_1 = require("../utils/config_utils");
13
13
  const string_utils_1 = require("../utils/string_utils");
14
14
  const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
@@ -197,7 +197,7 @@ Also, list any test files that might be relevant to these files.`;
197
197
  ## List of Files
198
198
  ${(files || []).join('\n')}
199
199
 
200
- ${command_utils_1.CODEGEN_INSTRUCTIONS}
200
+ ${prompt_utils_1.CODEGEN_INSTRUCTIONS}
201
201
 
202
202
  ## Context
203
203
 
@@ -221,7 +221,7 @@ ${await (0, git_utils_1.getGitDiff)()}
221
221
 
222
222
  ${context.join('\n\n')}
223
223
 
224
- ${command_utils_1.CODEGEN_INSTRUCTIONS}
224
+ ${prompt_utils_1.CODEGEN_INSTRUCTIONS}
225
225
 
226
226
  ## Prompt
227
227
 
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",