@getcodesentinel/codesentinel 1.18.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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: stdout.trim() };
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${ANSI.reset}`
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("interrupt");
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,217 @@ 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 renderMenu = (currentVersion, actions, selectedIndex) => {
3706
+ const optionLabels = actions.map((action, index) => `${index + 1}. ${action.label}`);
3707
+ const labelWidth = optionLabels.reduce((max, label) => Math.max(max, label.length), 0);
3708
+ const lines = [
3709
+ ` ${ANSI2.bold}${ANSI2.cyan}CodeSentinel${ANSI2.reset} ${ANSI2.dim}v${currentVersion}${ANSI2.reset}`,
3710
+ "",
3711
+ " Choose an action:",
3712
+ "",
3713
+ ...actions.map((action, index) => {
3714
+ const selected = index === selectedIndex;
3715
+ const prefix = selected ? `${ANSI2.green}>${ANSI2.reset}` : " ";
3716
+ const label = optionLabels[index]?.padEnd(labelWidth, " ") ?? "";
3717
+ const renderedLabel = selected ? `${ANSI2.bold}${label}${ANSI2.reset}` : label;
3718
+ return `${prefix} ${renderedLabel} ${ANSI2.dim}${action.description}${ANSI2.reset}`;
3719
+ }),
3720
+ "",
3721
+ ` ${ANSI2.dim}Use \u2191/\u2193 to choose. Press enter to continue. Press q or Ctrl+C to exit.${ANSI2.reset}`
3722
+ ];
3723
+ stderr2.write(lines.join("\n"));
3724
+ };
3725
+ var clearTerminal = () => {
3726
+ cursorTo2(stderr2, 0, 0);
3727
+ clearScreenDown2(stderr2);
3728
+ };
3729
+ var hideCursor2 = () => {
3730
+ stderr2.write("\x1B[?25l");
3731
+ };
3732
+ var showCursor2 = () => {
3733
+ stderr2.write("\x1B[?25h");
3734
+ };
3735
+ var promptSelection = async (currentVersion, actions) => {
3736
+ if (!stdin2.isTTY || !stderr2.isTTY || typeof stdin2.setRawMode !== "function") {
3737
+ return "exit";
3738
+ }
3739
+ return await new Promise((resolve6) => {
3740
+ emitKeypressEvents2(stdin2);
3741
+ let selectedIndex = 0;
3742
+ const previousRawMode = stdin2.isRaw;
3743
+ const redraw = () => {
3744
+ clearTerminal();
3745
+ renderMenu(currentVersion, actions, selectedIndex);
3746
+ moveCursor(stderr2, -1, 0);
3747
+ };
3748
+ const cleanup = (selection) => {
3749
+ stdin2.off("keypress", onKeypress);
3750
+ stdin2.pause();
3751
+ stdin2.setRawMode(previousRawMode);
3752
+ clearTerminal();
3753
+ showCursor2();
3754
+ resolve6(selection);
3755
+ };
3756
+ const onKeypress = (_str, key) => {
3757
+ if (key.ctrl === true && key.name === "c") {
3758
+ cleanup("exit");
3759
+ return;
3760
+ }
3761
+ if (key.name === "q") {
3762
+ cleanup("exit");
3763
+ return;
3764
+ }
3765
+ if (key.name === "up") {
3766
+ selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : actions.length - 1;
3767
+ redraw();
3768
+ return;
3769
+ }
3770
+ if (key.name === "down") {
3771
+ selectedIndex = selectedIndex < actions.length - 1 ? selectedIndex + 1 : 0;
3772
+ redraw();
3773
+ return;
3774
+ }
3775
+ if (key.name === "return" || key.name === "enter") {
3776
+ cleanup(selectedIndex);
3777
+ }
3778
+ };
3779
+ stdin2.on("keypress", onKeypress);
3780
+ hideCursor2();
3781
+ stdin2.setRawMode(true);
3782
+ stdin2.resume();
3783
+ redraw();
3784
+ });
3785
+ };
3786
+ var createPrompt = () => createPromisesInterface({
3787
+ input: stdin2,
3788
+ output: stderr2
3789
+ });
3790
+ var promptText = async (prompt, label, defaultValue) => {
3791
+ const suffix = defaultValue === void 0 ? "" : ` [${defaultValue}]`;
3792
+ const answer = await prompt.question(
3793
+ `${label}${suffix}: `
3794
+ );
3795
+ const trimmed = answer.trim();
3796
+ return trimmed.length > 0 ? trimmed : defaultValue ?? "";
3797
+ };
3798
+ var buildDependencyRiskArgs = async () => {
3799
+ const prompt = createPrompt();
3800
+ try {
3801
+ const dependency = await promptText(prompt, "Dependency spec", "");
3802
+ if (dependency.length === 0) {
3803
+ stderr2.write("A dependency spec is required.\n");
3804
+ return null;
3805
+ }
3806
+ return ["dependency-risk", dependency];
3807
+ } finally {
3808
+ prompt.close();
3809
+ }
3810
+ };
3811
+ var waitForReturnToMenu = async () => {
3812
+ const prompt = createPromisesInterface({
3813
+ input: stdin2,
3814
+ output: stderr2
3815
+ });
3816
+ try {
3817
+ await prompt.question("Press enter to return to the menu...");
3818
+ } finally {
3819
+ prompt.close();
3820
+ }
3821
+ };
3822
+ var runCliCommand = async (scriptPath2, args) => {
3823
+ return await new Promise((resolve6, reject) => {
3824
+ const child = spawn2(process.execPath, [...process.execArgv, scriptPath2, ...args], {
3825
+ stdio: "inherit",
3826
+ env: {
3827
+ ...process.env,
3828
+ CODESENTINEL_NO_UPDATE_NOTIFIER: "1"
3829
+ }
3830
+ });
3831
+ child.on("error", (error) => {
3832
+ reject(error);
3833
+ });
3834
+ child.on("close", (code) => {
3835
+ resolve6(code ?? 1);
3836
+ });
3837
+ });
3838
+ };
3839
+ var runInteractiveCliMenu = async (input) => {
3840
+ if (!stdin2.isTTY || !stderr2.isTTY || !stdout.isTTY) {
3841
+ stderr2.write("Interactive menu requires a TTY.\n");
3842
+ return 1;
3843
+ }
3844
+ const actions = [
3845
+ {
3846
+ label: "Run overview",
3847
+ description: "combined analyze + explain + report",
3848
+ commandBuilder: () => ["run"]
3849
+ },
3850
+ {
3851
+ label: "Analyze repository",
3852
+ description: "structural and health scoring summary",
3853
+ commandBuilder: () => ["analyze"]
3854
+ },
3855
+ {
3856
+ label: "Explain hotspots",
3857
+ description: "top findings in markdown by default",
3858
+ commandBuilder: () => ["explain", "--format", "md"]
3859
+ },
3860
+ {
3861
+ label: "Generate report",
3862
+ description: "create a full report for a repository",
3863
+ commandBuilder: () => ["report", "--format", "md"]
3864
+ },
3865
+ {
3866
+ label: "Run policy check",
3867
+ description: "execute governance gates",
3868
+ commandBuilder: () => ["check"]
3869
+ },
3870
+ {
3871
+ label: "Scan dependency risk",
3872
+ description: "inspect a package from the registry",
3873
+ commandBuilder: buildDependencyRiskArgs
3874
+ }
3875
+ ];
3876
+ while (true) {
3877
+ const selectedIndex = await promptSelection(input.currentVersion, actions);
3878
+ if (selectedIndex === "exit") {
3879
+ stderr2.write("\n");
3880
+ return 0;
3881
+ }
3882
+ const selectedAction = actions[selectedIndex];
3883
+ if (selectedAction === void 0) {
3884
+ stderr2.write("\n");
3885
+ return 1;
3886
+ }
3887
+ const args = await selectedAction.commandBuilder();
3888
+ if (args === null) {
3889
+ await waitForReturnToMenu();
3890
+ continue;
3891
+ }
3892
+ const exitCode = await runCliCommand(input.scriptPath, args);
3893
+ if (exitCode !== 0) {
3894
+ stderr2.write(`
3895
+ Command exited with code ${exitCode}.
3896
+ `);
3897
+ } else {
3898
+ stderr2.write("\n");
3899
+ }
3900
+ await waitForReturnToMenu();
3901
+ }
3902
+ };
3903
+
3687
3904
  // src/application/run-analyze-command.ts
3688
3905
  import { resolve as resolve3 } from "path";
3689
3906
 
@@ -8052,14 +8269,17 @@ program.command("ci").argument("[path]", "path to the project to analyze").addOp
8052
8269
  }
8053
8270
  }
8054
8271
  );
8055
- if (process.argv.length <= 2) {
8056
- program.outputHelp();
8057
- process.exit(0);
8058
- }
8059
8272
  var executablePath = process.argv[0] ?? "";
8060
8273
  var scriptPath = process.argv[1] ?? "";
8061
8274
  var argv = process.argv[2] === "--" ? [executablePath, scriptPath, ...process.argv.slice(3)] : process.argv;
8062
8275
  if (argv.length <= 2) {
8276
+ if (process.stdin.isTTY && process.stdout.isTTY && process.stderr.isTTY) {
8277
+ process.exitCode = await runInteractiveCliMenu({
8278
+ currentVersion: version,
8279
+ scriptPath
8280
+ });
8281
+ process.exit(process.exitCode ?? 0);
8282
+ }
8063
8283
  program.outputHelp();
8064
8284
  process.exit(0);
8065
8285
  }