scai
Version:
> **AI-powered CLI for local code analysis, commit message suggestions, and natural-language queries.** 100% local, private, GDPR-friendly, made in Denmark/EU with ❤️.
135 lines (134 loc) • 4.98 kB
JavaScript
// src/commands/ChangeLogUpdateCmd.ts
import { execSync } from 'child_process';
import fs from 'fs/promises';
import path from 'path';
import { runModulePipeline } from '../pipeline/runModulePipeline.js';
import { changelogModule } from '../pipeline/modules/changeLogModule.js';
import { askChangelogApproval } from '../utils/changeLogPrompt.js';
import { openTextEditor } from '../utils/editor.js';
export async function handleStandaloneChangelogUpdate() {
let entry = await generateChangelogEntry();
if (!entry) {
console.log('⚠️ No significant changes found.');
return;
}
while (true) {
const userChoice = await askChangelogApproval(entry);
if (userChoice === 'yes') {
const path = await updateChangelogFile(entry);
console.log(`✅ CHANGELOG.md updated: ${path}`);
break;
}
else if (userChoice === 'redo') {
console.log('🔁 Regenerating changelog...');
entry = await generateChangelogEntry();
if (!entry) {
console.log('⚠️ Could not regenerate entry. Exiting.');
break;
}
}
else {
console.log('❌ Skipped changelog update.');
break;
}
}
}
export async function generateChangelogEntry(currentCommitMsg) {
try {
const lastTag = getLastGitTag();
if (!lastTag) {
console.log("⚠️ No previous git tag found. Using all commits.");
}
let commits = lastTag
? getCommitsSinceTag(lastTag)
: execSync('git log --pretty=format:%s', { encoding: 'utf-8' }).trim();
if (currentCommitMsg) {
commits = commits ? `${commits}\n${currentCommitMsg}` : currentCommitMsg;
}
console.log('Number of commits:', commits.split('\n').length);
if (!commits) {
console.log("⚠️ No commits found since last release.");
return null;
}
// --- ModuleIO usage ---
const result = await runModulePipeline([changelogModule], {
query: 'generate changelog entry',
content: commits
});
const output = result?.data
? typeof result.data === 'string'
? result.data.trim()
: JSON.stringify(result.data, null, 2)
: null;
if (!output || output === 'NO UPDATE') {
console.log('⚠️ No significant changes detected for changelog.');
return null;
}
return output;
}
catch (err) {
console.error("❌ Failed to generate changelog entry:", err.message);
return null;
}
}
function getLastGitTag() {
try {
return execSync('git describe --tags --abbrev=0', { encoding: 'utf-8' }).trim();
}
catch {
return null;
}
}
function getCommitsSinceTag(tag) {
return execSync(`git log ${tag}..HEAD --pretty=format:%s`, { encoding: 'utf-8' }).trim();
}
export async function updateChangelogFile(entry) {
const root = execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
const changelogPath = path.join(root, "CHANGELOG.md");
let existing = '';
try {
existing = await fs.readFile(changelogPath, 'utf-8');
}
catch {
console.log("📄 Creating new CHANGELOG.md");
}
const today = new Date().toISOString().split("T")[0];
const newEntry = `\n\n## ${today}\n\n${entry.trim()}`;
await fs.writeFile(changelogPath, existing + newEntry, 'utf-8');
return changelogPath;
}
export async function handleChangelogWithCommitMessage(commitMsg) {
let entryFinalized = false;
let changelogEntry = await generateChangelogEntry(commitMsg);
if (!changelogEntry) {
console.log("ℹ️ No changelog entry generated.");
return;
}
while (!entryFinalized) {
const userChoice = await askChangelogApproval(changelogEntry);
if (userChoice === 'yes') {
const changelogPath = await updateChangelogFile(changelogEntry);
execSync(`git add "${changelogPath}"`);
console.log("✅ CHANGELOG.md staged.");
entryFinalized = true;
}
else if (userChoice === 'redo') {
console.log("🔁 Regenerating changelog entry...");
changelogEntry = await generateChangelogEntry(commitMsg);
if (!changelogEntry) {
console.log('⚠️ Could not regenerate entry. Exiting.');
break;
}
}
else if (userChoice === 'edit') {
changelogEntry = await openTextEditor(changelogEntry, 'scai-changelog.txt');
if (!changelogEntry) {
console.log('⚠️ No changes made to changelog. Returning to prompt.');
}
}
else {
console.log("❌ Skipped changelog update.");
entryFinalized = true;
}
}
}