bitch-cli/src/commands/commit.ts

106 lines
3 KiB
TypeScript

import { Command } from 'commander'
import { spawn } from 'node:child_process'
import { access } from 'node:fs/promises'
import { join } from 'node:path'
import { homedir } from 'node:os'
import { colors, logError } from '../utils/output.js'
const BITCH_DATA_DIR = join(homedir(), '.local', 'share', 'bitch')
const COMMITS_SCRIPT_PATH = join(BITCH_DATA_DIR, 'commits-daemon.sh')
const OLD_COMMITS_PATH = join(homedir(), '.local', 'bin', 'commits')
/**
* Check if commits script exists at a path
*/
async function scriptExists(path: string): Promise<boolean> {
try {
await access(path)
return true
} catch {
return false
}
}
/**
* Find the commits script location
*/
async function findCommitsScript(): Promise<string | null> {
// Check bitch's copy first
if (await scriptExists(COMMITS_SCRIPT_PATH)) {
return COMMITS_SCRIPT_PATH
}
// Fall back to standalone
if (await scriptExists(OLD_COMMITS_PATH)) {
return OLD_COMMITS_PATH
}
return null
}
/**
* Run the commits script with the 'commit' subcommand
*/
function runCommit(scriptPath: string, args: string[]): void {
const child = spawn('bash', [scriptPath, 'commit', ...args], {
stdio: 'inherit',
env: process.env,
})
child.on('close', (code) => {
process.exit(code || 0)
})
child.on('error', (err) => {
logError(`Failed to run commit: ${err.message}`)
process.exit(1)
})
}
export function createCommitCommand(): Command {
const cmd = new Command('commit')
.description('One-shot AI-generated commit (uses daemon if running, otherwise standalone)')
.option('-A, --all', 'Commit all configured repos (default: current directory only)')
.option('-p, --push', 'Push after committing (default: true)')
.option('--no-push', 'Commit without pushing')
.option('-n, --dry-run', 'Preview what would be committed without committing')
.allowUnknownOption(true)
.action(async (options, command) => {
// Build args for the Python CLI
const args: string[] = []
if (options.all) {
args.push('--all')
}
if (options.push === false) {
args.push('--no-push')
}
if (options.dryRun) {
args.push('--dry-run')
}
// Add any additional unknown args
const extraArgs = command.args.filter((arg: string) => !['commit'].includes(arg))
args.push(...extraArgs)
// Find the commits script
const scriptPath = await findCommitsScript()
if (!scriptPath) {
logError('Commits script not found.')
console.log()
console.log('To install:')
console.log('1. Install auto-commit-service:')
console.log(colors.dim(' cd ~/Code/@applications/@ml/auto-commit-service'))
console.log(colors.dim(' pip install -e .'))
console.log()
console.log('2. Copy the commits CLI script to ~/.local/bin/commits')
console.log()
console.log('3. Run:')
console.log(colors.cyan(' bitch commits install'))
process.exit(1)
}
runCommit(scriptPath, args)
})
return cmd
}