llm-commit
Version:
Turns diffs into concise, readable git commit messages
103 lines • 4.61 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { config } from 'dotenv';
config();
import { writeFile } from 'fs/promises';
import process from 'node:process';
import { program } from 'commander';
import pc from 'picocolors';
import clipboard from 'clipboardy';
import { oraPromise } from 'ora';
// todo make us not have to write file extension
import { DEFAULT_TOKENS, defaultProvider, providerData } from './constants.js';
import { genCommitMessage } from './genMessage.js';
export const run = () => __awaiter(void 0, void 0, void 0, function* () {
try {
program
.description(`${pc.greenBright('LLM Commit')} - Turn diffs into somewhat accurate commit messages`)
.option('-p, --provider <provider>', 'LLM provider')
.option('-m, --model-id <id>', 'model id')
.option('-t, --max-tokens <tokens>', 'max tokens')
.option('-o, --out-path <path>', 'write path for full LLM output')
.option('-u, --unstaged', 'Use unstaged changes (omits --cached in git diff)')
.option('-c, --copy', 'copies output to clipboard')
.option('-r, --reason-tokens <reasonTokens>', 'enable reasoning/thinking for eligible models')
.option('-s, --silent', 'do not print output')
.action(commanderActionEndpoint);
yield program.parseAsync(process.argv);
}
catch (error) {
console.error(error);
}
});
const commanderActionEndpoint = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (options = {}) {
yield runCli(process.cwd(), options);
});
export const runCli = (cwd_1, _a) => __awaiter(void 0, [cwd_1, _a], void 0, function* (cwd, { modelId, maxTokens, outPath, reasonTokens, silent, unstaged, provider, copy }) {
if (!provider) {
console.log(`Defaulting to ${defaultProvider}...`);
provider = defaultProvider;
}
const useProviderKey = convertProviderInput(provider);
if (!(useProviderKey in providerData)) {
throw new Error(`provider "${provider}" not recognized`);
}
const { defaultModelId, sdkProvider } = providerData[useProviderKey];
const useModelId = modelId || defaultModelId;
const model = sdkProvider(useModelId);
console.log('Running ' + useModelId);
try {
const llmResult = yield oraPromise(genCommitMessage({
model,
maxTokens: maxTokens || DEFAULT_TOKENS,
reasonTokens,
diffOptions: { cached: !unstaged }
}));
if (!llmResult.text) {
throw new Error('No commit message generated');
}
if (copy) {
clipboard.write(llmResult.text); // if this were browser we'd want to catch err in case we don't have permission
}
if (!silent) {
console.log('\n' + llmResult.text);
}
if (outPath) {
try {
yield writeFile(outPath, JSON.stringify(llmResult), 'utf8');
}
catch (writeError) {
// We don't want to fail the entire operation if just the output file fails
console.error(`Warning: Failed to write output to ${outPath}: ${writeError.message}`);
}
}
}
catch (err) {
// seems like ai-sdk already handles any retryable errors, just error includes a lot of the request and res which seems unnecessary
if ('data' in err && 'error' in err.data) {
console.error(err.data.error.type + '\n' + err.data.error.message);
}
else {
console.error(err);
}
process.exit(1);
}
});
// tries to find close matches/shorthands
const convertProviderInput = (rawInput) => {
const normalized = rawInput.toLowerCase().trim();
for (const provider in providerData) {
if (providerData[provider].names.includes(normalized)) {
return provider;
}
}
return null;
};
//# sourceMappingURL=cli.js.map