cloving 0.1.0 → 0.1.1-8.1

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.
@@ -7,14 +7,15 @@ exports.init = void 0;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const os_1 = __importDefault(require("os"));
9
9
  const path_1 = __importDefault(require("path"));
10
- const string_utils_1 = require("../utils/string_utils");
10
+ const prompts_1 = require("@inquirer/prompts");
11
11
  const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
12
12
  const ignore_1 = __importDefault(require("ignore"));
13
- const string_utils_2 = require("../utils/string_utils");
13
+ const string_utils_1 = require("../utils/string_utils");
14
+ const config_utils_1 = require("../utils/config_utils");
14
15
  const command_utils_1 = require("../utils/command_utils");
15
16
  // Main function for the describe command
16
17
  const init = async (options) => {
17
- options.silent = (0, command_utils_1.getConfig)().silent || false;
18
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
18
19
  const gpt = new cloving_gpt_1.default(options);
19
20
  const specialFileContents = (0, command_utils_1.collectSpecialFileContents)();
20
21
  const specialFileNames = Object.keys(specialFileContents).map(file => ' - ' + file);
@@ -22,33 +23,30 @@ const init = async (options) => {
22
23
  if (specialFileNames.length > 0) {
23
24
  console.log(`Cloving will analyze the list of files and the contents of the following files:
24
25
 
25
- ${specialFileNames.join("\n")}
26
-
27
- Cloving will send AI a request to summarize the technologies used in this project.
26
+ ${specialFileNames.join("\n")}
28
27
 
29
- This will provide better context for future Cloving requests.
28
+ Cloving will send AI a request to summarize the technologies used in this project.
30
29
 
31
- For increased privacy, you can run \`cloving config\` and make sure to configure a local ollama model (llama3:70b-instruct currently works well for this) so that this data is not sent to an AI service provider.
32
- `);
30
+ This will provide better context for future Cloving requests.`);
33
31
  }
34
32
  else {
35
33
  console.log(`
36
- This script will analyze the list of files in the current directory using GPT to summarize the
37
- technologies used. This will provide better context for future Cloving requests.
34
+ This script will analyze the list of files in the current directory using GPT to summarize the
35
+ technologies used. This will provide better context for future Cloving requests.
38
36
  `);
39
37
  }
40
38
  }
41
- const config = (0, command_utils_1.getConfig)();
42
- if (!config || !config?.primaryModel || !config?.models) {
39
+ const config = (0, config_utils_1.getConfig)(options);
40
+ if (!config || !config?.models) {
43
41
  console.error('No cloving configuration found. Please run `cloving config`');
44
42
  return;
45
43
  }
46
44
  if (!(0, command_utils_1.checkForSpecialFiles)()) {
47
- const continueAnswer = await (0, command_utils_1.promptUser)('No special files detected. Are you currently inside a software project\'s main directory? Do you want to continue analyzing this directory for the Cloving setup process? [Yn] ');
48
- if (continueAnswer.toLowerCase() !== 'y' && continueAnswer.trim() !== '') {
49
- console.log('Operation aborted by the user.');
50
- return;
51
- }
45
+ const technologies = await (0, prompts_1.input)({
46
+ message: 'No special files detected. Please describe the main technologies used in your project:',
47
+ validate: (input) => input.trim().length > 0 || 'Please provide a description of the technologies used.',
48
+ });
49
+ specialFileContents['description'] = technologies;
52
50
  }
53
51
  const tempFilePath = path_1.default.join(os_1.default.tmpdir(), `describe_${Date.now()}.tmp`);
54
52
  try {
@@ -68,10 +66,6 @@ const init = async (options) => {
68
66
  }
69
67
  });
70
68
  const limitedFileList = filteredFileList.slice(0, 100);
71
- if (specialFileNames.length === 0) {
72
- console.error('No special files found.');
73
- return;
74
- }
75
69
  const projectDetails = {
76
70
  files: limitedFileList,
77
71
  specialFiles: specialFileContents
@@ -80,10 +74,65 @@ const init = async (options) => {
80
74
  ${JSON.stringify(projectDetails, null, 2)}
81
75
 
82
76
  Please return JSON-formatted metadata about the project, including:
77
+
83
78
  - The programming language(s) used
84
79
  - The detected framework(s) (if any)
85
80
  - The version of the language(s)
86
81
 
82
+ Here is a typescript interface for the expected response:
83
+
84
+ interface LanguageConfig {
85
+ name: string
86
+ version?: string
87
+ primary?: boolean
88
+ directory: string
89
+ extension: string
90
+ }
91
+
92
+ interface FrameworkConfig {
93
+ name: string
94
+ type: string
95
+ version?: string
96
+ primary?: boolean
97
+ directory?: string
98
+ extension?: string
99
+ }
100
+
101
+ interface TestingFrameworkConfig {
102
+ name: string
103
+ type: string
104
+ version?: string
105
+ directory?: string
106
+ }
107
+
108
+ interface BuildToolConfig {
109
+ name: string
110
+ type: string
111
+ version?: string
112
+ }
113
+
114
+ interface LinterConfig {
115
+ name: string
116
+ version?: string
117
+ type?: string
118
+ }
119
+
120
+ interface DatabaseConfig {
121
+ name: string
122
+ primary?: boolean
123
+ }
124
+
125
+ export interface ClovingfileConfig {
126
+ languages: LanguageConfig[]
127
+ frameworks: FrameworkConfig[]
128
+ testingFrameworks?: TestingFrameworkConfig[]
129
+ buildTools: BuildToolConfig[]
130
+ packageManager: string
131
+ linters: LinterConfig[]
132
+ databases?: DatabaseConfig[]
133
+ projectType: string
134
+ }
135
+
87
136
  Here is an example response:
88
137
 
89
138
  {
@@ -91,18 +140,24 @@ Here is an example response:
91
140
  {
92
141
  "name": "TypeScript",
93
142
  "version": "~> 5.5.3"
94
- "primary": true
143
+ "primary": true,
144
+ "directory": "src",
145
+ "extension": ".ts"
95
146
  },
96
147
  {
97
148
  "name": "JavaScript",
98
- "version": "ES6+"
149
+ "version": "ES6+",
150
+ "directory": "src",
151
+ "extension": ".js"
99
152
  }
100
153
  ],
101
154
  "frameworks": [
102
155
  {
103
156
  "name": "Node.js",
104
157
  "type": "Runtime environment",
105
- "primary": true
158
+ "primary": true,
159
+ "directory": "src",
160
+ "extension": ".js"
106
161
  }
107
162
  ],
108
163
  "testingFrameworks": [
@@ -140,21 +195,19 @@ Here is an example response:
140
195
  ],
141
196
  "projectType": "Command-line tool",
142
197
  }`;
143
- if (process.env.DEBUG === '1') {
144
- (0, string_utils_1.estimateTokens)(prompt).then(tokenCount => {
145
- console.log(`Estimated token count: ${tokenCount}`);
146
- });
147
- }
148
198
  const aiChatResponse = await gpt.generateText({ prompt });
149
- const cleanAiChatResponse = (0, string_utils_2.extractJsonMetadata)(aiChatResponse);
199
+ const cleanAiChatResponse = (0, string_utils_1.extractJsonMetadata)(aiChatResponse);
150
200
  fs_1.default.writeFileSync(tempFilePath, cleanAiChatResponse);
151
201
  // Save the AI chat response to cloving.json
152
202
  fs_1.default.writeFileSync('cloving.json', cleanAiChatResponse);
153
203
  console.log('[done] Project data saved to cloving.json');
154
204
  // Prompt the user if they want to review the generated cloving.json
155
205
  if (!options.silent) {
156
- const reviewAnswer = await (0, command_utils_1.promptUser)('Do you want to review the generated data? [Yn] ');
157
- if (reviewAnswer.toLowerCase() === 'y' || reviewAnswer.trim() === '') {
206
+ const reviewAnswer = await (0, prompts_1.confirm)({
207
+ message: 'Do you want to review the generated data?',
208
+ default: true,
209
+ });
210
+ if (reviewAnswer) {
158
211
  console.log(cleanAiChatResponse);
159
212
  }
160
213
  }
@@ -1,17 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- // models.ts
4
3
  const claude_1 = require("../cloving_gpt/adapters/claude");
5
4
  const openai_1 = require("../cloving_gpt/adapters/openai");
6
5
  const ollama_1 = require("../cloving_gpt/adapters/ollama");
6
+ const gemini_1 = require("../cloving_gpt/adapters/gemini");
7
+ const mistral_1 = require("../cloving_gpt/adapters/mistral");
7
8
  const listModels = async () => {
9
+ claude_1.ClaudeAdapter.listSupportedModels();
10
+ gemini_1.GeminiAdapter.listSupportedModels();
11
+ mistral_1.MistralAdapter.listSupportedModels();
8
12
  try {
9
13
  await ollama_1.OllamaAdapter.listSupportedModels();
10
14
  }
11
15
  catch (error) {
12
16
  // do nothing, no ollama server running
13
17
  }
14
- claude_1.ClaudeAdapter.listSupportedModels();
15
18
  openai_1.OpenAIAdapter.listSupportedModels();
16
19
  };
17
20
  exports.default = listModels;
@@ -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 copy_paste_1 = __importDefault(require("copy-paste"));
8
+ const fs_1 = require("fs");
9
+ const cli_highlight_1 = __importDefault(require("cli-highlight"));
10
+ const prompts_1 = require("@inquirer/prompts");
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 generatePrompt = async (projectName, projectTask, filesList, incompletePlanItem) => {
16
+ const context = [];
17
+ for (const codeFile of filesList) {
18
+ if (await fs_1.promises?.stat(codeFile).then(stat => stat.isFile()).catch(() => false)) {
19
+ const fileContents = await fs_1.promises.readFile(codeFile, 'utf-8');
20
+ context.push(`### **Contents of ${codeFile}**\n\n${fileContents}\n\n### **End of ${codeFile}**`);
21
+ }
22
+ }
23
+ return `==== begin task description ====
24
+ Task name: ${projectName}
25
+ Task description: ${projectTask}
26
+ ==== end task description ====
27
+
28
+ ${context}
29
+
30
+ Please implement the following plan item, please include full file path names in all code snippets:
31
+
32
+ ${incompletePlanItem}`;
33
+ };
34
+ const buildProject = async (options) => {
35
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
36
+ const gpt = new cloving_gpt_1.default(options);
37
+ const projectName = (0, git_utils_1.getCurrentBranchName)();
38
+ const projectConfig = (0, config_utils_1.getProjectConfig)(projectName);
39
+ // Filter out completed plan items for selection
40
+ const planItems = Object.entries(projectConfig.plan || {});
41
+ if (planItems.length === 0) {
42
+ console.log('No plan items found.');
43
+ process.exit(1);
44
+ }
45
+ // Use @inquirer/prompts to select a plan item
46
+ const selectedPlanItemKey = await (0, prompts_1.select)({
47
+ message: 'Select a plan item:',
48
+ choices: planItems.map(([key, item]) => ({ name: key, value: key })),
49
+ });
50
+ const selectedPlanItem = planItems.find(([key]) => key === selectedPlanItemKey);
51
+ if (!selectedPlanItem) {
52
+ console.log('Selected plan item not found.');
53
+ process.exit(1);
54
+ }
55
+ const planText = `## **${selectedPlanItem[0]}**\n\n${selectedPlanItem[1].details.join('\n\n')}`;
56
+ const prompt = await generatePrompt(projectName, projectConfig.task, projectConfig.files || [], planText);
57
+ const projectCode = await gpt.generateText({ prompt });
58
+ (0, string_utils_1.parseMarkdownInstructions)(projectCode).map(code => {
59
+ if (code.trim().startsWith('```')) {
60
+ const lines = code.split('\n');
61
+ const language = code.match(/```(\w+)/)?.[1] || 'plaintext';
62
+ console.log(lines[0]);
63
+ try {
64
+ console.log((0, cli_highlight_1.default)(lines.slice(1, -1).join('\n'), { language }));
65
+ }
66
+ catch (error) {
67
+ // don't highlight if it fails
68
+ console.log(lines.slice(1, -1).join('\n'));
69
+ }
70
+ console.log(lines.slice(-1)[0]);
71
+ }
72
+ else {
73
+ console.log((0, cli_highlight_1.default)(code, { language: 'markdown' }));
74
+ }
75
+ });
76
+ // Prompt the user to copy the project to the clipboard using @inquirer/prompts
77
+ const copyToClipboard = await (0, prompts_1.confirm)({
78
+ message: 'Do you want to copy this project to the clipboard?',
79
+ default: true,
80
+ });
81
+ if (copyToClipboard) {
82
+ copy_paste_1.default.copy(projectCode, () => {
83
+ console.log('Project copied 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,82 @@
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 prompts_1 = require("@inquirer/prompts");
9
+ const cloving_gpt_1 = __importDefault(require("../../cloving_gpt"));
10
+ const config_utils_1 = require("../../utils/config_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 description of my app:
16
+
17
+ ${JSON.stringify((0, config_utils_2.getClovingConfig)(), null, 2)}
18
+
19
+ Here is a list of all my source files:
20
+
21
+ ==== begin list of source files ====
22
+ ${filesList.join("\n")}
23
+ ==== end list of source files ====
24
+
25
+ Please enumerate all the files in the provided list that are pertinent for this task:
26
+
27
+ ==== begin task description ====
28
+ Task name: ${projectName}
29
+ Task description: ${projectTask}
30
+ ==== end task description ====
31
+
32
+ Also, list any test files that might be relevant to these files.
33
+
34
+ Give me this output format for your answer:
35
+
36
+ ## files
37
+
38
+ app/models/foo.rb
39
+ app/views/baz/bar.html.erb
40
+
41
+ ## relevant test files
42
+
43
+ test/models/foo_test.rb
44
+ test/controllers/baz_controller_test.rb`;
45
+ };
46
+ const initProject = async (options) => {
47
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
48
+ const gpt = new cloving_gpt_1.default(options);
49
+ const defaultBranchName = await (0, git_utils_1.getDefaultBranchName)();
50
+ const projectName = (0, git_utils_1.getCurrentBranchName)();
51
+ if (projectName === defaultBranchName) {
52
+ 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`);
53
+ process.exit(1);
54
+ }
55
+ const projectTask = await (0, prompts_1.input)({
56
+ message: 'Describe the task you want to accomplish with this project:',
57
+ });
58
+ const prompt = await generatePrompt(projectName, projectTask);
59
+ const contextFiles = await gpt.generateText({ prompt });
60
+ // Initialize variables
61
+ const lines = [];
62
+ const files = [];
63
+ // Read each line of the context files
64
+ contextFiles.split('\n').forEach((line) => {
65
+ if (line.trim()) {
66
+ lines.push(line.trim());
67
+ }
68
+ });
69
+ for (const codeFile of lines) {
70
+ if (await fs_1.promises?.stat(codeFile).then(stat => stat.isFile()).catch(() => false)) {
71
+ files.push(codeFile);
72
+ }
73
+ }
74
+ const projectConfig = {
75
+ name: projectName,
76
+ task: projectTask,
77
+ files
78
+ };
79
+ (0, config_utils_1.saveProjectConfig)(projectName, projectConfig);
80
+ };
81
+ exports.initProject = initProject;
82
+ 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)().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