cloving 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,81 +3,68 @@ 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
- const copy_paste_1 = __importDefault(require("copy-paste"));
7
6
  const prompts_1 = require("@inquirer/prompts");
8
7
  const cli_highlight_1 = __importDefault(require("cli-highlight"));
9
- const fs_1 = __importDefault(require("fs"));
10
- const path_1 = __importDefault(require("path"));
8
+ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
11
9
  const command_utils_1 = require("../utils/command_utils");
12
- const config_utils_1 = require("../utils/config_utils");
13
10
  const string_utils_1 = require("../utils/string_utils");
14
11
  class CodeManager {
15
- constructor(gpt, options, allSrcFiles) {
16
- this.gpt = gpt;
12
+ constructor(options) {
17
13
  this.options = options;
18
- this.allSrcFiles = allSrcFiles;
19
14
  this.contextFiles = {};
15
+ this.chatHistory = [];
16
+ this.gpt = new cloving_gpt_1.default(options);
20
17
  }
21
- generateCodePrompt(prompt, previousCode) {
22
- const specialFileContents = (0, command_utils_1.collectSpecialFileContents)();
23
- const specialFiles = Object.keys(specialFileContents).map((file) => `### Contents of ${file}\n\n${JSON.stringify(specialFileContents[file], null, 2)}\n\n`).join('\n');
24
- const contextFileContents = Object.keys(this.contextFiles).map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`).join('\n');
25
- let promptText = `### Request
26
-
27
- Generate code that does the following: ${prompt}
28
-
29
- ### Description of App
30
-
31
- ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
32
-
33
- ${specialFiles}
34
-
35
- ${contextFileContents}
36
-
37
- ### Directory structure
38
-
39
- ${this.allSrcFiles.join('\n')}
40
-
41
- `;
42
- if (previousCode) {
43
- promptText += `### Previously generated code
44
-
45
- ${previousCode}
46
-
47
- `;
18
+ async initialize() {
19
+ try {
20
+ if (this.options.files) {
21
+ for (const file of this.options.files) {
22
+ this.contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(file, this.contextFiles, this.options);
23
+ }
24
+ }
25
+ else {
26
+ let includeMoreFiles = true;
27
+ while (includeMoreFiles) {
28
+ const contextFile = await (0, prompts_1.input)({
29
+ message: `Enter the relative path of a file or directory you would like to include as context (or press enter to continue):`,
30
+ });
31
+ if (contextFile) {
32
+ this.contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(contextFile, this.contextFiles, this.options);
33
+ }
34
+ else {
35
+ includeMoreFiles = false;
36
+ }
37
+ }
38
+ }
39
+ if (!this.options.prompt) {
40
+ const userPrompt = await (0, prompts_1.input)({
41
+ message: 'What would you like the code to do:',
42
+ });
43
+ this.options.prompt = userPrompt;
44
+ }
45
+ let response = await this.generateCode(this.options.prompt);
46
+ this.displayGeneratedCode(response);
47
+ if (this.options.save) {
48
+ const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
49
+ await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
50
+ }
51
+ else {
52
+ await this.handleUserAction(response, this.options.prompt);
53
+ }
48
54
  }
49
- promptText += `### Example of a well-structured response
50
-
51
- Here are the files that I would like to generate:
52
-
53
- 1. **src/commands/generate/shell.ts**
54
-
55
- \`\`\`typescript
56
- import inquirer from 'inquirer'
57
- import highlight from 'cli-highlight'
58
- import { execSync } from 'child_process'
59
- import ncp from 'copy-paste'
60
- import { extractMarkdown } from '../../utils/string_utils'
61
- import { getConfig } from '../../utils/config_utils'
62
- import ClovingGPT from '../../cloving_gpt'
63
- import type { ClovingGPTOptions } from '../../utils/types'
64
-
65
- const generateShellPrompt = (prompt: string | undefined): string => {
66
- const shell = execSync('echo $SHELL').toString().trim()
67
- const os = execSync('echo $OSTYPE').toString().trim()
68
- 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.
69
-
70
- Do not add any commentary or context to the message other than the commit message itself.
71
-
72
- An example of the output for this should look like the following:
73
- \`\`\`
74
-
75
- ### Request
76
-
77
- Generate code that does the following: ${prompt}
78
-
79
- Do not use any data from the example response structure, only use the structure. Generate the code and include filenames with paths to the code files mentioned and do not be lazy and ask me to keep the existing code or show things like previous code remains unchanged, always include existing code in the response.`;
80
- return promptText;
55
+ catch (err) {
56
+ const error = err;
57
+ console.error('Could not generate code', error.message);
58
+ }
59
+ }
60
+ async generateCode(userPrompt) {
61
+ if (this.chatHistory.length === 0) {
62
+ const systemPrompt = (0, command_utils_1.generateCodegenPrompt)(this.contextFiles);
63
+ this.chatHistory.push({ role: 'user', content: systemPrompt });
64
+ this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
65
+ }
66
+ this.chatHistory.push({ role: 'user', content: userPrompt });
67
+ return await this.gpt.generateText({ prompt: userPrompt, messages: this.chatHistory });
81
68
  }
82
69
  displayGeneratedCode(rawCodeCommand) {
83
70
  (0, string_utils_1.parseMarkdownInstructions)(rawCodeCommand).map(code => {
@@ -99,19 +86,11 @@ Do not use any data from the example response structure, only use the structure.
99
86
  }
100
87
  });
101
88
  }
102
- async updateContextFiles(fileContents) {
103
- for (const file of Object.keys(fileContents)) {
104
- if (fileContents[file]) {
105
- this.contextFiles[file] = fileContents[file];
106
- }
107
- }
108
- }
109
- async handleUserAction(rawCodeCommand, prompt) {
110
- const fileContents = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
111
- const files = Object.keys(fileContents);
89
+ async handleUserAction(response, prompt) {
90
+ const currentNewBlocks = (0, string_utils_1.extractCurrentNewBlocks)(response);
91
+ const files = Array.from(new Set(currentNewBlocks.map(block => block.filePath)));
112
92
  if (this.options.save) {
113
- await (0, string_utils_1.saveGeneratedFiles)(fileContents);
114
- await this.updateContextFiles(fileContents);
93
+ await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
115
94
  return;
116
95
  }
117
96
  const action = await (0, prompts_1.select)({
@@ -143,42 +122,16 @@ Do not use any data from the example response structure, only use the structure.
143
122
  includeMoreFiles = false;
144
123
  }
145
124
  }
146
- const newRawCodeCommand = await this.generateCode(newPrompt, rawCodeCommand);
147
- this.displayGeneratedCode(newRawCodeCommand);
148
- await this.handleUserAction(newRawCodeCommand, newPrompt);
125
+ const newResponse = await this.generateCode(newPrompt);
126
+ this.displayGeneratedCode(newResponse);
127
+ await this.handleUserAction(newResponse, newPrompt);
149
128
  break;
150
129
  case 'explain':
151
- const explainPrompt = this.generateExplainCodePrompt(rawCodeCommand);
152
- const explainCodeCommand = await this.gpt.generateText({ prompt: explainPrompt });
130
+ const explainPrompt = this.generateExplainCodePrompt(response);
131
+ this.chatHistory.push({ role: 'user', content: explainPrompt });
132
+ const explainCodeCommand = await this.gpt.generateText({ prompt: explainPrompt, messages: this.chatHistory });
153
133
  console.log((0, cli_highlight_1.default)(explainCodeCommand, { language: 'markdown' }));
154
134
  break;
155
- case 'copySource':
156
- let copyAnother = true;
157
- while (copyAnother) {
158
- const fileToCopy = await (0, prompts_1.select)({
159
- message: 'Which file do you want to copy to the clipboard?',
160
- choices: files.map(file => ({ name: file, value: file })),
161
- });
162
- if (fileContents[fileToCopy]) {
163
- copy_paste_1.default.copy(fileContents[fileToCopy], () => {
164
- console.log(`${fileToCopy} copied to clipboard`);
165
- });
166
- }
167
- else {
168
- console.log('File content not found.');
169
- }
170
- const copyMore = await (0, prompts_1.confirm)({
171
- message: 'Do you want to copy another file?',
172
- default: true,
173
- });
174
- copyAnother = copyMore;
175
- }
176
- break;
177
- case 'copyAll':
178
- copy_paste_1.default.copy(rawCodeCommand, () => {
179
- console.log('Entire response copied to clipboard');
180
- });
181
- break;
182
135
  case 'save':
183
136
  let saveAnother = true;
184
137
  while (saveAnother) {
@@ -186,16 +139,7 @@ Do not use any data from the example response structure, only use the structure.
186
139
  message: 'Which file do you want to save?',
187
140
  choices: files.map(file => ({ name: file, value: file })),
188
141
  });
189
- if (fileContents[fileToSave]) {
190
- const filePath = path_1.default.resolve(fileToSave);
191
- await fs_1.default.promises.mkdir(path_1.default.dirname(filePath), { recursive: true });
192
- await fs_1.default.promises.writeFile(filePath, fileContents[fileToSave]);
193
- console.log(`${fileToSave} has been saved.`);
194
- await this.updateContextFiles({ [fileToSave]: fileContents[fileToSave] });
195
- }
196
- else {
197
- console.log('File content not found.');
198
- }
142
+ await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks.filter(block => block.filePath === fileToSave));
199
143
  const saveMore = await (0, prompts_1.confirm)({
200
144
  message: 'Do you want to save another file?',
201
145
  default: true,
@@ -204,8 +148,7 @@ Do not use any data from the example response structure, only use the structure.
204
148
  }
205
149
  break;
206
150
  case 'saveAll':
207
- await (0, string_utils_1.saveGeneratedFiles)(fileContents);
208
- await this.updateContextFiles(fileContents);
151
+ await (0, string_utils_1.applyAndSaveCurrentNewBlocks)(currentNewBlocks);
209
152
  console.log('All files have been saved.');
210
153
  break;
211
154
  case 'done':
@@ -215,71 +158,9 @@ Do not use any data from the example response structure, only use the structure.
215
158
  generateExplainCodePrompt(prompt) {
216
159
  return `${prompt}
217
160
 
218
- ## Task
161
+ # Task
219
162
 
220
163
  Please briefly explain how the code works in this.`;
221
164
  }
222
- async generateCode(prompt, previousCode) {
223
- const codePrompt = this.generateCodePrompt(prompt, previousCode);
224
- return await this.gpt.generateText({ prompt: codePrompt });
225
- }
226
- async processCode(prompt) {
227
- try {
228
- if (this.options.files) {
229
- let expandedFiles = [];
230
- for (const file of this.options.files) {
231
- const filePath = path_1.default.resolve(file);
232
- if (await fs_1.default.promises.stat(filePath).then(stat => stat.isDirectory()).catch(() => false)) {
233
- const dirFiles = await (0, command_utils_1.getAllFilesInDirectory)(filePath);
234
- expandedFiles = expandedFiles.concat(dirFiles.map(f => path_1.default.relative(process.cwd(), f)));
235
- }
236
- else {
237
- expandedFiles.push(path_1.default.relative(process.cwd(), filePath));
238
- }
239
- }
240
- this.options.files = expandedFiles;
241
- for (const file of this.options.files) {
242
- const filePath = path_1.default.resolve(file);
243
- if (await fs_1.default.promises.stat(filePath).then(stat => stat.isFile()).catch(() => false)) {
244
- const content = await fs_1.default.promises.readFile(filePath, 'utf-8');
245
- this.contextFiles[file] = content;
246
- }
247
- }
248
- }
249
- else {
250
- let includeMoreFiles = true;
251
- while (includeMoreFiles) {
252
- const contextFile = await (0, prompts_1.input)({
253
- message: `Enter the relative path of a file or directory you would like to include as context (or press enter to continue):`,
254
- });
255
- if (contextFile) {
256
- this.contextFiles = await (0, command_utils_1.addFileOrDirectoryToContext)(contextFile, this.contextFiles, this.options);
257
- }
258
- else {
259
- includeMoreFiles = false;
260
- }
261
- }
262
- }
263
- if (!prompt) {
264
- const userPrompt = await (0, prompts_1.input)({
265
- message: 'What would you like the code to do:',
266
- });
267
- prompt = userPrompt;
268
- }
269
- let rawCodeCommand = await this.generateCode(prompt);
270
- this.displayGeneratedCode(rawCodeCommand);
271
- if (this.options.save) {
272
- const fileContents = (0, string_utils_1.extractFilesAndContent)(rawCodeCommand);
273
- await (0, string_utils_1.saveGeneratedFiles)(fileContents);
274
- await this.updateContextFiles(fileContents);
275
- }
276
- else {
277
- await this.handleUserAction(rawCodeCommand, prompt);
278
- }
279
- }
280
- catch (error) {
281
- console.error('Could not generate code', error);
282
- }
283
- }
284
165
  }
285
166
  exports.default = CodeManager;
@@ -0,0 +1,55 @@
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
+ const fs_1 = __importDefault(require("fs"));
7
+ const path_1 = __importDefault(require("path"));
8
+ const child_process_1 = require("child_process");
9
+ const string_utils_1 = require("../utils/string_utils");
10
+ const git_utils_1 = require("../utils/git_utils");
11
+ const config_utils_1 = require("../utils/config_utils");
12
+ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
13
+ class CommitManager {
14
+ constructor(options) {
15
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
16
+ this.gpt = new cloving_gpt_1.default(options);
17
+ }
18
+ async commit() {
19
+ try {
20
+ // Get the git diff
21
+ const diff = (0, child_process_1.execSync)('git diff HEAD').toString().trim();
22
+ // Check if the diff is blank
23
+ if (!diff) {
24
+ console.error('No changes to commit.');
25
+ return;
26
+ }
27
+ // Generate the prompt for commit message
28
+ const prompt = (0, git_utils_1.generateCommitMessagePrompt)(diff);
29
+ // Instantiate ClovingGPT and get the commit message
30
+ const rawCommitMessage = await this.gpt.generateText({ prompt });
31
+ // Clean the commit message using extractMarkdown
32
+ const commitMessage = (0, string_utils_1.extractMarkdown)(rawCommitMessage);
33
+ // Write the commit message to a temporary file
34
+ const tempCommitFilePath = path_1.default.join('.git', 'SUGGESTED_COMMIT_EDITMSG');
35
+ fs_1.default.writeFileSync(tempCommitFilePath, commitMessage);
36
+ // Commit the changes using the generated commit message
37
+ try {
38
+ (0, child_process_1.execFileSync)('git', ['commit', '-a', '--edit', '--file', tempCommitFilePath], { stdio: 'inherit' });
39
+ }
40
+ catch (commitError) {
41
+ // If commit is canceled (non-zero exit), handle it here
42
+ console.log('Commit was canceled or failed.');
43
+ }
44
+ // Remove the temporary file using fs
45
+ fs_1.default.unlink(tempCommitFilePath, (err) => {
46
+ if (err)
47
+ throw err;
48
+ });
49
+ }
50
+ catch (error) {
51
+ console.error('Could not generate commit message:', error);
52
+ }
53
+ }
54
+ }
55
+ exports.default = CommitManager;
@@ -60,7 +60,7 @@ class ProxyManager {
60
60
  const { messages, stream } = req.body;
61
61
  const content = messages.map((message) => message.content).join('\n\n');
62
62
  const contextFileContents = Object.keys(this.contextFiles).map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`).join('\n');
63
- const prompt = `### Task
63
+ const prompt = `# Task
64
64
 
65
65
  ${content}
66
66
 
@@ -70,7 +70,7 @@ ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
70
70
 
71
71
  ${contextFileContents}
72
72
 
73
- ### Task
73
+ # Task
74
74
 
75
75
  ${content}`;
76
76
  if (stream) {
@@ -8,15 +8,18 @@ const prompts_1 = require("@inquirer/prompts");
8
8
  const copy_paste_1 = __importDefault(require("copy-paste"));
9
9
  const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
+ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
11
12
  const git_utils_1 = require("../utils/git_utils");
12
13
  const string_utils_1 = require("../utils/string_utils");
13
14
  const command_utils_1 = require("../utils/command_utils");
14
15
  const config_utils_1 = require("../utils/config_utils");
15
16
  class ReviewManager {
16
- constructor(gpt, options) {
17
+ constructor(options) {
17
18
  this.contextFiles = {};
18
- this.gpt = gpt;
19
+ this.chatHistory = [];
20
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
19
21
  this.options = options;
22
+ this.gpt = new cloving_gpt_1.default(options);
20
23
  }
21
24
  async loadContextFiles() {
22
25
  if (this.options.files) {
@@ -41,12 +44,9 @@ class ReviewManager {
41
44
  }
42
45
  }
43
46
  }
44
- generatePrompt() {
45
- let promptText = `### Prompt
46
-
47
- ${this.options.prompt || 'Please provide a code review.'}
48
-
49
- ### Example of a well-structured response
47
+ generateReviewPrompt() {
48
+ if (this.chatHistory.length === 0) {
49
+ let systemPrompt = `### Example of a well-structured response
50
50
 
51
51
  # Code Review: Enhanced Code Generation and File Handling
52
52
 
@@ -95,11 +95,11 @@ ${this.options.prompt || 'Please provide a code review.'}
95
95
  // ...
96
96
  }
97
97
  \`\`\`
98
+
98
99
  `;
99
- if (this.options.files) {
100
- const contextFileContents = Object.keys(this.contextFiles).map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`).join('\n');
101
- promptText += `
102
- ### Description of App
100
+ if (this.options.files) {
101
+ const contextFileContents = Object.keys(this.contextFiles).map((file) => `### Contents of ${file}\n\n${this.contextFiles[file]}\n\n`).join('\n');
102
+ systemPrompt += `### Description of App
103
103
 
104
104
  ${JSON.stringify((0, config_utils_1.getClovingConfig)(), null, 2)}
105
105
 
@@ -110,26 +110,31 @@ ${contextFileContents}
110
110
  I would like you to explain the code and document a description of it.
111
111
  List any bugs in the new code as well as recommended fixes for those bugs with code examples.
112
112
  Format the output of this code review in Markdown format.`;
113
- }
114
- else {
115
- promptText += `
116
-
117
- ### Request
113
+ }
114
+ else {
115
+ systemPrompt += `### Request
118
116
 
119
117
  Do not use any data from the example response structure, only use the structure.
120
118
  I would like you to explain why these change are being made and document a description of these changes.
121
119
  Also list any bugs in the new code as well as recommended fixes for those bugs with code examples.
122
120
  Format the output of this code review in Markdown format.`;
121
+ }
122
+ this.chatHistory.push({ role: 'user', content: systemPrompt });
123
+ this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
123
124
  }
124
- return promptText;
125
+ this.chatHistory.push({ role: 'user', content: this.options.prompt || 'Please provide a code review.' });
126
+ return this.options.prompt || 'Please provide a code review.';
125
127
  }
126
128
  async getGitDiffPrompt() {
127
129
  const gitDiff = await (0, git_utils_1.getGitDiff)();
128
- return `==== begin diff =====
130
+ this.options.prompt = `## Diff Content
129
131
  ${gitDiff}
130
- ==== end diff =====
131
132
 
132
- ${this.generatePrompt()}`;
133
+ # Task
134
+
135
+ ${this.options.prompt || 'Please provide a code review.'}`;
136
+ const prompt = this.generateReviewPrompt();
137
+ return prompt;
133
138
  }
134
139
  displayAnalysis(analysis) {
135
140
  (0, string_utils_1.parseMarkdownInstructions)(analysis).map(code => {
@@ -195,8 +200,8 @@ ${this.generatePrompt()}`;
195
200
  async review() {
196
201
  try {
197
202
  await this.loadContextFiles();
198
- const prompt = this.options.files ? this.generatePrompt() : await this.getGitDiffPrompt();
199
- const analysis = await this.gpt.generateText({ prompt });
203
+ const prompt = this.options.files ? this.generateReviewPrompt() : await this.getGitDiffPrompt();
204
+ const analysis = await this.gpt.generateText({ prompt, messages: this.chatHistory });
200
205
  this.displayAnalysis(analysis);
201
206
  await this.handleUserAction(analysis);
202
207
  }
@@ -0,0 +1,106 @@
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
+ const cli_highlight_1 = __importDefault(require("cli-highlight"));
7
+ const copy_paste_1 = __importDefault(require("copy-paste"));
8
+ const child_process_1 = require("child_process");
9
+ const prompts_1 = require("@inquirer/prompts");
10
+ const cloving_gpt_1 = __importDefault(require("../cloving_gpt"));
11
+ const command_utils_1 = require("../utils/command_utils");
12
+ const string_utils_1 = require("../utils/string_utils");
13
+ const config_utils_1 = require("../utils/config_utils");
14
+ class ShellManager {
15
+ constructor(options) {
16
+ this.options = options;
17
+ this.chatHistory = [];
18
+ options.silent = (0, config_utils_1.getConfig)(options).globalSilent || false;
19
+ this.gpt = new cloving_gpt_1.default(options);
20
+ }
21
+ generateExplainShellPrompt(prompt) {
22
+ return `${prompt}
23
+
24
+ # Task
25
+
26
+ Please briefly explain how this shell script works.`;
27
+ }
28
+ async generateShell(prompt) {
29
+ if (this.chatHistory.length === 0) {
30
+ const systemPrompt = (0, command_utils_1.generateShellPrompt)();
31
+ this.chatHistory.push({ role: 'user', content: systemPrompt });
32
+ this.chatHistory.push({ role: 'assistant', content: 'What would you like to do?' });
33
+ }
34
+ this.chatHistory.push({ role: 'user', content: prompt });
35
+ return await this.gpt.generateText({ prompt, messages: this.chatHistory });
36
+ }
37
+ displayGeneratedShell(rawShellCommand) {
38
+ const generatedShell = (0, string_utils_1.extractMarkdown)(rawShellCommand);
39
+ const generatedShellWithoutShebang = generatedShell.replace(/^#!.*?\s/, '');
40
+ console.log((0, cli_highlight_1.default)(generatedShellWithoutShebang));
41
+ }
42
+ async handleUserAction(response, prompt) {
43
+ this.chatHistory.push({ role: 'assistant', content: response });
44
+ const generatedShell = (0, string_utils_1.extractMarkdown)(response);
45
+ const generatedShellWithoutShebang = generatedShell.replace(/^#!.*?\s/, '');
46
+ const action = await (0, prompts_1.select)({
47
+ message: 'What would you like to do?',
48
+ choices: [
49
+ { name: 'Execute', value: 'execute' },
50
+ { name: 'Revise', value: 'revise' },
51
+ { name: 'Explain', value: 'explain' },
52
+ { name: 'Copy to Clipboard', value: 'copy' },
53
+ { name: 'Cancel', value: 'cancel' },
54
+ ],
55
+ });
56
+ switch (action) {
57
+ case 'execute':
58
+ (0, child_process_1.execSync)(generatedShellWithoutShebang, { stdio: 'inherit' });
59
+ break;
60
+ case 'revise':
61
+ const newPrompt = await (0, prompts_1.input)({
62
+ message: 'How would you like to modify the output:',
63
+ });
64
+ const newRawShellCommand = await this.generateShell(newPrompt);
65
+ this.displayGeneratedShell(newRawShellCommand);
66
+ await this.handleUserAction(newRawShellCommand, newPrompt);
67
+ break;
68
+ case 'explain':
69
+ const explainPrompt = this.generateExplainShellPrompt(generatedShellWithoutShebang);
70
+ this.chatHistory.push({ role: 'user', content: explainPrompt });
71
+ const explainShellCommand = await this.gpt.generateText({ prompt: explainPrompt, messages: this.chatHistory });
72
+ console.log((0, cli_highlight_1.default)(explainShellCommand, { language: 'markdown' }));
73
+ break;
74
+ case 'copy':
75
+ copy_paste_1.default.copy(generatedShellWithoutShebang, (err) => {
76
+ if (err) {
77
+ console.error('Error: Unable to copy to clipboard.', err);
78
+ }
79
+ else {
80
+ console.log('Script copied to clipboard.');
81
+ }
82
+ });
83
+ break;
84
+ case 'cancel':
85
+ console.log('Operation cancelled.');
86
+ break;
87
+ }
88
+ }
89
+ async generateAndHandleShell() {
90
+ let { prompt: userPrompt } = this.options;
91
+ try {
92
+ if (!userPrompt) {
93
+ userPrompt = await (0, prompts_1.input)({
94
+ message: 'What would you like to do: '
95
+ });
96
+ }
97
+ const rawShellCommand = await this.generateShell(userPrompt);
98
+ this.displayGeneratedShell(rawShellCommand);
99
+ await this.handleUserAction(rawShellCommand, userPrompt);
100
+ }
101
+ catch (error) {
102
+ console.error('Could not generate shell script', error);
103
+ }
104
+ }
105
+ }
106
+ exports.default = ShellManager;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ const newContent = 'This is new';
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cloving",
3
- "version": "0.1.25",
3
+ "version": "0.3.0",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -36,25 +36,26 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
- "@inquirer/prompts": "^5.3.6",
39
+ "@inquirer/prompts": "^5.3.8",
40
40
  "axios": "^1.7.3",
41
41
  "cli-highlight": "^2.1.11",
42
+ "colors": "^1.4.0",
42
43
  "commander": "^12.1.0",
43
44
  "copy-paste": "^1.5.3",
44
- "eslint": "^9.8.0",
45
+ "eslint": "^9.9.0",
45
46
  "eslint-config-prettier": "^9.1.0",
46
47
  "eslint-config-raine": "^0.5.0",
47
48
  "eslint-config-standard": "^17.1.0",
48
49
  "eslint-plugin-import": "^2.29.1",
49
- "eslint-plugin-jsdoc": "^48.11.0",
50
- "eslint-plugin-n": "^17.10.1",
51
- "eslint-plugin-promise": "^7.0.0",
50
+ "eslint-plugin-jsdoc": "^50.1.0",
51
+ "eslint-plugin-n": "^17.10.2",
52
+ "eslint-plugin-promise": "^7.1.0",
52
53
  "express": "^4.19.2",
53
- "ignore": "^5.3.1",
54
+ "ignore": "^5.3.2",
54
55
  "ini": "^4.1.3",
55
56
  "isbinaryfile": "^5.0.2",
56
57
  "nock": "^13.5.4",
57
- "vite": "^5.3.5",
58
+ "vite": "^5.4.0",
58
59
  "vite-node": "^2.0.5"
59
60
  },
60
61
  "devDependencies": {
@@ -64,11 +65,11 @@
64
65
  "@types/ini": "^4.1.1",
65
66
  "@types/inquirer": "^9.0.7",
66
67
  "@types/jest": "^29.5.12",
67
- "@types/node": "^22.1.0",
68
+ "@types/node": "^22.2.0",
68
69
  "husky": "^9.1.4",
69
70
  "jest": "^29.7.0",
70
71
  "lint-prepush": "^2.2.2",
71
- "lint-staged": "^15.2.8",
72
+ "lint-staged": "^15.2.9",
72
73
  "ts-jest": "^29.2.4",
73
74
  "typescript": "^5.5.4"
74
75
  },