cloving 0.1.3 → 0.1.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.
@@ -0,0 +1,88 @@
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.buildProject = void 0;
7
+ const fs_1 = require("fs");
8
+ const child_process_1 = require("child_process");
9
+ const cli_highlight_1 = __importDefault(require("cli-highlight"));
10
+ const inquirer_1 = __importDefault(require("inquirer"));
11
+ const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
12
+ const config_utils_1 = require("../../utils/config_utils");
13
+ const git_utils_1 = require("../../utils/git_utils");
14
+ const string_utils_1 = require("../../utils/string_utils");
15
+ const command_utils_1 = require("../../utils/command_utils");
16
+ const generatePrompt = async (projectName, projectTask, filesList, incompletePlanItem) => {
17
+ const context = [];
18
+ for (const codeFile of filesList) {
19
+ if (await fs_1.promises?.stat(codeFile).then(stat => stat.isFile()).catch(() => false)) {
20
+ const fileContents = await fs_1.promises.readFile(codeFile, 'utf-8');
21
+ context.push(`### **Contents of ${codeFile}**\n\n${fileContents}\n\n### **End of ${codeFile}**`);
22
+ }
23
+ }
24
+ return `==== begin task description ====
25
+ Task name: ${projectName}
26
+ Task description: ${projectTask}
27
+ ==== end task description ====
28
+
29
+ ${context}
30
+
31
+ Please implement the following plan item, please include full file path names in all code snippets:
32
+
33
+ ${incompletePlanItem}`;
34
+ };
35
+ const buildProject = async (options) => {
36
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
37
+ const gpt = new cloving_gpt_1.default(options);
38
+ const projectName = (0, git_utils_1.getCurrentBranchName)();
39
+ const projectConfig = (0, config_utils_1.getProjectConfig)(projectName);
40
+ // Filter out completed plan items for selection
41
+ const planItems = Object.entries(projectConfig.plan || {});
42
+ if (planItems.length === 0) {
43
+ console.log('No plan items found.');
44
+ process.exit(1);
45
+ }
46
+ // Use inquirer to select a plan item
47
+ const { selectedPlanItemKey } = await inquirer_1.default.prompt([
48
+ {
49
+ type: 'list',
50
+ name: 'selectedPlanItemKey',
51
+ message: 'Select a plan item:',
52
+ choices: planItems.map(([key, item]) => ({ name: key, value: key })),
53
+ },
54
+ ]);
55
+ const selectedPlanItem = planItems.find(([key]) => key === selectedPlanItemKey);
56
+ if (!selectedPlanItem) {
57
+ console.log('Selected plan item not found.');
58
+ process.exit(1);
59
+ }
60
+ const planText = `## **${selectedPlanItem[0]}**\n\n${selectedPlanItem[1].details.join('\n\n')}`;
61
+ const prompt = await generatePrompt(projectName, projectConfig.task, projectConfig.files || [], planText);
62
+ const projectCode = await gpt.generateText({ prompt });
63
+ (0, string_utils_1.parseMarkdownInstructions)(projectCode).map(code => {
64
+ if (code.trim().startsWith('```')) {
65
+ const lines = code.split('\n');
66
+ const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
67
+ console.log(lines[0]);
68
+ console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
69
+ console.log(lines.slice(-1)[0]);
70
+ }
71
+ else {
72
+ console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
73
+ }
74
+ });
75
+ // Prompt the user to copy the project to the clipboard
76
+ const answer = await (0, command_utils_1.promptUser)('Do you want to copy this project to the clipboard? [Y/n] ');
77
+ if (answer.toLowerCase() === 'y' || answer === '') {
78
+ try {
79
+ (0, child_process_1.execFileSync)('pbcopy', { input: projectCode });
80
+ console.log('Project copied to clipboard');
81
+ }
82
+ catch (error) {
83
+ console.error('Error: pbcopy command not found. Unable to copy to clipboard.');
84
+ }
85
+ }
86
+ };
87
+ exports.buildProject = buildProject;
88
+ exports.default = exports.buildProject;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.completeProject = void 0;
4
+ const config_utils_1 = require("../../utils/config_utils");
5
+ const git_utils_1 = require("../../utils/git_utils");
6
+ const completeProject = async (options) => {
7
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
8
+ const projectName = (0, git_utils_1.getCurrentBranchName)();
9
+ (0, config_utils_1.removeProjectConfig)(projectName);
10
+ };
11
+ exports.completeProject = completeProject;
12
+ exports.default = exports.completeProject;
@@ -0,0 +1,76 @@
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.initProject = void 0;
7
+ const fs_1 = require("fs");
8
+ const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
9
+ const config_utils_1 = require("../../utils/config_utils");
10
+ const command_utils_1 = require("../../utils/command_utils");
11
+ const git_utils_1 = require("../../utils/git_utils");
12
+ const config_utils_2 = require("../../utils/config_utils");
13
+ const generatePrompt = async (projectName, projectTask) => {
14
+ const filesList = await (0, config_utils_2.getAllFiles)({}, false);
15
+ return `Here is a list of all my source files:
16
+
17
+ ==== begin list of source files ====
18
+ ${filesList.join("\n")}
19
+ ==== end list of source files ====
20
+
21
+ Please enumerate all the files in the provided list that are pertinent for this task:
22
+
23
+ ==== begin task description ====
24
+ Task name: ${projectName}
25
+ Task description: ${projectTask}
26
+ ==== end task description ====
27
+
28
+ Also, list any test files that might be relevant to these files.
29
+
30
+ Give me this output format for your answer:
31
+
32
+ ## files
33
+
34
+ app/models/foo.rb
35
+ app/views/baz/bar.html.erb
36
+
37
+ ## relevant test files
38
+
39
+ test/models/foo_test.rb
40
+ test/controllers/baz_controller_test.rb`;
41
+ };
42
+ const initProject = async (options) => {
43
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
44
+ const gpt = new cloving_gpt_1.default(options);
45
+ const defaultBranchName = await (0, git_utils_1.getDefaultBranchName)();
46
+ const projectName = (0, git_utils_1.getCurrentBranchName)();
47
+ if (projectName === defaultBranchName) {
48
+ console.log(`You are on the ${defaultBranchName} branch. Please checkout to a git branch and try again. For example: git branch -b my-new-project`);
49
+ process.exit(1);
50
+ }
51
+ const projectTask = await (0, command_utils_1.promptUser)('Describe the task you want to accomplish with this project: ');
52
+ const prompt = await generatePrompt(projectName, projectTask);
53
+ const contextFiles = await gpt.generateText({ prompt });
54
+ // Initialize variables
55
+ const lines = [];
56
+ const files = [];
57
+ // Read each line of the context files
58
+ contextFiles.split('\n').forEach((line) => {
59
+ if (line.trim()) {
60
+ lines.push(line.trim());
61
+ }
62
+ });
63
+ for (const codeFile of lines) {
64
+ if (await fs_1.promises?.stat(codeFile).then(stat => stat.isFile()).catch(() => false)) {
65
+ files.push(codeFile);
66
+ }
67
+ }
68
+ const projectConfig = {
69
+ name: projectName,
70
+ task: projectTask,
71
+ files
72
+ };
73
+ (0, config_utils_1.saveProjectConfig)(projectName, projectConfig);
74
+ };
75
+ exports.initProject = initProject;
76
+ exports.default = exports.initProject;
@@ -0,0 +1,108 @@
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.planProject = void 0;
7
+ const fs_1 = require("fs");
8
+ const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
9
+ const config_utils_1 = require("../../utils/config_utils");
10
+ const git_utils_1 = require("../../utils/git_utils");
11
+ const parsePlan = (plan) => {
12
+ const lines = plan.split('\n').map(line => line.trim());
13
+ const structured = {};
14
+ let currentTitle = null;
15
+ let currentDetails = [];
16
+ lines.forEach(line => {
17
+ const sectionMatch = line.match(/^#### \d+\. \*\*(.+)\*\*$/);
18
+ const detailMatch = line.match(/^-\s*\*\*(.+)\*\*:?\s*(.+)$/);
19
+ if (sectionMatch) {
20
+ if (currentTitle) {
21
+ structured[currentTitle] = {
22
+ details: currentDetails
23
+ };
24
+ }
25
+ currentTitle = sectionMatch[1];
26
+ currentDetails = [];
27
+ }
28
+ else if (detailMatch && currentDetails) {
29
+ currentDetails.push(`${detailMatch[1]}: ${detailMatch[2]}`);
30
+ }
31
+ else if (currentDetails && line.startsWith('- ')) {
32
+ currentDetails.push(line.replace(/^- /, ''));
33
+ }
34
+ });
35
+ if (currentTitle) {
36
+ structured[currentTitle] = {
37
+ details: currentDetails
38
+ };
39
+ }
40
+ return structured;
41
+ };
42
+ const generatePrompt = async (projectName, projectTask, filesList) => {
43
+ const context = [];
44
+ for (const codeFile of filesList) {
45
+ if (await fs_1.promises?.stat(codeFile).then(stat => stat.isFile()).catch(() => false)) {
46
+ const fileContents = await fs_1.promises.readFile(codeFile, 'utf-8');
47
+ context.push(`### **Contents of ${codeFile}**\n\n${fileContents}\n\n### **End of ${codeFile}**`);
48
+ }
49
+ }
50
+ return `==== begin task description ====
51
+ Task name: ${projectName}
52
+ Task description: ${projectTask}
53
+ ==== end task description ====
54
+
55
+ ${context}
56
+
57
+ Please create a plan to execute this project. Do not include the Implementation Steps. Only include Plan for Project Execution.
58
+
59
+ Here is an example plan. Do not use any of the information from this plan in your response, only use it as a template to create your own plan.
60
+
61
+ ==== begin example plan ====
62
+ ### Plan for Project Execution
63
+
64
+ #### 1. **Understanding Existing Code**
65
+ - **Review existing command implementations**: Familiarize yourself with the structure and implementation style of existing commands such as project init and project plan.
66
+ - **Identify utility functions**: Note the utility functions being used across the commands, such as those in config_utils, git_utils, and command_utils.
67
+
68
+ #### 2. **Design New Commands**
69
+ - **Define functionalities**: Clearly outline what each new command should accomplish:
70
+ - project build: This command should compile or prepare the project based on the planned actions.
71
+ - project complete: This command should finalize the project, possibly including cleanup, documentation, or deployment steps.
72
+
73
+ #### 3. **Implement project build**
74
+ - **Create build.ts**: Implement the logic for building the project.
75
+ - **Generate prompt**: Create a prompt that describes the build process based on the project configuration.
76
+ - **Invoke AI Model**: Use an AI model to generate the steps or scripts needed to build the project.
77
+ - **Execute Build Steps**: Implement logic to execute the build steps.
78
+
79
+ #### 4. **Implement project complete**
80
+ - **Create complete.ts**: Implement the logic for completing the project.
81
+ - **Generate prompt**: Create a prompt that describes the completion process based on the project configuration.
82
+ - **Invoke AI Model**: Use an AI model to generate the steps or scripts needed to complete the project.
83
+ - **Execute Completion Steps**: Implement logic to execute the completion steps.
84
+
85
+ #### 5. **Integration and Testing**
86
+ - **Integrate new commands**: Ensure the new commands are integrated into the existing command structure.
87
+ - **Unit tests**: Develop unit tests for the new commands to ensure they function correctly.
88
+ - **End-to-end tests**: Run end-to-end tests to validate the entire workflow from project initialization to completion.
89
+
90
+ #### 6. **Documentation and Cleanup**
91
+ - **Update README**: Document the new commands and their usage.
92
+ - **Code cleanup**: Refactor and clean up any redundant code or comments.
93
+ ==== end example plan ====`;
94
+ };
95
+ const planProject = async (options) => {
96
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
97
+ const gpt = new cloving_gpt_1.default(options);
98
+ const projectName = (0, git_utils_1.getCurrentBranchName)();
99
+ const projectConfig = (0, config_utils_1.getProjectConfig)(projectName);
100
+ const prompt = await generatePrompt(projectName, projectConfig.task, projectConfig.files || []);
101
+ const projectPlan = await gpt.generateText({ prompt });
102
+ const planItems = parsePlan(projectPlan);
103
+ console.log(planItems);
104
+ projectConfig.plan = planItems;
105
+ (0, config_utils_1.saveProjectConfig)(projectName, projectConfig);
106
+ };
107
+ exports.planProject = planProject;
108
+ exports.default = exports.planProject;
@@ -8,9 +8,9 @@ const child_process_1 = require("child_process");
8
8
  const git_utils_1 = require("../utils/git_utils");
9
9
  const string_utils_1 = require("../utils/string_utils");
10
10
  const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
11
- const command_utils_1 = require("../utils/command_utils");
11
+ const config_utils_1 = require("../utils/config_utils");
12
12
  const review = async (options) => {
13
- options.silent = (0, command_utils_1.getConfig)(options).silent || false;
13
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
14
14
  const gpt = new cloving_gpt_1.default(options);
15
15
  try {
16
16
  // Define the prompt for analysis
@@ -7,7 +7,7 @@ const fs_1 = require("fs");
7
7
  const child_process_1 = require("child_process");
8
8
  const git_utils_1 = require("../utils/git_utils");
9
9
  const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
10
- const command_utils_1 = require("../utils/command_utils");
10
+ const config_utils_1 = require("../utils/config_utils");
11
11
  const extractChangedFiles = (gitDiff) => {
12
12
  const fileRegex = /diff --git a\/(.+?) b\/(.+?)\n/g;
13
13
  const files = new Set();
@@ -72,7 +72,7 @@ const unitTests = async (options) => {
72
72
  const { files } = options;
73
73
  const gpt = new cloving_gpt_1.default(options);
74
74
  // Read the cloving.json file
75
- const config = (0, command_utils_1.readClovingConfig)();
75
+ const config = (0, config_utils_1.readClovingConfig)();
76
76
  const testingDirectory = config.testingFrameworks?.find((framework) => framework.directory)?.directory;
77
77
  if (!testingDirectory) {
78
78
  await setupTestingPrompt(gpt, config);
package/dist/index.js CHANGED
@@ -5,44 +5,82 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const commander_1 = require("commander");
8
- const commit_1 = __importDefault(require("./commands/commit"));
9
- const unit_tests_1 = __importDefault(require("./commands/unit_tests"));
10
- const review_1 = __importDefault(require("./commands/review"));
11
- const init_1 = __importDefault(require("./commands/init"));
12
8
  const config_1 = __importDefault(require("./commands/config"));
9
+ const init_1 = __importDefault(require("./commands/init"));
13
10
  const models_1 = __importDefault(require("./commands/models"));
11
+ const commit_1 = __importDefault(require("./commands/generate/commit"));
12
+ const unit_tests_1 = __importDefault(require("./commands/generate/unit_tests"));
13
+ const review_1 = __importDefault(require("./commands/generate/review"));
14
+ const init_2 = __importDefault(require("./commands/project/init"));
15
+ const plan_1 = __importDefault(require("./commands/project/plan"));
16
+ const build_1 = __importDefault(require("./commands/project/build"));
17
+ const complete_1 = __importDefault(require("./commands/project/complete"));
14
18
  const program = new commander_1.Command();
15
19
  program
16
20
  .name('cloving')
17
21
  .description('Integrate AI into your development workflow for generating commit messages, code reviews, and unit tests.')
18
22
  .version('1.0.0');
19
23
  program
24
+ .command('config')
25
+ .description('Configure cloving with your API key and models to use')
26
+ .action(config_1.default);
27
+ program
28
+ .command('init')
29
+ .description('Setup cloving in the current directory')
30
+ .option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
31
+ .option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
32
+ .action(init_1.default);
33
+ program
34
+ .command('models')
35
+ .description('List available models')
36
+ .action(models_1.default);
37
+ program
38
+ .command('commit')
39
+ .description('Alias for cloving generate commit')
40
+ .option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
41
+ .option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
42
+ .action(commit_1.default);
43
+ // Generate commands
44
+ const generate = program
45
+ .command('generate')
46
+ .description('Generate various items like unit-tests and code reviews');
47
+ generate
20
48
  .command('commit')
21
49
  .description('Generate a commit message and commit the changes')
22
50
  .option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
51
+ .option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
23
52
  .action(commit_1.default);
24
- program
53
+ generate
25
54
  .command('unit-tests')
26
- .description('Generate unit tests for the changes')
55
+ .description('Generate unit tests (if you don\'t specify filenames, it will generate tests for commited changes that differ from the main/master branch)')
27
56
  .option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
28
57
  .option('-f, --files <filenames...>', 'Specify filenames for the unit tests')
58
+ .option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
29
59
  .action(unit_tests_1.default);
30
- program
31
- .command('models')
32
- .description('List available models')
33
- .action(models_1.default);
34
- program
60
+ generate
35
61
  .command('review')
36
- .description('Review the changes and propose ')
62
+ .description('Review the code for commited changes that differ from the main/master branch')
37
63
  .option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
64
+ .option('-m, --model <model>', 'Select the model to use (e.g., openai, claude, ollama, ollama:llama3, claude:claude-3-5-sonnet-20240620)')
38
65
  .action(review_1.default);
39
- program
66
+ // Project commands
67
+ const project = program
68
+ .command('project')
69
+ .description('Commands for cloving a project');
70
+ project
40
71
  .command('init')
41
- .description('Setup cloving in the current project')
42
- .option('-s, --silent', 'Run the command without asking for confirmation of submitting prompts')
43
- .action(init_1.default);
44
- program
45
- .command('config')
46
- .description('Configure cloving with your API key and model')
47
- .action(config_1.default);
72
+ .description('Setup a new cloving project')
73
+ .action(init_2.default);
74
+ project
75
+ .command('plan')
76
+ .description('Plan a cloving project')
77
+ .action(plan_1.default);
78
+ project
79
+ .command('build')
80
+ .description('Build the next step in the project')
81
+ .action(build_1.default);
82
+ project
83
+ .command('complete')
84
+ .description('Clean up and finalize the project')
85
+ .action(complete_1.default);
48
86
  program.parse(process.argv);
@@ -3,13 +3,10 @@ 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.readClovingConfig = exports.getConfig = exports.saveConfig = exports.fetchModels = exports.promptUser = exports.runCommand = exports.generateFileList = exports.checkForSpecialFiles = exports.collectSpecialFileContents = exports.SPECIAL_FILES = exports.CONFIG_PATH = void 0;
6
+ exports.fetchModels = exports.promptUser = exports.runCommand = exports.generateFileList = exports.checkForSpecialFiles = exports.collectSpecialFileContents = exports.SPECIAL_FILES = void 0;
7
7
  const child_process_1 = require("child_process");
8
8
  const readline_1 = __importDefault(require("readline"));
9
9
  const fs_1 = __importDefault(require("fs"));
10
- const os_1 = __importDefault(require("os"));
11
- const path_1 = __importDefault(require("path"));
12
- exports.CONFIG_PATH = path_1.default.join(os_1.default.homedir(), '.cloving.json');
13
10
  exports.SPECIAL_FILES = [
14
11
  'package.json',
15
12
  'Gemfile',
@@ -122,47 +119,3 @@ const fetchModels = async () => {
122
119
  }
123
120
  };
124
121
  exports.fetchModels = fetchModels;
125
- const saveConfig = (config) => {
126
- fs_1.default.writeFileSync(exports.CONFIG_PATH, JSON.stringify(config, null, 2));
127
- console.log(`Configuration saved to ${exports.CONFIG_PATH}`);
128
- };
129
- exports.saveConfig = saveConfig;
130
- const getConfig = (options) => {
131
- try {
132
- if (fs_1.default.existsSync(exports.CONFIG_PATH)) {
133
- const rawConfig = fs_1.default.readFileSync(exports.CONFIG_PATH, 'utf-8');
134
- const config = JSON.parse(rawConfig);
135
- if (options.silent) {
136
- return { ...config, silent: true };
137
- }
138
- else {
139
- return config;
140
- }
141
- }
142
- }
143
- catch (err) {
144
- console.error('Error reading configuration:', err);
145
- }
146
- if (process.env.CLOVING_MODEL) {
147
- return {
148
- models: { [`${process.env.CLOVING_MODEL}`]: process.env.CLOVING_API_KEY ?? '' },
149
- primaryModel: process.env.CLOVING_MODEL,
150
- silent: options.silent || false
151
- };
152
- }
153
- else {
154
- return { models: {}, primaryModel: null, silent: options.silent || false };
155
- }
156
- };
157
- exports.getConfig = getConfig;
158
- const readClovingConfig = () => {
159
- const configPath = path_1.default.resolve(process.cwd(), 'cloving.json');
160
- if (fs_1.default.existsSync(configPath)) {
161
- const configFile = fs_1.default.readFileSync(configPath, 'utf-8');
162
- return JSON.parse(configFile);
163
- }
164
- else {
165
- throw new Error('cloving.json file not found');
166
- }
167
- };
168
- exports.readClovingConfig = readClovingConfig;
@@ -0,0 +1,168 @@
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.getTestingDirectory = exports.getAllFiles = exports.removeProjectConfig = exports.saveProjectConfig = exports.getProjectConfig = exports.getPrimaryModel = exports.saveConfig = exports.readClovingConfig = exports.getConfig = exports.CLOVINGPROJECT_PATH = exports.CLOVINGFILE_PATH = exports.CLOVING_CONFIG_PATH = void 0;
7
+ const os_1 = __importDefault(require("os"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const ini_1 = __importDefault(require("ini"));
11
+ const child_process_1 = require("child_process");
12
+ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
13
+ exports.CLOVING_CONFIG_PATH = path_1.default.join(os_1.default.homedir(), '.clovingconfig');
14
+ exports.CLOVINGFILE_PATH = 'cloving.json';
15
+ exports.CLOVINGPROJECT_PATH = '.clovingprojects';
16
+ let clovingConfig;
17
+ const getConfig = (options) => {
18
+ try {
19
+ if (fs_1.default.existsSync(exports.CLOVING_CONFIG_PATH)) {
20
+ const rawConfig = fs_1.default.readFileSync(exports.CLOVING_CONFIG_PATH, 'utf-8');
21
+ const config = ini_1.default.parse(rawConfig);
22
+ if (options.silent) {
23
+ config.globalSilent = true;
24
+ }
25
+ return config;
26
+ }
27
+ }
28
+ catch (err) {
29
+ console.error('Error reading configuration:', err);
30
+ }
31
+ if (process.env.CLOVING_MODEL) {
32
+ const [provider, ...modelParts] = process.env.CLOVING_MODEL.split(':');
33
+ const model = modelParts.join(':');
34
+ return {
35
+ models: {
36
+ [provider]: {
37
+ [model]: {
38
+ apiKey: process.env.CLOVING_API_KEY ?? '',
39
+ primary: true,
40
+ priority: 10,
41
+ silent: options.silent || false,
42
+ trust: false
43
+ }
44
+ }
45
+ },
46
+ globalSilent: options.silent || false
47
+ };
48
+ }
49
+ else {
50
+ return { models: {}, globalSilent: options.silent || false };
51
+ }
52
+ };
53
+ exports.getConfig = getConfig;
54
+ const readClovingConfig = () => {
55
+ if (clovingConfig) {
56
+ return clovingConfig;
57
+ }
58
+ else if (fs_1.default.existsSync(exports.CLOVINGFILE_PATH)) {
59
+ const configFile = fs_1.default.readFileSync(exports.CLOVINGFILE_PATH, 'utf-8');
60
+ clovingConfig = JSON.parse(configFile);
61
+ return clovingConfig;
62
+ }
63
+ else {
64
+ throw new Error(`${exports.CLOVING_CONFIG_PATH} file not found`);
65
+ }
66
+ };
67
+ exports.readClovingConfig = readClovingConfig;
68
+ const saveConfig = (config) => {
69
+ const iniString = ini_1.default.stringify(config);
70
+ fs_1.default.writeFileSync(exports.CLOVING_CONFIG_PATH, iniString);
71
+ console.log(`Configuration saved to ${exports.CLOVING_CONFIG_PATH}`);
72
+ };
73
+ exports.saveConfig = saveConfig;
74
+ const getPrimaryModel = (partialModel) => {
75
+ try {
76
+ const config = (0, exports.getConfig)({});
77
+ const models = [];
78
+ for (const provider in config.models) {
79
+ for (const model in config.models[provider]) {
80
+ if (!partialModel || `${provider}:${model}`.startsWith(partialModel)) {
81
+ models.push({
82
+ provider,
83
+ model,
84
+ config: config.models[provider][model],
85
+ });
86
+ }
87
+ }
88
+ }
89
+ models.sort((a, b) => parseInt(`${b.config.priority}`, 10) - parseInt(`${a.config.priority}`, 10));
90
+ return models.length > 0 ? models[0] : null;
91
+ }
92
+ catch (err) {
93
+ console.error('Error reading model from configuration:', err);
94
+ return null;
95
+ }
96
+ };
97
+ exports.getPrimaryModel = getPrimaryModel;
98
+ const getProjectConfig = (name) => {
99
+ const projectConfigPath = path_1.default.join(exports.CLOVINGPROJECT_PATH, name, 'config.ini');
100
+ if (fs_1.default.existsSync(projectConfigPath)) {
101
+ const configFile = fs_1.default.readFileSync(projectConfigPath, 'utf-8');
102
+ return ini_1.default.parse(configFile);
103
+ }
104
+ else {
105
+ throw new Error(`${projectConfigPath} file not found`);
106
+ }
107
+ };
108
+ exports.getProjectConfig = getProjectConfig;
109
+ const saveProjectConfig = (name, config) => {
110
+ const projectConfigPath = path_1.default.join(exports.CLOVINGPROJECT_PATH, name, 'config.ini');
111
+ const projectConfigDir = path_1.default.dirname(projectConfigPath);
112
+ // Create directory if it doesn't exist
113
+ if (!fs_1.default.existsSync(projectConfigDir)) {
114
+ fs_1.default.mkdirSync(projectConfigDir, { recursive: true });
115
+ }
116
+ const iniString = ini_1.default.stringify(config);
117
+ fs_1.default.writeFileSync(projectConfigPath, iniString);
118
+ console.log(`Configuration saved to ${projectConfigPath}`);
119
+ };
120
+ exports.saveProjectConfig = saveProjectConfig;
121
+ const removeProjectConfig = (name) => {
122
+ const projectConfigPath = path_1.default.join(exports.CLOVINGPROJECT_PATH, name, 'config.ini');
123
+ if (fs_1.default.existsSync(projectConfigPath)) {
124
+ fs_1.default.unlinkSync(projectConfigPath);
125
+ console.log(`Project completed and file removed: ${projectConfigPath}`);
126
+ }
127
+ else {
128
+ console.error(`Configuration not found: ${projectConfigPath}`);
129
+ }
130
+ };
131
+ exports.removeProjectConfig = removeProjectConfig;
132
+ const setupTestingPrompt = async (gpt, config) => {
133
+ const prompt = `I have the following cloving.json configuration file:
134
+
135
+ ==== begin cloving.json ====
136
+ ${JSON.stringify(config, null, 2)}
137
+ ==== end cloving.json ====
138
+
139
+ Please provide instructions on how to set up testing for this project.`;
140
+ const setupInstructions = await gpt.generateText({ prompt });
141
+ console.log(setupInstructions);
142
+ process.exit(0);
143
+ };
144
+ const getAllFiles = async (options, forTesting) => {
145
+ const gpt = new cloving_gpt_1.default(options);
146
+ // Read the cloving.json file
147
+ const config = (0, exports.readClovingConfig)();
148
+ const testingDirectory = (0, exports.getTestingDirectory)();
149
+ let allSrcFiles = [];
150
+ if (forTesting && !testingDirectory) {
151
+ await setupTestingPrompt(gpt, config);
152
+ }
153
+ // Collect srcFiles for each language with specified directory and extension
154
+ for (const language of config.languages) {
155
+ if (language.directory && language.extension) {
156
+ const srcFiles = (0, child_process_1.execFileSync)('find', [language.directory, '-type', 'f', '-name', `*${language.extension}`]).toString().trim().split('\n');
157
+ allSrcFiles = allSrcFiles.concat(srcFiles);
158
+ }
159
+ }
160
+ // Ensure allSrcFiles is unique
161
+ return Array.from(new Set(allSrcFiles));
162
+ };
163
+ exports.getAllFiles = getAllFiles;
164
+ const getTestingDirectory = () => {
165
+ const config = (0, exports.readClovingConfig)();
166
+ return config.testingFrameworks?.find((framework) => framework.directory)?.directory;
167
+ };
168
+ exports.getTestingDirectory = getTestingDirectory;