scai 0.1.8 → 0.1.9

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,17 +7,21 @@ function askUserToChoose(suggestions) {
7
7
  console.log(`${i + 1}) ${msg}`);
8
8
  });
9
9
  console.log(`${suggestions.length + 1}) šŸ” Regenerate suggestions`);
10
+ console.log(`${suggestions.length + 2}) āœļø Write your own commit message`);
10
11
  const rl = readline.createInterface({
11
12
  input: process.stdin,
12
13
  output: process.stdout,
13
14
  });
14
- rl.question(`\nšŸ‘‰ Choose a commit message [1-${suggestions.length + 1}]: `, (answer) => {
15
+ rl.question(`\nšŸ‘‰ Choose a commit message [1-${suggestions.length + 2}]: `, (answer) => {
15
16
  rl.close();
16
17
  const choice = parseInt(answer, 10);
17
- if (isNaN(choice) || choice < 1 || choice > suggestions.length + 1) {
18
+ if (isNaN(choice) || choice < 1 || choice > suggestions.length + 2) {
18
19
  console.log('āš ļø Invalid selection. Using the first suggestion by default.');
19
20
  resolve(0);
20
21
  }
22
+ else if (choice === suggestions.length + 2) {
23
+ resolve('custom');
24
+ }
21
25
  else {
22
26
  resolve(choice - 1); // Return 0-based index (0 to 3)
23
27
  }
@@ -45,6 +49,18 @@ async function generateSuggestions(prompt) {
45
49
  }
46
50
  return messages;
47
51
  }
52
+ function promptCustomMessage() {
53
+ return new Promise((resolve) => {
54
+ const rl = readline.createInterface({
55
+ input: process.stdin,
56
+ output: process.stdout,
57
+ });
58
+ rl.question('\nšŸ“ Enter your custom commit message:\n> ', (input) => {
59
+ rl.close();
60
+ resolve(input.trim());
61
+ });
62
+ });
63
+ }
48
64
  export async function suggestCommitMessage(options) {
49
65
  try {
50
66
  let diff = execSync("git diff", { encoding: "utf-8" }).trim();
@@ -65,13 +81,17 @@ ${diff}`;
65
81
  let message = null;
66
82
  while (message === null) {
67
83
  const suggestions = await generateSuggestions(prompt);
68
- const choiceIndex = await askUserToChoose(suggestions);
69
- if (choiceIndex === suggestions.length) {
84
+ const choice = await askUserToChoose(suggestions);
85
+ if (choice === suggestions.length) {
70
86
  // User chose "Regenerate"
71
87
  console.log('\nšŸ”„ Regenerating suggestions...\n');
72
88
  continue;
73
89
  }
74
- message = suggestions[choiceIndex];
90
+ if (choice === 'custom') {
91
+ message = await promptCustomMessage();
92
+ break;
93
+ }
94
+ message = suggestions[choice];
75
95
  }
76
96
  console.log(`\nāœ… Selected commit message:\n${message}\n`);
77
97
  if (options.commit) {
@@ -80,7 +100,7 @@ ${diff}`;
80
100
  execSync("git add .", { encoding: "utf-8" });
81
101
  }
82
102
  execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, { stdio: 'inherit' });
83
- console.log('āœ… Committed with AI-suggested message.');
103
+ console.log('āœ… Committed with selected message.');
84
104
  }
85
105
  }
86
106
  catch (err) {
@@ -1,44 +1,60 @@
1
- "use strict";
2
- Here;
3
- is;
4
- the;
5
- refactored;
6
- code;
7
- with (comments)
8
- added;
9
- and;
10
- a;
11
- summary;
12
- of;
13
- refactor;
14
- at;
15
- the;
16
- end: `` `typescript
17
- // Import necessary modules for executing child processes, fetch requests, and readline interface
18
- import { execSync } from 'child_process';
19
- import readline from 'readline';
20
- import { fetch } from 'node-fetch';
21
-
22
- // Define an asynchronous function to generate messages based on the provided prompt
23
- async function generateMessages(prompt: string): Promise<string[]> {
24
- // ... (Existing code)
1
+ // Prompt function asks user to choose a commit message from given suggestions
2
+ function askUserToChoose(suggestions) {
3
+ // Create and close readline interface after getting user input
4
+ const rl = readline.createInterface({
5
+ input: process.stdin,
6
+ output: process.stdout,
7
+ });
8
+ rl.question(...);
9
+ rl.close();
25
10
  }
26
-
27
- // Define an asynchronous function to ask for a user choice among the given suggestions
28
- async function askForChoice(options: { suggestions: string[] }): Promise<number> {
29
- // ... (Existing code)
11
+ // Fetches suggestions from the API and processes the response
12
+ async function generateSuggestions(prompt) {
13
+ // Fetch JSON data from the given API endpoint
14
+ const res = await fetch("http://localhost:11434/api/generate", { ... });
15
+ // Filter out any invalid lines from the response and map them to a list of commit messages
16
+ const messages = lines.map(...);
30
17
  }
31
-
32
- // Export an asynchronous function to suggest a commit message based on the given options (with the commit flag for final committing)
33
- export async function suggestCommitMessage({ commit = false }: { commit?: boolean }) {
34
- try {
35
- // ... (Existing code)
36
- } catch (err) {
37
- console.error('āŒ Error in commit message suggestion:', err.message);
38
- }
18
+ // Export function for suggesting and committing a commit message if needed
19
+ export async function suggestCommitMessage(options) {
20
+ try {
21
+ // Get the diff between local changes and staged changes
22
+ let diff = execSync("git diff", { encoding: "utf-8" }).trim();
23
+ if (!diff) {
24
+ diff = execSync("git diff --cached", { encoding: "utf-8" }).trim();
25
+ }
26
+ // Check if there are any changes to suggest a message for
27
+ if (!diff) {
28
+ console.log('āš ļø No staged changes to suggest a message for.');
29
+ return;
30
+ }
31
+ // Construct the prompt based on the diff and generate suggestions using the 'generateSuggestions' function
32
+ const prompt = `...`;
33
+ let message = null;
34
+ // Continuously ask user for their choice until a valid commit message is selected
35
+ while (message === null) {
36
+ const suggestions = await generateSuggestions(prompt);
37
+ // Ask user to choose from the given suggestions and handle 'Regenerate' option
38
+ const choiceIndex = await askUserToChoose(suggestions);
39
+ if (choiceIndex === suggestions.length) {
40
+ console.log('šŸ”„ Regenerating suggestions...\n');
41
+ continue;
42
+ }
43
+ message = suggestions[choiceIndex];
44
+ }
45
+ // Print the selected commit message and commit it if needed
46
+ console.log(...);
47
+ if (options.commit) {
48
+ const commitDiff = execSync("git diff", { encoding: "utf-8" }).trim();
49
+ if (commitDiff) {
50
+ execSync("git add .", { encoding: "utf-8" });
51
+ }
52
+ execSync(`git commit -m "${message.replace(/"/g, '\\"')}"`, { stdio: 'inherit' });
53
+ console.log('āœ… Committed with AI-suggested message.');
54
+ }
55
+ }
56
+ catch (err) {
57
+ // Print error message if an error occurs during the commit message suggestion process
58
+ console.error(...);
59
+ }
39
60
  }
40
-
41
- // Summary of refactor:
42
- // - Added comments to each block for better understanding.
43
- // - Extracted the functions generateMessages and askForChoice into separate blocks for code organization.
44
- ` ``;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ // Import the 'child_process' module from 'node:child_process'.
3
+ // Export a function called 'checkGit', which checks the Git status.
4
+ // Try to execute the command "git rev-parse --is-inside-work-tree" with ignored stdio, ensuring we're in a Git repository.
5
+ // If the Git working directory is clean (no uncommitted changes), log a message indicating this.
6
+ // Otherwise, if there are uncommitted changes, log a warning message.
7
+ // Execute "git status --porcelain" to get a compact representation of the current Git state and store its output as a string.
8
+ // Trim any leading/trailing whitespace from the string.
9
+ // Compare the trimmed string's length to zero. If it is equal to zero, the working directory is clean (length > 0 indicates uncommitted changes).
10
+ // If the Git branch matches the head commit hash of 'origin/main', log a message indicating that the current branch is up-to-date with 'origin/main'.
11
+ // Otherwise, log a message indicating that the current branch is not up-to-date with 'origin/main'.
12
+ // Catch any errors that may occur during Git execution and log an error message.
@@ -1,7 +1,6 @@
1
1
  import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import { runPromptPipeline } from '../pipeline/runPipeline.js';
4
- import { refactorModule } from '../pipeline/modules/refactorModule.js';
5
4
  import { addCommentsModule } from '../pipeline/modules/commentModule.js';
6
5
  import { cleanupModule } from '../pipeline/modules/cleanupModule.js';
7
6
  export async function handleRefactor(filepath, options = {}) {
@@ -28,7 +27,7 @@ export async function handleRefactor(filepath, options = {}) {
28
27
  // Read source code
29
28
  const originalCode = await fs.readFile(filepath, 'utf-8');
30
29
  // Run through pipeline modules
31
- const refactored = await runPromptPipeline([refactorModule, addCommentsModule, cleanupModule], { code: originalCode });
30
+ const refactored = await runPromptPipeline([addCommentsModule, cleanupModule], { code: originalCode });
32
31
  if (!refactored.trim())
33
32
  throw new Error('āš ļø Model returned empty result');
34
33
  // Save refactored output
@@ -1,34 +1,42 @@
1
+ function isNaturalLanguageNoise(line) {
2
+ const trimmed = line.trim().toLowerCase();
3
+ return (trimmed.startsWith('i ') ||
4
+ trimmed.startsWith('here') ||
5
+ trimmed.startsWith('this') ||
6
+ trimmed.startsWith('the following') ||
7
+ trimmed.startsWith('below') ||
8
+ trimmed.startsWith('in this') ||
9
+ trimmed.startsWith('we have') ||
10
+ trimmed.includes('the code above') ||
11
+ trimmed.includes('ensures that') ||
12
+ trimmed.includes('it handles') ||
13
+ trimmed.includes('used to'));
14
+ }
1
15
  export const cleanupModule = {
2
16
  name: 'cleanup',
3
- description: 'Removes markdown formatting and explanations from the output',
17
+ description: 'Remove markdown fences and natural language noise from top/bottom of code',
4
18
  async run({ code }) {
5
- const prompt = `
6
- You are a code cleanup assistant.
7
-
8
- Your task is to take the following code output and:
9
- - Remove any markdown formatting like triple backticks from the top of the file
10
- - Remove any markdown formatting like triple backticks from the bottom of the file
11
- - Keep all actual TypeScript/JavaScript code
12
- - keep all comments that start with // or /* and ends with */
13
- - do not tell me what you have done or make any such similar comments
14
- - do not add any tripple backticks
15
-
16
- Return ONLY the cleaned code, without any markdown or extra text.
17
-
18
- --- CODE START ---
19
- ${code}
20
- --- CODE END ---
21
- `.trim();
22
- const res = await fetch('http://localhost:11434/api/generate', {
23
- method: 'POST',
24
- headers: { 'Content-Type': 'application/json' },
25
- body: JSON.stringify({
26
- model: 'llama3',
27
- prompt,
28
- stream: false
29
- }),
30
- });
31
- const data = await res.json();
32
- return { code: data.response?.trim() ?? '' };
19
+ let lines = code.trim().split('\n');
20
+ // ───── Clean top ─────
21
+ while (lines.length) {
22
+ const line = lines[0].trim();
23
+ if (line === '' || line.startsWith('```') || isNaturalLanguageNoise(line)) {
24
+ lines.shift();
25
+ }
26
+ else {
27
+ break;
28
+ }
29
+ }
30
+ // ───── Clean bottom ─────
31
+ while (lines.length) {
32
+ const line = lines[lines.length - 1].trim();
33
+ if (line === '' || line.startsWith('```') || isNaturalLanguageNoise(line)) {
34
+ lines.pop();
35
+ }
36
+ else {
37
+ break;
38
+ }
39
+ }
40
+ return { code: lines.join('\n').trim() };
33
41
  }
34
42
  };
@@ -5,9 +5,9 @@ export const addCommentsModule = {
5
5
  const prompt = `
6
6
  You are a senior JavaScript engineer.
7
7
 
8
- Add clear and helpful single-line // comments only to each block of the following code.
9
- - Keep the code unchanged otherwise.
10
- - Return ONLY the valid refactored code (no markdown)
8
+ Add clear and helpful single-line // comments to complex parts of the code.
9
+ - Do NOT remove or change in ANY way any parts of the code!
10
+ - Output ALL of the original code with your comments included
11
11
 
12
12
  --- CODE START ---
13
13
  ${input.code}
@@ -6,9 +6,8 @@ export const refactorModule = {
6
6
  You are a senior JavaScript/TypeScript engineer.
7
7
 
8
8
  Refactor the following code:
9
- - Split into ~10 line functions
10
- - Improve naming, structure, and clarity
11
- - Return ONLY the valid refactored code (no markdown)
9
+ - Refactor all functions longer than 20 lines by extracting logical parts into smaller helper functions.
10
+ - Each resulting function should aim for clarity and ideally be under 20 lines, unless splitting would harm readability or functionality.
12
11
 
13
12
  --- CODE START ---
14
13
  ${code}
@@ -2,8 +2,8 @@ export async function runPromptPipeline(modules, input) {
2
2
  let current = input;
3
3
  console.log('Input: ', input);
4
4
  for (const mod of modules) {
5
- console.log(`āš™ļø Running: ${mod.name}`);
6
5
  current = await mod.run(current);
6
+ console.log(`āš™ļø Running: ${mod.name}`);
7
7
  console.log("Current: ", current);
8
8
  }
9
9
  return current.code;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scai",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "scai": "./dist/index.js"