accs-cli
Version:
ACCS CLI — Full-featured developer tool for scaffolding, running, building, and managing multi-language projects
123 lines (108 loc) • 3.63 kB
JavaScript
/**
* Scan command - Scan for errors and suggest fixes using Gemini
*/
import { logger } from '../utils/logger.js';
import { FileUtils } from '../utils/file-utils.js';
import { execa } from 'execa';
export function scanCommand(program) {
program
.command('scan')
.option('--fix', 'Automatically apply the suggested fix')
.description('Scan for errors and get AI-powered suggestions')
.action(async (options) => {
try {
await scanForErrors(options);
} catch (error) {
logger.error('Scan failed:', error.message);
process.exit(1);
}
});
}
async function scanForErrors(options) {
logger.info('Scanning for errors with the linter...');
let lintResults;
const { stdout } = await execa('npm', ['run', 'lint', '--', '--format=json'], { reject: false });
if (!stdout) {
logger.error('Failed to run the lint command.');
logger.error('Please make sure you have a `lint` script in your package.json.');
return;
}
try {
const startIndex = stdout.indexOf('[');
const jsonOutput = stdout.substring(startIndex);
lintResults = JSON.parse(jsonOutput);
} catch (e) {
logger.error('Failed to parse lint output.');
logger.error('Please ensure your lint script can output JSON format.');
console.error(e);
return;
}
const errors = lintResults.flatMap(result =>
result.messages.map(msg => ({ ...msg, filePath: result.filePath }))
).filter(msg => msg.severity === 2);
if (errors.length === 0) {
logger.success('No linting errors found!');
return;
}
const error = errors[0];
logger.info(`Found an error in ${error.filePath} on line ${error.line}`);
const fileContent = await FileUtils.readFile(error.filePath);
const fileLines = fileContent.split('\n');
const startLine = Math.max(0, error.line - 10);
const endLine = Math.min(fileLines.length, error.line + 10);
const codeSnippet = fileLines.slice(startLine, endLine).join('\n');
const prompt = options.fix
? `I have a linting error in a file. Your task is to provide a fix for the error.
File Path: ${error.filePath}
Code Snippet:
---
${codeSnippet}
---
Linting Error:
---
${error.message} (rule: ${error.ruleId})
---
Please provide the corrected code for the given snippet.`
: `I have a linting error in a file. Your task is to provide a fix for the error.
File Path: ${error.filePath}
Code Snippet:
---
${codeSnippet}
---
Linting Error:
---
${error.message} (rule: ${error.ruleId})
---
Please explain the fix and provide the corrected code for the snippet.`;
logger.info('Asking Gemini for a fix...');
try {
const { execa } = await import('execa');
const { stdout } = await execa('gemini', ['-m', 'gemini-2.5-flash', '-p', prompt]);
if (options.fix) {
logger.success('Gemini suggests the following fix:');
console.log(stdout);
const inquirer = await import('inquirer');
const { applyFix } = await inquirer.prompt([
{
type: 'confirm',
name: 'applyFix',
message: 'Do you want to apply this fix?',
default: false,
},
]);
if (applyFix) {
await FileUtils.writeFile(error.filePath, stdout);
logger.success('The fix has been applied.');
}
} else {
logger.success('Gemini suggests the following fix:');
console.log(stdout);
}
} catch (error) {
if (error.code === 'ENOENT') {
logger.error('Gemini CLI not found. Please make sure it is installed and in your PATH.');
} else {
logger.error('Failed to get a fix from Gemini:', error.message);
}
}
}