diff --git a/README.md b/README.md index edd3a546..76d80e66 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,12 @@ Default: `50` aicommits config set max-length=100 ``` +#### Non-interactive mode +Enable non-interactive mode to use automatically the commit retrieved without review, specially useful for automations: +```sh +aicommits --noninteractive # or -y +``` + ## How it works This CLI tool runs `git diff` to grab all your latest code changes, sends them to OpenAI's GPT-3, then returns the AI generated commit message. diff --git a/package.json b/package.json index 370948a2..2d4a6504 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,8 @@ "eslintConfig": { "extends": "@pvtnbr", "rules": { - "unicorn/no-process-exit": "off" + "unicorn/no-process-exit": "off", + "complexity": ["error", { "max": 14 }] }, "overrides": [ { diff --git a/src/cli.ts b/src/cli.ts index 129bac3d..9aa401ab 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -24,6 +24,12 @@ cli( description: 'Number of messages to generate (Warning: generating multiple costs more) (default: 1)', alias: 'g', }, + noninteractive: { + type: Boolean, + description: 'Non interactive mode', + alias: 'y', + default: false, + }, exclude: { type: [String], description: 'Files to exclude from AI analysis', @@ -59,6 +65,7 @@ cli( } else { aicommits( argv.flags.generate, + argv.flags.noninteractive, argv.flags.exclude, argv.flags.all, argv.flags.type, diff --git a/src/commands/aicommits.ts b/src/commands/aicommits.ts index 2c6cb2ba..3560cbb0 100644 --- a/src/commands/aicommits.ts +++ b/src/commands/aicommits.ts @@ -16,6 +16,7 @@ import { KnownError, handleCliError } from '../utils/error.js'; export default async ( generate: number | undefined, + noninteractive: boolean | undefined, excludeFiles: string[], stageAll: boolean, commitType: string | undefined, @@ -76,19 +77,19 @@ export default async ( let message: string; if (messages.length === 1) { [message] = messages; - const confirmed = await confirm({ - message: `Use this commit message?\n\n ${message}\n`, - }); + const confirmed = noninteractive ? true : await confirm({ message: `Use this commit message?\n\n ${message}\n` }); if (!confirmed || isCancel(confirmed)) { outro('Commit cancelled'); return; } } else { - const selected = await select({ - message: `Pick a commit message to use: ${dim('(Ctrl+c to exit)')}`, - options: messages.map(value => ({ label: value, value })), - }); + const selected = noninteractive + ? messages[0] + : await select({ + message: `Pick a commit message to use: ${dim('(Ctrl+c to exit)')}`, + options: messages.map(value => ({ label: value, value })), + }); if (isCancel(selected)) { outro('Commit cancelled'); diff --git a/tests/specs/cli/commits.ts b/tests/specs/cli/commits.ts index f88916ad..a6d4990a 100644 --- a/tests/specs/cli/commits.ts +++ b/tests/specs/cli/commits.ts @@ -171,6 +171,45 @@ export default testSuite(({ describe }) => { await fixture.rm(); }); + test('Accepts --non-interactive flag', async () => { + const { fixture, aicommits } = await createFixture(files); + const git = await createGit(fixture.path); + + await git('add', ['data.json']); + await git('commit', ['-m', 'wip']); + + // Change tracked file + await fixture.writeFile('data.json', 'Test'); + + const statusBefore = await git('status', ['--short']); + console.log(statusBefore); + expect(statusBefore.stdout).toBe(' M data.json\n?? .aicommits'); + + const committing = aicommits(['--all', '--non-interactive']); + committing.stdout!.on('data', (buffer: Buffer) => { + const stdout = buffer.toString(); + console.log(stdout); + if (stdout.match('└')) { + committing.stdin!.end(); + } + }); + + await committing; + + const statusAfter = await git('status', ['--short']); + console.log(statusAfter); + expect(statusAfter.stdout).toBe('M data.json\n?? .aicommits'); + + const { stdout: commitMessage } = await git('log', ['-n1', '--pretty=format:%s']); + console.log({ + commitMessage, + length: commitMessage.length, + }); + expect(commitMessage.length).toBeLessThanOrEqual(50); + + await fixture.rm(); + }); + test('Generates Japanese commit message via locale config', async () => { // https://stackoverflow.com/a/15034560/911407 const japanesePattern = /[\u3000-\u303F\u3040-\u309F\u30A0-\u30FF\uFF00-\uFF9F\u4E00-\u9FAF\u3400-\u4DBF]/;