commit-whisper 1.0.8 → 1.1.1
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/index.js +1760 -537
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -145,18 +145,35 @@ function aiKeyEnvVar(provider) {
|
|
|
145
145
|
return "OPENAI_API_KEY";
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
var PROVIDER_KEY_VARS = [
|
|
149
|
+
{ provider: "openai", name: "OPENAI_API_KEY" },
|
|
150
|
+
{ provider: "anthropic", name: "ANTHROPIC_API_KEY" },
|
|
151
|
+
{ provider: "gemini", name: "GOOGLE_GENERATIVE_AI_API_KEY" }
|
|
152
|
+
];
|
|
153
|
+
function isActiveKeyRow(rowProvider, active) {
|
|
154
|
+
if (active === rowProvider) {
|
|
155
|
+
return true;
|
|
153
156
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
return active === "openai-compatible" && rowProvider === "openai";
|
|
158
|
+
}
|
|
159
|
+
function readEnvDiagnostics(env, provider) {
|
|
160
|
+
return [
|
|
161
|
+
...PROVIDER_KEY_VARS.map(({ provider: p, name }) => ({
|
|
162
|
+
name,
|
|
163
|
+
set: readAiKey(env, p) !== void 0,
|
|
164
|
+
note: isActiveKeyRow(p, provider) ? "active provider" : void 0
|
|
165
|
+
})),
|
|
166
|
+
{
|
|
167
|
+
name: "COMMIT_WHISPER_GIT_TOKEN",
|
|
168
|
+
set: readGitToken(env) !== void 0,
|
|
169
|
+
note: "only needed for private remotes"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "COMMIT_WHISPER_LICENSE_KEY",
|
|
173
|
+
set: readLicenseKey(env) !== void 0,
|
|
174
|
+
note: "unlocks paid tiers when valid"
|
|
175
|
+
}
|
|
176
|
+
];
|
|
160
177
|
}
|
|
161
178
|
function readProcessEnv() {
|
|
162
179
|
return process.env;
|
|
@@ -660,11 +677,18 @@ function tierForVariantName(variantName) {
|
|
|
660
677
|
}
|
|
661
678
|
return "single-device";
|
|
662
679
|
}
|
|
680
|
+
function tierForLicense(input) {
|
|
681
|
+
const limit = input.activationLimit;
|
|
682
|
+
if (limit === void 0) {
|
|
683
|
+
return tierForVariantName(input.variantName);
|
|
684
|
+
}
|
|
685
|
+
return limit === 1 ? "single-device" : "unlimited";
|
|
686
|
+
}
|
|
663
687
|
function tierForValidation(v) {
|
|
664
688
|
if (!v.valid) {
|
|
665
689
|
return "free";
|
|
666
690
|
}
|
|
667
|
-
return
|
|
691
|
+
return tierForLicense({ variantName: v.variantName, activationLimit: v.activationLimit });
|
|
668
692
|
}
|
|
669
693
|
function entitlementForTier(tier) {
|
|
670
694
|
if (tier === "free") {
|
|
@@ -714,7 +738,7 @@ async function activateLicense(deps) {
|
|
|
714
738
|
reason: `Activated online, but couldn't save it on this device (${detail}). Set COMMIT_WHISPER_LICENSE_KEY so it applies on your next run \u2014 do NOT re-activate (that would use another device slot).`
|
|
715
739
|
};
|
|
716
740
|
}
|
|
717
|
-
return { ok: true, tier:
|
|
741
|
+
return { ok: true, tier: tierForLicense({ variantName: result.variantName, activationLimit: result.activationLimit }) };
|
|
718
742
|
}
|
|
719
743
|
async function deactivateLicense(deps) {
|
|
720
744
|
if (deps.licenseKey === void 0 || deps.licenseKey === "") {
|
|
@@ -807,6 +831,7 @@ function createLemonSqueezyActivator(deps = {}) {
|
|
|
807
831
|
instanceId: json.instance?.id,
|
|
808
832
|
variantName: json.meta?.variant_name,
|
|
809
833
|
variantId: json.meta?.variant_id,
|
|
834
|
+
activationLimit: json.license_key?.activation_limit,
|
|
810
835
|
error: json.error ?? void 0
|
|
811
836
|
};
|
|
812
837
|
};
|
|
@@ -1018,6 +1043,106 @@ function resolveColor(input) {
|
|
|
1018
1043
|
return input.isTTY;
|
|
1019
1044
|
}
|
|
1020
1045
|
var ui = createUi();
|
|
1046
|
+
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
1047
|
+
var SPINNER_INTERVAL_MS = 80;
|
|
1048
|
+
var CLEAR_LINE = "\r\x1B[2K";
|
|
1049
|
+
var noopProgress = {
|
|
1050
|
+
start() {
|
|
1051
|
+
},
|
|
1052
|
+
update() {
|
|
1053
|
+
},
|
|
1054
|
+
done() {
|
|
1055
|
+
},
|
|
1056
|
+
fail() {
|
|
1057
|
+
},
|
|
1058
|
+
clear() {
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
function createProgress(stream = process.stderr, opts = {}) {
|
|
1062
|
+
const level = opts.level ?? "normal";
|
|
1063
|
+
if (level === "quiet") {
|
|
1064
|
+
return noopProgress;
|
|
1065
|
+
}
|
|
1066
|
+
const colors = pc.createColors(opts.color ?? false);
|
|
1067
|
+
const animate = opts.tty === true;
|
|
1068
|
+
const total = opts.total;
|
|
1069
|
+
let label = "";
|
|
1070
|
+
let step = 0;
|
|
1071
|
+
let frame = 0;
|
|
1072
|
+
let active = false;
|
|
1073
|
+
let timer;
|
|
1074
|
+
const counter = () => total === void 0 ? "" : `[${Math.min(step, total)}/${total}] `;
|
|
1075
|
+
const render = () => {
|
|
1076
|
+
const spinner = colors.cyan(SPINNER_FRAMES[frame % SPINNER_FRAMES.length]);
|
|
1077
|
+
stream.write(`${CLEAR_LINE}${spinner} ${counter()}${label}`);
|
|
1078
|
+
};
|
|
1079
|
+
const stopTimer = () => {
|
|
1080
|
+
if (timer !== void 0) {
|
|
1081
|
+
clearInterval(timer);
|
|
1082
|
+
timer = void 0;
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
const finish = (symbol, finalLabel) => {
|
|
1086
|
+
if (!active) {
|
|
1087
|
+
if (finalLabel !== void 0) {
|
|
1088
|
+
stream.write(`${symbol} ${counter()}${finalLabel}
|
|
1089
|
+
`);
|
|
1090
|
+
}
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
active = false;
|
|
1094
|
+
stopTimer();
|
|
1095
|
+
const text = finalLabel ?? label;
|
|
1096
|
+
stream.write(animate ? `${CLEAR_LINE}${symbol} ${counter()}${text}
|
|
1097
|
+
` : `${symbol} ${counter()}${text}
|
|
1098
|
+
`);
|
|
1099
|
+
};
|
|
1100
|
+
return {
|
|
1101
|
+
start(next) {
|
|
1102
|
+
step += 1;
|
|
1103
|
+
label = next;
|
|
1104
|
+
active = true;
|
|
1105
|
+
if (animate) {
|
|
1106
|
+
frame = 0;
|
|
1107
|
+
render();
|
|
1108
|
+
stopTimer();
|
|
1109
|
+
timer = setInterval(() => {
|
|
1110
|
+
frame += 1;
|
|
1111
|
+
render();
|
|
1112
|
+
}, SPINNER_INTERVAL_MS);
|
|
1113
|
+
timer.unref?.();
|
|
1114
|
+
} else {
|
|
1115
|
+
stream.write(`${counter()}${label}
|
|
1116
|
+
`);
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
update(next) {
|
|
1120
|
+
label = next;
|
|
1121
|
+
if (!active) {
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (animate) {
|
|
1125
|
+
render();
|
|
1126
|
+
} else {
|
|
1127
|
+
stream.write(`${counter()}${label}
|
|
1128
|
+
`);
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
done(finalLabel) {
|
|
1132
|
+
finish(colors.green("\u2713"), finalLabel);
|
|
1133
|
+
},
|
|
1134
|
+
fail(finalLabel) {
|
|
1135
|
+
finish(colors.red("\u2717"), finalLabel);
|
|
1136
|
+
},
|
|
1137
|
+
clear() {
|
|
1138
|
+
stopTimer();
|
|
1139
|
+
if (active && animate) {
|
|
1140
|
+
stream.write(CLEAR_LINE);
|
|
1141
|
+
}
|
|
1142
|
+
active = false;
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1021
1146
|
|
|
1022
1147
|
// src/cli/exit-codes.ts
|
|
1023
1148
|
var ExitCode = {
|
|
@@ -1043,8 +1168,10 @@ function messageForError(err) {
|
|
|
1043
1168
|
}
|
|
1044
1169
|
|
|
1045
1170
|
// src/cli/interactive.ts
|
|
1171
|
+
import pc2 from "picocolors";
|
|
1046
1172
|
import { isCancel, multiselect as clackMultiselect, select as clackSelect, text as clackText } from "@clack/prompts";
|
|
1047
|
-
var LAUNCHPAD_TAGLINE = "commit-whisper \xB7 \u{1F575}\uFE0F
|
|
1173
|
+
var LAUNCHPAD_TAGLINE = "commit-whisper \xB7 \u{1F575}\uFE0F I know what you did last commit";
|
|
1174
|
+
var QUIT_MESSAGE = "Case closed. \u{1F575}\uFE0F Until your next commit \u2014 here's the cheatsheet for the road:";
|
|
1048
1175
|
var FLAGS_CHEATSHEET = [
|
|
1049
1176
|
"Common commands:",
|
|
1050
1177
|
" commit-whisper . analyze the current repository",
|
|
@@ -1069,6 +1196,11 @@ var TIER_LABEL = {
|
|
|
1069
1196
|
"single-device": "Single-device",
|
|
1070
1197
|
unlimited: "Unlimited"
|
|
1071
1198
|
};
|
|
1199
|
+
function tierSegment(tier, color) {
|
|
1200
|
+
const c = pc2.createColors(color);
|
|
1201
|
+
const label = TIER_LABEL[tier];
|
|
1202
|
+
return tier === "free" ? c.red(label) : c.green(label);
|
|
1203
|
+
}
|
|
1072
1204
|
var OUTPUT_FORMAT_OPTIONS = [
|
|
1073
1205
|
{ value: "terminal", label: "terminal" },
|
|
1074
1206
|
{ value: "html", label: "html" },
|
|
@@ -1083,11 +1215,16 @@ var PROVIDER_OPTIONS = [
|
|
|
1083
1215
|
{ value: "openai-compatible", label: "OpenAI-compatible" }
|
|
1084
1216
|
];
|
|
1085
1217
|
var BASE_URL_PROVIDERS = /* @__PURE__ */ new Set(["ollama", "openai-compatible"]);
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1218
|
+
function settingsSavedNote(provider) {
|
|
1219
|
+
const keyVar = aiKeyEnvVar(provider);
|
|
1220
|
+
if (keyVar === void 0) {
|
|
1221
|
+
return [
|
|
1222
|
+
"Ollama runs locally \u2014 no key needed, but it must be running:",
|
|
1223
|
+
" `ollama serve`, then `ollama pull <model>`. Saving selects it; it doesn't start it."
|
|
1224
|
+
].join("\n");
|
|
1225
|
+
}
|
|
1226
|
+
return `This provider reads its key from your environment (${keyVar}) \u2014 never entered here.`;
|
|
1227
|
+
}
|
|
1091
1228
|
function aiSegment(state) {
|
|
1092
1229
|
if (state.provider === void 0) {
|
|
1093
1230
|
return "\u26A0 not configured";
|
|
@@ -1103,16 +1240,16 @@ function cwdSegment(state) {
|
|
|
1103
1240
|
}
|
|
1104
1241
|
return `${state.cwdLabel} (${state.branch ?? "detached"})`;
|
|
1105
1242
|
}
|
|
1106
|
-
function formatReadinessLine(state) {
|
|
1107
|
-
return `${
|
|
1243
|
+
function formatReadinessLine(state, color = false) {
|
|
1244
|
+
return `${tierSegment(state.tier, color)} \xB7 AI: ${aiSegment(state)} \xB7 cwd: ${cwdSegment(state)}`;
|
|
1108
1245
|
}
|
|
1109
1246
|
function buildLaunchpadOptions(state) {
|
|
1110
1247
|
const options = [
|
|
1111
1248
|
{ value: "analyze-cwd", label: "Analyze this repository", hint: "the current directory" },
|
|
1112
1249
|
{ value: "analyze-remote", label: "Analyze a remote repository", hint: "clone a URL" },
|
|
1113
1250
|
{ value: "settings", label: "Settings", hint: "provider, model, default format" },
|
|
1114
|
-
{ value: "status", label: "
|
|
1115
|
-
{ value: "help", label: "Help
|
|
1251
|
+
{ value: "status", label: "Doctor", hint: "diagnose your setup" },
|
|
1252
|
+
{ value: "help", label: "Help", hint: "show the flag reference" }
|
|
1116
1253
|
];
|
|
1117
1254
|
if (state.licensed) {
|
|
1118
1255
|
options.push({ value: "deactivate", label: "Deactivate license", hint: "free this device" });
|
|
@@ -1131,19 +1268,16 @@ var STATUS_LABEL_WIDTH = 12;
|
|
|
1131
1268
|
function statusLabel(text) {
|
|
1132
1269
|
return text.padEnd(STATUS_LABEL_WIDTH);
|
|
1133
1270
|
}
|
|
1134
|
-
function
|
|
1271
|
+
function reachabilityAnnotation(reachability) {
|
|
1135
1272
|
switch (reachability.kind) {
|
|
1136
1273
|
case "reachable":
|
|
1137
|
-
return
|
|
1274
|
+
return "\u2713 reachable";
|
|
1138
1275
|
case "unreachable":
|
|
1139
|
-
return
|
|
1276
|
+
return `\u26A0 unreachable \u2014 ${reachability.reason}`;
|
|
1140
1277
|
default:
|
|
1141
|
-
return
|
|
1278
|
+
return "\u26A0 not configured";
|
|
1142
1279
|
}
|
|
1143
1280
|
}
|
|
1144
|
-
function aiBlock(state, reachability) {
|
|
1145
|
-
return [`${statusLabel("AI")}${aiSegment(state)}`, reachabilityLine(reachability)];
|
|
1146
|
-
}
|
|
1147
1281
|
function environmentBlock(envVars) {
|
|
1148
1282
|
const prefixes = envVars.map((v) => ` ${v.set ? "\u2713" : "\u2717"} ${v.name}`);
|
|
1149
1283
|
const stateColumn = Math.max(0, ...prefixes.map((p) => p.length)) + 2;
|
|
@@ -1157,20 +1291,100 @@ function environmentBlock(envVars) {
|
|
|
1157
1291
|
function repositoryLine(state) {
|
|
1158
1292
|
return state.isRepo ? `${statusLabel("Repository")}\u2713 ${cwdSegment(state)}` : `${statusLabel("Repository")}\u2014 not a git repo`;
|
|
1159
1293
|
}
|
|
1160
|
-
|
|
1161
|
-
|
|
1294
|
+
var CONFIG_LABEL_WIDTH = 14;
|
|
1295
|
+
function renderRows(rows) {
|
|
1296
|
+
const envColumn = Math.max(0, ...rows.map((r2) => (r2.envVar ?? "").length)) + 2;
|
|
1297
|
+
return rows.map((r2) => ` ${(r2.envVar ?? "").padEnd(envColumn)}${r2.label.padEnd(CONFIG_LABEL_WIDTH)}${r2.value}`);
|
|
1298
|
+
}
|
|
1299
|
+
function branchValue(branch) {
|
|
1300
|
+
if (branch === void 0) {
|
|
1301
|
+
return "HEAD (default)";
|
|
1302
|
+
}
|
|
1303
|
+
switch (branch.kind) {
|
|
1304
|
+
case "all":
|
|
1305
|
+
return "all branches";
|
|
1306
|
+
case "head":
|
|
1307
|
+
return "HEAD";
|
|
1308
|
+
default:
|
|
1309
|
+
return branch.name;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
function formatValue(formats) {
|
|
1313
|
+
return formats !== void 0 && formats.length > 0 ? formats.join(", ") : "terminal (default)";
|
|
1314
|
+
}
|
|
1315
|
+
function settingsBlock(config) {
|
|
1316
|
+
if (config === void 0) {
|
|
1317
|
+
return [];
|
|
1318
|
+
}
|
|
1319
|
+
const baseUrlRow = config.llmBaseUrl === void 0 ? [] : [{ label: "Base URL", value: config.llmBaseUrl, envVar: "COMMIT_WHISPER_LLM_BASE_URL" }];
|
|
1320
|
+
const rows = [
|
|
1321
|
+
{ label: "Timezone", value: config.timezone ?? "UTC (default)", envVar: "COMMIT_WHISPER_TZ" },
|
|
1322
|
+
{ label: "Format", value: formatValue(config.outputFormats), envVar: "COMMIT_WHISPER_FORMAT" },
|
|
1323
|
+
{
|
|
1324
|
+
label: "Max commits",
|
|
1325
|
+
value: config.maxCommits === void 0 ? "unbounded" : String(config.maxCommits),
|
|
1326
|
+
envVar: "COMMIT_WHISPER_MAX_COMMITS"
|
|
1327
|
+
},
|
|
1328
|
+
...baseUrlRow
|
|
1329
|
+
];
|
|
1330
|
+
return ["", "Settings (saved in ~/.commit-whisper)", ...renderRows(rows)];
|
|
1331
|
+
}
|
|
1332
|
+
function runScopeBlock(config) {
|
|
1333
|
+
if (config === void 0) {
|
|
1334
|
+
return [];
|
|
1335
|
+
}
|
|
1336
|
+
const rows = [
|
|
1337
|
+
{ label: "Branch", value: branchValue(config.branch), envVar: "COMMIT_WHISPER_BRANCH" },
|
|
1338
|
+
{ label: "Author", value: config.authorFilter ?? "any (default)", envVar: "COMMIT_WHISPER_AUTHOR" },
|
|
1339
|
+
{ label: "Since", value: config.startDate ?? "\u2014 (unbounded)", envVar: "COMMIT_WHISPER_START_DATE" },
|
|
1340
|
+
{ label: "Until", value: config.endDate ?? "\u2014 (unbounded)", envVar: "COMMIT_WHISPER_END_DATE" },
|
|
1341
|
+
{ label: "No-merges", value: config.noMerges === true ? "on" : "off (default)", envVar: "COMMIT_WHISPER_NO_MERGES" },
|
|
1342
|
+
{ label: "Output path", value: config.outputPath ?? "\u2014 (auto)", envVar: "COMMIT_WHISPER_OUT" },
|
|
1343
|
+
{ label: "AI mode", value: config.aiMode ?? "auto (default)", envVar: "COMMIT_WHISPER_AI_MODE" }
|
|
1344
|
+
];
|
|
1345
|
+
return ["", "Run scope (env vars / flags only \u2014 not saved)", ...renderRows(rows)];
|
|
1346
|
+
}
|
|
1347
|
+
function colorState(color) {
|
|
1348
|
+
if (color === void 0) {
|
|
1349
|
+
return "auto";
|
|
1350
|
+
}
|
|
1351
|
+
return color ? "on" : "off";
|
|
1352
|
+
}
|
|
1353
|
+
function operationalBlock(config) {
|
|
1354
|
+
if (config === void 0 || config.logLevel === void 0 && config.color === void 0) {
|
|
1355
|
+
return [];
|
|
1356
|
+
}
|
|
1357
|
+
const rows = [
|
|
1358
|
+
{ label: "Log level", value: config.logLevel ?? "normal (default)", envVar: "COMMIT_WHISPER_LOG_LEVEL" },
|
|
1359
|
+
{ label: "Color", value: colorState(config.color), envVar: "NO_COLOR / FORCE_COLOR" }
|
|
1360
|
+
];
|
|
1361
|
+
return ["", "Operational", ...renderRows(rows)];
|
|
1362
|
+
}
|
|
1363
|
+
function formatStatusReport(state, envVars, reachability, config) {
|
|
1364
|
+
const licenseHead = `${statusLabel("License")}${TIER_LABEL[state.tier]}`;
|
|
1365
|
+
const aiHead = `${statusLabel("AI")}${aiSegment(state)}`;
|
|
1366
|
+
const annotationColumn = Math.max(licenseHead.length, aiHead.length) + 3;
|
|
1367
|
+
const licenseLine = state.tier === "free" ? `${licenseHead.padEnd(annotationColumn)}100-commit cap` : licenseHead;
|
|
1368
|
+
const aiLine = `${aiHead.padEnd(annotationColumn)}${reachabilityAnnotation(reachability)}`;
|
|
1162
1369
|
const lines = [
|
|
1163
|
-
"
|
|
1370
|
+
"Doctor",
|
|
1164
1371
|
"",
|
|
1165
|
-
|
|
1166
|
-
|
|
1372
|
+
licenseLine,
|
|
1373
|
+
aiLine,
|
|
1374
|
+
repositoryLine(state),
|
|
1167
1375
|
...environmentBlock(envVars),
|
|
1168
|
-
|
|
1376
|
+
...settingsBlock(config),
|
|
1377
|
+
...runScopeBlock(config),
|
|
1378
|
+
...operationalBlock(config)
|
|
1169
1379
|
];
|
|
1170
1380
|
if (reachability.kind === "not-configured") {
|
|
1171
1381
|
lines.push("", NO_AI_FIX);
|
|
1172
1382
|
}
|
|
1173
|
-
return lines.join("\n");
|
|
1383
|
+
return paintStatusMarks(lines.join("\n"), config?.color ?? false);
|
|
1384
|
+
}
|
|
1385
|
+
function paintStatusMarks(text, color) {
|
|
1386
|
+
const c = pc2.createColors(color);
|
|
1387
|
+
return text.replaceAll("\u2713", c.green("\u2713")).replaceAll("\u2717", c.red("\u2717"));
|
|
1174
1388
|
}
|
|
1175
1389
|
var GUIDED_DATE = /^\d{4}-\d{2}-\d{2}/;
|
|
1176
1390
|
function formatEquivalentCommand(target, flags) {
|
|
@@ -1245,6 +1459,28 @@ function writeLine(output, text) {
|
|
|
1245
1459
|
output.write(text.endsWith("\n") ? text : `${text}
|
|
1246
1460
|
`);
|
|
1247
1461
|
}
|
|
1462
|
+
function isInteractive(output) {
|
|
1463
|
+
return output.isTTY === true;
|
|
1464
|
+
}
|
|
1465
|
+
function clearScreen(output) {
|
|
1466
|
+
output.write("\x1B[2J\x1B[3J\x1B[H");
|
|
1467
|
+
}
|
|
1468
|
+
async function waitForKey(output) {
|
|
1469
|
+
writeLine(output, "");
|
|
1470
|
+
output.write("Press any key to return to the menu\u2026 (Esc to quit)");
|
|
1471
|
+
const stdin = process.stdin;
|
|
1472
|
+
const wasRaw = stdin.isRaw === true;
|
|
1473
|
+
stdin.setRawMode?.(true);
|
|
1474
|
+
stdin.resume();
|
|
1475
|
+
return await new Promise((resolve) => {
|
|
1476
|
+
stdin.once("data", (data) => {
|
|
1477
|
+
stdin.setRawMode?.(wasRaw);
|
|
1478
|
+
stdin.pause();
|
|
1479
|
+
const key = data.toString("utf8");
|
|
1480
|
+
resolve(key === "" || key === "\x1B" ? "quit" : "continue");
|
|
1481
|
+
});
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1248
1484
|
function clackLaunchpadSelect(output) {
|
|
1249
1485
|
return async ({ message, options }) => {
|
|
1250
1486
|
const result = await clackSelect({ message, options, output });
|
|
@@ -1259,6 +1495,7 @@ function clackGuidedPrompts(output) {
|
|
|
1259
1495
|
message: opts.message,
|
|
1260
1496
|
placeholder: opts.placeholder,
|
|
1261
1497
|
defaultValue: opts.defaultValue,
|
|
1498
|
+
initialValue: opts.initialValue,
|
|
1262
1499
|
// `@clack` may pass `undefined` (empty input); our validators are total over strings.
|
|
1263
1500
|
validate: validate === void 0 ? void 0 : (value) => validate(value ?? ""),
|
|
1264
1501
|
output
|
|
@@ -1292,7 +1529,7 @@ function validateRemoteUrl(value) {
|
|
|
1292
1529
|
function errorOf(result) {
|
|
1293
1530
|
return "error" in result ? result.error : void 0;
|
|
1294
1531
|
}
|
|
1295
|
-
async function collectGuidedInputs(prompts) {
|
|
1532
|
+
async function collectGuidedInputs(prompts, initialFormats = ["terminal"]) {
|
|
1296
1533
|
const limitRaw = await prompts.text({
|
|
1297
1534
|
message: "Limit \u2014 most-recent commits to analyze",
|
|
1298
1535
|
placeholder: "all history",
|
|
@@ -1312,7 +1549,7 @@ async function collectGuidedInputs(prompts) {
|
|
|
1312
1549
|
const formats = await prompts.multiselect({
|
|
1313
1550
|
message: "Output \u2014 one or more formats",
|
|
1314
1551
|
options: OUTPUT_FORMAT_OPTIONS,
|
|
1315
|
-
initialValues:
|
|
1552
|
+
initialValues: initialFormats
|
|
1316
1553
|
});
|
|
1317
1554
|
if (formats === null) {
|
|
1318
1555
|
return null;
|
|
@@ -1332,6 +1569,17 @@ async function collectGuidedInputs(prompts) {
|
|
|
1332
1569
|
flags.outputFormats = formats.length > 0 ? formats : ["terminal"];
|
|
1333
1570
|
return flags;
|
|
1334
1571
|
}
|
|
1572
|
+
async function loadDefaultFormats(deps) {
|
|
1573
|
+
if (deps.loadSettings === void 0) {
|
|
1574
|
+
return ["terminal"];
|
|
1575
|
+
}
|
|
1576
|
+
try {
|
|
1577
|
+
const saved = (await deps.loadSettings()).outputFormats;
|
|
1578
|
+
return saved !== void 0 && saved.length > 0 ? saved : ["terminal"];
|
|
1579
|
+
} catch {
|
|
1580
|
+
return ["terminal"];
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1335
1583
|
async function runGuidedAnalyze(deps, mode, output) {
|
|
1336
1584
|
if (deps.state.provider === void 0) {
|
|
1337
1585
|
writeLine(output, NO_AI_INTERSTITIAL);
|
|
@@ -1352,7 +1600,7 @@ async function runGuidedAnalyze(deps, mode, output) {
|
|
|
1352
1600
|
);
|
|
1353
1601
|
}
|
|
1354
1602
|
}
|
|
1355
|
-
const inputs = await collectGuidedInputs(prompts);
|
|
1603
|
+
const inputs = await collectGuidedInputs(prompts, await loadDefaultFormats(deps));
|
|
1356
1604
|
if (inputs === null) {
|
|
1357
1605
|
return;
|
|
1358
1606
|
}
|
|
@@ -1363,15 +1611,25 @@ async function runGuidedAnalyze(deps, mode, output) {
|
|
|
1363
1611
|
writeLine(output, `\u25B8 Next time: ${formatEquivalentCommand(target, flags)}`);
|
|
1364
1612
|
}
|
|
1365
1613
|
async function runStatusDoctor(deps, output) {
|
|
1614
|
+
let envDiagnostics = deps.envDiagnostics ?? [];
|
|
1615
|
+
let doctorConfig = deps.doctorConfig;
|
|
1616
|
+
if (deps.refreshDoctor !== void 0) {
|
|
1617
|
+
const fresh = await deps.refreshDoctor();
|
|
1618
|
+
deps.state.provider = fresh.provider;
|
|
1619
|
+
deps.state.llmModel = fresh.llmModel;
|
|
1620
|
+
envDiagnostics = fresh.envDiagnostics;
|
|
1621
|
+
doctorConfig = fresh.doctorConfig;
|
|
1622
|
+
}
|
|
1366
1623
|
let reachability = { kind: "not-configured" };
|
|
1367
1624
|
if (deps.state.provider !== void 0 && deps.probeReachability !== void 0) {
|
|
1625
|
+
writeLine(output, `Testing connection to ${deps.state.provider}\u2026`);
|
|
1368
1626
|
try {
|
|
1369
1627
|
reachability = await deps.probeReachability();
|
|
1370
1628
|
} catch (err) {
|
|
1371
1629
|
reachability = { kind: "unreachable", reason: err instanceof Error ? err.message : "probe failed" };
|
|
1372
1630
|
}
|
|
1373
1631
|
}
|
|
1374
|
-
writeLine(output, formatStatusReport(deps.state,
|
|
1632
|
+
writeLine(output, formatStatusReport(deps.state, envDiagnostics, reachability, doctorConfig));
|
|
1375
1633
|
}
|
|
1376
1634
|
async function runSettings(deps, output) {
|
|
1377
1635
|
const prompts = deps.prompts ?? clackGuidedPrompts(output);
|
|
@@ -1391,7 +1649,15 @@ async function runSettings(deps, output) {
|
|
|
1391
1649
|
if (provider === null) {
|
|
1392
1650
|
return;
|
|
1393
1651
|
}
|
|
1394
|
-
const model = await prompts.text({
|
|
1652
|
+
const model = await prompts.text({
|
|
1653
|
+
message: "Model",
|
|
1654
|
+
placeholder: "e.g. llama3 / gpt-4o",
|
|
1655
|
+
initialValue: current.llmModel,
|
|
1656
|
+
// A model is REQUIRED: every run narrates with it, and there is no default —
|
|
1657
|
+
// saving a provider without one persists a config that fails at run time
|
|
1658
|
+
// ("llmModel is missing"). Require it here so Settings can't save a dead config.
|
|
1659
|
+
validate: (v) => v.trim() === "" ? "A model is required \u2014 every run narrates with it (e.g. gpt-4o, llama3)." : void 0
|
|
1660
|
+
});
|
|
1395
1661
|
if (model === null) {
|
|
1396
1662
|
return;
|
|
1397
1663
|
}
|
|
@@ -1399,46 +1665,56 @@ async function runSettings(deps, output) {
|
|
|
1399
1665
|
if (BASE_URL_PROVIDERS.has(provider)) {
|
|
1400
1666
|
baseUrl = await prompts.text({
|
|
1401
1667
|
message: "Base URL",
|
|
1402
|
-
placeholder: "e.g. http://localhost:11434"
|
|
1668
|
+
placeholder: "e.g. http://localhost:11434",
|
|
1669
|
+
initialValue: current.llmBaseUrl
|
|
1403
1670
|
});
|
|
1404
1671
|
if (baseUrl === null) {
|
|
1405
1672
|
return;
|
|
1406
1673
|
}
|
|
1407
1674
|
}
|
|
1408
|
-
const
|
|
1409
|
-
message: "Default output
|
|
1410
|
-
options: OUTPUT_FORMAT_OPTIONS
|
|
1411
|
-
|
|
1675
|
+
const formats = await prompts.multiselect({
|
|
1676
|
+
message: "Default output \u2014 one or more formats",
|
|
1677
|
+
options: OUTPUT_FORMAT_OPTIONS,
|
|
1678
|
+
initialValues: current.outputFormats
|
|
1412
1679
|
});
|
|
1413
|
-
if (
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
const timezone = await prompts.text({ message: "Timezone (optional)", placeholder: "e.g. UTC / Europe/Athens" });
|
|
1417
|
-
if (timezone === null) {
|
|
1680
|
+
if (formats === null) {
|
|
1418
1681
|
return;
|
|
1419
1682
|
}
|
|
1420
|
-
const
|
|
1421
|
-
message: "
|
|
1422
|
-
placeholder: "
|
|
1423
|
-
|
|
1683
|
+
const timezone = await prompts.text({
|
|
1684
|
+
message: "Timezone (optional)",
|
|
1685
|
+
placeholder: "e.g. UTC / Europe/Athens",
|
|
1686
|
+
initialValue: current.timezone
|
|
1424
1687
|
});
|
|
1425
|
-
if (
|
|
1688
|
+
if (timezone === null) {
|
|
1426
1689
|
return;
|
|
1427
1690
|
}
|
|
1428
|
-
const data = assembleSettings({ provider, model, baseUrl,
|
|
1691
|
+
const data = assembleSettings({ provider, model, baseUrl, formats, timezone });
|
|
1429
1692
|
if (deps.saveSettings === void 0) {
|
|
1430
1693
|
return;
|
|
1431
1694
|
}
|
|
1432
1695
|
try {
|
|
1433
1696
|
const path = await deps.saveSettings(data);
|
|
1434
1697
|
writeLine(output, `\u2713 Saved to ${path}`);
|
|
1435
|
-
writeLine(output,
|
|
1698
|
+
writeLine(output, settingsSavedNote(provider));
|
|
1699
|
+
await refreshAiState(deps);
|
|
1436
1700
|
} catch (err) {
|
|
1437
1701
|
writeLine(output, `\u26A0 Could not save settings: ${err instanceof Error ? err.message : "write failed"}`);
|
|
1438
1702
|
}
|
|
1439
1703
|
}
|
|
1704
|
+
async function refreshAiState(deps) {
|
|
1705
|
+
if (deps.reloadAiState === void 0) {
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
try {
|
|
1709
|
+
const ai = await deps.reloadAiState();
|
|
1710
|
+
deps.state.provider = ai.provider;
|
|
1711
|
+
deps.state.llmModel = ai.llmModel;
|
|
1712
|
+
} catch {
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1440
1715
|
function assembleSettings(input) {
|
|
1441
|
-
const
|
|
1716
|
+
const outputFormats = input.formats.length > 0 ? input.formats : ["terminal"];
|
|
1717
|
+
const data = { provider: input.provider, outputFormats };
|
|
1442
1718
|
const model = input.model.trim();
|
|
1443
1719
|
if (model !== "") {
|
|
1444
1720
|
data.llmModel = model;
|
|
@@ -1451,10 +1727,6 @@ function assembleSettings(input) {
|
|
|
1451
1727
|
if (timezone !== "") {
|
|
1452
1728
|
data.timezone = timezone;
|
|
1453
1729
|
}
|
|
1454
|
-
const limit = interpretLimit(input.limitRaw);
|
|
1455
|
-
if ("maxCommits" in limit && limit.maxCommits !== void 0) {
|
|
1456
|
-
data.maxCommits = limit.maxCommits;
|
|
1457
|
-
}
|
|
1458
1730
|
return data;
|
|
1459
1731
|
}
|
|
1460
1732
|
async function runActivate(deps, output) {
|
|
@@ -1469,7 +1741,9 @@ async function runActivate(deps, output) {
|
|
|
1469
1741
|
try {
|
|
1470
1742
|
const outcome = await deps.activateLicense(key);
|
|
1471
1743
|
if (outcome.ok) {
|
|
1472
|
-
|
|
1744
|
+
deps.state.licensed = true;
|
|
1745
|
+
deps.state.tier = outcome.tier;
|
|
1746
|
+
writeLine(output, `\u2713 License activated \u2014 ${TIER_LABEL[outcome.tier]} tier.`);
|
|
1473
1747
|
} else {
|
|
1474
1748
|
writeLine(output, `\u26A0 ${outcome.reason}`);
|
|
1475
1749
|
}
|
|
@@ -1496,6 +1770,8 @@ async function runDeactivate(deps, output) {
|
|
|
1496
1770
|
try {
|
|
1497
1771
|
const outcome = await deps.deactivateLicense();
|
|
1498
1772
|
if (outcome.ok) {
|
|
1773
|
+
deps.state.licensed = false;
|
|
1774
|
+
deps.state.tier = "free";
|
|
1499
1775
|
writeLine(output, "\u2713 License deactivated \u2014 freed on this device. Re-activate anytime with your key.");
|
|
1500
1776
|
} else {
|
|
1501
1777
|
writeLine(output, `\u26A0 ${outcome.reason}`);
|
|
@@ -1527,6 +1803,8 @@ var DEFAULT_COFFEE_URL = "https://buymeacoffee.com/georgiosnikitas";
|
|
|
1527
1803
|
async function dispatchAction(deps, action, output) {
|
|
1528
1804
|
switch (action) {
|
|
1529
1805
|
case "quit":
|
|
1806
|
+
writeLine(output, QUIT_MESSAGE);
|
|
1807
|
+
writeLine(output, "");
|
|
1530
1808
|
writeLine(output, FLAGS_CHEATSHEET);
|
|
1531
1809
|
return "quit";
|
|
1532
1810
|
case "help":
|
|
@@ -1567,14 +1845,30 @@ function assertNeverAction(action) {
|
|
|
1567
1845
|
async function runLaunchpad(deps) {
|
|
1568
1846
|
const output = deps.output ?? process.stderr;
|
|
1569
1847
|
const select = deps.select ?? clackLaunchpadSelect(output);
|
|
1570
|
-
const
|
|
1571
|
-
|
|
1572
|
-
|
|
1848
|
+
const repaint = deps.repaint ?? isInteractive(output);
|
|
1849
|
+
const pause = deps.waitForKey ?? waitForKey;
|
|
1850
|
+
const writeHeader = () => {
|
|
1851
|
+
writeLine(output, LAUNCHPAD_TAGLINE);
|
|
1852
|
+
writeLine(output, formatReadinessLine(deps.state, deps.doctorConfig?.color));
|
|
1853
|
+
};
|
|
1854
|
+
if (!repaint) {
|
|
1855
|
+
writeHeader();
|
|
1856
|
+
}
|
|
1573
1857
|
for (; ; ) {
|
|
1858
|
+
if (repaint) {
|
|
1859
|
+
clearScreen(output);
|
|
1860
|
+
writeHeader();
|
|
1861
|
+
}
|
|
1862
|
+
const options = buildLaunchpadOptions(deps.state);
|
|
1574
1863
|
const action = await select({ message: "What would you like to do?", options }) ?? "quit";
|
|
1575
1864
|
if (await dispatchAction(deps, action, output) === "quit") {
|
|
1576
1865
|
return ExitCode.Success;
|
|
1577
1866
|
}
|
|
1867
|
+
if (repaint && await pause(output) === "quit") {
|
|
1868
|
+
writeLine(output, "");
|
|
1869
|
+
writeLine(output, FLAGS_CHEATSHEET);
|
|
1870
|
+
return ExitCode.Success;
|
|
1871
|
+
}
|
|
1578
1872
|
}
|
|
1579
1873
|
}
|
|
1580
1874
|
|
|
@@ -3165,6 +3459,8 @@ var FullNarrativeSchema = NarrativeSchema.extend({
|
|
|
3165
3459
|
|
|
3166
3460
|
// src/assemble/report-schema.ts
|
|
3167
3461
|
var SCHEMA_VERSION = "1.0.0";
|
|
3462
|
+
var ProviderSchema = z2.enum(["ollama", "openai", "gemini", "anthropic", "openai-compatible"]);
|
|
3463
|
+
var TierSchema = z2.enum(["free", "single-device", "unlimited"]);
|
|
3168
3464
|
var JsonValueSchema = z2.lazy(
|
|
3169
3465
|
() => z2.union([
|
|
3170
3466
|
z2.string(),
|
|
@@ -3197,11 +3493,42 @@ var NarrativeSchema2 = z2.object({
|
|
|
3197
3493
|
message: "escalation must be present exactly when the confidence level is 'low'"
|
|
3198
3494
|
}).optional()
|
|
3199
3495
|
}).strict();
|
|
3496
|
+
var ProvenanceSchema = z2.object({
|
|
3497
|
+
repo: z2.object({
|
|
3498
|
+
name: z2.string(),
|
|
3499
|
+
target: z2.string(),
|
|
3500
|
+
// credential-stripped — never a token-bearing URL
|
|
3501
|
+
source: z2.enum(["local", "remote"]),
|
|
3502
|
+
branch: z2.string().optional()
|
|
3503
|
+
}).strict().optional(),
|
|
3504
|
+
scale: z2.object({
|
|
3505
|
+
totalCommits: z2.number().optional(),
|
|
3506
|
+
analyzedCommits: z2.number().optional(),
|
|
3507
|
+
contributors: z2.number().optional()
|
|
3508
|
+
}).strict().optional(),
|
|
3509
|
+
// Present ONLY when narration ran (absent on `--no-ai` / fail-open degraded),
|
|
3510
|
+
// mirroring the `narrative`-subtree presence rule exactly.
|
|
3511
|
+
ai: z2.object({
|
|
3512
|
+
provider: ProviderSchema,
|
|
3513
|
+
model: z2.string()
|
|
3514
|
+
}).strict().optional(),
|
|
3515
|
+
run: z2.object({
|
|
3516
|
+
generatedAt: z2.string(),
|
|
3517
|
+
// == RunConfig.analysisTimestamp, never Date.now()
|
|
3518
|
+
toolVersion: z2.string()
|
|
3519
|
+
}).strict().optional(),
|
|
3520
|
+
entitlement: z2.object({
|
|
3521
|
+
tier: TierSchema,
|
|
3522
|
+
commitCap: z2.number().optional()
|
|
3523
|
+
// present only on the Free tier
|
|
3524
|
+
}).strict().optional()
|
|
3525
|
+
}).strict();
|
|
3200
3526
|
var ReportSchema = z2.object({
|
|
3201
3527
|
schemaVersion: z2.literal(SCHEMA_VERSION),
|
|
3202
3528
|
degraded: z2.boolean(),
|
|
3203
3529
|
analysis: AnalysisSchema,
|
|
3204
|
-
narrative: NarrativeSchema2.optional()
|
|
3530
|
+
narrative: NarrativeSchema2.optional(),
|
|
3531
|
+
provenance: ProvenanceSchema.optional()
|
|
3205
3532
|
}).strict();
|
|
3206
3533
|
|
|
3207
3534
|
// src/assemble/report.ts
|
|
@@ -3214,22 +3541,33 @@ function assembleReport(input) {
|
|
|
3214
3541
|
if (input.narrative !== void 0) {
|
|
3215
3542
|
report.narrative = structuredClone(input.narrative);
|
|
3216
3543
|
}
|
|
3544
|
+
if (input.provenance !== void 0) {
|
|
3545
|
+
report.provenance = structuredClone(input.provenance);
|
|
3546
|
+
}
|
|
3217
3547
|
return report;
|
|
3218
3548
|
}
|
|
3219
|
-
function reportFromOutcome(analysis, outcome) {
|
|
3549
|
+
function reportFromOutcome(analysis, outcome, provenance) {
|
|
3220
3550
|
switch (outcome.kind) {
|
|
3221
3551
|
case "narrated":
|
|
3222
|
-
return assembleReport({ analysis, narrative: outcome.narrative, degraded: false });
|
|
3552
|
+
return assembleReport({ analysis, narrative: outcome.narrative, degraded: false, provenance });
|
|
3223
3553
|
case "skipped":
|
|
3224
|
-
return assembleReport({ analysis, degraded: false });
|
|
3554
|
+
return assembleReport({ analysis, degraded: false, provenance: withoutAi(provenance) });
|
|
3225
3555
|
// intentional metrics-only
|
|
3226
3556
|
case "degraded":
|
|
3227
|
-
return assembleReport({ analysis, degraded: true });
|
|
3557
|
+
return assembleReport({ analysis, degraded: true, provenance: withoutAi(provenance) });
|
|
3228
3558
|
// fail-open, narrative lost
|
|
3229
3559
|
default:
|
|
3230
3560
|
return assertNever(outcome);
|
|
3231
3561
|
}
|
|
3232
3562
|
}
|
|
3563
|
+
function withoutAi(provenance) {
|
|
3564
|
+
if (provenance?.ai === void 0) {
|
|
3565
|
+
return provenance;
|
|
3566
|
+
}
|
|
3567
|
+
const rest = { ...provenance };
|
|
3568
|
+
delete rest.ai;
|
|
3569
|
+
return rest;
|
|
3570
|
+
}
|
|
3233
3571
|
function assertNever(value) {
|
|
3234
3572
|
throw new Error(`Unhandled narrate outcome: ${JSON.stringify(value)}`);
|
|
3235
3573
|
}
|
|
@@ -3629,7 +3967,7 @@ function narrationReason(err, secret) {
|
|
|
3629
3967
|
}
|
|
3630
3968
|
|
|
3631
3969
|
// src/render/terminal/terminal-renderer.ts
|
|
3632
|
-
import
|
|
3970
|
+
import pc3 from "picocolors";
|
|
3633
3971
|
|
|
3634
3972
|
// src/render/render.port.ts
|
|
3635
3973
|
function classifyReport(report) {
|
|
@@ -3644,104 +3982,65 @@ function classifyReport(report) {
|
|
|
3644
3982
|
};
|
|
3645
3983
|
}
|
|
3646
3984
|
|
|
3647
|
-
// src/render/
|
|
3648
|
-
var
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
"",
|
|
3664
|
-
summary.overview,
|
|
3665
|
-
"",
|
|
3666
|
-
c.bold("Key findings"),
|
|
3667
|
-
...summary.keyFindings.map((finding) => ` ${c.cyan("\u2022")} ${finding}`),
|
|
3668
|
-
"",
|
|
3669
|
-
c.bold("Explanation"),
|
|
3670
|
-
...explanation.paragraphs.flatMap((paragraph) => [paragraph, ""]),
|
|
3671
|
-
c.bold("Coaching"),
|
|
3672
|
-
coaching.introduction,
|
|
3673
|
-
"",
|
|
3674
|
-
...coaching.chapters.flatMap((chapter) => [
|
|
3675
|
-
c.bold(chapter.theme),
|
|
3676
|
-
...chapter.steps.map((step, i) => {
|
|
3677
|
-
const marker = c.cyan(`${i + 1}.`);
|
|
3678
|
-
return ` ${marker} ${step}`;
|
|
3679
|
-
}),
|
|
3680
|
-
""
|
|
3681
|
-
]),
|
|
3682
|
-
coaching.closingSummary,
|
|
3683
|
-
"",
|
|
3684
|
-
metricsTable(report.analysis, c)
|
|
3685
|
-
].join("\n");
|
|
3686
|
-
}
|
|
3687
|
-
function renderSubstrate(analysis, framing, c) {
|
|
3688
|
-
const banner = framing === "degraded" ? c.bold(c.yellow(DEGRADED_BANNER)) : c.dim(METRICS_ONLY_NOTE);
|
|
3689
|
-
return [heading(c), "", banner, "", metricsTable(analysis, c)].join("\n");
|
|
3690
|
-
}
|
|
3691
|
-
function heading(c) {
|
|
3692
|
-
return c.bold("commit-whisper");
|
|
3693
|
-
}
|
|
3694
|
-
function confidenceBand(confidence, c) {
|
|
3695
|
-
if (confidence === void 0) {
|
|
3696
|
-
return [];
|
|
3697
|
-
}
|
|
3698
|
-
const paintByLevel = { high: c.green, medium: c.yellow, low: c.red };
|
|
3699
|
-
const paint = paintByLevel[confidence.level];
|
|
3700
|
-
const label = `Confidence: ${paint(c.bold(confidence.level.toUpperCase()))}`;
|
|
3701
|
-
const lines = ["", `${label} \u2014 ${c.dim(confidence.rationale)}`];
|
|
3702
|
-
if (confidence.escalation !== void 0) {
|
|
3703
|
-
lines.push(c.bold(c.red(`\u26A0 ${confidence.escalation}`)));
|
|
3704
|
-
}
|
|
3705
|
-
return lines;
|
|
3706
|
-
}
|
|
3707
|
-
function metricsTable(analysis, c) {
|
|
3708
|
-
if (analysis.metrics.length === 0) {
|
|
3709
|
-
return c.dim("No metrics computed.");
|
|
3710
|
-
}
|
|
3711
|
-
const rows = analysis.metrics.map((metric) => ({
|
|
3712
|
-
title: metric.title,
|
|
3713
|
-
status: metric.status,
|
|
3714
|
-
detail: metric.status === "computed" ? formatValue(metric.value) : metric.reason ?? ""
|
|
3715
|
-
}));
|
|
3716
|
-
const titleW = maxWidth(rows.map((row) => row.title), "Metric".length);
|
|
3717
|
-
const statusW = maxWidth(rows.map((row) => row.status), "Status".length);
|
|
3718
|
-
const lines = [c.bold(`${pad2("Metric", titleW)} ${pad2("Status", statusW)} Detail`)];
|
|
3719
|
-
for (const row of rows) {
|
|
3720
|
-
const status = row.status === "computed" ? c.green(pad2(row.status, statusW)) : c.yellow(pad2(row.status, statusW));
|
|
3721
|
-
lines.push(`${pad2(row.title, titleW)} ${status} ${c.dim(row.detail)}`);
|
|
3722
|
-
}
|
|
3723
|
-
return lines.join("\n");
|
|
3724
|
-
}
|
|
3725
|
-
function formatValue(value) {
|
|
3726
|
-
if (value === void 0) {
|
|
3727
|
-
return "";
|
|
3728
|
-
}
|
|
3729
|
-
const json = JSON.stringify(value);
|
|
3730
|
-
if (json === void 0) {
|
|
3731
|
-
return "";
|
|
3985
|
+
// src/render/html/health.ts
|
|
3986
|
+
var HEALTH_GLYPH = {
|
|
3987
|
+
ok: "\u25CF",
|
|
3988
|
+
watch: "\u25D0",
|
|
3989
|
+
risk: "\u25B2",
|
|
3990
|
+
na: "\u25CB"
|
|
3991
|
+
};
|
|
3992
|
+
var HEALTH_LABEL = {
|
|
3993
|
+
ok: "ok",
|
|
3994
|
+
watch: "watch",
|
|
3995
|
+
risk: "risk",
|
|
3996
|
+
na: "n/a"
|
|
3997
|
+
};
|
|
3998
|
+
function field(value, key) {
|
|
3999
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4000
|
+
return void 0;
|
|
3732
4001
|
}
|
|
3733
|
-
|
|
4002
|
+
const v = value[key];
|
|
4003
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
3734
4004
|
}
|
|
3735
|
-
function
|
|
3736
|
-
return
|
|
4005
|
+
function higherBetter(key, okMin, watchMin) {
|
|
4006
|
+
return (value) => {
|
|
4007
|
+
const n = field(value, key);
|
|
4008
|
+
if (n === void 0) return "ok";
|
|
4009
|
+
if (n >= okMin) return "ok";
|
|
4010
|
+
if (n >= watchMin) return "watch";
|
|
4011
|
+
return "risk";
|
|
4012
|
+
};
|
|
3737
4013
|
}
|
|
3738
|
-
function
|
|
3739
|
-
return value
|
|
4014
|
+
function lowerBetter(key, okMax, watchMax) {
|
|
4015
|
+
return (value) => {
|
|
4016
|
+
const n = field(value, key);
|
|
4017
|
+
if (n === void 0) return "ok";
|
|
4018
|
+
if (n <= okMax) return "ok";
|
|
4019
|
+
if (n <= watchMax) return "watch";
|
|
4020
|
+
return "risk";
|
|
4021
|
+
};
|
|
3740
4022
|
}
|
|
3741
|
-
|
|
3742
|
-
//
|
|
3743
|
-
|
|
3744
|
-
|
|
4023
|
+
var REGISTRY = {
|
|
4024
|
+
// Knowledge concentration — fewer key people is riskier.
|
|
4025
|
+
"b-bus-factor": higherBetter("busFactor", 3, 2),
|
|
4026
|
+
"f-bus-factor-risk": higherBetter("busFactor", 3, 2),
|
|
4027
|
+
// Contribution concentration — a very high top share concentrates load.
|
|
4028
|
+
"b-contribution-distribution": lowerBetter("topCommitSharePct", 60, 80),
|
|
4029
|
+
// Message quality — higher Conventional-Commits adherence is healthier.
|
|
4030
|
+
"c-conventional-commits": higherBetter("adherenceSharePct", 70, 40),
|
|
4031
|
+
// Low-information commit messages — fewer is healthier.
|
|
4032
|
+
"c-low-information-rate": lowerBetter("lowInfoSharePct", 10, 25),
|
|
4033
|
+
// Workflow discipline — a high direct-to-default share is riskier.
|
|
4034
|
+
"d-direct-to-default": lowerBetter("directToDefaultSharePct", 20, 50),
|
|
4035
|
+
// Overall hygiene composite (0–100).
|
|
4036
|
+
"f-hygiene-score": higherBetter("score", 75, 50)
|
|
4037
|
+
};
|
|
4038
|
+
function classifyHealth(metric) {
|
|
4039
|
+
if (metric.status === "not_available") {
|
|
4040
|
+
return "na";
|
|
4041
|
+
}
|
|
4042
|
+
const classifier = REGISTRY[metric.id];
|
|
4043
|
+
return classifier === void 0 ? "ok" : classifier(metric.value);
|
|
3745
4044
|
}
|
|
3746
4045
|
|
|
3747
4046
|
// src/render/html/shape.ts
|
|
@@ -3843,111 +4142,607 @@ function extractSeries(value) {
|
|
|
3843
4142
|
return numericEntries(value);
|
|
3844
4143
|
}
|
|
3845
4144
|
|
|
3846
|
-
// src/render/
|
|
3847
|
-
var
|
|
3848
|
-
var
|
|
3849
|
-
|
|
3850
|
-
|
|
3851
|
-
|
|
3852
|
-
function r(n) {
|
|
3853
|
-
return Math.round(safe(n) * 100) / 100;
|
|
4145
|
+
// src/render/value-tree.ts
|
|
4146
|
+
var LABEL_FIELDS2 = ["path", "file", "directory", "area", "name", "id", "label", "key"];
|
|
4147
|
+
var PRIMARY_NUMERIC_FIELDS = ["churn", "total", "value", "count", "sum", "score", "commitCount"];
|
|
4148
|
+
var MAX_STRING = 80;
|
|
4149
|
+
function isRecord(value) {
|
|
4150
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
3854
4151
|
}
|
|
3855
|
-
function
|
|
3856
|
-
return
|
|
4152
|
+
function isFiniteNumber(value) {
|
|
4153
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
3857
4154
|
}
|
|
3858
|
-
function
|
|
3859
|
-
if (
|
|
3860
|
-
return
|
|
4155
|
+
function formatScalar(value) {
|
|
4156
|
+
if (value === null || value === void 0) {
|
|
4157
|
+
return "\u2014";
|
|
3861
4158
|
}
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
4159
|
+
if (typeof value === "string") {
|
|
4160
|
+
return value.length > MAX_STRING ? `${value.slice(0, MAX_STRING - 1)}\u2026` : value;
|
|
4161
|
+
}
|
|
4162
|
+
if (typeof value === "boolean") {
|
|
4163
|
+
return String(value);
|
|
4164
|
+
}
|
|
4165
|
+
if (typeof value === "number") {
|
|
4166
|
+
return Number.isFinite(value) ? String(Math.round(value * 100) / 100) : "0";
|
|
3868
4167
|
}
|
|
3869
|
-
return
|
|
4168
|
+
return "";
|
|
3870
4169
|
}
|
|
3871
|
-
function
|
|
3872
|
-
|
|
4170
|
+
function labelField(record) {
|
|
4171
|
+
for (const key of LABEL_FIELDS2) {
|
|
4172
|
+
const candidate = record[key];
|
|
4173
|
+
if (typeof candidate === "string" && candidate !== "") {
|
|
4174
|
+
return { key, value: candidate };
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
return void 0;
|
|
3873
4178
|
}
|
|
3874
|
-
function
|
|
3875
|
-
|
|
4179
|
+
function primaryNumeric(record) {
|
|
4180
|
+
for (const key of PRIMARY_NUMERIC_FIELDS) {
|
|
4181
|
+
const candidate = record[key];
|
|
4182
|
+
if (isFiniteNumber(candidate)) {
|
|
4183
|
+
return candidate;
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
for (const candidate of Object.values(record)) {
|
|
4187
|
+
if (isFiniteNumber(candidate)) {
|
|
4188
|
+
return candidate;
|
|
4189
|
+
}
|
|
4190
|
+
}
|
|
4191
|
+
return void 0;
|
|
3876
4192
|
}
|
|
3877
|
-
function
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
4193
|
+
function arrayEntry(element, index) {
|
|
4194
|
+
if (isRecord(element)) {
|
|
4195
|
+
const label = labelField(element);
|
|
4196
|
+
const numerics = Object.entries(element).filter(([, v]) => isFiniteNumber(v));
|
|
4197
|
+
if (label !== void 0 && numerics.length === 1) {
|
|
4198
|
+
return { label: label.value, child: { kind: "scalar", text: formatScalar(numerics[0][1]) } };
|
|
4199
|
+
}
|
|
4200
|
+
if (label !== void 0) {
|
|
4201
|
+
const rest = {};
|
|
4202
|
+
for (const [k, v] of Object.entries(element)) {
|
|
4203
|
+
if (k !== label.key) {
|
|
4204
|
+
rest[k] = v;
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
return { label: label.value, child: recordTree(rest) };
|
|
4208
|
+
}
|
|
3881
4209
|
}
|
|
3882
|
-
|
|
3883
|
-
return VIEW_H - pad3 - t * (VIEW_H - 2 * pad3);
|
|
4210
|
+
return { label: String(index + 1), child: buildValueTree(element) };
|
|
3884
4211
|
}
|
|
3885
|
-
function
|
|
3886
|
-
|
|
3887
|
-
|
|
4212
|
+
function recordTree(record) {
|
|
4213
|
+
const entries = Object.entries(record);
|
|
4214
|
+
const everyHasPrimary = entries.length > 0 && entries.every(([, v]) => isRecord(v) && primaryNumeric(v) !== void 0);
|
|
4215
|
+
if (everyHasPrimary) {
|
|
4216
|
+
return {
|
|
4217
|
+
kind: "branch",
|
|
4218
|
+
entries: entries.map(([key, v]) => ({
|
|
4219
|
+
label: key,
|
|
4220
|
+
child: { kind: "scalar", text: formatScalar(primaryNumeric(v)) }
|
|
4221
|
+
}))
|
|
4222
|
+
};
|
|
3888
4223
|
}
|
|
3889
|
-
|
|
3890
|
-
const [min, max] = extent(values);
|
|
3891
|
-
const step = series.length === 1 ? 0 : VIEW_W / (series.length - 1);
|
|
3892
|
-
const points = series.map((p, i) => `${r(i * step)},${r(yScale(p.value, min, max))}`).join(" ");
|
|
3893
|
-
return `${open(label, "chart-line")}<polyline fill="none" stroke="currentColor" stroke-width="1.5" points="${points}"/></svg>`;
|
|
4224
|
+
return { kind: "branch", entries: entries.map(([key, v]) => ({ label: key, child: buildValueTree(v) })) };
|
|
3894
4225
|
}
|
|
3895
|
-
function
|
|
3896
|
-
|
|
4226
|
+
function buildValueTree(value) {
|
|
4227
|
+
if (Array.isArray(value)) {
|
|
4228
|
+
return { kind: "branch", entries: value.map((element, i) => arrayEntry(element, i)) };
|
|
4229
|
+
}
|
|
4230
|
+
if (isRecord(value)) {
|
|
4231
|
+
return recordTree(value);
|
|
4232
|
+
}
|
|
4233
|
+
return { kind: "scalar", text: formatScalar(value) };
|
|
3897
4234
|
}
|
|
3898
|
-
|
|
3899
|
-
|
|
3900
|
-
|
|
4235
|
+
|
|
4236
|
+
// src/render/terminal/terminal-renderer.ts
|
|
4237
|
+
var GROUPS = [
|
|
4238
|
+
{ id: "A", title: "Activity & Cadence", description: "How the project moves over time." },
|
|
4239
|
+
{ id: "B", title: "Contribution & Ownership", description: "How the work is distributed across the team." },
|
|
4240
|
+
{ id: "C", title: "Commit Message Quality", description: "How clearly the history communicates intent." },
|
|
4241
|
+
{ id: "D", title: "Branching & Merge Structure", description: "How branching and merging are structured." },
|
|
4242
|
+
{ id: "E", title: "Churn & Hotspots", description: "Where change and instability concentrate." },
|
|
4243
|
+
{ id: "F", title: "Repository Health Signals", description: "Overall repository health signals." }
|
|
4244
|
+
];
|
|
4245
|
+
var DEGRADED_BANNER = "\u26A0 Narrative unavailable \u2014 raw analysis below";
|
|
4246
|
+
var METRICS_ONLY_NOTE = "Metrics-only run \u2014 no AI narrative requested";
|
|
4247
|
+
function renderTerminal(report, opts = {}) {
|
|
4248
|
+
const c = opts.color === void 0 ? pc3.createColors() : pc3.createColors(opts.color);
|
|
4249
|
+
const route = classifyReport(report);
|
|
4250
|
+
return route.kind === "showpiece" ? renderShowpiece(route.report, report.provenance, c) : renderSubstrate(route.analysis, route.framing, report.provenance, c);
|
|
4251
|
+
}
|
|
4252
|
+
function renderShowpiece(report, provenance, c) {
|
|
4253
|
+
const { summary, explanation, coaching, confidence } = report.narrative;
|
|
4254
|
+
return [
|
|
4255
|
+
masthead(provenance, c),
|
|
4256
|
+
...confidenceBand(confidence, c),
|
|
4257
|
+
"",
|
|
4258
|
+
c.bold("Summary"),
|
|
4259
|
+
c.bold(summary.headline),
|
|
4260
|
+
"",
|
|
4261
|
+
summary.overview,
|
|
4262
|
+
"",
|
|
4263
|
+
c.bold("Key findings"),
|
|
4264
|
+
...summary.keyFindings.map((finding) => ` ${c.cyan("\u2022")} ${finding}`),
|
|
4265
|
+
"",
|
|
4266
|
+
c.bold("Explanation"),
|
|
4267
|
+
...explanation.paragraphs.flatMap((paragraph) => [paragraph, ""]),
|
|
4268
|
+
c.bold("Coaching"),
|
|
4269
|
+
coaching.introduction,
|
|
4270
|
+
"",
|
|
4271
|
+
...coaching.chapters.flatMap((chapter) => [
|
|
4272
|
+
c.bold(chapter.theme),
|
|
4273
|
+
...chapter.steps.map((step, i) => {
|
|
4274
|
+
const marker = c.cyan(`${i + 1}.`);
|
|
4275
|
+
return ` ${marker} ${step}`;
|
|
4276
|
+
}),
|
|
4277
|
+
""
|
|
4278
|
+
]),
|
|
4279
|
+
coaching.closingSummary,
|
|
4280
|
+
"",
|
|
4281
|
+
metricsSection(report.analysis, report.narrative.explanations, c)
|
|
4282
|
+
].join("\n");
|
|
4283
|
+
}
|
|
4284
|
+
function renderSubstrate(analysis, framing, provenance, c) {
|
|
4285
|
+
const banner = framing === "degraded" ? c.bold(c.yellow(DEGRADED_BANNER)) : c.dim(METRICS_ONLY_NOTE);
|
|
4286
|
+
return [masthead(provenance, c), "", banner, "", metricsSection(analysis, void 0, c)].join("\n");
|
|
4287
|
+
}
|
|
4288
|
+
function masthead(provenance, c) {
|
|
4289
|
+
const lines = [c.bold("commit-whisper")];
|
|
4290
|
+
const chips = provenanceChips(provenance);
|
|
4291
|
+
if (chips !== "") {
|
|
4292
|
+
lines.push(c.dim(chips));
|
|
3901
4293
|
}
|
|
3902
|
-
const
|
|
3903
|
-
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
4294
|
+
const cap = capLine(provenance);
|
|
4295
|
+
if (cap !== "") {
|
|
4296
|
+
lines.push(c.dim(cap));
|
|
4297
|
+
}
|
|
4298
|
+
return lines.join("\n");
|
|
4299
|
+
}
|
|
4300
|
+
function provenanceChips(provenance) {
|
|
4301
|
+
const repo = provenance?.repo;
|
|
4302
|
+
const scale = provenance?.scale;
|
|
4303
|
+
const chips = [];
|
|
4304
|
+
if (repo?.name !== void 0) {
|
|
4305
|
+
chips.push(repo.name);
|
|
4306
|
+
}
|
|
4307
|
+
if (repo?.branch !== void 0) {
|
|
4308
|
+
chips.push(repo.branch);
|
|
4309
|
+
}
|
|
4310
|
+
const commits = scale?.totalCommits ?? scale?.analyzedCommits;
|
|
4311
|
+
if (commits !== void 0) {
|
|
4312
|
+
chips.push(`${formatCount(commits)} ${commits === 1 ? "commit" : "commits"}`);
|
|
4313
|
+
}
|
|
4314
|
+
if (scale?.contributors !== void 0) {
|
|
4315
|
+
chips.push(`${formatCount(scale.contributors)} ${scale.contributors === 1 ? "contributor" : "contributors"}`);
|
|
4316
|
+
}
|
|
4317
|
+
const generatedAt = provenance?.run?.generatedAt;
|
|
4318
|
+
if (generatedAt !== void 0) {
|
|
4319
|
+
chips.push(`analyzed ${isoDate(generatedAt)}`);
|
|
4320
|
+
}
|
|
4321
|
+
return chips.join(" \xB7 ");
|
|
4322
|
+
}
|
|
4323
|
+
function capLine(provenance) {
|
|
4324
|
+
if (provenance?.entitlement?.tier !== "free") {
|
|
4325
|
+
return "";
|
|
4326
|
+
}
|
|
4327
|
+
const analyzed = provenance.scale?.analyzedCommits;
|
|
4328
|
+
if (analyzed === void 0) {
|
|
4329
|
+
return "";
|
|
4330
|
+
}
|
|
4331
|
+
const total = provenance.scale?.totalCommits;
|
|
4332
|
+
const detail = total === void 0 ? `${formatCount(analyzed)} commits analyzed` : `${formatCount(analyzed)} of ${formatCount(total)} commits analyzed`;
|
|
4333
|
+
return `Free \xB7 ${detail}`;
|
|
4334
|
+
}
|
|
4335
|
+
function formatCount(n) {
|
|
4336
|
+
if (!Number.isFinite(n)) {
|
|
4337
|
+
return "0";
|
|
4338
|
+
}
|
|
4339
|
+
const negative = n < 0;
|
|
4340
|
+
const digits = Math.abs(Math.trunc(n)).toString();
|
|
4341
|
+
let grouped = "";
|
|
4342
|
+
for (let i = 0; i < digits.length; i++) {
|
|
4343
|
+
if (i > 0 && (digits.length - i) % 3 === 0) {
|
|
4344
|
+
grouped += ",";
|
|
4345
|
+
}
|
|
4346
|
+
grouped += digits[i];
|
|
4347
|
+
}
|
|
4348
|
+
return negative ? `-${grouped}` : grouped;
|
|
4349
|
+
}
|
|
4350
|
+
function isoDate(iso) {
|
|
4351
|
+
return iso.slice(0, 10);
|
|
4352
|
+
}
|
|
4353
|
+
function confidenceBand(confidence, c) {
|
|
4354
|
+
if (confidence === void 0) {
|
|
4355
|
+
return [];
|
|
4356
|
+
}
|
|
4357
|
+
const paintByLevel = { high: c.green, medium: c.yellow, low: c.red };
|
|
4358
|
+
const paint = paintByLevel[confidence.level];
|
|
4359
|
+
const label = `Confidence: ${paint(c.bold(confidence.level.toUpperCase()))}`;
|
|
4360
|
+
const lines = ["", `${label} \u2014 ${c.dim(confidence.rationale)}`];
|
|
4361
|
+
if (confidence.escalation !== void 0) {
|
|
4362
|
+
lines.push(c.bold(c.red(`\u26A0 ${confidence.escalation}`)));
|
|
4363
|
+
}
|
|
4364
|
+
return lines;
|
|
4365
|
+
}
|
|
4366
|
+
function metricsSection(analysis, explanations, c) {
|
|
4367
|
+
if (analysis.metrics.length === 0) {
|
|
4368
|
+
return c.dim("No metrics computed.");
|
|
4369
|
+
}
|
|
4370
|
+
const present = GROUPS.filter((group) => analysis.metrics.some((metric) => metric.group === group.id));
|
|
4371
|
+
const sections = present.map((group) => {
|
|
4372
|
+
const metrics = analysis.metrics.filter((metric) => metric.group === group.id);
|
|
4373
|
+
const cards = metrics.map((metric) => metricCard(metric, explanations, c)).join("\n\n");
|
|
4374
|
+
return [c.bold(`${group.id} \xB7 ${group.title}`), c.dim(group.description), "", cards].join("\n");
|
|
4375
|
+
});
|
|
4376
|
+
return [c.bold("Metrics"), "", sections.join("\n\n")].join("\n");
|
|
4377
|
+
}
|
|
4378
|
+
function metricCard(metric, explanations, c) {
|
|
4379
|
+
const band = classifyHealth(metric);
|
|
4380
|
+
const heading = `${c.bold(metric.title)} ${healthTag(band, c)}`;
|
|
4381
|
+
const lines = [heading, valueBullet(metric, c)];
|
|
4382
|
+
const explanation = explanations?.[metric.id];
|
|
4383
|
+
if (explanation !== void 0) {
|
|
4384
|
+
lines.push(...facetBullets(explanation, c));
|
|
4385
|
+
}
|
|
4386
|
+
return lines.join("\n");
|
|
4387
|
+
}
|
|
4388
|
+
function healthTag(band, c) {
|
|
4389
|
+
const paintByBand = { ok: c.green, watch: c.yellow, risk: c.red, na: c.dim };
|
|
4390
|
+
return paintByBand[band](`${HEALTH_GLYPH[band]} ${HEALTH_LABEL[band]}`);
|
|
4391
|
+
}
|
|
4392
|
+
function valueBullet(metric, c) {
|
|
4393
|
+
const label = ` ${c.cyan("\u2022")} ${c.bold("Value")}`;
|
|
4394
|
+
if (metric.status !== "computed") {
|
|
4395
|
+
const reason = metric.reason === void 0 ? "" : ` \u2014 ${metric.reason}`;
|
|
4396
|
+
const note = `not available${reason}`;
|
|
4397
|
+
return `${label} \u2014 ${c.dim(note)}`;
|
|
4398
|
+
}
|
|
4399
|
+
const value = metric.value;
|
|
4400
|
+
if (value === null || typeof value !== "object") {
|
|
4401
|
+
return `${label} \u2014 ${formatValue2(value)}`;
|
|
4402
|
+
}
|
|
4403
|
+
const series = extractSeries(value);
|
|
4404
|
+
if (series.length === 1) {
|
|
4405
|
+
return `${label} \u2014 ${formatNumber(series[0].value)}`;
|
|
4406
|
+
}
|
|
4407
|
+
if (series.length > 1) {
|
|
4408
|
+
const rows = series.map((point) => ` ${c.dim("-")} ${point.label}: ${formatNumber(point.value)}`);
|
|
4409
|
+
return [label, ...rows].join("\n");
|
|
4410
|
+
}
|
|
4411
|
+
const tree = buildValueTree(value);
|
|
4412
|
+
if (tree.kind === "scalar") {
|
|
4413
|
+
return `${label} \u2014 ${tree.text}`;
|
|
4414
|
+
}
|
|
4415
|
+
return [label, ...treeLines(tree.entries, " ", c)].join("\n");
|
|
4416
|
+
}
|
|
4417
|
+
function treeLines(entries, indent, c) {
|
|
4418
|
+
return entries.flatMap((entry) => {
|
|
4419
|
+
if (entry.child.kind === "scalar") {
|
|
4420
|
+
const text = entry.child.text === "" ? entry.label : `${entry.label}: ${entry.child.text}`;
|
|
4421
|
+
return [`${indent}${c.dim("-")} ${text}`];
|
|
4422
|
+
}
|
|
4423
|
+
return [`${indent}${c.dim("-")} ${entry.label}`, ...treeLines(entry.child.entries, `${indent} `, c)];
|
|
4424
|
+
});
|
|
4425
|
+
}
|
|
4426
|
+
function formatNumber(n) {
|
|
4427
|
+
return Number.isFinite(n) ? String(Math.round(n * 100) / 100) : "0";
|
|
4428
|
+
}
|
|
4429
|
+
function facetBullets(explanation, c) {
|
|
4430
|
+
return [
|
|
4431
|
+
` ${c.cyan("\u2022")} ${c.bold("What it means")} \u2014 ${explanation.explanation}`,
|
|
4432
|
+
...arrayFacet("Strengths", explanation.goodBehaviours, c),
|
|
4433
|
+
...arrayFacet("Needs improvement", explanation.needsImprovement, c),
|
|
4434
|
+
...arrayFacet("Suggestions", explanation.suggestions, c)
|
|
4435
|
+
];
|
|
4436
|
+
}
|
|
4437
|
+
function arrayFacet(label, items, c) {
|
|
4438
|
+
if (items.length === 0) {
|
|
4439
|
+
return [` ${c.cyan("\u2022")} ${c.bold(label)} \u2014 \u2014`];
|
|
4440
|
+
}
|
|
4441
|
+
return [` ${c.cyan("\u2022")} ${c.bold(label)}`, ...items.map((item) => ` ${c.dim("-")} ${item}`)];
|
|
4442
|
+
}
|
|
4443
|
+
function formatValue2(value) {
|
|
4444
|
+
if (value === void 0) {
|
|
4445
|
+
return "";
|
|
4446
|
+
}
|
|
4447
|
+
if (typeof value === "string") {
|
|
4448
|
+
return value.length > 60 ? `${value.slice(0, 59)}\u2026` : value;
|
|
4449
|
+
}
|
|
4450
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
4451
|
+
return String(value);
|
|
4452
|
+
}
|
|
4453
|
+
const json = JSON.stringify(value);
|
|
4454
|
+
if (json === void 0) {
|
|
4455
|
+
return "";
|
|
4456
|
+
}
|
|
4457
|
+
return json.length > 60 ? `${json.slice(0, 59)}\u2026` : json;
|
|
4458
|
+
}
|
|
4459
|
+
|
|
4460
|
+
// src/render/html/escape.ts
|
|
4461
|
+
function escapeHtml(text) {
|
|
4462
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
4463
|
+
}
|
|
4464
|
+
|
|
4465
|
+
// src/render/html/svg.ts
|
|
4466
|
+
function safe(n) {
|
|
4467
|
+
return Number.isFinite(n) ? n : 0;
|
|
4468
|
+
}
|
|
4469
|
+
function r(n) {
|
|
4470
|
+
return Math.round(safe(n) * 100) / 100;
|
|
4471
|
+
}
|
|
4472
|
+
function esc(text) {
|
|
4473
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
4474
|
+
}
|
|
4475
|
+
function hashId(text) {
|
|
4476
|
+
let h = 2166136261;
|
|
4477
|
+
for (let i = 0; i < text.length; i++) {
|
|
4478
|
+
h ^= text.codePointAt(i) ?? 0;
|
|
4479
|
+
h = Math.imul(h, 16777619);
|
|
4480
|
+
}
|
|
4481
|
+
return (h >>> 0).toString(36);
|
|
4482
|
+
}
|
|
4483
|
+
function maxValue(values) {
|
|
4484
|
+
let max = 0;
|
|
4485
|
+
for (const raw of values) {
|
|
4486
|
+
const v = safe(raw);
|
|
4487
|
+
if (v > max) max = v;
|
|
4488
|
+
}
|
|
4489
|
+
return max;
|
|
4490
|
+
}
|
|
4491
|
+
function fmtNum(v) {
|
|
4492
|
+
if (!Number.isFinite(v)) return "0";
|
|
4493
|
+
return Number.isInteger(v) ? String(v) : String(Math.round(v * 100) / 100);
|
|
4494
|
+
}
|
|
4495
|
+
var MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
4496
|
+
function tickLabel(raw) {
|
|
4497
|
+
const month = /^(\d{4})-(\d{2})$/.exec(raw);
|
|
4498
|
+
if (month) {
|
|
4499
|
+
const mi = Number(month[2]);
|
|
4500
|
+
if (mi >= 1 && mi <= 12) return MONTHS[mi - 1];
|
|
4501
|
+
}
|
|
4502
|
+
const week = /^(\d{4})-W(\d{2})$/.exec(raw);
|
|
4503
|
+
if (week) return `W${week[2]}`;
|
|
4504
|
+
const slash = raw.lastIndexOf("/");
|
|
4505
|
+
const base = slash >= 0 ? raw.slice(slash + 1) : raw;
|
|
4506
|
+
return base.length > 16 ? `${base.slice(0, 15)}\u2026` : base;
|
|
4507
|
+
}
|
|
4508
|
+
function niceStep(range, n) {
|
|
4509
|
+
if (range <= 0) return 1;
|
|
4510
|
+
const raw = range / n;
|
|
4511
|
+
const exp = Math.floor(Math.log10(raw));
|
|
4512
|
+
const f = raw / 10 ** exp;
|
|
4513
|
+
let nf;
|
|
4514
|
+
if (f < 1.5) nf = 1;
|
|
4515
|
+
else if (f < 3) nf = 2;
|
|
4516
|
+
else if (f < 7) nf = 5;
|
|
4517
|
+
else nf = 10;
|
|
4518
|
+
return nf * 10 ** exp;
|
|
4519
|
+
}
|
|
4520
|
+
function valueTicks(max) {
|
|
4521
|
+
const m = max <= 0 ? 1 : max;
|
|
4522
|
+
const step = niceStep(m, 4);
|
|
4523
|
+
const top = Math.max(step, Math.ceil(m / step) * step);
|
|
4524
|
+
const ticks = [];
|
|
4525
|
+
for (let v = 0; v <= top + step / 1e3; v += step) {
|
|
4526
|
+
ticks.push(Math.round(v * 1e3) / 1e3);
|
|
4527
|
+
}
|
|
4528
|
+
return { ticks, top };
|
|
4529
|
+
}
|
|
4530
|
+
function open(label, extraClass, viewBox, par = "xMidYMid meet") {
|
|
4531
|
+
return `<svg class="chart-svg ${extraClass}" viewBox="${viewBox}" preserveAspectRatio="${par}" role="img" aria-label="${esc(label)}">`;
|
|
4532
|
+
}
|
|
4533
|
+
function empty(label, extraClass) {
|
|
4534
|
+
return `${open(label, extraClass, "0 0 640 240")}</svg>`;
|
|
4535
|
+
}
|
|
4536
|
+
function areaGradient(id) {
|
|
4537
|
+
return `<defs><linearGradient id="${id}" x1="0" y1="0" x2="0" y2="1"><stop offset="0" class="grad-area-top"/><stop offset="1" class="grad-area-bottom"/></linearGradient></defs>`;
|
|
4538
|
+
}
|
|
4539
|
+
function fillGradient(id, vertical) {
|
|
4540
|
+
const dir = vertical ? `x1="0" y1="0" x2="0" y2="1"` : `x1="0" y1="0" x2="1" y2="0"`;
|
|
4541
|
+
return `<defs><linearGradient id="${id}" ${dir}><stop offset="0" class="grad-fill-1"/><stop offset="1" class="grad-fill-2"/></linearGradient></defs>`;
|
|
4542
|
+
}
|
|
4543
|
+
function smoothPath(coords) {
|
|
4544
|
+
if (coords.length === 1) {
|
|
4545
|
+
const [x, y] = coords[0];
|
|
4546
|
+
return `M ${r(x)} ${r(y)}`;
|
|
4547
|
+
}
|
|
4548
|
+
let d = `M ${r(coords[0][0])} ${r(coords[0][1])}`;
|
|
4549
|
+
for (let i = 0; i < coords.length - 1; i++) {
|
|
4550
|
+
const p0 = coords[i === 0 ? 0 : i - 1];
|
|
4551
|
+
const p1 = coords[i];
|
|
4552
|
+
const p2 = coords[i + 1];
|
|
4553
|
+
const p3 = coords[i + 2 < coords.length ? i + 2 : coords.length - 1];
|
|
4554
|
+
const c1x = p1[0] + (p2[0] - p0[0]) / 6;
|
|
4555
|
+
const c1y = p1[1] + (p2[1] - p0[1]) / 6;
|
|
4556
|
+
const c2x = p2[0] - (p3[0] - p1[0]) / 6;
|
|
4557
|
+
const c2y = p2[1] - (p3[1] - p1[1]) / 6;
|
|
4558
|
+
d += ` C ${r(c1x)} ${r(c1y)}, ${r(c2x)} ${r(c2y)}, ${r(p2[0])} ${r(p2[1])}`;
|
|
4559
|
+
}
|
|
4560
|
+
return d;
|
|
4561
|
+
}
|
|
4562
|
+
function roundedTopRect(x, y, w, h, rad, gradId) {
|
|
4563
|
+
if (h <= 0 || w <= 0) return "";
|
|
4564
|
+
const k = Math.min(rad, w / 2, h);
|
|
4565
|
+
const d = `M ${r(x)} ${r(y + h)} L ${r(x)} ${r(y + k)} Q ${r(x)} ${r(y)} ${r(x + k)} ${r(y)} L ${r(x + w - k)} ${r(y)} Q ${r(x + w)} ${r(y)} ${r(x + w)} ${r(y + k)} L ${r(x + w)} ${r(y + h)} Z`;
|
|
4566
|
+
return `<path class="bar" d="${d}" fill="url(#${gradId})"/>`;
|
|
4567
|
+
}
|
|
4568
|
+
function roundedRightRect(x, y, w, h, rad, gradId) {
|
|
4569
|
+
if (h <= 0 || w <= 0) return "";
|
|
4570
|
+
const k = Math.min(rad, h / 2, w);
|
|
4571
|
+
const d = `M ${r(x)} ${r(y)} L ${r(x + w - k)} ${r(y)} Q ${r(x + w)} ${r(y)} ${r(x + w)} ${r(y + k)} L ${r(x + w)} ${r(y + h - k)} Q ${r(x + w)} ${r(y + h)} ${r(x + w - k)} ${r(y + h)} L ${r(x)} ${r(y + h)} Z`;
|
|
4572
|
+
return `<path class="bar" d="${d}" fill="url(#${gradId})"/>`;
|
|
4573
|
+
}
|
|
4574
|
+
function valueGrid(ticks, top, x0, x1, y0, y1) {
|
|
4575
|
+
return ticks.map((t) => {
|
|
4576
|
+
const y = r(y1 - t / top * (y1 - y0));
|
|
4577
|
+
return `<line class="chart-grid" x1="${x0}" y1="${y}" x2="${x1}" y2="${y}"/><text class="chart-tick" x="${x0 - 6}" y="${r(y + 3.5)}" text-anchor="end">${esc(fmtNum(t))}</text>`;
|
|
4578
|
+
}).join("");
|
|
4579
|
+
}
|
|
4580
|
+
function svgLine(series, label) {
|
|
4581
|
+
if (series.length === 0) return empty(label, "chart-line");
|
|
4582
|
+
const id = hashId(label);
|
|
4583
|
+
const areaId = `cw-area-${id}`;
|
|
4584
|
+
const strokeId = `cw-stroke-${id}`;
|
|
4585
|
+
const W = 640;
|
|
4586
|
+
const H = 240;
|
|
4587
|
+
const mL = 46;
|
|
4588
|
+
const mR = 16;
|
|
4589
|
+
const mT = 14;
|
|
4590
|
+
const mB = 30;
|
|
4591
|
+
const x0 = mL;
|
|
4592
|
+
const x1 = W - mR;
|
|
4593
|
+
const y0 = mT;
|
|
4594
|
+
const y1 = H - mB;
|
|
4595
|
+
const { ticks, top } = valueTicks(maxValue(series.map((p) => p.value)));
|
|
4596
|
+
const xAt = (i) => series.length === 1 ? (x0 + x1) / 2 : x0 + i / (series.length - 1) * (x1 - x0);
|
|
4597
|
+
const yAt = (v) => y1 - Math.max(0, safe(v)) / top * (y1 - y0);
|
|
4598
|
+
const coords = series.map((p, i) => [r(xAt(i)), r(yAt(p.value))]);
|
|
4599
|
+
const line = smoothPath(coords);
|
|
4600
|
+
const last = coords.at(-1);
|
|
4601
|
+
const area = `${line} L ${r(last[0])} ${y1} L ${r(coords[0][0])} ${y1} Z`;
|
|
4602
|
+
const every = series.length > 12 ? Math.ceil(series.length / 12) : 1;
|
|
4603
|
+
const xLabels = series.map((p, i) => i % every === 0 ? `<text class="chart-label" x="${r(xAt(i))}" y="${H - 10}" text-anchor="middle">${esc(tickLabel(p.label))}</text>` : "").join("");
|
|
4604
|
+
const dot = coords.length === 1 ? `<circle class="chart-dot" cx="${r(coords[0][0])}" cy="${r(coords[0][1])}" r="4"/>` : "";
|
|
4605
|
+
const viewBox = `0 0 ${W} ${H}`;
|
|
4606
|
+
return `${open(label, "chart-line", viewBox)}${areaGradient(areaId)}${fillGradient(strokeId, false)}${valueGrid(ticks, top, x0, x1, y0, y1)}<line class="chart-axis" x1="${x0}" y1="${y1}" x2="${x1}" y2="${y1}"/><path class="chart-area" d="${area}" fill="url(#${areaId})"/><path class="chart-stroke" d="${line}" fill="none" stroke="url(#${strokeId})" stroke-width="2.5" stroke-linejoin="round" stroke-linecap="round"/>${dot}${xLabels}</svg>`;
|
|
4607
|
+
}
|
|
4608
|
+
function svgBars(series, label) {
|
|
4609
|
+
if (series.length === 0) return empty(label, "chart-bars");
|
|
4610
|
+
const id = `cw-bar-${hashId(label)}`;
|
|
4611
|
+
const W = 640;
|
|
4612
|
+
const H = 240;
|
|
4613
|
+
const mL = 46;
|
|
4614
|
+
const mR = 16;
|
|
4615
|
+
const mT = 14;
|
|
4616
|
+
const mB = 30;
|
|
4617
|
+
const x0 = mL;
|
|
4618
|
+
const x1 = W - mR;
|
|
4619
|
+
const y0 = mT;
|
|
4620
|
+
const y1 = H - mB;
|
|
4621
|
+
const { ticks, top } = valueTicks(maxValue(series.map((p) => p.value)));
|
|
4622
|
+
const slot = (x1 - x0) / series.length;
|
|
4623
|
+
const barW = Math.min(slot * 0.62, 64);
|
|
4624
|
+
const rad = Math.min(barW / 2, 6);
|
|
3907
4625
|
const bars = series.map((p, i) => {
|
|
3908
|
-
const h = Math.max(0, safe(p.value)) / top * (
|
|
3909
|
-
const x = i * slot + (slot - barW) / 2;
|
|
3910
|
-
|
|
3911
|
-
return `<rect class="bar" x="${r(x)}" y="${r(y)}" width="${r(barW)}" height="${r(h)}"/>`;
|
|
4626
|
+
const h = Math.max(0, safe(p.value)) / top * (y1 - y0);
|
|
4627
|
+
const x = x0 + i * slot + (slot - barW) / 2;
|
|
4628
|
+
return roundedTopRect(x, y1 - h, barW, h, rad, id);
|
|
3912
4629
|
}).join("");
|
|
3913
|
-
|
|
4630
|
+
const xLabels = series.map((p, i) => `<text class="chart-label" x="${r(x0 + i * slot + slot / 2)}" y="${H - 10}" text-anchor="middle">${esc(tickLabel(p.label))}</text>`).join("");
|
|
4631
|
+
const viewBox = `0 0 ${W} ${H}`;
|
|
4632
|
+
return `${open(label, "chart-bars", viewBox)}${fillGradient(id, true)}${valueGrid(ticks, top, x0, x1, y0, y1)}<line class="chart-axis" x1="${x0}" y1="${y1}" x2="${x1}" y2="${y1}"/>${bars}${xLabels}</svg>`;
|
|
3914
4633
|
}
|
|
3915
4634
|
function svgHBars(series, label) {
|
|
3916
|
-
if (series.length === 0)
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
const
|
|
3920
|
-
const
|
|
3921
|
-
const
|
|
3922
|
-
const
|
|
3923
|
-
const
|
|
4635
|
+
if (series.length === 0) return empty(label, "chart-hbars");
|
|
4636
|
+
const id = `cw-hbar-${hashId(label)}`;
|
|
4637
|
+
const n = series.length;
|
|
4638
|
+
const rowH = 30;
|
|
4639
|
+
const mL = 150;
|
|
4640
|
+
const mR = 22;
|
|
4641
|
+
const mT = 14;
|
|
4642
|
+
const mB = 30;
|
|
4643
|
+
const W = 640;
|
|
4644
|
+
const H = mT + n * rowH + mB;
|
|
4645
|
+
const x0 = mL;
|
|
4646
|
+
const x1 = W - mR;
|
|
4647
|
+
const y0 = mT;
|
|
4648
|
+
const y1 = H - mB;
|
|
4649
|
+
const { ticks, top } = valueTicks(maxValue(series.map((p) => p.value)));
|
|
4650
|
+
const grid = ticks.map((t) => {
|
|
4651
|
+
const x = r(x0 + t / top * (x1 - x0));
|
|
4652
|
+
return `<line class="chart-grid" x1="${x}" y1="${y0}" x2="${x}" y2="${y1}"/><text class="chart-tick" x="${x}" y="${H - 10}" text-anchor="middle">${esc(fmtNum(t))}</text>`;
|
|
4653
|
+
}).join("");
|
|
4654
|
+
const barH = rowH * 0.6;
|
|
3924
4655
|
const bars = series.map((p, i) => {
|
|
3925
|
-
const w = Math.max(0, safe(p.value)) / top *
|
|
3926
|
-
const y = i *
|
|
3927
|
-
return
|
|
4656
|
+
const w = Math.max(0, safe(p.value)) / top * (x1 - x0);
|
|
4657
|
+
const y = y0 + i * rowH + (rowH - barH) / 2;
|
|
4658
|
+
return roundedRightRect(x0, y, w, barH, Math.min(barH / 2, 5), id);
|
|
3928
4659
|
}).join("");
|
|
3929
|
-
|
|
3930
|
-
}
|
|
3931
|
-
|
|
3932
|
-
const denom = max <= 0 ? 1 : max;
|
|
3933
|
-
const t = Math.min(1, Math.max(0, safe(value) / denom));
|
|
3934
|
-
return `${open(label, "chart-gauge")}<rect class="gauge-track" x="0" y="${r(VIEW_H / 2 - 4)}" width="${VIEW_W}" height="8" rx="4"/><rect class="gauge-fill" x="0" y="${r(VIEW_H / 2 - 4)}" width="${r(t * VIEW_W)}" height="8" rx="4"/></svg>`;
|
|
4660
|
+
const yLabels = series.map((p, i) => `<text class="chart-label" x="${x0 - 8}" y="${r(y0 + i * rowH + rowH / 2 + 3.5)}" text-anchor="end">${esc(tickLabel(p.label))}</text>`).join("");
|
|
4661
|
+
const viewBox = `0 0 ${W} ${H}`;
|
|
4662
|
+
return `${open(label, "chart-hbars", viewBox)}${fillGradient(id, false)}${grid}<line class="chart-axis" x1="${x0}" y1="${y0}" x2="${x0}" y2="${y1}"/>${bars}${yLabels}</svg>`;
|
|
3935
4663
|
}
|
|
3936
4664
|
function svgRadar(points, max, label) {
|
|
3937
4665
|
if (points.length < 3) {
|
|
3938
4666
|
return svgBars(points, label).replace("chart-bars", "chart-radar");
|
|
3939
4667
|
}
|
|
4668
|
+
const id = `cw-radar-${hashId(label)}`;
|
|
3940
4669
|
const denom = max <= 0 ? 1 : max;
|
|
3941
|
-
const
|
|
3942
|
-
const
|
|
3943
|
-
const
|
|
4670
|
+
const W = 300;
|
|
4671
|
+
const H = 230;
|
|
4672
|
+
const cx = W / 2;
|
|
4673
|
+
const cy = 108;
|
|
4674
|
+
const radius = 72;
|
|
3944
4675
|
const n = points.length;
|
|
3945
|
-
const
|
|
4676
|
+
const angle = (i) => Math.PI * 2 * i / n - Math.PI / 2;
|
|
4677
|
+
const ringFor = (factor) => {
|
|
4678
|
+
const pts = points.map((_, i) => `${r(cx + Math.cos(angle(i)) * radius * factor)},${r(cy + Math.sin(angle(i)) * radius * factor)}`).join(" ");
|
|
4679
|
+
return `<polygon class="radar-grid" points="${pts}"/>`;
|
|
4680
|
+
};
|
|
4681
|
+
const rings = [0.25, 0.5, 0.75, 1].map(ringFor).join("");
|
|
4682
|
+
const axes = points.map((_, i) => `<line class="radar-axis" x1="${cx}" y1="${cy}" x2="${r(cx + Math.cos(angle(i)) * radius)}" y2="${r(cy + Math.sin(angle(i)) * radius)}"/>`).join("");
|
|
4683
|
+
const dataCoords = points.map((p, i) => {
|
|
3946
4684
|
const t = Math.min(1, Math.max(0, safe(p.value) / denom));
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
}).join(" ");
|
|
3950
|
-
|
|
4685
|
+
return [r(cx + Math.cos(angle(i)) * radius * t), r(cy + Math.sin(angle(i)) * radius * t)];
|
|
4686
|
+
});
|
|
4687
|
+
const dataPts = dataCoords.map(([x, y]) => `${x},${y}`).join(" ");
|
|
4688
|
+
const dots = dataCoords.map(([x, y]) => `<circle class="radar-dot" cx="${x}" cy="${y}" r="2.4"/>`).join("");
|
|
4689
|
+
const labels = points.map((p, i) => {
|
|
4690
|
+
const lx = cx + Math.cos(angle(i)) * (radius + 16);
|
|
4691
|
+
const ly = cy + Math.sin(angle(i)) * (radius + 16);
|
|
4692
|
+
let anchor;
|
|
4693
|
+
if (lx > cx + 1) anchor = "start";
|
|
4694
|
+
else if (lx < cx - 1) anchor = "end";
|
|
4695
|
+
else anchor = "middle";
|
|
4696
|
+
const name = p.label.length > 12 ? `${p.label.slice(0, 11)}\u2026` : p.label;
|
|
4697
|
+
return `<text class="radar-label" x="${r(lx)}" y="${r(ly + 3)}" text-anchor="${anchor}">${esc(name)}</text>`;
|
|
4698
|
+
}).join("");
|
|
4699
|
+
const viewBox = `0 0 ${W} ${H}`;
|
|
4700
|
+
return `${open(label, "chart-radar", viewBox)}${fillGradient(id, true)}${rings}${axes}<polygon class="radar-area" points="${dataPts}" fill="url(#${id})"/>${dots}${labels}</svg>`;
|
|
4701
|
+
}
|
|
4702
|
+
function svgRadialGauge(value, max, label) {
|
|
4703
|
+
const id = `cw-rgauge-${hashId(label)}`;
|
|
4704
|
+
const denom = max <= 0 ? 1 : max;
|
|
4705
|
+
const t = Math.min(1, Math.max(0, safe(value) / denom));
|
|
4706
|
+
const cx = 100;
|
|
4707
|
+
const cy = 100;
|
|
4708
|
+
const rr = 74;
|
|
4709
|
+
const circ = 2 * Math.PI * rr;
|
|
4710
|
+
const dash = r(t * circ);
|
|
4711
|
+
const center = `${fmtNum(Math.round(safe(value) * 100) / 100)}${max === 100 ? "%" : ""}`;
|
|
4712
|
+
return `${open(label, "chart-radialgauge", "0 0 200 200")}${fillGradient(id, false)}<circle class="gauge-ring-track" cx="${cx}" cy="${cy}" r="${rr}" fill="none" stroke-width="20"/><circle class="gauge-ring-fill" cx="${cx}" cy="${cy}" r="${rr}" fill="none" stroke="url(#${id})" stroke-width="20" stroke-linecap="round" stroke-dasharray="${dash} ${r(circ)}" transform="rotate(-90 ${cx} ${cy})"/><text class="gauge-value" x="${cx}" y="${cy}" text-anchor="middle" dominant-baseline="central">${esc(center)}</text></svg>`;
|
|
4713
|
+
}
|
|
4714
|
+
function svgDonut(series, label) {
|
|
4715
|
+
if (series.length === 0) return empty(label, "chart-donut");
|
|
4716
|
+
const total = series.reduce((sum, p) => sum + Math.max(0, safe(p.value)), 0);
|
|
4717
|
+
if (total <= 0) return empty(label, "chart-donut");
|
|
4718
|
+
const cx = 100;
|
|
4719
|
+
const cy = 100;
|
|
4720
|
+
const rOuter = 82;
|
|
4721
|
+
const rInner = 50;
|
|
4722
|
+
let cum = 0;
|
|
4723
|
+
const segments = series.map((p, i) => {
|
|
4724
|
+
const frac = Math.max(0, safe(p.value)) / total;
|
|
4725
|
+
const a0 = (-90 + cum * 360) * Math.PI / 180;
|
|
4726
|
+
const a1 = (-90 + (cum + frac) * 360) * Math.PI / 180;
|
|
4727
|
+
cum += frac;
|
|
4728
|
+
const large = frac > 0.5 ? 1 : 0;
|
|
4729
|
+
const x0o = r(cx + rOuter * Math.cos(a0));
|
|
4730
|
+
const y0o = r(cy + rOuter * Math.sin(a0));
|
|
4731
|
+
const x1o = r(cx + rOuter * Math.cos(a1));
|
|
4732
|
+
const y1o = r(cy + rOuter * Math.sin(a1));
|
|
4733
|
+
const x1i = r(cx + rInner * Math.cos(a1));
|
|
4734
|
+
const y1i = r(cy + rInner * Math.sin(a1));
|
|
4735
|
+
const x0i = r(cx + rInner * Math.cos(a0));
|
|
4736
|
+
const y0i = r(cy + rInner * Math.sin(a0));
|
|
4737
|
+
const d = `M ${x0o} ${y0o} A ${rOuter} ${rOuter} 0 ${large} 1 ${x1o} ${y1o} L ${x1i} ${y1i} A ${rInner} ${rInner} 0 ${large} 0 ${x0i} ${y0i} Z`;
|
|
4738
|
+
return `<path class="donut-seg slice-${i % 6}" d="${d}"/>`;
|
|
4739
|
+
}).join("");
|
|
4740
|
+
const legend = series.map((p, i) => {
|
|
4741
|
+
const pct2 = Math.round(Math.max(0, safe(p.value)) / total * 100);
|
|
4742
|
+
const y = 44 + i * 24;
|
|
4743
|
+
return `<rect class="slice-${i % 6}" x="200" y="${y}" width="13" height="13" rx="3"/><text class="donut-label" x="220" y="${y + 11}">${esc(tickLabel(p.label))} \xB7 ${pct2}%</text>`;
|
|
4744
|
+
}).join("");
|
|
4745
|
+
return `${open(label, "chart-donut", "0 0 360 200")}${segments}${legend}</svg>`;
|
|
3951
4746
|
}
|
|
3952
4747
|
|
|
3953
4748
|
// src/render/html/charts.ts
|
|
@@ -3960,7 +4755,7 @@ var GROUP_DESCRIPTION = {
|
|
|
3960
4755
|
F: "Overall repository health signals."
|
|
3961
4756
|
};
|
|
3962
4757
|
function dataTable(series, valueHeader, caption) {
|
|
3963
|
-
const rows = series.map((p) => `<tr><th scope="row">${escapeHtml(p.label)}</th><td>${escapeHtml(
|
|
4758
|
+
const rows = series.map((p) => `<tr><th scope="row">${escapeHtml(p.label)}</th><td>${escapeHtml(formatNumber2(p.value))}</td></tr>`).join("\n");
|
|
3964
4759
|
return `<details class="data-table" open>
|
|
3965
4760
|
<summary>Show data table</summary>
|
|
3966
4761
|
<table>
|
|
@@ -3972,157 +4767,96 @@ ${rows}
|
|
|
3972
4767
|
</table>
|
|
3973
4768
|
</details>`;
|
|
3974
4769
|
}
|
|
3975
|
-
function
|
|
4770
|
+
function formatNumber2(value) {
|
|
3976
4771
|
if (!Number.isFinite(value)) {
|
|
3977
4772
|
return "\u2014";
|
|
3978
4773
|
}
|
|
3979
4774
|
return Number.isInteger(value) ? String(value) : String(Math.round(value * 100) / 100);
|
|
3980
4775
|
}
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
4776
|
+
var GROUP_CHARTS = {
|
|
4777
|
+
A: [{ kind: "line", pick: "timeseries", index: 0 }, { kind: "bars", pick: "timeseries", index: 1 }],
|
|
4778
|
+
B: [{ kind: "donut", pick: "distribution", index: 0 }, { kind: "gauge", pick: "range", index: 0 }],
|
|
4779
|
+
C: [{ kind: "bars", pick: "distribution", index: 0 }, { kind: "gauge", pick: "range", index: 0 }],
|
|
4780
|
+
D: [{ kind: "line", pick: "timeseries", index: 0 }, { kind: "gauge", pick: "range", index: 0 }],
|
|
4781
|
+
E: [{ kind: "hbars", pick: "distribution", index: 0 }, { kind: "line", pick: "timeseries", index: 0 }],
|
|
4782
|
+
F: [{ kind: "radar", pick: "distribution", index: 0 }, { kind: "gauge", pick: "range", index: 0 }]
|
|
4783
|
+
};
|
|
4784
|
+
function metricsOfShape(metrics, shape) {
|
|
4785
|
+
return metrics.filter((m) => m.status === "computed" && detectShape(m.value) === shape && extractSeries(m.value).length > 0);
|
|
4786
|
+
}
|
|
4787
|
+
function rangeMetrics(metrics) {
|
|
4788
|
+
return metrics.filter((m) => m.status === "computed" && rangeField(m.value) !== void 0);
|
|
4789
|
+
}
|
|
4790
|
+
function subFigure(title2, svg, table) {
|
|
4791
|
+
return `<div class="chart-sub">
|
|
4792
|
+
<h4>${escapeHtml(title2)}</h4>
|
|
4793
|
+
${svg}
|
|
4794
|
+
${table}
|
|
4795
|
+
</div>`;
|
|
4796
|
+
}
|
|
4797
|
+
function renderSubChart(group, spec, metrics) {
|
|
4798
|
+
const pool = spec.pick === "range" ? rangeMetrics(metrics) : metricsOfShape(metrics, spec.pick);
|
|
4799
|
+
const metric = pool[spec.index];
|
|
4800
|
+
if (metric === void 0) {
|
|
4801
|
+
return void 0;
|
|
3994
4802
|
}
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
case "
|
|
4008
|
-
|
|
4803
|
+
const label = `Group ${group} \u2014 ${metric.title}`;
|
|
4804
|
+
if (spec.kind === "gauge") {
|
|
4805
|
+
const range = rangeField(metric.value);
|
|
4806
|
+
if (range === void 0) {
|
|
4807
|
+
return void 0;
|
|
4808
|
+
}
|
|
4809
|
+
const table = dataTable([{ label: metric.title, value: range.value }], "Value", metric.title);
|
|
4810
|
+
return subFigure(metric.title, svgRadialGauge(range.value, range.max, label), table);
|
|
4811
|
+
}
|
|
4812
|
+
const series = extractSeries(metric.value);
|
|
4813
|
+
let svg;
|
|
4814
|
+
switch (spec.kind) {
|
|
4815
|
+
case "line":
|
|
4816
|
+
svg = svgLine(series, label);
|
|
4817
|
+
break;
|
|
4818
|
+
case "bars":
|
|
4819
|
+
svg = svgBars(series, label);
|
|
4820
|
+
break;
|
|
4821
|
+
case "hbars":
|
|
4822
|
+
svg = svgHBars(series, label);
|
|
4823
|
+
break;
|
|
4824
|
+
case "radar":
|
|
4825
|
+
svg = svgRadar(series, 100, label);
|
|
4826
|
+
break;
|
|
4009
4827
|
default:
|
|
4010
|
-
|
|
4828
|
+
svg = svgDonut(series, label);
|
|
4011
4829
|
}
|
|
4830
|
+
return subFigure(metric.title, svg, dataTable(series, "Value", metric.title));
|
|
4012
4831
|
}
|
|
4013
4832
|
function groupOverviewPanel(group, metrics) {
|
|
4014
4833
|
const description = GROUP_DESCRIPTION[group];
|
|
4015
|
-
const
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
return `<figure class="chart-panel" aria-label="${escapeHtml(
|
|
4834
|
+
const label = `Group ${group} overview`;
|
|
4835
|
+
const subs = GROUP_CHARTS[group].map((spec) => renderSubChart(group, spec, metrics)).filter((html) => html !== void 0);
|
|
4836
|
+
if (subs.length === 0) {
|
|
4837
|
+
return `<figure class="chart-panel" aria-label="${escapeHtml(label)}">
|
|
4019
4838
|
<figcaption>${escapeHtml(description)}</figcaption>
|
|
4020
4839
|
<p class="chart-empty">No chartable series for this group \u2014 see the metric cards below.</p>
|
|
4021
4840
|
</figure>`;
|
|
4022
4841
|
}
|
|
4023
|
-
const
|
|
4842
|
+
const gridClass = subs.length > 1 ? "chart-cells two" : "chart-cells";
|
|
4024
4843
|
return `<figure class="chart-panel" aria-label="${escapeHtml(label)}">
|
|
4025
|
-
<figcaption>${escapeHtml(description)}
|
|
4026
|
-
${
|
|
4027
|
-
${
|
|
4844
|
+
<figcaption>${escapeHtml(description)}</figcaption>
|
|
4845
|
+
<div class="${gridClass}">
|
|
4846
|
+
${subs.join("\n")}
|
|
4847
|
+
</div>
|
|
4028
4848
|
</figure>`;
|
|
4029
4849
|
}
|
|
4030
|
-
function metricVisual(metric) {
|
|
4031
|
-
if (metric.status === "not_available") {
|
|
4032
|
-
return "";
|
|
4033
|
-
}
|
|
4034
|
-
const label = `${metric.title} visual`;
|
|
4035
|
-
const shape = detectShape(metric.value);
|
|
4036
|
-
switch (shape) {
|
|
4037
|
-
case "timeseries": {
|
|
4038
|
-
const series = extractSeries(metric.value);
|
|
4039
|
-
return `<div class="metric-visual">${svgLine(series, label)}
|
|
4040
|
-
${dataTable(series, "Value", metric.title)}</div>`;
|
|
4041
|
-
}
|
|
4042
|
-
case "distribution": {
|
|
4043
|
-
const series = extractSeries(metric.value);
|
|
4044
|
-
return `<div class="metric-visual">${svgBars(series, label)}
|
|
4045
|
-
${dataTable(series, "Value", metric.title)}</div>`;
|
|
4046
|
-
}
|
|
4047
|
-
case "scalar-range": {
|
|
4048
|
-
const range = rangeField(metric.value);
|
|
4049
|
-
const series = extractSeries(metric.value);
|
|
4050
|
-
const gauge = range === void 0 ? "" : svgGauge(range.value, range.max, label);
|
|
4051
|
-
const spark = series.length > 1 ? svgSparkline(series, label) : "";
|
|
4052
|
-
const number = range === void 0 ? "" : `<span class="metric-number">${escapeHtml(formatNumber(range.value))}</span>`;
|
|
4053
|
-
return `<div class="metric-visual metric-visual-range">${gauge}${spark}${number}
|
|
4054
|
-
${dataTable(series, "Value", metric.title)}</div>`;
|
|
4055
|
-
}
|
|
4056
|
-
case "scalar":
|
|
4057
|
-
case "none":
|
|
4058
|
-
default:
|
|
4059
|
-
return "";
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
4850
|
|
|
4063
|
-
// src/render/html/
|
|
4064
|
-
var HEALTH_GLYPH = {
|
|
4065
|
-
ok: "\u25CF",
|
|
4066
|
-
watch: "\u25D0",
|
|
4067
|
-
risk: "\u25B2",
|
|
4068
|
-
|
|
4069
|
-
};
|
|
4070
|
-
var HEALTH_LABEL = {
|
|
4071
|
-
ok: "ok",
|
|
4072
|
-
watch: "watch",
|
|
4073
|
-
risk: "risk",
|
|
4074
|
-
na: "n/a"
|
|
4075
|
-
};
|
|
4076
|
-
function field(value, key) {
|
|
4077
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
4078
|
-
return void 0;
|
|
4079
|
-
}
|
|
4080
|
-
const v = value[key];
|
|
4081
|
-
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
4082
|
-
}
|
|
4083
|
-
function higherBetter(key, okMin, watchMin) {
|
|
4084
|
-
return (value) => {
|
|
4085
|
-
const n = field(value, key);
|
|
4086
|
-
if (n === void 0) return "ok";
|
|
4087
|
-
if (n >= okMin) return "ok";
|
|
4088
|
-
if (n >= watchMin) return "watch";
|
|
4089
|
-
return "risk";
|
|
4090
|
-
};
|
|
4091
|
-
}
|
|
4092
|
-
function lowerBetter(key, okMax, watchMax) {
|
|
4093
|
-
return (value) => {
|
|
4094
|
-
const n = field(value, key);
|
|
4095
|
-
if (n === void 0) return "ok";
|
|
4096
|
-
if (n <= okMax) return "ok";
|
|
4097
|
-
if (n <= watchMax) return "watch";
|
|
4098
|
-
return "risk";
|
|
4099
|
-
};
|
|
4100
|
-
}
|
|
4101
|
-
var REGISTRY = {
|
|
4102
|
-
// Knowledge concentration — fewer key people is riskier.
|
|
4103
|
-
"b-bus-factor": higherBetter("busFactor", 3, 2),
|
|
4104
|
-
"f-bus-factor-risk": higherBetter("busFactor", 3, 2),
|
|
4105
|
-
// Contribution concentration — a very high top share concentrates load.
|
|
4106
|
-
"b-contribution-distribution": lowerBetter("topCommitSharePct", 60, 80),
|
|
4107
|
-
// Message quality — higher Conventional-Commits adherence is healthier.
|
|
4108
|
-
"c-conventional-commits": higherBetter("adherenceSharePct", 70, 40),
|
|
4109
|
-
// Low-information commit messages — fewer is healthier.
|
|
4110
|
-
"c-low-information-rate": lowerBetter("lowInfoSharePct", 10, 25),
|
|
4111
|
-
// Workflow discipline — a high direct-to-default share is riskier.
|
|
4112
|
-
"d-direct-to-default": lowerBetter("directToDefaultSharePct", 20, 50),
|
|
4113
|
-
// Overall hygiene composite (0–100).
|
|
4114
|
-
"f-hygiene-score": higherBetter("score", 75, 50)
|
|
4115
|
-
};
|
|
4116
|
-
function classifyHealth(metric) {
|
|
4117
|
-
if (metric.status === "not_available") {
|
|
4118
|
-
return "na";
|
|
4119
|
-
}
|
|
4120
|
-
const classifier = REGISTRY[metric.id];
|
|
4121
|
-
return classifier === void 0 ? "ok" : classifier(metric.value);
|
|
4122
|
-
}
|
|
4851
|
+
// src/render/html/inter-font.ts
|
|
4852
|
+
var INTER_FONT_CSS = `@font-face{font-family:'Inter';font-style:normal;font-weight:400;font-display:swap;src:url(data:font/woff2;base64,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) format('woff2');}
|
|
4853
|
+
@font-face{font-family:'Inter';font-style:normal;font-weight:600;font-display:swap;src:url(data:font/woff2;base64,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) format('woff2');}
|
|
4854
|
+
@font-face{font-family:'Inter';font-style:normal;font-weight:700;font-display:swap;src:url(data:font/woff2;base64,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) format('woff2');}
|
|
4855
|
+
@font-face{font-family:'Inter';font-style:normal;font-weight:800;font-display:swap;src:url(data:font/woff2;base64,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) format('woff2');}
|
|
4856
|
+
`;
|
|
4123
4857
|
|
|
4124
4858
|
// src/render/html/html-renderer.ts
|
|
4125
|
-
var
|
|
4859
|
+
var GROUPS2 = [
|
|
4126
4860
|
{ id: "A", title: "Activity & Cadence" },
|
|
4127
4861
|
{ id: "B", title: "Contribution & Ownership" },
|
|
4128
4862
|
{ id: "C", title: "Commit Message Quality" },
|
|
@@ -4134,30 +4868,31 @@ var HTML_DEGRADED_BANNER = "\u26A0 Narrative unavailable \u2014 showing raw anal
|
|
|
4134
4868
|
var HTML_METRICS_ONLY_NOTE = "Metrics-only run \u2014 no AI narrative requested";
|
|
4135
4869
|
function renderHtml(report) {
|
|
4136
4870
|
const route = classifyReport(report);
|
|
4137
|
-
const
|
|
4138
|
-
|
|
4871
|
+
const provenance = report.provenance;
|
|
4872
|
+
const body = route.kind === "showpiece" ? renderShowpiece2(route.report, provenance) : renderSubstrate2(route.analysis, route.framing, provenance);
|
|
4873
|
+
return document(body, provenance);
|
|
4139
4874
|
}
|
|
4140
|
-
function document(body) {
|
|
4875
|
+
function document(body, provenance) {
|
|
4141
4876
|
return `<!doctype html>
|
|
4142
4877
|
<html lang="en">
|
|
4143
4878
|
<head>
|
|
4144
4879
|
<meta charset="utf-8">
|
|
4145
4880
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
4146
4881
|
<title>commit-whisper report</title>
|
|
4147
|
-
<style>${STYLE}</style>
|
|
4882
|
+
<style>${INTER_FONT_CSS}${STYLE}</style>
|
|
4148
4883
|
</head>
|
|
4149
4884
|
<body>
|
|
4150
4885
|
<a class="skip-link" href="#main">Skip to content</a>
|
|
4151
4886
|
${body}
|
|
4152
|
-
${footer()}
|
|
4887
|
+
${footer(provenance)}
|
|
4153
4888
|
<script>${DISCLOSURE_SCRIPT}</script>
|
|
4154
4889
|
</body>
|
|
4155
4890
|
</html>`;
|
|
4156
4891
|
}
|
|
4157
|
-
function renderShowpiece2(report) {
|
|
4892
|
+
function renderShowpiece2(report, provenance) {
|
|
4158
4893
|
const { summary, explanation, coaching, explanations, confidence } = report.narrative;
|
|
4159
4894
|
return [
|
|
4160
|
-
|
|
4895
|
+
masthead2(confidence, provenance),
|
|
4161
4896
|
summaryBand(summary),
|
|
4162
4897
|
toc(true, report.analysis),
|
|
4163
4898
|
explanationBand(explanation),
|
|
@@ -4165,17 +4900,63 @@ function renderShowpiece2(report) {
|
|
|
4165
4900
|
metricGroups(report.analysis, explanations)
|
|
4166
4901
|
].join("\n");
|
|
4167
4902
|
}
|
|
4168
|
-
function renderSubstrate2(analysis, framing) {
|
|
4169
|
-
return [
|
|
4170
|
-
}
|
|
4171
|
-
function
|
|
4172
|
-
const
|
|
4903
|
+
function renderSubstrate2(analysis, framing, provenance) {
|
|
4904
|
+
return [masthead2(void 0, provenance), substrateBanner(framing), toc(false, analysis), metricGroups(analysis, void 0)].join("\n");
|
|
4905
|
+
}
|
|
4906
|
+
function masthead2(confidence, provenance) {
|
|
4907
|
+
const parts = [
|
|
4908
|
+
"<h1>commit-whisper</h1>",
|
|
4909
|
+
`<p class="tagline">Deterministic git-history analysis with a grounded AI narrative.</p>`,
|
|
4910
|
+
provenanceChips2(provenance),
|
|
4911
|
+
capLine2(provenance),
|
|
4912
|
+
confidence === void 0 ? "" : confidenceBand2(confidence)
|
|
4913
|
+
].filter((part) => part !== "");
|
|
4173
4914
|
return `<header class="masthead">
|
|
4174
|
-
|
|
4175
|
-
<p class="tagline">Deterministic git-history analysis with a grounded AI narrative.</p>
|
|
4176
|
-
${band}
|
|
4915
|
+
${parts.join("\n")}
|
|
4177
4916
|
</header>`;
|
|
4178
4917
|
}
|
|
4918
|
+
function provenanceChips2(provenance) {
|
|
4919
|
+
const repo = provenance?.repo;
|
|
4920
|
+
const scale = provenance?.scale;
|
|
4921
|
+
const chips = [];
|
|
4922
|
+
if (repo?.name !== void 0) {
|
|
4923
|
+
chips.push(provChip(repo.name));
|
|
4924
|
+
}
|
|
4925
|
+
if (repo?.branch !== void 0) {
|
|
4926
|
+
chips.push(provChip(repo.branch));
|
|
4927
|
+
}
|
|
4928
|
+
const commits = scale?.totalCommits ?? scale?.analyzedCommits;
|
|
4929
|
+
if (commits !== void 0) {
|
|
4930
|
+
chips.push(provChip(`${formatCount2(commits)} ${commits === 1 ? "commit" : "commits"}`));
|
|
4931
|
+
}
|
|
4932
|
+
if (scale?.contributors !== void 0) {
|
|
4933
|
+
chips.push(provChip(`${formatCount2(scale.contributors)} ${scale.contributors === 1 ? "contributor" : "contributors"}`));
|
|
4934
|
+
}
|
|
4935
|
+
const generatedAt = provenance?.run?.generatedAt;
|
|
4936
|
+
if (generatedAt !== void 0) {
|
|
4937
|
+
chips.push(provChip(`analyzed ${isoDate2(generatedAt)}`));
|
|
4938
|
+
}
|
|
4939
|
+
if (chips.length === 0) {
|
|
4940
|
+
return "";
|
|
4941
|
+
}
|
|
4942
|
+
return `<p class="prov-chips">${chips.join('<span class="prov-sep" aria-hidden="true">\xB7</span>')}</p>`;
|
|
4943
|
+
}
|
|
4944
|
+
function provChip(text) {
|
|
4945
|
+
return `<span class="prov-chip">${escapeHtml(text)}</span>`;
|
|
4946
|
+
}
|
|
4947
|
+
function capLine2(provenance) {
|
|
4948
|
+
if (provenance?.entitlement?.tier !== "free") {
|
|
4949
|
+
return "";
|
|
4950
|
+
}
|
|
4951
|
+
const analyzed = provenance.scale?.analyzedCommits;
|
|
4952
|
+
if (analyzed === void 0) {
|
|
4953
|
+
return "";
|
|
4954
|
+
}
|
|
4955
|
+
const total = provenance.scale?.totalCommits;
|
|
4956
|
+
const detail = total === void 0 ? `${formatCount2(analyzed)} commits analyzed` : `${formatCount2(analyzed)} of ${formatCount2(total)} commits analyzed`;
|
|
4957
|
+
const label = `Free \xB7 ${detail}`;
|
|
4958
|
+
return `<p class="cap-line">${escapeHtml(label)}</p>`;
|
|
4959
|
+
}
|
|
4179
4960
|
function confidenceBand2(confidence) {
|
|
4180
4961
|
const level = escapeHtml(confidence.level);
|
|
4181
4962
|
const escalation = confidence.escalation === void 0 ? "" : `<p class="confidence-escalation" role="alert">\u26A0 ${escapeHtml(confidence.escalation)}</p>`;
|
|
@@ -4188,7 +4969,7 @@ function substrateBanner(framing) {
|
|
|
4188
4969
|
return framing === "degraded" ? `<p class="banner banner-degraded" role="alert">${escapeHtml(HTML_DEGRADED_BANNER)}</p>` : `<p class="banner banner-metrics-only">${escapeHtml(HTML_METRICS_ONLY_NOTE)}</p>`;
|
|
4189
4970
|
}
|
|
4190
4971
|
function presentGroups(analysis) {
|
|
4191
|
-
return
|
|
4972
|
+
return GROUPS2.filter((group) => analysis.metrics.some((metric) => metric.group === group.id));
|
|
4192
4973
|
}
|
|
4193
4974
|
function toc(narrated, analysis) {
|
|
4194
4975
|
const narrativeLinks = narrated ? [
|
|
@@ -4208,7 +4989,7 @@ ${groupLinks}
|
|
|
4208
4989
|
function summaryBand(summary) {
|
|
4209
4990
|
const findings = summary.keyFindings.map((finding) => `<li>${escapeHtml(finding)}</li>`).join("\n");
|
|
4210
4991
|
return `<section id="summary" class="band" aria-labelledby="summary-h">
|
|
4211
|
-
<h2 id="summary-h">Summary</h2>
|
|
4992
|
+
<h2 id="summary-h"><span class="kicker">TL;DR</span> Summary</h2>
|
|
4212
4993
|
<p class="headline">${escapeHtml(summary.headline)}</p>
|
|
4213
4994
|
<p>${escapeHtml(summary.overview)}</p>
|
|
4214
4995
|
${findings === "" ? "" : `<ul class="key-findings">
|
|
@@ -4219,7 +5000,7 @@ ${findings}
|
|
|
4219
5000
|
function explanationBand(explanation) {
|
|
4220
5001
|
const paragraphs = explanation.paragraphs.map((p) => `<p>${escapeHtml(p)}</p>`).join("\n");
|
|
4221
5002
|
return `<section id="explanation" class="band" aria-labelledby="explanation-h">
|
|
4222
|
-
<h2 id="explanation-h">Explanation</h2>
|
|
5003
|
+
<h2 id="explanation-h"><span class="kicker">What the metrics show</span> Explanation</h2>
|
|
4223
5004
|
${paragraphs}
|
|
4224
5005
|
</section>`;
|
|
4225
5006
|
}
|
|
@@ -4234,7 +5015,7 @@ ${steps}
|
|
|
4234
5015
|
</article>`;
|
|
4235
5016
|
}).join("\n");
|
|
4236
5017
|
return `<section id="coaching" class="band" aria-labelledby="coaching-h">
|
|
4237
|
-
<h2 id="coaching-h">Coaching</h2>
|
|
5018
|
+
<h2 id="coaching-h"><span class="kicker">Improvement plan</span> Coaching</h2>
|
|
4238
5019
|
<p class="coaching-intro">${escapeHtml(coaching.introduction)}</p>
|
|
4239
5020
|
${chapters}
|
|
4240
5021
|
<p class="coaching-closing">${escapeHtml(coaching.closingSummary)}</p>
|
|
@@ -4243,28 +5024,32 @@ ${chapters}
|
|
|
4243
5024
|
function metricGroups(analysis, explanations) {
|
|
4244
5025
|
const sections = presentGroups(analysis).map((group) => {
|
|
4245
5026
|
const metrics = analysis.metrics.filter((metric) => metric.group === group.id);
|
|
4246
|
-
const cards = metrics.map((metric) =>
|
|
5027
|
+
const cards = metrics.map((metric) => metricCard2(metric, explanations)).join("\n");
|
|
4247
5028
|
return `<section id="group-${group.id.toLowerCase()}" class="metric-group" aria-labelledby="group-${group.id.toLowerCase()}-h">
|
|
4248
|
-
<h2 id="group-${group.id.toLowerCase()}-h"
|
|
5029
|
+
<h2 id="group-${group.id.toLowerCase()}-h"><span class="kicker">Group ${escapeHtml(group.id)}</span> ${escapeHtml(group.title)}</h2>
|
|
4249
5030
|
${groupOverviewPanel(group.id, metrics)}
|
|
5031
|
+
<div class="cards">
|
|
4250
5032
|
${cards}
|
|
5033
|
+
</div>
|
|
4251
5034
|
</section>`;
|
|
4252
5035
|
});
|
|
4253
5036
|
return `<main id="main" class="metric-groups">
|
|
4254
5037
|
${sections.join("\n")}
|
|
4255
5038
|
</main>`;
|
|
4256
5039
|
}
|
|
4257
|
-
function
|
|
5040
|
+
function metricCard2(metric, explanations) {
|
|
4258
5041
|
const band = classifyHealth(metric);
|
|
4259
5042
|
const bandHtml = `<span class="health health-${band}"><span class="health-glyph" aria-hidden="true">${HEALTH_GLYPH[band]}</span> ${escapeHtml(HEALTH_LABEL[band])}</span>`;
|
|
4260
|
-
const
|
|
5043
|
+
const stat = metricStat(metric);
|
|
5044
|
+
const statHtml = stat === "" ? "" : `<span class="metric-stat">${escapeHtml(stat)}</span>`;
|
|
4261
5045
|
const explanation = explanations?.[metric.id];
|
|
5046
|
+
const facets = explanation === void 0 ? "" : fourFacets(explanation);
|
|
5047
|
+
const reason = metric.status === "computed" ? "" : `<p class="why">${escapeHtml(metric.reason ?? "Not available.")}</p>`;
|
|
4262
5048
|
return `<details class="metric-card" data-status="${escapeHtml(metric.status)}" data-health="${band}" open>
|
|
4263
|
-
<summary><h3 class="metric-title">${escapeHtml(metric.title)}</h3> ${bandHtml}</summary>
|
|
5049
|
+
<summary><h3 class="metric-title">${escapeHtml(metric.title)}</h3> ${bandHtml}${statHtml}</summary>
|
|
4264
5050
|
<div class="metric-body">
|
|
4265
|
-
${
|
|
4266
|
-
${
|
|
4267
|
-
${explanation === void 0 ? "" : fourFacets(explanation)}
|
|
5051
|
+
${reason}
|
|
5052
|
+
${facets}
|
|
4268
5053
|
</div>
|
|
4269
5054
|
</details>`;
|
|
4270
5055
|
}
|
|
@@ -4288,94 +5073,248 @@ ${list(explanation.needsImprovement)}
|
|
|
4288
5073
|
${list(explanation.suggestions)}
|
|
4289
5074
|
</div>`;
|
|
4290
5075
|
}
|
|
4291
|
-
function
|
|
4292
|
-
if (
|
|
5076
|
+
function metricStat(metric) {
|
|
5077
|
+
if (metric.status !== "computed") {
|
|
4293
5078
|
return "";
|
|
4294
5079
|
}
|
|
5080
|
+
const value = metric.value;
|
|
5081
|
+
if (typeof value === "number") {
|
|
5082
|
+
return Number.isFinite(value) ? fmtStat(value) : "";
|
|
5083
|
+
}
|
|
4295
5084
|
if (typeof value === "string") {
|
|
4296
5085
|
return value;
|
|
4297
5086
|
}
|
|
4298
|
-
const
|
|
4299
|
-
|
|
5087
|
+
const range = rangeField(value);
|
|
5088
|
+
if (range !== void 0) {
|
|
5089
|
+
return `${fmtStat(range.value)}${range.max === 100 ? "%" : ""}`;
|
|
5090
|
+
}
|
|
5091
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
5092
|
+
return objectStat(value);
|
|
5093
|
+
}
|
|
5094
|
+
return "";
|
|
4300
5095
|
}
|
|
4301
|
-
function
|
|
5096
|
+
function objectStat(value) {
|
|
5097
|
+
const nums = Object.entries(value).filter((e) => typeof e[1] === "number" && Number.isFinite(e[1]));
|
|
5098
|
+
for (const key of ["total", "count", "score", "busFactor", "value"]) {
|
|
5099
|
+
const hit = nums.find(([k]) => k === key);
|
|
5100
|
+
if (hit !== void 0) {
|
|
5101
|
+
return fmtStat(hit[1]);
|
|
5102
|
+
}
|
|
5103
|
+
}
|
|
5104
|
+
return nums.length === 1 ? fmtStat(nums[0][1]) : "";
|
|
5105
|
+
}
|
|
5106
|
+
function fmtStat(n) {
|
|
5107
|
+
return Number.isInteger(n) ? String(n) : String(Math.round(n * 100) / 100);
|
|
5108
|
+
}
|
|
5109
|
+
function footer(provenance) {
|
|
5110
|
+
const version = provenance?.run?.toolVersion;
|
|
5111
|
+
const parts = [version === void 0 ? "Generated by commit-whisper" : `Generated by commit-whisper v${escapeHtml(version)}`, "schemaVersion 1.0.0"];
|
|
5112
|
+
const ai = provenance?.ai;
|
|
5113
|
+
if (ai !== void 0) {
|
|
5114
|
+
parts.push(`${escapeHtml(ai.provider)}/${escapeHtml(ai.model)}`);
|
|
5115
|
+
}
|
|
5116
|
+
const generatedAt = provenance?.run?.generatedAt;
|
|
5117
|
+
if (generatedAt !== void 0) {
|
|
5118
|
+
parts.push(escapeHtml(generatedAt));
|
|
5119
|
+
}
|
|
4302
5120
|
return `<footer class="footer">
|
|
4303
|
-
<p
|
|
5121
|
+
<p>${parts.join(" \xB7 ")}</p>
|
|
4304
5122
|
</footer>`;
|
|
4305
5123
|
}
|
|
4306
|
-
|
|
5124
|
+
function formatCount2(n) {
|
|
5125
|
+
if (!Number.isFinite(n)) {
|
|
5126
|
+
return "0";
|
|
5127
|
+
}
|
|
5128
|
+
const negative = n < 0;
|
|
5129
|
+
const digits = Math.abs(Math.trunc(n)).toString();
|
|
5130
|
+
let grouped = "";
|
|
5131
|
+
for (let i = 0; i < digits.length; i++) {
|
|
5132
|
+
if (i > 0 && (digits.length - i) % 3 === 0) {
|
|
5133
|
+
grouped += ",";
|
|
5134
|
+
}
|
|
5135
|
+
grouped += digits[i];
|
|
5136
|
+
}
|
|
5137
|
+
return negative ? `-${grouped}` : grouped;
|
|
5138
|
+
}
|
|
5139
|
+
function isoDate2(iso) {
|
|
5140
|
+
return iso.slice(0, 10);
|
|
5141
|
+
}
|
|
5142
|
+
var STYLE = String.raw`
|
|
4307
5143
|
:root {
|
|
4308
5144
|
color-scheme: dark light;
|
|
4309
|
-
--bg: #
|
|
4310
|
-
--
|
|
4311
|
-
--
|
|
5145
|
+
--bg: #0a0e14; --surface: #11161f; --surface-2: #161c28;
|
|
5146
|
+
--border: #232b3a; --border-soft: #1c2430;
|
|
5147
|
+
--fg: #e8eef6; --fg-soft: #cdd7e4; --muted: #93a1b5; --faint: #5d6b80;
|
|
5148
|
+
--accent: #58a6ff; --accent-2: #7c5cff;
|
|
5149
|
+
--ok: #3fb950; --watch: #e3b341; --risk: #ff6b6b;
|
|
5150
|
+
--shadow: 0 14px 40px -20px rgba(0,0,0,0.7);
|
|
4312
5151
|
}
|
|
4313
5152
|
@media (prefers-color-scheme: light) {
|
|
4314
5153
|
:root {
|
|
4315
|
-
--bg: #ffffff; --surface: #f6f8fa; --
|
|
4316
|
-
--
|
|
4317
|
-
--
|
|
5154
|
+
--bg: #ffffff; --surface: #f6f8fa; --surface-2: #eef1f5;
|
|
5155
|
+
--border: #d0d7de; --border-soft: #e4e8ec;
|
|
5156
|
+
--fg: #1f2328; --fg-soft: #2c333d; --muted: #57606a; --faint: #8b949e;
|
|
5157
|
+
--accent: #0969da; --accent-2: #8250df;
|
|
5158
|
+
--ok: #1a7f37; --watch: #9a6700; --risk: #cf222e;
|
|
5159
|
+
--shadow: 0 14px 40px -22px rgba(140,150,170,0.5);
|
|
4318
5160
|
}
|
|
4319
5161
|
}
|
|
4320
5162
|
* { box-sizing: border-box; }
|
|
4321
5163
|
body {
|
|
4322
|
-
margin: 0; padding: 0
|
|
4323
|
-
background:
|
|
4324
|
-
|
|
5164
|
+
margin: 0; padding: 0 1.25rem 4rem;
|
|
5165
|
+
background:
|
|
5166
|
+
radial-gradient(1100px 520px at 85% -8%, rgba(124,92,255,0.13), transparent 60%),
|
|
5167
|
+
radial-gradient(900px 460px at -5% 2%, rgba(88,166,255,0.11), transparent 55%),
|
|
5168
|
+
var(--bg);
|
|
5169
|
+
color: var(--fg);
|
|
5170
|
+
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
4325
5171
|
line-height: 1.6;
|
|
5172
|
+
-webkit-font-smoothing: antialiased;
|
|
4326
5173
|
}
|
|
4327
|
-
main, header, nav, section, footer { max-width:
|
|
4328
|
-
h1 { font-size: 2rem; margin: 1.5rem 0 0.25rem; }
|
|
4329
|
-
h2 { font-size: 1.
|
|
5174
|
+
main, header, nav, section, footer { max-width: 66rem; margin-inline: auto; }
|
|
5175
|
+
h1 { font-size: 2rem; margin: 1.5rem 0 0.25rem; letter-spacing: -0.02em; }
|
|
5176
|
+
h2 { font-size: 1.5rem; margin: 2.25rem 0 1rem; letter-spacing: -0.01em; }
|
|
4330
5177
|
h3 { font-size: 1.1rem; margin: 1.25rem 0 0.5rem; }
|
|
4331
|
-
h4 { font-size: 0.
|
|
4332
|
-
a { color: var(--accent); }
|
|
4333
|
-
a:
|
|
5178
|
+
h4 { font-size: 0.8rem; margin: 0.75rem 0 0.25rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); }
|
|
5179
|
+
a { color: var(--accent); text-decoration: none; }
|
|
5180
|
+
a:hover { text-decoration: underline; }
|
|
5181
|
+
a:focus-visible, :focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 6px; }
|
|
4334
5182
|
.skip-link {
|
|
4335
5183
|
position: absolute; left: -9999px; top: 0;
|
|
4336
|
-
background: var(--surface); color: var(--fg); padding: 0.5rem 1rem; z-index:
|
|
5184
|
+
background: var(--surface); color: var(--fg); padding: 0.5rem 1rem; z-index: 50; border-radius: 0 0 8px 0;
|
|
4337
5185
|
}
|
|
4338
5186
|
.skip-link:focus { left: 0; }
|
|
4339
|
-
.
|
|
4340
|
-
.
|
|
4341
|
-
.
|
|
4342
|
-
|
|
4343
|
-
|
|
5187
|
+
.band > h2, .metric-group > h2 { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
|
|
5188
|
+
.kicker { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent); background: rgba(88,166,255,0.12); padding: 0.22rem 0.55rem; border-radius: 6px; }
|
|
5189
|
+
.masthead {
|
|
5190
|
+
position: relative; margin-top: 2.5rem; padding: 2.4rem 2.5rem; overflow: hidden;
|
|
5191
|
+
border: 1px solid var(--border); border-radius: 22px;
|
|
5192
|
+
background: linear-gradient(135deg, rgba(88,166,255,0.10), rgba(124,92,255,0.06)), var(--surface);
|
|
5193
|
+
box-shadow: var(--shadow);
|
|
5194
|
+
}
|
|
5195
|
+
.masthead::before {
|
|
5196
|
+
content: ""; position: absolute; inset: 0; pointer-events: none;
|
|
5197
|
+
background: radial-gradient(600px 200px at 90% -40%, rgba(124,92,255,0.22), transparent 70%);
|
|
5198
|
+
}
|
|
5199
|
+
.masthead h1 { margin: 0; font-size: 1.95rem; position: relative; display: flex; align-items: center; gap: 0.75rem; }
|
|
5200
|
+
.masthead h1::before {
|
|
5201
|
+
content: "\25D1"; display: inline-grid; place-items: center;
|
|
5202
|
+
width: 2.7rem; height: 2.7rem; font-size: 1.45rem; color: #fff;
|
|
5203
|
+
background: linear-gradient(135deg, var(--accent), var(--accent-2));
|
|
5204
|
+
border-radius: 0.72rem; box-shadow: 0 10px 26px -8px rgba(124,92,255,0.75);
|
|
5205
|
+
}
|
|
5206
|
+
.masthead .tagline { color: var(--muted); margin: 0.4rem 0 0; position: relative; max-width: 46rem; }
|
|
5207
|
+
.prov-chips { position: relative; margin: 0.85rem 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; color: var(--fg-soft); font-size: 0.92rem; }
|
|
5208
|
+
.prov-chip { white-space: nowrap; }
|
|
5209
|
+
.prov-sep { color: var(--faint); }
|
|
5210
|
+
.cap-line { position: relative; margin: 0.7rem 0 0; color: var(--muted); font-size: 0.9rem; }
|
|
5211
|
+
.confidence {
|
|
5212
|
+
position: relative; margin: 1.5rem 0 0; padding: 0.85rem 1.1rem;
|
|
5213
|
+
border: 1px solid rgba(63,185,80,0.4); border-left: 4px solid var(--ok); border-radius: 12px;
|
|
5214
|
+
background: linear-gradient(90deg, rgba(63,185,80,0.12), transparent);
|
|
5215
|
+
}
|
|
5216
|
+
.confidence-high { border-color: rgba(63,185,80,0.4); border-left-color: var(--ok); }
|
|
5217
|
+
.confidence-medium { border-color: rgba(227,179,65,0.4); border-left-color: var(--watch); background: linear-gradient(90deg, rgba(227,179,65,0.12), transparent); }
|
|
5218
|
+
.confidence-low { border-color: rgba(255,107,107,0.4); border-left-color: var(--risk); background: linear-gradient(90deg, rgba(255,107,107,0.12), transparent); }
|
|
5219
|
+
.confidence p { margin: 0; }
|
|
5220
|
+
.confidence-label, .confidence strong { text-transform: uppercase; letter-spacing: 0.04em; font-weight: 700; }
|
|
5221
|
+
.confidence-high .confidence-label, .confidence-high strong { color: var(--ok); }
|
|
5222
|
+
.confidence-medium .confidence-label, .confidence-medium strong { color: var(--watch); }
|
|
5223
|
+
.confidence-low .confidence-label, .confidence-low strong { color: var(--risk); }
|
|
4344
5224
|
.confidence-escalation { color: var(--risk); font-weight: 600; }
|
|
4345
|
-
.banner { border-radius:
|
|
4346
|
-
.banner-degraded { border: 1px solid
|
|
4347
|
-
.banner-metrics-only { border: 1px solid var(--border); color: var(--muted); }
|
|
4348
|
-
.toc {
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
:
|
|
4352
|
-
|
|
4353
|
-
.
|
|
4354
|
-
.
|
|
5225
|
+
.banner { border-radius: 12px; padding: 0.85rem 1.1rem; margin: 1.25rem 0; }
|
|
5226
|
+
.banner-degraded { border: 1px solid rgba(255,107,107,0.4); color: var(--risk); font-weight: 600; background: linear-gradient(90deg, rgba(255,107,107,0.12), transparent); }
|
|
5227
|
+
.banner-metrics-only { border: 1px solid var(--border); color: var(--muted); background: var(--surface); }
|
|
5228
|
+
.toc {
|
|
5229
|
+
position: sticky; top: 0; z-index: 20; margin: 1.5rem auto 0; padding: 0.6rem 0;
|
|
5230
|
+
background: color-mix(in srgb, var(--bg) 78%, transparent); backdrop-filter: blur(12px);
|
|
5231
|
+
border-bottom: 1px solid var(--border-soft);
|
|
5232
|
+
}
|
|
5233
|
+
.toc h2 { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.12em; color: var(--faint); margin: 0 0 0.45rem; }
|
|
5234
|
+
.toc h2::before { content: none; }
|
|
5235
|
+
.toc ul { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; gap: 0.4rem; }
|
|
5236
|
+
.toc a { display: inline-block; padding: 0.32rem 0.8rem; border-radius: 999px; font-size: 0.85rem; color: var(--muted); border: 1px solid transparent; }
|
|
5237
|
+
.toc a:hover { color: var(--fg); background: var(--surface-2); border-color: var(--border); text-decoration: none; }
|
|
5238
|
+
.band, .metric-group { scroll-margin-top: 4rem; }
|
|
5239
|
+
.band { margin: 2.5rem auto; }
|
|
5240
|
+
:target { outline: 2px solid var(--accent); outline-offset: 4px; border-radius: 8px; }
|
|
5241
|
+
.headline { font-size: 1.3rem; font-weight: 700; letter-spacing: -0.01em; }
|
|
5242
|
+
.lead, .explanation p, .coaching-intro, .coaching-closing { color: var(--fg-soft); }
|
|
5243
|
+
.key-findings { list-style: none; padding: 0; margin: 1.2rem 0 0; display: grid; gap: 0.6rem; }
|
|
5244
|
+
.key-findings li { position: relative; padding: 0.8rem 1rem 0.8rem 2.5rem; background: var(--surface); border: 1px solid var(--border); border-radius: 12px; }
|
|
5245
|
+
.key-findings li::before { content: "\203A"; position: absolute; left: 1rem; top: 0.7rem; color: var(--accent); font-weight: 800; font-size: 1.1rem; }
|
|
5246
|
+
.chapter { background: var(--surface); border: 1px solid var(--border); border-left: 4px solid var(--accent-2); border-radius: 14px; padding: 1.1rem 1.35rem; margin: 1rem 0; }
|
|
5247
|
+
.chapter h3 { margin: 0 0 0.6rem; }
|
|
5248
|
+
.chapter ol { margin: 0; padding-left: 1.2rem; display: grid; gap: 0.5rem; color: var(--fg-soft); }
|
|
5249
|
+
.coaching-closing { margin-top: 1rem; padding: 1rem 1.2rem; border: 1px solid rgba(124,92,255,0.35); border-radius: 12px; background: linear-gradient(90deg, rgba(124,92,255,0.10), transparent); }
|
|
5250
|
+
.metric-group .chart-panel + .metric-card { margin-top: 1.2rem; }
|
|
5251
|
+
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 1rem; margin-top: 1.2rem; align-items: stretch; }
|
|
5252
|
+
.cards > .metric-card { margin: 0; }
|
|
5253
|
+
.metric-card { background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 0.7rem 1.1rem; margin: 0.85rem 0; transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease; }
|
|
5254
|
+
.metric-card:hover { transform: translateY(-2px); border-color: #2f3a4d; box-shadow: var(--shadow); }
|
|
5255
|
+
.metric-card > summary { cursor: pointer; list-style-position: inside; display: flex; align-items: center; gap: 0.55rem; flex-wrap: wrap; }
|
|
4355
5256
|
.metric-card[data-health="risk"] { border-left: 4px solid var(--risk); }
|
|
4356
5257
|
.metric-card[data-health="watch"] { border-left: 4px solid var(--watch); }
|
|
4357
|
-
.metric-card[data-
|
|
5258
|
+
.metric-card[data-health="ok"] { border-left: 4px solid var(--ok); }
|
|
5259
|
+
.metric-card[data-status="not_available"] { opacity: 0.72; border-left: 4px solid var(--faint); }
|
|
4358
5260
|
.metric-title { display: inline; font-size: 1.05rem; margin: 0; font-weight: 600; }
|
|
5261
|
+
.metric-stat { margin-left: auto; font-size: 1.5rem; font-weight: 700; letter-spacing: -0.01em; color: var(--fg); }
|
|
5262
|
+
.why { color: var(--muted); margin: 0.4rem 0 0; }
|
|
4359
5263
|
.metric-body { margin-top: 0.5rem; }
|
|
4360
5264
|
.metric-status { color: var(--muted); font-size: 0.85rem; margin: 0.25rem 0; }
|
|
5265
|
+
.metric-value code { background: var(--surface-2); border: 1px solid var(--border-soft); border-radius: 6px; padding: 0.15rem 0.45rem; }
|
|
4361
5266
|
.metric-card code { word-break: break-word; }
|
|
5267
|
+
.facets { margin-top: 0.6rem; }
|
|
5268
|
+
.facet-meaning { color: var(--fg-soft); }
|
|
4362
5269
|
.facets ul { margin: 0.25rem 0 0.5rem; }
|
|
4363
|
-
.health { font-size: 0.
|
|
4364
|
-
.health-glyph { font-size:
|
|
4365
|
-
.health-ok { color: var(--ok); }
|
|
4366
|
-
.health-watch { color: var(--watch); }
|
|
4367
|
-
.health-risk { color: var(--risk); }
|
|
4368
|
-
.health-na { color: var(--
|
|
4369
|
-
.chart-panel { margin: 1rem 0; background: var(--surface); border: 1px solid var(--border); border-radius:
|
|
4370
|
-
.chart-panel figcaption { color: var(--muted); font-size: 0.9rem; margin-bottom: 0.
|
|
5270
|
+
.health { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.78rem; font-weight: 700; padding: 0.18rem 0.55rem; border-radius: 999px; white-space: nowrap; }
|
|
5271
|
+
.health-glyph { font-size: 0.95rem; }
|
|
5272
|
+
.health-ok { color: var(--ok); background: rgba(63,185,80,0.13); }
|
|
5273
|
+
.health-watch { color: var(--watch); background: rgba(227,179,65,0.14); }
|
|
5274
|
+
.health-risk { color: var(--risk); background: rgba(255,107,107,0.14); }
|
|
5275
|
+
.health-na { color: var(--faint); background: rgba(93,107,128,0.14); }
|
|
5276
|
+
.chart-panel { margin: 1rem 0; background: linear-gradient(180deg, color-mix(in srgb, var(--surface) 88%, var(--accent) 4%), var(--surface)); border: 1px solid var(--border); border-radius: 10px; padding: 1rem 1.25rem; box-shadow: 0 10px 30px -18px rgba(0,0,0,0.5); }
|
|
5277
|
+
.chart-panel figcaption { color: var(--muted); font-size: 0.9rem; margin-bottom: 0.75rem; }
|
|
4371
5278
|
.chart-source { font-style: italic; }
|
|
4372
5279
|
.chart-empty { color: var(--muted); }
|
|
4373
|
-
.chart-svg { width: 100%; height:
|
|
4374
|
-
.chart-svg.chart-sparkline { height:
|
|
5280
|
+
.chart-svg { width: 100%; height: auto; color: var(--accent); display: block; }
|
|
5281
|
+
.chart-svg.chart-sparkline { height: 2.25rem; width: 8rem; }
|
|
5282
|
+
.chart-svg.chart-gauge { height: 0.9rem; width: 12rem; max-width: 60%; }
|
|
5283
|
+
.chart-svg.chart-radar { max-width: 30rem; margin-inline: auto; }
|
|
5284
|
+
.metric-visual .chart-svg { max-width: 38rem; }
|
|
5285
|
+
.chart-svg text { font-family: inherit; }
|
|
5286
|
+
.chart-svg .chart-tick, .chart-svg .chart-label { fill: var(--muted); font-size: 11px; }
|
|
5287
|
+
.chart-svg .radar-label { fill: var(--muted); font-size: 10px; }
|
|
5288
|
+
.chart-svg .chart-grid { stroke: var(--border); stroke-width: 1; opacity: 0.55; }
|
|
5289
|
+
.chart-svg .chart-axis { stroke: var(--border); stroke-width: 1; }
|
|
5290
|
+
.chart-svg .chart-dot { fill: var(--accent); stroke: var(--surface); stroke-width: 1; }
|
|
5291
|
+
.chart-svg .grad-area-top { stop-color: var(--accent); stop-opacity: 0.38; }
|
|
5292
|
+
.chart-svg .grad-area-bottom { stop-color: var(--accent); stop-opacity: 0; }
|
|
5293
|
+
.chart-svg .grad-fill-1 { stop-color: var(--accent); }
|
|
5294
|
+
.chart-svg .grad-fill-2 { stop-color: var(--accent-2); }
|
|
4375
5295
|
.chart-svg .bar { fill: var(--accent); }
|
|
4376
5296
|
.chart-svg .gauge-track { fill: var(--border); }
|
|
4377
5297
|
.chart-svg .gauge-fill { fill: var(--accent); }
|
|
4378
|
-
.chart-svg .radar-
|
|
5298
|
+
.chart-svg .radar-grid { fill: none; stroke: var(--border); stroke-width: 0.5; opacity: 0.7; }
|
|
5299
|
+
.chart-svg .radar-axis { stroke: var(--border); stroke-width: 0.4; opacity: 0.7; }
|
|
5300
|
+
.chart-svg .radar-area { fill: var(--accent); fill-opacity: 0.45; stroke: var(--accent); stroke-width: 1; stroke-linejoin: round; }
|
|
5301
|
+
.chart-svg .radar-dot { fill: var(--accent); stroke: var(--surface); stroke-width: 0.6; }
|
|
5302
|
+
.chart-svg.chart-radialgauge { max-width: 12rem; margin-inline: auto; }
|
|
5303
|
+
.chart-svg.chart-donut { max-width: 30rem; }
|
|
5304
|
+
.chart-svg .gauge-ring-track { stroke: var(--border); }
|
|
5305
|
+
.chart-svg .gauge-value { fill: var(--fg); font-weight: 700; font-size: 40px; }
|
|
5306
|
+
.chart-svg .donut-label { fill: var(--fg-soft); font-size: 13px; }
|
|
5307
|
+
.chart-svg .slice-0 { fill: #58a6ff; stroke: #58a6ff; }
|
|
5308
|
+
.chart-svg .slice-1 { fill: #7c5cff; stroke: #7c5cff; }
|
|
5309
|
+
.chart-svg .slice-2 { fill: #3fb950; stroke: #3fb950; }
|
|
5310
|
+
.chart-svg .slice-3 { fill: #e3b341; stroke: #e3b341; }
|
|
5311
|
+
.chart-svg .slice-4 { fill: #2dd4bf; stroke: #2dd4bf; }
|
|
5312
|
+
.chart-svg .slice-5 { fill: #f472b6; stroke: #f472b6; }
|
|
5313
|
+
.chart-svg .donut-seg { stroke: var(--surface); stroke-width: 1.5; }
|
|
5314
|
+
.chart-cells { display: grid; gap: 1.2rem; margin-top: 0.25rem; }
|
|
5315
|
+
.chart-sub { min-width: 0; }
|
|
5316
|
+
.chart-sub h4 { margin: 0 0 0.4rem; font-size: 0.9rem; color: var(--fg); text-transform: none; letter-spacing: 0; }
|
|
5317
|
+
@media (min-width: 720px) { .chart-cells.two { grid-template-columns: 1.5fr 1fr; align-items: center; } }
|
|
4379
5318
|
.metric-visual { margin: 0.5rem 0; }
|
|
4380
5319
|
.metric-visual-range { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
|
|
4381
5320
|
.metric-number { font-size: 1.4rem; font-weight: 700; }
|
|
@@ -4384,13 +5323,12 @@ a:focus-visible, :focus-visible { outline: 2px solid var(--accent); outline-offs
|
|
|
4384
5323
|
.data-table table { border-collapse: collapse; width: 100%; font-size: 0.85rem; margin-top: 0.5rem; }
|
|
4385
5324
|
.data-table th, .data-table td { border: 1px solid var(--border); padding: 0.25rem 0.5rem; text-align: left; }
|
|
4386
5325
|
.data-table caption { text-align: left; color: var(--muted); padding-bottom: 0.25rem; }
|
|
4387
|
-
.footer { color: var(--
|
|
5326
|
+
.footer { color: var(--faint); border-top: 1px solid var(--border-soft); margin-top: 3rem; padding-top: 1.2rem; font-size: 0.85rem; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
|
|
4388
5327
|
@media (prefers-reduced-motion: reduce) {
|
|
4389
5328
|
* { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
|
|
4390
5329
|
}
|
|
4391
5330
|
`;
|
|
4392
5331
|
var DISCLOSURE_SCRIPT = `
|
|
4393
|
-
document.querySelectorAll('details.metric-card[data-health="ok"]').forEach(function(d){d.open=false;});
|
|
4394
5332
|
document.querySelectorAll('details.data-table').forEach(function(d){d.open=false;});
|
|
4395
5333
|
`;
|
|
4396
5334
|
|
|
@@ -4429,7 +5367,7 @@ function safe2(n) {
|
|
|
4429
5367
|
function round2(n) {
|
|
4430
5368
|
return Math.round(safe2(n) * 100) / 100;
|
|
4431
5369
|
}
|
|
4432
|
-
function
|
|
5370
|
+
function extent(values) {
|
|
4433
5371
|
let min = Number.POSITIVE_INFINITY;
|
|
4434
5372
|
let max = Number.NEGATIVE_INFINITY;
|
|
4435
5373
|
for (const raw of values) {
|
|
@@ -4445,7 +5383,7 @@ function sparkline(series) {
|
|
|
4445
5383
|
return "";
|
|
4446
5384
|
}
|
|
4447
5385
|
const values = series.map((p) => safe2(p.value));
|
|
4448
|
-
const [min, max] =
|
|
5386
|
+
const [min, max] = extent(values);
|
|
4449
5387
|
const span = max - min;
|
|
4450
5388
|
const midGlyph = SPARK_GLYPHS[Math.floor(SPARK_GLYPHS.length / 2)];
|
|
4451
5389
|
if (span === 0) {
|
|
@@ -4462,7 +5400,7 @@ function textBars(series) {
|
|
|
4462
5400
|
if (series.length === 0) {
|
|
4463
5401
|
return "";
|
|
4464
5402
|
}
|
|
4465
|
-
const [, max] =
|
|
5403
|
+
const [, max] = extent(series.map((p) => p.value));
|
|
4466
5404
|
const top = max <= 0 ? 1 : max;
|
|
4467
5405
|
const labels = series.map((p) => escapeCell(p.label));
|
|
4468
5406
|
const labelWidth = labels.reduce((w, label) => Math.max(w, label.length), 0);
|
|
@@ -4475,26 +5413,7 @@ function textBars(series) {
|
|
|
4475
5413
|
});
|
|
4476
5414
|
return ["```", ...rows, "```"].join("\n");
|
|
4477
5415
|
}
|
|
4478
|
-
function
|
|
4479
|
-
const cleaned = text.replaceAll(/["[\],\r\n\t]+/g, " ").replaceAll(/\s+/g, " ").trim();
|
|
4480
|
-
return cleaned === "" ? "-" : cleaned;
|
|
4481
|
-
}
|
|
4482
|
-
function mermaidXychart(series, title2) {
|
|
4483
|
-
if (series.length === 0) {
|
|
4484
|
-
return "";
|
|
4485
|
-
}
|
|
4486
|
-
const axis = series.map((p) => `"${mermaidLabel(p.label)}"`).join(", ");
|
|
4487
|
-
const values = series.map((p) => round2(p.value)).join(", ");
|
|
4488
|
-
return [
|
|
4489
|
-
"```mermaid",
|
|
4490
|
-
"xychart-beta",
|
|
4491
|
-
` title "${mermaidLabel(title2)}"`,
|
|
4492
|
-
` x-axis [${axis}]`,
|
|
4493
|
-
` bar [${values}]`,
|
|
4494
|
-
"```"
|
|
4495
|
-
].join("\n");
|
|
4496
|
-
}
|
|
4497
|
-
function representativeSeries2(metrics) {
|
|
5416
|
+
function representativeSeries(metrics) {
|
|
4498
5417
|
for (const metric of metrics) {
|
|
4499
5418
|
if (metric.status !== "computed") {
|
|
4500
5419
|
continue;
|
|
@@ -4505,20 +5424,17 @@ function representativeSeries2(metrics) {
|
|
|
4505
5424
|
}
|
|
4506
5425
|
const series = extractSeries(metric.value);
|
|
4507
5426
|
if (series.length > 0) {
|
|
4508
|
-
return
|
|
5427
|
+
return series;
|
|
4509
5428
|
}
|
|
4510
5429
|
}
|
|
4511
5430
|
return void 0;
|
|
4512
5431
|
}
|
|
4513
|
-
function groupOverview(
|
|
4514
|
-
const
|
|
4515
|
-
if (
|
|
5432
|
+
function groupOverview(_group, metrics) {
|
|
5433
|
+
const series = representativeSeries(metrics);
|
|
5434
|
+
if (series === void 0) {
|
|
4516
5435
|
return GROUP_OVERVIEW_NONE;
|
|
4517
5436
|
}
|
|
4518
|
-
|
|
4519
|
-
return mermaidXychart(rep.series, `Group ${group} overview`);
|
|
4520
|
-
}
|
|
4521
|
-
return textBars(rep.series);
|
|
5437
|
+
return textBars(series);
|
|
4522
5438
|
}
|
|
4523
5439
|
function scalarNumber(value) {
|
|
4524
5440
|
if (typeof value === "number") {
|
|
@@ -4555,7 +5471,7 @@ function metricVisualMarkdown(metric) {
|
|
|
4555
5471
|
}
|
|
4556
5472
|
|
|
4557
5473
|
// src/render/markdown/markdown-renderer.ts
|
|
4558
|
-
var
|
|
5474
|
+
var GROUPS3 = [
|
|
4559
5475
|
{ id: "A", title: "Activity & Cadence", description: "How the project moves over time." },
|
|
4560
5476
|
{ id: "B", title: "Contribution & Ownership", description: "How the work is distributed across the team." },
|
|
4561
5477
|
{ id: "C", title: "Commit Message Quality", description: "How clearly the history communicates intent." },
|
|
@@ -4567,42 +5483,100 @@ var MD_DEGRADED_BANNER = "> \u26A0 **Narrative unavailable \u2014 showing raw an
|
|
|
4567
5483
|
var MD_METRICS_ONLY_NOTE = "_Narrative skipped (--no-ai) \u2014 run interactively or add a key for the full report._";
|
|
4568
5484
|
function renderMarkdown(report) {
|
|
4569
5485
|
const route = classifyReport(report);
|
|
4570
|
-
const
|
|
5486
|
+
const provenance = report.provenance;
|
|
5487
|
+
const body = route.kind === "showpiece" ? renderShowpiece3(route.report, provenance) : renderSubstrate3(route.analysis, route.framing, provenance);
|
|
4571
5488
|
return `${body}
|
|
4572
5489
|
`;
|
|
4573
5490
|
}
|
|
4574
5491
|
function blocks(...parts) {
|
|
4575
5492
|
return parts.filter((part) => part !== "").join("\n\n");
|
|
4576
5493
|
}
|
|
4577
|
-
function renderShowpiece3(report) {
|
|
5494
|
+
function renderShowpiece3(report, provenance) {
|
|
4578
5495
|
const { summary, explanation, coaching, explanations, confidence } = report.narrative;
|
|
4579
5496
|
return blocks(
|
|
4580
|
-
|
|
5497
|
+
masthead3(provenance),
|
|
4581
5498
|
confidence === void 0 ? "" : confidenceBlock(confidence),
|
|
4582
5499
|
summaryBand2(summary),
|
|
4583
5500
|
explanationBand2(explanation),
|
|
4584
5501
|
coachingBand2(coaching),
|
|
4585
|
-
|
|
4586
|
-
footer2("showpiece")
|
|
5502
|
+
metricsSection2(report.analysis, explanations),
|
|
5503
|
+
footer2("showpiece", provenance)
|
|
4587
5504
|
);
|
|
4588
5505
|
}
|
|
4589
|
-
function renderSubstrate3(analysis, framing) {
|
|
5506
|
+
function renderSubstrate3(analysis, framing, provenance) {
|
|
4590
5507
|
if (framing === "degraded") {
|
|
4591
5508
|
return blocks(
|
|
4592
5509
|
MD_DEGRADED_BANNER,
|
|
4593
|
-
|
|
5510
|
+
masthead3(provenance),
|
|
4594
5511
|
"**Confidence:** \u2014 _(no narrative to assess)_",
|
|
4595
5512
|
stubBand("Summary", "Narrative unavailable \u2014 showing raw analysis."),
|
|
4596
5513
|
stubBand("Explanation", "Narrative unavailable."),
|
|
4597
5514
|
stubBand("Coaching", "Narrative unavailable."),
|
|
4598
|
-
|
|
4599
|
-
footer2("degraded")
|
|
5515
|
+
metricsSection2(analysis, void 0),
|
|
5516
|
+
footer2("degraded", provenance)
|
|
4600
5517
|
);
|
|
4601
5518
|
}
|
|
4602
|
-
return blocks(
|
|
5519
|
+
return blocks(masthead3(provenance), metricsSection2(analysis, void 0), footer2("metrics-only", provenance));
|
|
5520
|
+
}
|
|
5521
|
+
function masthead3(provenance) {
|
|
5522
|
+
return blocks(title(provenance), provenanceChips3(provenance), capLine3(provenance));
|
|
5523
|
+
}
|
|
5524
|
+
function title(provenance) {
|
|
5525
|
+
const name = provenance?.repo?.name;
|
|
5526
|
+
return name === void 0 ? "# commit-whisper" : `# commit-whisper \u2014 ${escapeCell(name)}`;
|
|
5527
|
+
}
|
|
5528
|
+
function provenanceChips3(provenance) {
|
|
5529
|
+
const repo = provenance?.repo;
|
|
5530
|
+
const scale = provenance?.scale;
|
|
5531
|
+
const chips = [];
|
|
5532
|
+
if (repo?.name !== void 0) {
|
|
5533
|
+
chips.push(escapeCell(repo.name));
|
|
5534
|
+
}
|
|
5535
|
+
if (repo?.branch !== void 0) {
|
|
5536
|
+
chips.push(escapeCell(repo.branch));
|
|
5537
|
+
}
|
|
5538
|
+
const commits = scale?.totalCommits ?? scale?.analyzedCommits;
|
|
5539
|
+
if (commits !== void 0) {
|
|
5540
|
+
chips.push(`${formatCount3(commits)} ${commits === 1 ? "commit" : "commits"}`);
|
|
5541
|
+
}
|
|
5542
|
+
if (scale?.contributors !== void 0) {
|
|
5543
|
+
chips.push(`${formatCount3(scale.contributors)} ${scale.contributors === 1 ? "contributor" : "contributors"}`);
|
|
5544
|
+
}
|
|
5545
|
+
const generatedAt = provenance?.run?.generatedAt;
|
|
5546
|
+
if (generatedAt !== void 0) {
|
|
5547
|
+
chips.push(`analyzed ${isoDate3(generatedAt)}`);
|
|
5548
|
+
}
|
|
5549
|
+
return chips.length === 0 ? "" : `_${chips.join(" \xB7 ")}_`;
|
|
4603
5550
|
}
|
|
4604
|
-
function
|
|
4605
|
-
|
|
5551
|
+
function capLine3(provenance) {
|
|
5552
|
+
if (provenance?.entitlement?.tier !== "free") {
|
|
5553
|
+
return "";
|
|
5554
|
+
}
|
|
5555
|
+
const analyzed = provenance.scale?.analyzedCommits;
|
|
5556
|
+
if (analyzed === void 0) {
|
|
5557
|
+
return "";
|
|
5558
|
+
}
|
|
5559
|
+
const total = provenance.scale?.totalCommits;
|
|
5560
|
+
const detail = total === void 0 ? `${formatCount3(analyzed)} commits analyzed` : `${formatCount3(analyzed)} of ${formatCount3(total)} commits analyzed`;
|
|
5561
|
+
return `Free \xB7 ${detail}`;
|
|
5562
|
+
}
|
|
5563
|
+
function formatCount3(n) {
|
|
5564
|
+
if (!Number.isFinite(n)) {
|
|
5565
|
+
return "0";
|
|
5566
|
+
}
|
|
5567
|
+
const negative = n < 0;
|
|
5568
|
+
const digits = Math.abs(Math.trunc(n)).toString();
|
|
5569
|
+
let grouped = "";
|
|
5570
|
+
for (let i = 0; i < digits.length; i++) {
|
|
5571
|
+
if (i > 0 && (digits.length - i) % 3 === 0) {
|
|
5572
|
+
grouped += ",";
|
|
5573
|
+
}
|
|
5574
|
+
grouped += digits[i];
|
|
5575
|
+
}
|
|
5576
|
+
return negative ? `-${grouped}` : grouped;
|
|
5577
|
+
}
|
|
5578
|
+
function isoDate3(iso) {
|
|
5579
|
+
return iso.slice(0, 10);
|
|
4606
5580
|
}
|
|
4607
5581
|
function confidenceBlock(confidence) {
|
|
4608
5582
|
const head = `**Confidence:** ${confidence.level} \u2014 ${inlineProse(confidence.rationale)}`;
|
|
@@ -4649,14 +5623,14 @@ function stubBand(name, note) {
|
|
|
4649
5623
|
|
|
4650
5624
|
_${note}_`;
|
|
4651
5625
|
}
|
|
4652
|
-
function
|
|
4653
|
-
const present =
|
|
5626
|
+
function metricsSection2(analysis, explanations) {
|
|
5627
|
+
const present = GROUPS3.filter((group) => analysis.metrics.some((metric) => metric.group === group.id));
|
|
4654
5628
|
if (present.length === 0) {
|
|
4655
5629
|
return "## Metrics\n\n_No metrics computed._";
|
|
4656
5630
|
}
|
|
4657
5631
|
const sections = present.map((group) => {
|
|
4658
5632
|
const metrics = analysis.metrics.filter((metric) => metric.group === group.id);
|
|
4659
|
-
const cards = metrics.map((metric) =>
|
|
5633
|
+
const cards = metrics.map((metric) => metricCard3(metric, explanations)).join("\n\n");
|
|
4660
5634
|
const overview = groupOverview(group.id, metrics);
|
|
4661
5635
|
return `### ${group.id} \xB7 ${escapeCell(group.title)}
|
|
4662
5636
|
|
|
@@ -4670,42 +5644,76 @@ ${cards}`;
|
|
|
4670
5644
|
|
|
4671
5645
|
${sections.join("\n\n")}`;
|
|
4672
5646
|
}
|
|
4673
|
-
function
|
|
5647
|
+
function metricCard3(metric, explanations) {
|
|
4674
5648
|
const band = classifyHealth(metric);
|
|
4675
5649
|
const visual = metricVisualMarkdown(metric);
|
|
4676
5650
|
const suffix = visual.headingSuffix === "" ? "" : ` ${visual.headingSuffix}`;
|
|
4677
|
-
const
|
|
5651
|
+
const heading = `#### ${escapeCell(metric.title)} ${HEALTH_GLYPH[band]} ${HEALTH_LABEL[band]}${suffix}`;
|
|
4678
5652
|
const body = visual.body === "" ? "" : `
|
|
4679
5653
|
|
|
4680
5654
|
${visual.body}`;
|
|
4681
|
-
return `${
|
|
5655
|
+
return `${heading}${body}
|
|
4682
5656
|
|
|
4683
5657
|
${metricBullets(metric, explanations)}`;
|
|
4684
5658
|
}
|
|
4685
5659
|
function metricBullets(metric, explanations) {
|
|
4686
|
-
const value =
|
|
5660
|
+
const value = valueBullet2(metric);
|
|
4687
5661
|
const explanation = explanations?.[metric.id];
|
|
4688
5662
|
if (explanation === void 0) {
|
|
4689
5663
|
return value;
|
|
4690
5664
|
}
|
|
4691
|
-
|
|
5665
|
+
const separator = value.includes("\n") ? "\n\n" : "\n";
|
|
5666
|
+
return [value, facetBullets2(explanation).join("\n")].join(separator);
|
|
4692
5667
|
}
|
|
4693
|
-
function
|
|
5668
|
+
function valueBullet2(metric) {
|
|
4694
5669
|
if (metric.status !== "computed") {
|
|
4695
5670
|
const reason = metric.reason === void 0 ? "" : ` \u2014 ${escapeCell(metric.reason)}`;
|
|
4696
5671
|
return `- **Value** \u2014 _not available${reason}_`;
|
|
4697
5672
|
}
|
|
4698
|
-
|
|
5673
|
+
const value = metric.value;
|
|
5674
|
+
if (value === null || typeof value !== "object") {
|
|
5675
|
+
return `- **Value** \u2014 ${escapeCell(formatValue3(value))}`;
|
|
5676
|
+
}
|
|
5677
|
+
const series = extractSeries(value);
|
|
5678
|
+
if (series.length === 0) {
|
|
5679
|
+
const tree = buildValueTree(value);
|
|
5680
|
+
if (tree.kind === "scalar") {
|
|
5681
|
+
return `- **Value** \u2014 ${escapeCell(tree.text)}`;
|
|
5682
|
+
}
|
|
5683
|
+
return [`- **Value**`, ...treeBullets(tree.entries, " ")].join("\n");
|
|
5684
|
+
}
|
|
5685
|
+
if (series.length === 1) {
|
|
5686
|
+
return `- **Value** \u2014 ${escapeCell(formatNumber3(series[0].value))}`;
|
|
5687
|
+
}
|
|
5688
|
+
return valueTable(value);
|
|
5689
|
+
}
|
|
5690
|
+
function treeBullets(entries, indent) {
|
|
5691
|
+
return entries.flatMap((entry) => {
|
|
5692
|
+
if (entry.child.kind === "scalar") {
|
|
5693
|
+
const text = entry.child.text === "" ? escapeCell(entry.label) : `${escapeCell(entry.label)}: ${escapeCell(entry.child.text)}`;
|
|
5694
|
+
return [`${indent}- ${text}`];
|
|
5695
|
+
}
|
|
5696
|
+
return [`${indent}- ${escapeCell(entry.label)}`, ...treeBullets(entry.child.entries, `${indent} `)];
|
|
5697
|
+
});
|
|
5698
|
+
}
|
|
5699
|
+
function formatNumber3(n) {
|
|
5700
|
+
return Number.isFinite(n) ? String(Math.round(n * 100) / 100) : "0";
|
|
4699
5701
|
}
|
|
4700
|
-
function
|
|
5702
|
+
function valueTable(value) {
|
|
5703
|
+
const shape = detectShape(value);
|
|
5704
|
+
const labelHeader = shape === "timeseries" ? "Period" : shape === "distribution" ? "Item" : "Field";
|
|
5705
|
+
const rows = extractSeries(value).map((point) => `| ${escapeCell(point.label)} | ${escapeCell(formatNumber3(point.value))} |`);
|
|
5706
|
+
return [`- **Value**`, "", `| ${labelHeader} | Value |`, "| --- | --- |", ...rows].join("\n");
|
|
5707
|
+
}
|
|
5708
|
+
function facetBullets2(explanation) {
|
|
4701
5709
|
return [
|
|
4702
5710
|
`- **What it means** \u2014 ${inlineProse(explanation.explanation)}`,
|
|
4703
|
-
...
|
|
4704
|
-
...
|
|
4705
|
-
...
|
|
5711
|
+
...arrayFacet2("Strengths", explanation.goodBehaviours),
|
|
5712
|
+
...arrayFacet2("Needs improvement", explanation.needsImprovement),
|
|
5713
|
+
...arrayFacet2("Suggestions", explanation.suggestions)
|
|
4706
5714
|
];
|
|
4707
5715
|
}
|
|
4708
|
-
function
|
|
5716
|
+
function arrayFacet2(label, items) {
|
|
4709
5717
|
if (items.length === 0) {
|
|
4710
5718
|
return [`- **${label}** \u2014 \u2014`];
|
|
4711
5719
|
}
|
|
@@ -4723,8 +5731,22 @@ function formatValue3(value) {
|
|
|
4723
5731
|
}
|
|
4724
5732
|
return JSON.stringify(value) ?? "";
|
|
4725
5733
|
}
|
|
4726
|
-
function footer2(kind) {
|
|
4727
|
-
const
|
|
5734
|
+
function footer2(kind, provenance) {
|
|
5735
|
+
const version = provenance?.run?.toolVersion;
|
|
5736
|
+
const parts = [
|
|
5737
|
+
version === void 0 ? "Generated by commit-whisper" : `Generated by commit-whisper v${escapeCell(version)}`,
|
|
5738
|
+
"schemaVersion 1.0.0"
|
|
5739
|
+
];
|
|
5740
|
+
const ai = provenance?.ai;
|
|
5741
|
+
if (ai !== void 0) {
|
|
5742
|
+
parts.push(`${escapeCell(ai.provider)}/${escapeCell(ai.model)}`);
|
|
5743
|
+
}
|
|
5744
|
+
const generatedAt = provenance?.run?.generatedAt;
|
|
5745
|
+
if (generatedAt !== void 0) {
|
|
5746
|
+
parts.push(escapeCell(generatedAt));
|
|
5747
|
+
}
|
|
5748
|
+
const base = `---
|
|
5749
|
+
${parts.join(" \xB7 ")}`;
|
|
4728
5750
|
return kind === "metrics-only" ? `${base}
|
|
4729
5751
|
|
|
4730
5752
|
${MD_METRICS_ONLY_NOTE}` : base;
|
|
@@ -5087,6 +6109,88 @@ function createRetrieve(deps = {}) {
|
|
|
5087
6109
|
return async (config) => (isRemoteTarget(config.repoTarget) ? remote : local)(config);
|
|
5088
6110
|
}
|
|
5089
6111
|
|
|
6112
|
+
// src/cli/provenance.ts
|
|
6113
|
+
import { basename } from "path";
|
|
6114
|
+
function buildProvenance(input) {
|
|
6115
|
+
const source = isRemoteTarget(input.target) ? "remote" : "local";
|
|
6116
|
+
const repo = {
|
|
6117
|
+
name: source === "remote" ? remoteSlug(input.target) : localName(input.target),
|
|
6118
|
+
target: source === "remote" ? stripCredentials(input.target) : input.target,
|
|
6119
|
+
source
|
|
6120
|
+
};
|
|
6121
|
+
const branch = branchLabel(input.branch);
|
|
6122
|
+
if (branch !== void 0) {
|
|
6123
|
+
repo.branch = branch;
|
|
6124
|
+
}
|
|
6125
|
+
const scale = {
|
|
6126
|
+
totalCommits: input.totalCommits,
|
|
6127
|
+
analyzedCommits: input.analyzedCommits
|
|
6128
|
+
};
|
|
6129
|
+
if (input.contributors !== void 0) {
|
|
6130
|
+
scale.contributors = input.contributors;
|
|
6131
|
+
}
|
|
6132
|
+
const provenance = {
|
|
6133
|
+
repo,
|
|
6134
|
+
scale,
|
|
6135
|
+
run: { generatedAt: input.generatedAt, toolVersion: input.toolVersion },
|
|
6136
|
+
entitlement: buildEntitlement(input.tier, input.commitCap)
|
|
6137
|
+
};
|
|
6138
|
+
if (input.provider !== void 0 && input.model !== void 0) {
|
|
6139
|
+
provenance.ai = { provider: input.provider, model: input.model };
|
|
6140
|
+
}
|
|
6141
|
+
return provenance;
|
|
6142
|
+
}
|
|
6143
|
+
function buildEntitlement(tier, commitCap) {
|
|
6144
|
+
const entitlement = { tier };
|
|
6145
|
+
if (tier === "free" && commitCap !== void 0) {
|
|
6146
|
+
entitlement.commitCap = commitCap;
|
|
6147
|
+
}
|
|
6148
|
+
return entitlement;
|
|
6149
|
+
}
|
|
6150
|
+
function branchLabel(branch) {
|
|
6151
|
+
switch (branch.kind) {
|
|
6152
|
+
case "named":
|
|
6153
|
+
return branch.name;
|
|
6154
|
+
case "all":
|
|
6155
|
+
return "all branches";
|
|
6156
|
+
case "head":
|
|
6157
|
+
return void 0;
|
|
6158
|
+
// the default sentinel carries no meaningful label
|
|
6159
|
+
default:
|
|
6160
|
+
return void 0;
|
|
6161
|
+
}
|
|
6162
|
+
}
|
|
6163
|
+
function stripCredentials(target) {
|
|
6164
|
+
return target.replace(/^(https?:\/\/)[^@/]*@/i, "$1");
|
|
6165
|
+
}
|
|
6166
|
+
function remoteSlug(target) {
|
|
6167
|
+
try {
|
|
6168
|
+
const url = new URL(target.trim());
|
|
6169
|
+
const path = stripTrailingSlashes(url.pathname).replace(/\.git$/i, "");
|
|
6170
|
+
const segments = path.split("/").filter((segment) => segment !== "");
|
|
6171
|
+
if (segments.length >= 2) {
|
|
6172
|
+
return `${segments.at(-2)}/${segments.at(-1)}`;
|
|
6173
|
+
}
|
|
6174
|
+
if (segments.length === 1) {
|
|
6175
|
+
return segments[0];
|
|
6176
|
+
}
|
|
6177
|
+
return url.host;
|
|
6178
|
+
} catch {
|
|
6179
|
+
return stripCredentials(target.trim());
|
|
6180
|
+
}
|
|
6181
|
+
}
|
|
6182
|
+
function localName(target) {
|
|
6183
|
+
const trimmed = stripTrailingSlashes(target.trim());
|
|
6184
|
+
const base = basename(trimmed);
|
|
6185
|
+
if (base === "" || base === "." || base === "..") {
|
|
6186
|
+
return trimmed === "" ? target.trim() : trimmed;
|
|
6187
|
+
}
|
|
6188
|
+
return base;
|
|
6189
|
+
}
|
|
6190
|
+
|
|
6191
|
+
// src/cli/version.ts
|
|
6192
|
+
var VERSION = "1.1.1";
|
|
6193
|
+
|
|
5090
6194
|
// src/cli/write-file.ts
|
|
5091
6195
|
import { writeFile as fsWriteFile } from "fs/promises";
|
|
5092
6196
|
var defaultWriteFile = async (path, content) => {
|
|
@@ -5099,6 +6203,7 @@ async function runPipeline(config, deps = {}) {
|
|
|
5099
6203
|
const narrate = deps.narrate ?? createNarrate();
|
|
5100
6204
|
const preflight = deps.preflight ?? preflightProvider;
|
|
5101
6205
|
const ui2 = deps.ui ?? ui;
|
|
6206
|
+
const progress = deps.progress ?? noopProgress;
|
|
5102
6207
|
const writeStdout2 = deps.writeStdout ?? ((text) => {
|
|
5103
6208
|
process.stdout.write(text);
|
|
5104
6209
|
});
|
|
@@ -5116,7 +6221,8 @@ async function runPipeline(config, deps = {}) {
|
|
|
5116
6221
|
if (config.aiMode === "off" && config.provenance.aiMode === "default") {
|
|
5117
6222
|
ui2.info("Running metrics-only \u2014 for the AI narrative, run interactively or set a provider key.");
|
|
5118
6223
|
}
|
|
5119
|
-
const history = await retrieve(config);
|
|
6224
|
+
const history = await stage(progress, "Retrieving commit history\u2026", () => retrieve(config));
|
|
6225
|
+
progress.done(`Retrieved ${history.commits.length} commit(s) from ${history.repoTarget}`);
|
|
5120
6226
|
ui2.debug?.(`Retrieved ${history.commits.length} commit(s) from ${history.repoTarget}.`);
|
|
5121
6227
|
const selection = selectCommitsWithNotice(history, projectSelection(config));
|
|
5122
6228
|
if (selection.truncation !== void 0) {
|
|
@@ -5129,17 +6235,60 @@ async function runPipeline(config, deps = {}) {
|
|
|
5129
6235
|
mailmap: emptyMailmap()
|
|
5130
6236
|
// real .mailmap ingestion is deferred
|
|
5131
6237
|
};
|
|
5132
|
-
const analysis = analyze(selection.history, ctx);
|
|
6238
|
+
const analysis = await stage(progress, "Computing metrics\u2026", () => analyze(selection.history, ctx));
|
|
6239
|
+
progress.done(`Computed metrics over ${selection.history.commits.length} commit(s)`);
|
|
5133
6240
|
ui2.debug?.(`Analyzed ${selection.history.commits.length} commit(s); aiMode=${config.aiMode}.`);
|
|
5134
|
-
const outcome = await
|
|
5135
|
-
|
|
6241
|
+
const outcome = await narrateStage(
|
|
6242
|
+
progress,
|
|
6243
|
+
config,
|
|
6244
|
+
() => narrateOutcome(config, narrateConfig, analysis, narrate, preflightReason)
|
|
6245
|
+
);
|
|
6246
|
+
const provenance = buildProvenance({
|
|
6247
|
+
target: config.repoTarget,
|
|
6248
|
+
branch: config.branch,
|
|
6249
|
+
totalCommits: history.commits.length,
|
|
6250
|
+
analyzedCommits: selection.history.commits.length,
|
|
6251
|
+
contributors: countContributors(selection.history.commits, ctx.mailmap),
|
|
6252
|
+
provider: config.provider,
|
|
6253
|
+
model: config.llmModel,
|
|
6254
|
+
generatedAt: config.analysisTimestamp,
|
|
6255
|
+
toolVersion: VERSION,
|
|
6256
|
+
tier: config.entitlement.tier,
|
|
6257
|
+
commitCap: config.entitlement.commitCap
|
|
6258
|
+
});
|
|
6259
|
+
const report = reportFromOutcome(analysis, outcome, provenance);
|
|
5136
6260
|
const targets = planOutputs(config.outputFormats, config.outputPath);
|
|
6261
|
+
progress.start("Rendering report\u2026");
|
|
6262
|
+
progress.done("Report ready");
|
|
5137
6263
|
const htmlFilePath = await emitOutputs(report, targets, { writeStdout: writeStdout2, writeFile: writeFile3, ui: ui2 });
|
|
5138
6264
|
if (autoOpen && htmlFilePath !== void 0) {
|
|
5139
6265
|
await tryOpen(openBrowser, htmlFilePath, ui2);
|
|
5140
6266
|
}
|
|
5141
6267
|
return report.degraded ? ExitCode.Degraded : ExitCode.Success;
|
|
5142
6268
|
}
|
|
6269
|
+
async function stage(progress, label, fn) {
|
|
6270
|
+
progress.start(label);
|
|
6271
|
+
try {
|
|
6272
|
+
return await fn();
|
|
6273
|
+
} catch (err) {
|
|
6274
|
+
progress.fail();
|
|
6275
|
+
throw err;
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
async function narrateStage(progress, config, run) {
|
|
6279
|
+
if (config.aiMode === "off") {
|
|
6280
|
+
return run();
|
|
6281
|
+
}
|
|
6282
|
+
progress.start("Generating AI narrative\u2026");
|
|
6283
|
+
try {
|
|
6284
|
+
const outcome = await run();
|
|
6285
|
+
progress.done(outcome.kind === "narrated" ? "AI narrative ready" : "Narrative unavailable \u2014 metrics-only");
|
|
6286
|
+
return outcome;
|
|
6287
|
+
} catch (err) {
|
|
6288
|
+
progress.fail("AI narrative failed");
|
|
6289
|
+
throw err;
|
|
6290
|
+
}
|
|
6291
|
+
}
|
|
5143
6292
|
async function emitOutputs(report, targets, io) {
|
|
5144
6293
|
let htmlFilePath;
|
|
5145
6294
|
for (const target of targets) {
|
|
@@ -5208,6 +6357,14 @@ async function narrateOutcome(config, narrateConfig, analysis, narrate, prefligh
|
|
|
5208
6357
|
}
|
|
5209
6358
|
return narrate(analysis, narrateConfig);
|
|
5210
6359
|
}
|
|
6360
|
+
function countContributors(commits, mailmap) {
|
|
6361
|
+
const keys = /* @__PURE__ */ new Set();
|
|
6362
|
+
for (const commit of commits) {
|
|
6363
|
+
const identity = canonicalizeIdentity(commit.author, mailmap);
|
|
6364
|
+
keys.add(`${identity.email}\0${identity.name}`);
|
|
6365
|
+
}
|
|
6366
|
+
return keys.size;
|
|
6367
|
+
}
|
|
5211
6368
|
|
|
5212
6369
|
// src/cli/show-config.ts
|
|
5213
6370
|
var UNSET = "(unset)";
|
|
@@ -5263,9 +6420,6 @@ function formatShowConfig(config, secrets) {
|
|
|
5263
6420
|
return lines.join("\n");
|
|
5264
6421
|
}
|
|
5265
6422
|
|
|
5266
|
-
// src/cli/version.ts
|
|
5267
|
-
var VERSION = "1.0.8";
|
|
5268
|
-
|
|
5269
6423
|
// src/cli/cli.ts
|
|
5270
6424
|
var PROVIDERS3 = ["ollama", "openai", "gemini", "anthropic", "openai-compatible"];
|
|
5271
6425
|
var OUTPUT_FORMATS3 = ["terminal", "html", "markdown", "json"];
|
|
@@ -5314,7 +6468,7 @@ async function main(argv, deps = {}) {
|
|
|
5314
6468
|
});
|
|
5315
6469
|
try {
|
|
5316
6470
|
if (argv.length === 0) {
|
|
5317
|
-
return await runZeroArg({ deps, ui: ui2, env, cwd, stdinIsTTY, stdoutIsTTY });
|
|
6471
|
+
return await runZeroArg({ deps, ui: ui2, env, cwd, stdinIsTTY, stdoutIsTTY, stderrIsTTY });
|
|
5318
6472
|
}
|
|
5319
6473
|
const program = buildProgram();
|
|
5320
6474
|
try {
|
|
@@ -5350,10 +6504,10 @@ async function main(argv, deps = {}) {
|
|
|
5350
6504
|
return ExitCode.Success;
|
|
5351
6505
|
}
|
|
5352
6506
|
const entitlement = entitlementForHeadlessRun(resolution);
|
|
5353
|
-
const
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
6507
|
+
const runLevel = resolveLogLevel({ verbose: opts.verbose, quiet: opts.quiet, env });
|
|
6508
|
+
const runColor = resolveColor({ env, isTTY: Boolean(stderrIsTTY) });
|
|
6509
|
+
const runUi = deps.ui ?? createUi(process.stderr, { level: runLevel, color: runColor });
|
|
6510
|
+
const progress = makeProgress(deps, stderrIsTTY, runLevel, runColor);
|
|
5357
6511
|
const config = resolveRunConfig({
|
|
5358
6512
|
cwd,
|
|
5359
6513
|
env,
|
|
@@ -5376,7 +6530,8 @@ async function main(argv, deps = {}) {
|
|
|
5376
6530
|
openAllowed: opts.open !== false,
|
|
5377
6531
|
// honoured only when interactive (false here)
|
|
5378
6532
|
deps,
|
|
5379
|
-
ui: runUi
|
|
6533
|
+
ui: runUi,
|
|
6534
|
+
progress
|
|
5380
6535
|
});
|
|
5381
6536
|
} catch (err) {
|
|
5382
6537
|
ui2.error(messageForError(err));
|
|
@@ -5413,7 +6568,8 @@ async function resolveAndRun(input) {
|
|
|
5413
6568
|
nonInteractive: input.nonInteractive,
|
|
5414
6569
|
openAllowed: input.openAllowed,
|
|
5415
6570
|
deps: input.deps,
|
|
5416
|
-
ui: input.ui
|
|
6571
|
+
ui: input.ui,
|
|
6572
|
+
progress: input.progress
|
|
5417
6573
|
});
|
|
5418
6574
|
}
|
|
5419
6575
|
async function runResolved(input) {
|
|
@@ -5431,11 +6587,21 @@ async function runResolved(input) {
|
|
|
5431
6587
|
aiKey,
|
|
5432
6588
|
gitToken,
|
|
5433
6589
|
ui: input.ui,
|
|
6590
|
+
progress: input.progress,
|
|
5434
6591
|
writeStdout: input.deps.writeStdout,
|
|
5435
6592
|
autoOpen,
|
|
5436
6593
|
...input.deps.runDeps
|
|
5437
6594
|
});
|
|
5438
6595
|
}
|
|
6596
|
+
function makeProgress(deps, stderrIsTTY, level, color) {
|
|
6597
|
+
if (deps.progress !== void 0) {
|
|
6598
|
+
return deps.progress;
|
|
6599
|
+
}
|
|
6600
|
+
if (stderrIsTTY !== true) {
|
|
6601
|
+
return noopProgress;
|
|
6602
|
+
}
|
|
6603
|
+
return createProgress(process.stderr, { tty: true, level, color });
|
|
6604
|
+
}
|
|
5439
6605
|
async function runZeroArg(ctx) {
|
|
5440
6606
|
const { interactive } = detectCapability({
|
|
5441
6607
|
nonInteractive: false,
|
|
@@ -5473,6 +6639,7 @@ async function runZeroArg(ctx) {
|
|
|
5473
6639
|
};
|
|
5474
6640
|
const runAnalysis = async (flags) => {
|
|
5475
6641
|
try {
|
|
6642
|
+
const liveConfigFile = ctx.deps.configFile ?? await readSettings(ctx.env);
|
|
5476
6643
|
return await resolveAndRun({
|
|
5477
6644
|
flags,
|
|
5478
6645
|
env: ctx.env,
|
|
@@ -5482,24 +6649,67 @@ async function runZeroArg(ctx) {
|
|
|
5482
6649
|
analysisTimestamp: ctx.deps.analysisTimestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
5483
6650
|
nonInteractive: false,
|
|
5484
6651
|
openAllowed: true,
|
|
5485
|
-
configFile,
|
|
6652
|
+
configFile: liveConfigFile,
|
|
5486
6653
|
entitlement,
|
|
5487
6654
|
deps: ctx.deps,
|
|
5488
|
-
ui: ctx.ui
|
|
6655
|
+
ui: ctx.ui,
|
|
6656
|
+
progress: makeProgress(
|
|
6657
|
+
ctx.deps,
|
|
6658
|
+
ctx.stderrIsTTY,
|
|
6659
|
+
resolveLogLevel({ env: ctx.env }),
|
|
6660
|
+
resolveColor({ env: ctx.env, isTTY: Boolean(ctx.stderrIsTTY) })
|
|
6661
|
+
)
|
|
5489
6662
|
});
|
|
5490
6663
|
} catch (err) {
|
|
5491
6664
|
ctx.ui.error(messageForError(err));
|
|
5492
6665
|
return exitCodeForError(err);
|
|
5493
6666
|
}
|
|
5494
6667
|
};
|
|
6668
|
+
const resolveLiveAi = async () => {
|
|
6669
|
+
const liveConfigFile = ctx.deps.configFile ?? await readSettings(ctx.env);
|
|
6670
|
+
const liveEnv = readEnvLayer(ctx.env);
|
|
6671
|
+
return {
|
|
6672
|
+
configFile: liveConfigFile,
|
|
6673
|
+
envLayer: liveEnv,
|
|
6674
|
+
provider: liveEnv.provider ?? liveConfigFile.provider,
|
|
6675
|
+
llmModel: liveEnv.llmModel ?? liveConfigFile.llmModel,
|
|
6676
|
+
llmBaseUrl: liveEnv.llmBaseUrl ?? liveConfigFile.llmBaseUrl
|
|
6677
|
+
};
|
|
6678
|
+
};
|
|
6679
|
+
const buildDoctorConfig = (envLayerNow, configFileNow) => ({
|
|
6680
|
+
branch: envLayerNow.branch,
|
|
6681
|
+
authorFilter: envLayerNow.authorFilter,
|
|
6682
|
+
startDate: envLayerNow.startDate,
|
|
6683
|
+
endDate: envLayerNow.endDate,
|
|
6684
|
+
timezone: envLayerNow.timezone ?? configFileNow.timezone,
|
|
6685
|
+
noMerges: envLayerNow.noMerges,
|
|
6686
|
+
outputFormats: envLayerNow.outputFormats ?? configFileNow.outputFormats,
|
|
6687
|
+
outputPath: envLayerNow.outputPath,
|
|
6688
|
+
maxCommits: envLayerNow.maxCommits ?? configFileNow.maxCommits,
|
|
6689
|
+
aiMode: envLayerNow.aiMode,
|
|
6690
|
+
llmBaseUrl: envLayerNow.llmBaseUrl ?? configFileNow.llmBaseUrl,
|
|
6691
|
+
logLevel: resolveLogLevel({ env: ctx.env }),
|
|
6692
|
+
color: resolveColor({ env: ctx.env, isTTY: Boolean(ctx.stdoutIsTTY) })
|
|
6693
|
+
});
|
|
5495
6694
|
const envDiagnostics = readEnvDiagnostics(ctx.env, aiLayer.provider);
|
|
6695
|
+
const doctorConfig = buildDoctorConfig(envLayer, configFile);
|
|
6696
|
+
const refreshDoctor = async () => {
|
|
6697
|
+
const live = await resolveLiveAi();
|
|
6698
|
+
return {
|
|
6699
|
+
provider: live.provider,
|
|
6700
|
+
llmModel: live.llmModel,
|
|
6701
|
+
envDiagnostics: readEnvDiagnostics(ctx.env, live.provider),
|
|
6702
|
+
doctorConfig: buildDoctorConfig(live.envLayer, live.configFile)
|
|
6703
|
+
};
|
|
6704
|
+
};
|
|
5496
6705
|
const probeReachability = async () => {
|
|
6706
|
+
const live = await resolveLiveAi();
|
|
5497
6707
|
const narrateConfig = {
|
|
5498
6708
|
aiMode: "auto",
|
|
5499
|
-
provider:
|
|
5500
|
-
llmModel:
|
|
5501
|
-
llmBaseUrl:
|
|
5502
|
-
aiKey: readAiKey(ctx.env,
|
|
6709
|
+
provider: live.provider,
|
|
6710
|
+
llmModel: live.llmModel,
|
|
6711
|
+
llmBaseUrl: live.llmBaseUrl,
|
|
6712
|
+
aiKey: readAiKey(ctx.env, live.provider)
|
|
5503
6713
|
};
|
|
5504
6714
|
const result = await (ctx.deps.preflight ?? preflightProvider)(narrateConfig, {});
|
|
5505
6715
|
return result.reachable ? { kind: "reachable" } : { kind: "unreachable", reason: result.reason };
|
|
@@ -5512,8 +6722,21 @@ async function runZeroArg(ctx) {
|
|
|
5512
6722
|
gitTokenConfigured: readGitToken(ctx.env) !== void 0,
|
|
5513
6723
|
envDiagnostics,
|
|
5514
6724
|
probeReachability,
|
|
6725
|
+
doctorConfig,
|
|
6726
|
+
refreshDoctor,
|
|
5515
6727
|
loadSettings: () => readSettings(ctx.env),
|
|
5516
6728
|
saveSettings: (data) => writeSettings(ctx.env, data),
|
|
6729
|
+
// Re-resolve the live AI provider/model after a Settings save (config < env,
|
|
6730
|
+
// mirroring the resolver) so a mid-session change cures the no-AI state and
|
|
6731
|
+
// refreshes the header without restarting the launchpad.
|
|
6732
|
+
reloadAiState: async () => {
|
|
6733
|
+
const liveConfigFile = ctx.deps.configFile ?? await readSettings(ctx.env);
|
|
6734
|
+
const liveEnv = readEnvLayer(ctx.env);
|
|
6735
|
+
return {
|
|
6736
|
+
provider: liveEnv.provider ?? liveConfigFile.provider,
|
|
6737
|
+
llmModel: liveEnv.llmModel ?? liveConfigFile.llmModel
|
|
6738
|
+
};
|
|
6739
|
+
},
|
|
5517
6740
|
// License actions (Story 7.2): the only in-app key entry + the browser hand-offs.
|
|
5518
6741
|
activateLicense: (key) => (ctx.deps.activateLicense ?? defaultActivateLicense)(ctx.env, key),
|
|
5519
6742
|
deactivateLicense: () => (ctx.deps.deactivateLicense ?? defaultDeactivateLicense)(ctx.env),
|