@getcodesentinel/codesentinel 1.18.0 → 1.19.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 +329 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2809,10 +2809,10 @@ var runGit = async (repositoryPath, args) => {
|
|
|
2809
2809
|
};
|
|
2810
2810
|
var tryRunGit = async (repositoryPath, args) => {
|
|
2811
2811
|
try {
|
|
2812
|
-
const { stdout } = await execFileAsync("git", ["-C", repositoryPath, ...args], {
|
|
2812
|
+
const { stdout: stdout2 } = await execFileAsync("git", ["-C", repositoryPath, ...args], {
|
|
2813
2813
|
encoding: "utf8"
|
|
2814
2814
|
});
|
|
2815
|
-
return { ok: true, stdout:
|
|
2815
|
+
return { ok: true, stdout: stdout2.trim() };
|
|
2816
2816
|
} catch (error) {
|
|
2817
2817
|
const message = error instanceof Error ? error.message : "unknown git error";
|
|
2818
2818
|
return { ok: false, message };
|
|
@@ -3336,6 +3336,12 @@ var ANSI = {
|
|
|
3336
3336
|
green: "\x1B[32m",
|
|
3337
3337
|
yellow: "\x1B[33m"
|
|
3338
3338
|
};
|
|
3339
|
+
var hideCursor = () => {
|
|
3340
|
+
stderr.write("\x1B[?25l");
|
|
3341
|
+
};
|
|
3342
|
+
var showCursor = () => {
|
|
3343
|
+
stderr.write("\x1B[?25h");
|
|
3344
|
+
};
|
|
3339
3345
|
var parsePrereleaseIdentifier = (identifier) => {
|
|
3340
3346
|
if (/^\d+$/.test(identifier)) {
|
|
3341
3347
|
return Number.parseInt(identifier, 10);
|
|
@@ -3539,7 +3545,7 @@ var renderUpdatePrompt = (packageName, latestVersion, currentVersion, selectedIn
|
|
|
3539
3545
|
return `${prefix} ${text}`;
|
|
3540
3546
|
}),
|
|
3541
3547
|
"",
|
|
3542
|
-
` ${ANSI.dim}Use \u2191/\u2193 to choose. Press enter to continue
|
|
3548
|
+
` ${ANSI.dim}Use \u2191/\u2193 to choose. Press enter to continue. Press q or Ctrl+C to exit.${ANSI.reset}`
|
|
3543
3549
|
];
|
|
3544
3550
|
stderr.write(lines.join("\n"));
|
|
3545
3551
|
return lines.length;
|
|
@@ -3571,6 +3577,7 @@ var promptInstall = async (packageName, latestVersion, currentVersion) => {
|
|
|
3571
3577
|
stdin.setRawMode(previousRawMode);
|
|
3572
3578
|
}
|
|
3573
3579
|
clearPromptArea();
|
|
3580
|
+
showCursor();
|
|
3574
3581
|
if (choice === "install") {
|
|
3575
3582
|
stderr.write(`${ANSI.yellow}${renderUpdateInProgressMessage(packageName)}${ANSI.reset}`);
|
|
3576
3583
|
} else {
|
|
@@ -3580,7 +3587,11 @@ var promptInstall = async (packageName, latestVersion, currentVersion) => {
|
|
|
3580
3587
|
};
|
|
3581
3588
|
const onKeypress = (_str, key) => {
|
|
3582
3589
|
if (key.ctrl === true && key.name === "c") {
|
|
3583
|
-
cleanup("
|
|
3590
|
+
cleanup("skip");
|
|
3591
|
+
return;
|
|
3592
|
+
}
|
|
3593
|
+
if (key.name === "q") {
|
|
3594
|
+
cleanup("skip");
|
|
3584
3595
|
return;
|
|
3585
3596
|
}
|
|
3586
3597
|
if (key.name === "up") {
|
|
@@ -3599,6 +3610,7 @@ var promptInstall = async (packageName, latestVersion, currentVersion) => {
|
|
|
3599
3610
|
};
|
|
3600
3611
|
stdin.on("keypress", onKeypress);
|
|
3601
3612
|
if (typeof stdin.setRawMode === "function") {
|
|
3613
|
+
hideCursor();
|
|
3602
3614
|
stdin.setRawMode(true);
|
|
3603
3615
|
}
|
|
3604
3616
|
stdin.resume();
|
|
@@ -3625,9 +3637,6 @@ var runManualCliUpdate = async (input) => {
|
|
|
3625
3637
|
return 0;
|
|
3626
3638
|
}
|
|
3627
3639
|
const choice = await promptInstall(input.packageName, latestVersion, input.currentVersion);
|
|
3628
|
-
if (choice === "interrupt") {
|
|
3629
|
-
return 130;
|
|
3630
|
-
}
|
|
3631
3640
|
if (choice !== "install") {
|
|
3632
3641
|
return 0;
|
|
3633
3642
|
}
|
|
@@ -3665,9 +3674,6 @@ var checkForCliUpdates = async (input) => {
|
|
|
3665
3674
|
return;
|
|
3666
3675
|
}
|
|
3667
3676
|
const choice = await promptInstall(input.packageName, latestVersion, input.currentVersion);
|
|
3668
|
-
if (choice === "interrupt") {
|
|
3669
|
-
process.exit(130);
|
|
3670
|
-
}
|
|
3671
3677
|
if (choice !== "install") {
|
|
3672
3678
|
return;
|
|
3673
3679
|
}
|
|
@@ -3684,6 +3690,312 @@ var checkForCliUpdates = async (input) => {
|
|
|
3684
3690
|
}
|
|
3685
3691
|
};
|
|
3686
3692
|
|
|
3693
|
+
// src/application/interactive-menu.ts
|
|
3694
|
+
import { spawn as spawn2 } from "child_process";
|
|
3695
|
+
import { stderr as stderr2, stdin as stdin2, stdout } from "process";
|
|
3696
|
+
import { clearScreenDown as clearScreenDown2, cursorTo as cursorTo2, emitKeypressEvents as emitKeypressEvents2, moveCursor } from "readline";
|
|
3697
|
+
import { createInterface as createPromisesInterface } from "readline/promises";
|
|
3698
|
+
var ANSI2 = {
|
|
3699
|
+
reset: "\x1B[0m",
|
|
3700
|
+
bold: "\x1B[1m",
|
|
3701
|
+
dim: "\x1B[2m",
|
|
3702
|
+
cyan: "\x1B[36m",
|
|
3703
|
+
green: "\x1B[32m"
|
|
3704
|
+
};
|
|
3705
|
+
var PROMPT_PADDING = " ";
|
|
3706
|
+
var renderMenu = (currentVersion, actions, selectedIndex) => {
|
|
3707
|
+
const optionLabels = actions.map((action, index) => `${index + 1}. ${action.label}`);
|
|
3708
|
+
const labelWidth = optionLabels.reduce((max, label) => Math.max(max, label.length), 0);
|
|
3709
|
+
const lines = [
|
|
3710
|
+
` ${ANSI2.bold}${ANSI2.cyan}CodeSentinel${ANSI2.reset} ${ANSI2.dim}v${currentVersion}${ANSI2.reset}`,
|
|
3711
|
+
"",
|
|
3712
|
+
" Choose an action:",
|
|
3713
|
+
"",
|
|
3714
|
+
...actions.map((action, index) => {
|
|
3715
|
+
const selected = index === selectedIndex;
|
|
3716
|
+
const prefix = selected ? `${ANSI2.green}>${ANSI2.reset}` : " ";
|
|
3717
|
+
const label = optionLabels[index]?.padEnd(labelWidth, " ") ?? "";
|
|
3718
|
+
const renderedLabel = selected ? `${ANSI2.bold}${label}${ANSI2.reset}` : label;
|
|
3719
|
+
return `${prefix} ${renderedLabel} ${ANSI2.dim}${action.description}${ANSI2.reset}`;
|
|
3720
|
+
}),
|
|
3721
|
+
"",
|
|
3722
|
+
` ${ANSI2.dim}Use \u2191/\u2193 to choose. Press enter to continue. Press q or Ctrl+C to exit.${ANSI2.reset}`
|
|
3723
|
+
];
|
|
3724
|
+
stderr2.write(lines.join("\n"));
|
|
3725
|
+
};
|
|
3726
|
+
var clearTerminal = () => {
|
|
3727
|
+
cursorTo2(stderr2, 0, 0);
|
|
3728
|
+
clearScreenDown2(stderr2);
|
|
3729
|
+
};
|
|
3730
|
+
var hideCursor2 = () => {
|
|
3731
|
+
stderr2.write("\x1B[?25l");
|
|
3732
|
+
};
|
|
3733
|
+
var showCursor2 = () => {
|
|
3734
|
+
stderr2.write("\x1B[?25h");
|
|
3735
|
+
};
|
|
3736
|
+
var pipeWithPadding = (stream, target, padding) => {
|
|
3737
|
+
if (stream === null) {
|
|
3738
|
+
return;
|
|
3739
|
+
}
|
|
3740
|
+
stream.setEncoding("utf8");
|
|
3741
|
+
let buffer = "";
|
|
3742
|
+
let needsPrefix = true;
|
|
3743
|
+
const writeChunk = (chunk) => {
|
|
3744
|
+
let start = 0;
|
|
3745
|
+
while (start < chunk.length) {
|
|
3746
|
+
if (needsPrefix) {
|
|
3747
|
+
target.write(padding);
|
|
3748
|
+
needsPrefix = false;
|
|
3749
|
+
}
|
|
3750
|
+
const newlineIndex = chunk.indexOf("\n", start);
|
|
3751
|
+
if (newlineIndex === -1) {
|
|
3752
|
+
target.write(chunk.slice(start));
|
|
3753
|
+
return;
|
|
3754
|
+
}
|
|
3755
|
+
target.write(chunk.slice(start, newlineIndex + 1));
|
|
3756
|
+
needsPrefix = true;
|
|
3757
|
+
start = newlineIndex + 1;
|
|
3758
|
+
}
|
|
3759
|
+
};
|
|
3760
|
+
stream.on("data", (chunk) => {
|
|
3761
|
+
buffer += chunk;
|
|
3762
|
+
const lastNewlineIndex = buffer.lastIndexOf("\n");
|
|
3763
|
+
if (lastNewlineIndex === -1) {
|
|
3764
|
+
return;
|
|
3765
|
+
}
|
|
3766
|
+
writeChunk(buffer.slice(0, lastNewlineIndex + 1));
|
|
3767
|
+
buffer = buffer.slice(lastNewlineIndex + 1);
|
|
3768
|
+
});
|
|
3769
|
+
stream.on("end", () => {
|
|
3770
|
+
if (buffer.length > 0) {
|
|
3771
|
+
writeChunk(buffer);
|
|
3772
|
+
buffer = "";
|
|
3773
|
+
}
|
|
3774
|
+
});
|
|
3775
|
+
};
|
|
3776
|
+
var promptSelection = async (currentVersion, actions) => {
|
|
3777
|
+
if (!stdin2.isTTY || !stderr2.isTTY || typeof stdin2.setRawMode !== "function") {
|
|
3778
|
+
return "exit";
|
|
3779
|
+
}
|
|
3780
|
+
return await new Promise((resolve6) => {
|
|
3781
|
+
emitKeypressEvents2(stdin2);
|
|
3782
|
+
let selectedIndex = 0;
|
|
3783
|
+
const previousRawMode = stdin2.isRaw;
|
|
3784
|
+
const redraw = () => {
|
|
3785
|
+
clearTerminal();
|
|
3786
|
+
renderMenu(currentVersion, actions, selectedIndex);
|
|
3787
|
+
moveCursor(stderr2, -1, 0);
|
|
3788
|
+
};
|
|
3789
|
+
const cleanup = (selection) => {
|
|
3790
|
+
stdin2.off("keypress", onKeypress);
|
|
3791
|
+
stdin2.pause();
|
|
3792
|
+
stdin2.setRawMode(previousRawMode);
|
|
3793
|
+
clearTerminal();
|
|
3794
|
+
showCursor2();
|
|
3795
|
+
resolve6(selection);
|
|
3796
|
+
};
|
|
3797
|
+
const onKeypress = (_str, key) => {
|
|
3798
|
+
if (key.ctrl === true && key.name === "c") {
|
|
3799
|
+
cleanup("exit");
|
|
3800
|
+
return;
|
|
3801
|
+
}
|
|
3802
|
+
if (key.name === "q") {
|
|
3803
|
+
cleanup("exit");
|
|
3804
|
+
return;
|
|
3805
|
+
}
|
|
3806
|
+
if (key.name === "up") {
|
|
3807
|
+
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : actions.length - 1;
|
|
3808
|
+
redraw();
|
|
3809
|
+
return;
|
|
3810
|
+
}
|
|
3811
|
+
if (key.name === "down") {
|
|
3812
|
+
selectedIndex = selectedIndex < actions.length - 1 ? selectedIndex + 1 : 0;
|
|
3813
|
+
redraw();
|
|
3814
|
+
return;
|
|
3815
|
+
}
|
|
3816
|
+
if (key.name === "return" || key.name === "enter") {
|
|
3817
|
+
cleanup(selectedIndex);
|
|
3818
|
+
}
|
|
3819
|
+
};
|
|
3820
|
+
stdin2.on("keypress", onKeypress);
|
|
3821
|
+
hideCursor2();
|
|
3822
|
+
stdin2.setRawMode(true);
|
|
3823
|
+
stdin2.resume();
|
|
3824
|
+
redraw();
|
|
3825
|
+
});
|
|
3826
|
+
};
|
|
3827
|
+
var createPrompt = () => createPromisesInterface({
|
|
3828
|
+
input: stdin2,
|
|
3829
|
+
output: stderr2
|
|
3830
|
+
});
|
|
3831
|
+
var promptText = async (prompt, label, defaultValue) => {
|
|
3832
|
+
const suffix = defaultValue === void 0 || defaultValue.length === 0 ? "" : ` [${defaultValue}]`;
|
|
3833
|
+
let answer;
|
|
3834
|
+
try {
|
|
3835
|
+
answer = await prompt.question(
|
|
3836
|
+
`${PROMPT_PADDING}${label}${suffix}: `
|
|
3837
|
+
);
|
|
3838
|
+
} catch (error) {
|
|
3839
|
+
if (error instanceof Error && "code" in error && error.code === "ABORT_ERR") {
|
|
3840
|
+
stderr2.write("\n");
|
|
3841
|
+
return null;
|
|
3842
|
+
}
|
|
3843
|
+
throw error;
|
|
3844
|
+
}
|
|
3845
|
+
const trimmed = answer.trim();
|
|
3846
|
+
return trimmed.length > 0 ? trimmed : defaultValue ?? "";
|
|
3847
|
+
};
|
|
3848
|
+
var renderDependencyRiskPrompt = (errorMessage) => {
|
|
3849
|
+
clearTerminal();
|
|
3850
|
+
stderr2.write(`${PROMPT_PADDING}${ANSI2.bold}Scan dependency risk${ANSI2.reset}
|
|
3851
|
+
`);
|
|
3852
|
+
if (errorMessage !== void 0) {
|
|
3853
|
+
stderr2.write(`
|
|
3854
|
+
${PROMPT_PADDING}${errorMessage}
|
|
3855
|
+
`);
|
|
3856
|
+
}
|
|
3857
|
+
stderr2.write("\n");
|
|
3858
|
+
};
|
|
3859
|
+
var buildDependencyRiskArgs = async () => {
|
|
3860
|
+
const prompt = createPrompt();
|
|
3861
|
+
try {
|
|
3862
|
+
let errorMessage;
|
|
3863
|
+
while (true) {
|
|
3864
|
+
renderDependencyRiskPrompt(errorMessage);
|
|
3865
|
+
const dependency = await promptText(prompt, "Package name");
|
|
3866
|
+
if (dependency === null) {
|
|
3867
|
+
return { kind: "cancel" };
|
|
3868
|
+
}
|
|
3869
|
+
if (dependency.length === 0) {
|
|
3870
|
+
errorMessage = "A package name is required.";
|
|
3871
|
+
continue;
|
|
3872
|
+
}
|
|
3873
|
+
return {
|
|
3874
|
+
kind: "run",
|
|
3875
|
+
args: ["dependency-risk", dependency]
|
|
3876
|
+
};
|
|
3877
|
+
}
|
|
3878
|
+
} finally {
|
|
3879
|
+
prompt.close();
|
|
3880
|
+
}
|
|
3881
|
+
};
|
|
3882
|
+
var waitForReturnToMenu = async () => {
|
|
3883
|
+
if (!stdin2.isTTY || !stderr2.isTTY || typeof stdin2.setRawMode !== "function") {
|
|
3884
|
+
return;
|
|
3885
|
+
}
|
|
3886
|
+
stderr2.write(`
|
|
3887
|
+
${PROMPT_PADDING}Press enter to return to the menu...`);
|
|
3888
|
+
await new Promise((resolve6) => {
|
|
3889
|
+
emitKeypressEvents2(stdin2);
|
|
3890
|
+
const previousRawMode = stdin2.isRaw;
|
|
3891
|
+
const cleanup = () => {
|
|
3892
|
+
stdin2.off("keypress", onKeypress);
|
|
3893
|
+
stdin2.pause();
|
|
3894
|
+
stdin2.setRawMode(previousRawMode);
|
|
3895
|
+
showCursor2();
|
|
3896
|
+
stderr2.write("\n");
|
|
3897
|
+
resolve6();
|
|
3898
|
+
};
|
|
3899
|
+
const onKeypress = (_str, key) => {
|
|
3900
|
+
if (key.ctrl === true && key.name === "c") {
|
|
3901
|
+
cleanup();
|
|
3902
|
+
return;
|
|
3903
|
+
}
|
|
3904
|
+
if (key.name === "return" || key.name === "enter") {
|
|
3905
|
+
cleanup();
|
|
3906
|
+
}
|
|
3907
|
+
};
|
|
3908
|
+
hideCursor2();
|
|
3909
|
+
stdin2.on("keypress", onKeypress);
|
|
3910
|
+
stdin2.setRawMode(true);
|
|
3911
|
+
stdin2.resume();
|
|
3912
|
+
});
|
|
3913
|
+
};
|
|
3914
|
+
var runCliCommand = async (scriptPath2, args) => {
|
|
3915
|
+
return await new Promise((resolve6, reject) => {
|
|
3916
|
+
const child = spawn2(process.execPath, [...process.execArgv, scriptPath2, ...args], {
|
|
3917
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
3918
|
+
env: {
|
|
3919
|
+
...process.env,
|
|
3920
|
+
CODESENTINEL_NO_UPDATE_NOTIFIER: "1"
|
|
3921
|
+
}
|
|
3922
|
+
});
|
|
3923
|
+
pipeWithPadding(child.stdout, stdout, PROMPT_PADDING);
|
|
3924
|
+
pipeWithPadding(child.stderr, stderr2, PROMPT_PADDING);
|
|
3925
|
+
child.on("error", (error) => {
|
|
3926
|
+
reject(error);
|
|
3927
|
+
});
|
|
3928
|
+
child.on("close", (code) => {
|
|
3929
|
+
resolve6(code ?? 1);
|
|
3930
|
+
});
|
|
3931
|
+
});
|
|
3932
|
+
};
|
|
3933
|
+
var runInteractiveCliMenu = async (input) => {
|
|
3934
|
+
if (!stdin2.isTTY || !stderr2.isTTY || !stdout.isTTY) {
|
|
3935
|
+
stderr2.write(`${PROMPT_PADDING}Interactive menu requires a TTY.
|
|
3936
|
+
`);
|
|
3937
|
+
return 1;
|
|
3938
|
+
}
|
|
3939
|
+
const actions = [
|
|
3940
|
+
{
|
|
3941
|
+
label: "Run overview",
|
|
3942
|
+
description: "combined analyze + explain + report",
|
|
3943
|
+
commandBuilder: () => ({ kind: "run", args: ["run"] })
|
|
3944
|
+
},
|
|
3945
|
+
{
|
|
3946
|
+
label: "Analyze repository",
|
|
3947
|
+
description: "structural and health scoring summary",
|
|
3948
|
+
commandBuilder: () => ({ kind: "run", args: ["analyze"] })
|
|
3949
|
+
},
|
|
3950
|
+
{
|
|
3951
|
+
label: "Explain hotspots",
|
|
3952
|
+
description: "top findings in markdown by default",
|
|
3953
|
+
commandBuilder: () => ({ kind: "run", args: ["explain", "--format", "md"] })
|
|
3954
|
+
},
|
|
3955
|
+
{
|
|
3956
|
+
label: "Generate report",
|
|
3957
|
+
description: "create a full report for a repository",
|
|
3958
|
+
commandBuilder: () => ({ kind: "run", args: ["report", "--format", "md"] })
|
|
3959
|
+
},
|
|
3960
|
+
{
|
|
3961
|
+
label: "Run policy check",
|
|
3962
|
+
description: "execute governance gates",
|
|
3963
|
+
commandBuilder: () => ({ kind: "run", args: ["check"] })
|
|
3964
|
+
},
|
|
3965
|
+
{
|
|
3966
|
+
label: "Scan dependency risk",
|
|
3967
|
+
description: "inspect a package from the registry",
|
|
3968
|
+
commandBuilder: buildDependencyRiskArgs
|
|
3969
|
+
}
|
|
3970
|
+
];
|
|
3971
|
+
while (true) {
|
|
3972
|
+
const selectedIndex = await promptSelection(input.currentVersion, actions);
|
|
3973
|
+
if (selectedIndex === "exit") {
|
|
3974
|
+
stderr2.write("\n");
|
|
3975
|
+
return 0;
|
|
3976
|
+
}
|
|
3977
|
+
const selectedAction = actions[selectedIndex];
|
|
3978
|
+
if (selectedAction === void 0) {
|
|
3979
|
+
stderr2.write(`
|
|
3980
|
+
${PROMPT_PADDING}`);
|
|
3981
|
+
return 1;
|
|
3982
|
+
}
|
|
3983
|
+
const actionResult = await selectedAction.commandBuilder();
|
|
3984
|
+
if (actionResult.kind === "cancel") {
|
|
3985
|
+
continue;
|
|
3986
|
+
}
|
|
3987
|
+
const exitCode = await runCliCommand(input.scriptPath, actionResult.args);
|
|
3988
|
+
if (exitCode !== 0) {
|
|
3989
|
+
stderr2.write(`
|
|
3990
|
+
${PROMPT_PADDING}Command exited with code ${exitCode}.
|
|
3991
|
+
`);
|
|
3992
|
+
} else {
|
|
3993
|
+
stderr2.write("\n");
|
|
3994
|
+
}
|
|
3995
|
+
await waitForReturnToMenu();
|
|
3996
|
+
}
|
|
3997
|
+
};
|
|
3998
|
+
|
|
3687
3999
|
// src/application/run-analyze-command.ts
|
|
3688
4000
|
import { resolve as resolve3 } from "path";
|
|
3689
4001
|
|
|
@@ -8052,14 +8364,17 @@ program.command("ci").argument("[path]", "path to the project to analyze").addOp
|
|
|
8052
8364
|
}
|
|
8053
8365
|
}
|
|
8054
8366
|
);
|
|
8055
|
-
if (process.argv.length <= 2) {
|
|
8056
|
-
program.outputHelp();
|
|
8057
|
-
process.exit(0);
|
|
8058
|
-
}
|
|
8059
8367
|
var executablePath = process.argv[0] ?? "";
|
|
8060
8368
|
var scriptPath = process.argv[1] ?? "";
|
|
8061
8369
|
var argv = process.argv[2] === "--" ? [executablePath, scriptPath, ...process.argv.slice(3)] : process.argv;
|
|
8062
8370
|
if (argv.length <= 2) {
|
|
8371
|
+
if (process.stdin.isTTY && process.stdout.isTTY && process.stderr.isTTY) {
|
|
8372
|
+
process.exitCode = await runInteractiveCliMenu({
|
|
8373
|
+
currentVersion: version,
|
|
8374
|
+
scriptPath
|
|
8375
|
+
});
|
|
8376
|
+
process.exit(process.exitCode ?? 0);
|
|
8377
|
+
}
|
|
8063
8378
|
program.outputHelp();
|
|
8064
8379
|
process.exit(0);
|
|
8065
8380
|
}
|