@cencori/scan 0.3.7 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +262 -2
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +262 -2
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -926,7 +926,7 @@ async function applyFixes(fixes, fileContents) {
|
|
|
926
926
|
}
|
|
927
927
|
|
|
928
928
|
// src/telemetry.ts
|
|
929
|
-
var TELEMETRY_URL = "https://cencori.com/
|
|
929
|
+
var TELEMETRY_URL = "https://api.cencori.com/v1/telemetry/scan";
|
|
930
930
|
var pendingTelemetry = null;
|
|
931
931
|
function sendTelemetry(data) {
|
|
932
932
|
pendingTelemetry = fetch(TELEMETRY_URL, {
|
|
@@ -973,10 +973,218 @@ function buildTelemetryData(result, version, hasApiKey) {
|
|
|
973
973
|
};
|
|
974
974
|
}
|
|
975
975
|
|
|
976
|
+
// src/changelog/index.ts
|
|
977
|
+
var import_child_process = require("child_process");
|
|
978
|
+
var CENCORI_API_URL2 = "https://api.cencori.com/v1";
|
|
979
|
+
function parseCommitType(message) {
|
|
980
|
+
const lower = message.toLowerCase();
|
|
981
|
+
if (lower.startsWith("feat") || lower.startsWith("feature")) return "feat";
|
|
982
|
+
if (lower.startsWith("fix") || lower.startsWith("bugfix")) return "fix";
|
|
983
|
+
if (lower.startsWith("chore")) return "chore";
|
|
984
|
+
if (lower.startsWith("docs")) return "docs";
|
|
985
|
+
if (lower.startsWith("style")) return "style";
|
|
986
|
+
if (lower.startsWith("refactor")) return "refactor";
|
|
987
|
+
if (lower.startsWith("test")) return "test";
|
|
988
|
+
return "other";
|
|
989
|
+
}
|
|
990
|
+
function getCommits(since = "1 week ago", cwd) {
|
|
991
|
+
try {
|
|
992
|
+
const output = (0, import_child_process.execSync)(
|
|
993
|
+
`git log --since="${since}" --pretty=format:"%H|%aI|%an|%s" --no-merges`,
|
|
994
|
+
{
|
|
995
|
+
cwd: cwd || process.cwd(),
|
|
996
|
+
encoding: "utf-8",
|
|
997
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
998
|
+
}
|
|
999
|
+
);
|
|
1000
|
+
if (!output.trim()) {
|
|
1001
|
+
return [];
|
|
1002
|
+
}
|
|
1003
|
+
return output.trim().split("\n").map((line) => {
|
|
1004
|
+
const [hash, date, author, ...messageParts] = line.split("|");
|
|
1005
|
+
const message = messageParts.join("|");
|
|
1006
|
+
return {
|
|
1007
|
+
hash: hash.substring(0, 7),
|
|
1008
|
+
date,
|
|
1009
|
+
author,
|
|
1010
|
+
message,
|
|
1011
|
+
type: parseCommitType(message)
|
|
1012
|
+
};
|
|
1013
|
+
});
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
return [];
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
function groupCommitsByType(commits) {
|
|
1019
|
+
const groups = {
|
|
1020
|
+
feat: [],
|
|
1021
|
+
fix: [],
|
|
1022
|
+
docs: [],
|
|
1023
|
+
other: []
|
|
1024
|
+
};
|
|
1025
|
+
for (const commit of commits) {
|
|
1026
|
+
if (commit.type === "feat") {
|
|
1027
|
+
groups.feat.push(commit);
|
|
1028
|
+
} else if (commit.type === "fix") {
|
|
1029
|
+
groups.fix.push(commit);
|
|
1030
|
+
} else if (commit.type === "docs") {
|
|
1031
|
+
groups.docs.push(commit);
|
|
1032
|
+
} else {
|
|
1033
|
+
groups.other.push(commit);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
const entries = [];
|
|
1037
|
+
if (groups.feat.length > 0) {
|
|
1038
|
+
entries.push({
|
|
1039
|
+
type: "feature",
|
|
1040
|
+
title: "Features",
|
|
1041
|
+
commits: groups.feat.map((c) => c.message)
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
if (groups.fix.length > 0) {
|
|
1045
|
+
entries.push({
|
|
1046
|
+
type: "fix",
|
|
1047
|
+
title: "Bug Fixes",
|
|
1048
|
+
commits: groups.fix.map((c) => c.message)
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
if (groups.docs.length > 0) {
|
|
1052
|
+
entries.push({
|
|
1053
|
+
type: "docs",
|
|
1054
|
+
title: "Documentation",
|
|
1055
|
+
commits: groups.docs.map((c) => c.message)
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
if (groups.other.length > 0) {
|
|
1059
|
+
entries.push({
|
|
1060
|
+
type: "other",
|
|
1061
|
+
title: "Other Changes",
|
|
1062
|
+
commits: groups.other.map((c) => c.message)
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
return entries;
|
|
1066
|
+
}
|
|
1067
|
+
function generateMarkdownFree(entries, period) {
|
|
1068
|
+
const startDate = new Date(period.start).toLocaleDateString("en-US", {
|
|
1069
|
+
month: "short",
|
|
1070
|
+
day: "numeric",
|
|
1071
|
+
year: "numeric"
|
|
1072
|
+
});
|
|
1073
|
+
const endDate = new Date(period.end).toLocaleDateString("en-US", {
|
|
1074
|
+
month: "short",
|
|
1075
|
+
day: "numeric",
|
|
1076
|
+
year: "numeric"
|
|
1077
|
+
});
|
|
1078
|
+
let md = `## Changelog (${startDate} - ${endDate})
|
|
1079
|
+
|
|
1080
|
+
`;
|
|
1081
|
+
const typeEmojis = {
|
|
1082
|
+
feature: "\u2728",
|
|
1083
|
+
fix: "\u{1F41B}",
|
|
1084
|
+
docs: "\u{1F4DA}",
|
|
1085
|
+
improvement: "\u{1F527}",
|
|
1086
|
+
other: "\u{1F4E6}"
|
|
1087
|
+
};
|
|
1088
|
+
for (const entry of entries) {
|
|
1089
|
+
const emoji = typeEmojis[entry.type] || "\u{1F4E6}";
|
|
1090
|
+
md += `### ${emoji} ${entry.title}
|
|
1091
|
+
|
|
1092
|
+
`;
|
|
1093
|
+
for (const commit of entry.commits) {
|
|
1094
|
+
const cleanMessage = commit.replace(/^(feat|fix|chore|docs|style|refactor|test)(\(.+?\))?:\s*/i, "").trim();
|
|
1095
|
+
md += `- ${cleanMessage}
|
|
1096
|
+
`;
|
|
1097
|
+
}
|
|
1098
|
+
md += "\n";
|
|
1099
|
+
}
|
|
1100
|
+
return md;
|
|
1101
|
+
}
|
|
1102
|
+
async function generateChangelogAI(commits, apiKey, period) {
|
|
1103
|
+
const commitMessages = commits.map((c) => c.message).join("\n");
|
|
1104
|
+
try {
|
|
1105
|
+
const response = await fetch(`${CENCORI_API_URL2}/changelog/generate`, {
|
|
1106
|
+
method: "POST",
|
|
1107
|
+
headers: {
|
|
1108
|
+
"Content-Type": "application/json",
|
|
1109
|
+
Authorization: `Bearer ${apiKey}`
|
|
1110
|
+
},
|
|
1111
|
+
body: JSON.stringify({
|
|
1112
|
+
commits: commitMessages,
|
|
1113
|
+
period
|
|
1114
|
+
})
|
|
1115
|
+
});
|
|
1116
|
+
if (!response.ok) {
|
|
1117
|
+
throw new Error("API request failed");
|
|
1118
|
+
}
|
|
1119
|
+
const data = await response.json();
|
|
1120
|
+
return {
|
|
1121
|
+
entries: data.entries || [],
|
|
1122
|
+
markdown: data.markdown || ""
|
|
1123
|
+
};
|
|
1124
|
+
} catch {
|
|
1125
|
+
const entries = groupCommitsByType(commits);
|
|
1126
|
+
return {
|
|
1127
|
+
entries,
|
|
1128
|
+
markdown: generateMarkdownFree(entries, period)
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
async function generateChangelog(since = "1 week ago", apiKey, cwd) {
|
|
1133
|
+
const commits = getCommits(since, cwd);
|
|
1134
|
+
const now = /* @__PURE__ */ new Date();
|
|
1135
|
+
const sinceDate = /* @__PURE__ */ new Date();
|
|
1136
|
+
const match = since.match(/(\d+)\s*(week|day|month)s?\s*ago/i);
|
|
1137
|
+
if (match) {
|
|
1138
|
+
const amount = parseInt(match[1], 10);
|
|
1139
|
+
const unit = match[2].toLowerCase();
|
|
1140
|
+
if (unit === "week") {
|
|
1141
|
+
sinceDate.setDate(sinceDate.getDate() - amount * 7);
|
|
1142
|
+
} else if (unit === "day") {
|
|
1143
|
+
sinceDate.setDate(sinceDate.getDate() - amount);
|
|
1144
|
+
} else if (unit === "month") {
|
|
1145
|
+
sinceDate.setMonth(sinceDate.getMonth() - amount);
|
|
1146
|
+
}
|
|
1147
|
+
} else {
|
|
1148
|
+
sinceDate.setDate(sinceDate.getDate() - 7);
|
|
1149
|
+
}
|
|
1150
|
+
const period = {
|
|
1151
|
+
start: sinceDate.toISOString(),
|
|
1152
|
+
end: now.toISOString()
|
|
1153
|
+
};
|
|
1154
|
+
if (commits.length === 0) {
|
|
1155
|
+
return {
|
|
1156
|
+
period,
|
|
1157
|
+
entries: [],
|
|
1158
|
+
markdown: `## Changelog
|
|
1159
|
+
|
|
1160
|
+
No commits found in the specified period.
|
|
1161
|
+
`,
|
|
1162
|
+
commitCount: 0
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
if (apiKey) {
|
|
1166
|
+
const { entries, markdown } = await generateChangelogAI(commits, apiKey, period);
|
|
1167
|
+
return {
|
|
1168
|
+
period,
|
|
1169
|
+
entries,
|
|
1170
|
+
markdown,
|
|
1171
|
+
commitCount: commits.length
|
|
1172
|
+
};
|
|
1173
|
+
} else {
|
|
1174
|
+
const entries = groupCommitsByType(commits);
|
|
1175
|
+
return {
|
|
1176
|
+
period,
|
|
1177
|
+
entries,
|
|
1178
|
+
markdown: generateMarkdownFree(entries, period),
|
|
1179
|
+
commitCount: commits.length
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
976
1184
|
// src/cli.ts
|
|
977
1185
|
var fs3 = __toESM(require("fs"));
|
|
978
1186
|
var path3 = __toESM(require("path"));
|
|
979
|
-
var VERSION = "0.
|
|
1187
|
+
var VERSION = "0.4.0";
|
|
980
1188
|
var scoreStyles = {
|
|
981
1189
|
A: { color: import_chalk.default.green },
|
|
982
1190
|
B: { color: import_chalk.default.blue },
|
|
@@ -1282,6 +1490,58 @@ async function main() {
|
|
|
1282
1490
|
process.exit(1);
|
|
1283
1491
|
}
|
|
1284
1492
|
});
|
|
1493
|
+
import_commander.program.command("changelog").description("Generate AI-powered changelog from git commits").option("-s, --since <time>", "Time range for commits", "1 week ago").option("-o, --output <file>", "Output to file instead of stdout").option("-f, --format <format>", "Output format (markdown or json)", "markdown").action(async (options) => {
|
|
1494
|
+
printBanner();
|
|
1495
|
+
const spinner = (0, import_ora.default)({
|
|
1496
|
+
text: "Reading git history...",
|
|
1497
|
+
color: "cyan"
|
|
1498
|
+
}).start();
|
|
1499
|
+
try {
|
|
1500
|
+
const apiKey = getApiKey();
|
|
1501
|
+
const result = await generateChangelog(options.since, apiKey || void 0);
|
|
1502
|
+
if (result.commitCount === 0) {
|
|
1503
|
+
spinner.warn("No commits found in the specified period");
|
|
1504
|
+
console.log(import_chalk.default.yellow(`
|
|
1505
|
+
Try a larger time range with --since "2 weeks ago"
|
|
1506
|
+
`));
|
|
1507
|
+
process.exit(0);
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
spinner.succeed(`Found ${result.commitCount} commits`);
|
|
1511
|
+
if (options.format === "json") {
|
|
1512
|
+
const output = JSON.stringify(result, null, 2);
|
|
1513
|
+
if (options.output) {
|
|
1514
|
+
fs3.writeFileSync(options.output, output);
|
|
1515
|
+
console.log(import_chalk.default.green(`
|
|
1516
|
+
\u2714 Changelog saved to ${options.output}
|
|
1517
|
+
`));
|
|
1518
|
+
} else {
|
|
1519
|
+
console.log(output);
|
|
1520
|
+
}
|
|
1521
|
+
} else {
|
|
1522
|
+
if (options.output) {
|
|
1523
|
+
fs3.writeFileSync(options.output, result.markdown);
|
|
1524
|
+
console.log(import_chalk.default.green(`
|
|
1525
|
+
\u2714 Changelog saved to ${options.output}
|
|
1526
|
+
`));
|
|
1527
|
+
} else {
|
|
1528
|
+
console.log("\n" + result.markdown);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
if (!apiKey) {
|
|
1532
|
+
console.log(import_chalk.default.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1533
|
+
console.log(import_chalk.default.cyan("\n \u{1F4A1} Want AI-enhanced changelogs?"));
|
|
1534
|
+
console.log(import_chalk.default.gray(" Get human-readable summaries with a Cencori API key"));
|
|
1535
|
+
console.log(import_chalk.default.gray(" Sign up free at https://cencori.com\n"));
|
|
1536
|
+
}
|
|
1537
|
+
process.exit(0);
|
|
1538
|
+
} catch (error) {
|
|
1539
|
+
spinner.fail("Changelog generation failed");
|
|
1540
|
+
console.error(import_chalk.default.red(`
|
|
1541
|
+
Error: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
1542
|
+
process.exit(1);
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1285
1545
|
import_commander.program.parse();
|
|
1286
1546
|
}
|
|
1287
1547
|
main();
|