@securityreviewai/securityreview-kit 0.1.27 → 0.1.29

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@securityreviewai/securityreview-kit",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "description": "Bootstrap security-review-mcp for AI IDEs and CLI tools",
5
5
  "author": "Debarshi Das <debarshi.das@we45.com>",
6
6
  "license": "UNLICENSED",
package/src/cli.js CHANGED
@@ -35,7 +35,7 @@ export function run() {
35
35
  )
36
36
  .option(
37
37
  '--profiler-cursor-login',
38
- 'Before Cursor profiling, run cursor-agent login in this terminal (then profiling runs in the same init)',
38
+ 'Before Cursor profiling, run `agent login` (or `cursor-agent login`) in this terminal (then profiling runs in the same init)',
39
39
  )
40
40
  .option(
41
41
  '--profiler-quiet',
@@ -479,7 +479,12 @@ export async function initCommand(options) {
479
479
  console.log(chalk.dim(' Typical fixes:'));
480
480
  console.log(
481
481
  chalk.dim(
482
- ' • Not signed in: re-run `securityreview-kit init` and choose Yes for “Run Cursor Agent login”, or pass `--profiler-cursor-login` with `--profile-repo`.',
482
+ ' • Not signed in: re-run `securityreview-kit init` and choose Yes for “Run Cursor Agent login”, or pass `--profiler-cursor-login` with `--profile-repo`. Use `agent login` (or `cursor-agent login`) if prompted manually.',
483
+ ),
484
+ );
485
+ console.log(
486
+ chalk.dim(
487
+ ' • CLI missing: install from https://cursor.com/docs/cli/installation and verify `agent --version` (new installs use the `agent` command; `cursor-agent` is legacy).',
483
488
  ),
484
489
  );
485
490
  console.log(
@@ -23,10 +23,10 @@ Do **not** ask the user to verbally approve MCP for `security-review-mcp`. The r
23
23
 
24
24
  From the repo root, non-interactive runs should include workspace trust, MCP approval, and **streaming progress** (matches default `securityreview-kit init`):
25
25
 
26
- `cursor-agent -p "<your profiling instructions>" --output-format stream-json --stream-partial-output --trust --approve-mcps`
26
+ `agent -p "<your profiling instructions>" --output-format stream-json --stream-partial-output --trust --approve-mcps` (or `cursor-agent` if that is what your install provides)
27
27
 
28
28
  Omit `--output-format` / `--stream-partial-output` if you want less verbose terminal output (or use `securityreview-kit init` with `--profiler-quiet`).
29
29
 
30
30
  During `securityreview-kit init`, choose **Yes** when asked to run Cursor login in-terminal, or pass **`--profiler-cursor-login`** with **`--profile-repo`** so login and profiling stay in one run.
31
31
 
32
- You can still sign in manually with `cursor-agent login`. To handle trust/login interactively in the terminal, omit `--trust` and `--approve-mcps`.
32
+ You can still sign in manually with `agent login` (or `cursor-agent login`). To handle trust/login interactively in the terminal, omit `--trust` and `--approve-mcps`.
@@ -70,7 +70,7 @@ Build the object for `.guardrails/profile.json`:
70
70
  "schema_version": "1.0",
71
71
  "project_name": "<directory name>",
72
72
  "profiled_at": "<ISO 8601 timestamp>",
73
- "profiled_by": "<ide or cli id, e.g. cursor-agent, claude, codex>",
73
+ "profiled_by": "<ide or cli id, e.g. agent, cursor-agent, claude, codex>",
74
74
  "detection_summary": {
75
75
  "languages": [],
76
76
  "frameworks": [],
@@ -149,4 +149,4 @@ If there are no signals:
149
149
 
150
150
  ## IDE-Specific Notes
151
151
 
152
- When run from Cursor Agent CLI, Claude Code, or Codex CLI, set `profiled_by` to a stable id (`cursor-agent`, `claude`, `codex`).
152
+ When run from Cursor Agent CLI, Claude Code, or Codex CLI, set `profiled_by` to a stable id (`agent` or `cursor-agent`, `claude`, `codex`).
@@ -0,0 +1,67 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { homedir } from 'node:os';
4
+ import { delimiter, isAbsolute, join } from 'node:path';
5
+
6
+ /**
7
+ * Extra PATH segments where Cursor / other CLIs are often installed.
8
+ * GUI-launched terminals load shell rc files; Node subprocesses often do not.
9
+ */
10
+ export function augmentPathEnv(baseEnv = process.env) {
11
+ const home = homedir();
12
+ const extra = [
13
+ join(home, '.local', 'bin'),
14
+ join(home, '.cursor', 'bin'),
15
+ '/opt/homebrew/bin',
16
+ '/usr/local/bin',
17
+ ];
18
+ if (process.platform === 'win32') {
19
+ extra.push(join(home, 'AppData', 'Local', 'Programs', 'cursor'));
20
+ }
21
+ const pathKey = process.platform === 'win32' ? 'Path' : 'PATH';
22
+ const current = baseEnv[pathKey] || baseEnv.PATH || '';
23
+ const merged = [...extra.filter((p) => p), current].filter(Boolean).join(delimiter);
24
+ return { ...baseEnv, [pathKey]: merged, PATH: merged };
25
+ }
26
+
27
+ /**
28
+ * Cursor Agent CLI binary (`agent` or legacy `cursor-agent`) that responds to `--version`, or null.
29
+ * New installs often only ship `agent` in ~/.local/bin (see https://cursor.com/docs/cli/installation).
30
+ */
31
+ export function resolveCursorAgentExecutable() {
32
+ const env = augmentPathEnv();
33
+ const home = homedir();
34
+ const candidates = [
35
+ join(home, '.local', 'bin', 'agent'),
36
+ join(home, '.local', 'bin', 'cursor-agent'),
37
+ ...(process.platform === 'win32'
38
+ ? [
39
+ join(home, '.local', 'bin', 'agent.cmd'),
40
+ join(home, '.local', 'bin', 'cursor-agent.cmd'),
41
+ ]
42
+ : []),
43
+ join(home, '.cursor', 'bin', 'agent'),
44
+ join(home, '.cursor', 'bin', 'cursor-agent'),
45
+ 'agent',
46
+ 'cursor-agent',
47
+ ];
48
+ if (process.platform === 'win32') {
49
+ const base = join(home, 'AppData', 'Local', 'Programs', 'cursor');
50
+ candidates.splice(
51
+ candidates.length - 2,
52
+ 0,
53
+ join(base, 'agent.exe'),
54
+ join(base, 'cursor-agent.exe'),
55
+ );
56
+ }
57
+ for (const cmd of candidates) {
58
+ if (isAbsolute(cmd) && !existsSync(cmd)) {
59
+ continue;
60
+ }
61
+ const r = spawnSync(cmd, ['--version'], { stdio: 'ignore', env });
62
+ if (r.status === 0) {
63
+ return cmd;
64
+ }
65
+ }
66
+ return null;
67
+ }
@@ -1,9 +1,10 @@
1
1
  import { spawnSync } from 'node:child_process';
2
+ import { augmentPathEnv, resolveCursorAgentExecutable } from './cursor-agent-path.js';
2
3
 
3
4
  const AGENT_CLI_TARGETS = new Set(['cursor', 'claude', 'codex']);
4
5
 
5
- function commandOk(cmd, args = ['--version']) {
6
- const r = spawnSync(cmd, args, { stdio: 'ignore' });
6
+ function commandOk(cmd, args = ['--version'], env = process.env) {
7
+ const r = spawnSync(cmd, args, { stdio: 'ignore', env });
7
8
  return r.status === 0;
8
9
  }
9
10
 
@@ -23,11 +24,16 @@ export function ensureIdeCliForTarget(target, options = {}) {
23
24
  }
24
25
 
25
26
  if (target === 'cursor') {
26
- if (commandOk('cursor-agent', ['--version'])) {
27
+ if (resolveCursorAgentExecutable()) {
27
28
  return { target, ok: true, already: true };
28
29
  }
29
30
  if (skipInstall) {
30
- return { target, ok: false, message: 'cursor-agent not found on PATH' };
31
+ return {
32
+ target,
33
+ ok: false,
34
+ message:
35
+ 'Cursor Agent CLI not found (`agent` or `cursor-agent`). Install from https://cursor.com/docs/cli/installation.',
36
+ };
31
37
  }
32
38
  if (process.platform === 'win32') {
33
39
  return {
@@ -37,13 +43,22 @@ export function ensureIdeCliForTarget(target, options = {}) {
37
43
  };
38
44
  }
39
45
  const r = runShell('curl -fsSL https://cursor.com/install | bash');
40
- return r.status === 0
41
- ? { target, ok: true }
42
- : { target, ok: false, message: 'Cursor CLI install script failed' };
46
+ if (r.status !== 0) {
47
+ return { target, ok: false, message: 'Cursor CLI install script failed' };
48
+ }
49
+ if (!resolveCursorAgentExecutable()) {
50
+ return {
51
+ target,
52
+ ok: false,
53
+ message:
54
+ 'Cursor Agent CLI (`agent` / `cursor-agent`) not found after install; open a new shell or ensure ~/.local/bin exists and re-run init',
55
+ };
56
+ }
57
+ return { target, ok: true };
43
58
  }
44
59
 
45
60
  if (target === 'claude') {
46
- if (commandOk('claude', ['--version'])) {
61
+ if (commandOk('claude', ['--version'], augmentPathEnv())) {
47
62
  return { target, ok: true, already: true };
48
63
  }
49
64
  if (skipInstall) {
@@ -62,7 +77,7 @@ export function ensureIdeCliForTarget(target, options = {}) {
62
77
  }
63
78
 
64
79
  if (target === 'codex') {
65
- if (commandOk('codex', ['--version'])) {
80
+ if (commandOk('codex', ['--version'], augmentPathEnv())) {
66
81
  return { target, ok: true, already: true };
67
82
  }
68
83
  if (skipInstall) {
@@ -1,10 +1,11 @@
1
1
  import { spawnSync } from 'node:child_process';
2
2
  import { GUARDRAILS_PROFILER_SKILL_REL_DIR } from './constants.js';
3
+ import { augmentPathEnv, resolveCursorAgentExecutable } from './cursor-agent-path.js';
3
4
 
4
5
  const PREFERRED_ORDER = ['cursor', 'claude', 'codex'];
5
6
 
6
- function commandOk(cmd, args = ['--version']) {
7
- const r = spawnSync(cmd, args, { stdio: 'ignore' });
7
+ function commandOk(cmd, args = ['--version'], env = process.env) {
8
+ const r = spawnSync(cmd, args, { stdio: 'ignore', env });
8
9
  return r.status === 0;
9
10
  }
10
11
 
@@ -29,11 +30,22 @@ export function buildProfilerAgentPrompt(projectName, agentTarget = 'cursor') {
29
30
  * Call this from init before profiling so the user does not leave the kit flow.
30
31
  */
31
32
  export function runCursorAgentLogin(cwd) {
32
- if (!commandOk('cursor-agent', ['--version'])) {
33
- return { ok: false, status: null, message: 'cursor-agent not on PATH' };
33
+ const bin = resolveCursorAgentExecutable();
34
+ if (!bin) {
35
+ return {
36
+ ok: false,
37
+ status: null,
38
+ message:
39
+ 'Cursor Agent CLI not found (`agent` or `cursor-agent`). Install from https://cursor.com/docs/cli/installation (add ~/.local/bin to PATH), or re-run init without --skip-ide-cli-install.',
40
+ };
34
41
  }
35
- const r = spawnSync('cursor-agent', ['login'], { cwd, stdio: 'inherit', env: { ...process.env } });
36
- return { ok: r.status === 0, status: r.status };
42
+ const env = augmentPathEnv(process.env);
43
+ const r = spawnSync(bin, ['login'], { cwd, stdio: 'inherit', env });
44
+ const spawnErr = r.error ? r.error.message : null;
45
+ if (r.status === null && spawnErr) {
46
+ return { ok: false, status: null, message: spawnErr };
47
+ }
48
+ return { ok: r.status === 0, status: r.status, message: r.status !== 0 ? spawnErr : undefined };
37
49
  }
38
50
 
39
51
  export function pickProfilerAgentTarget(targets) {
@@ -57,7 +69,8 @@ export function pickProfilerAgentTarget(targets) {
57
69
  */
58
70
  export function runProfilerAgent(cwd, { target, projectName, cursorTrust = true, streamProgress = true }) {
59
71
  const prompt = buildProfilerAgentPrompt(projectName, target);
60
- const opts = { cwd, stdio: 'inherit', env: { ...process.env } };
72
+ const env = augmentPathEnv(process.env);
73
+ const opts = { cwd, stdio: 'inherit', env };
61
74
 
62
75
  if (streamProgress) {
63
76
  console.error(
@@ -67,8 +80,13 @@ export function runProfilerAgent(cwd, { target, projectName, cursorTrust = true,
67
80
  }
68
81
 
69
82
  if (target === 'cursor') {
70
- if (!commandOk('cursor-agent', ['--version'])) {
71
- return { ok: false, message: 'cursor-agent not on PATH' };
83
+ const bin = resolveCursorAgentExecutable();
84
+ if (!bin) {
85
+ return {
86
+ ok: false,
87
+ message:
88
+ 'Cursor Agent CLI not found (`agent` or `cursor-agent`). Install from https://cursor.com/docs/cli/installation and ensure ~/.local/bin exists; run init Step 1b without --skip-ide-cli-install (Node subprocesses get an augmented PATH, but the binary must be installed).',
89
+ };
72
90
  }
73
91
  const args = ['-p', prompt];
74
92
  if (streamProgress) {
@@ -77,12 +95,15 @@ export function runProfilerAgent(cwd, { target, projectName, cursorTrust = true,
77
95
  if (cursorTrust) {
78
96
  args.push('--trust', '--approve-mcps');
79
97
  }
80
- const r = spawnSync('cursor-agent', args, opts);
98
+ const r = spawnSync(bin, args, opts);
99
+ if (r.error) {
100
+ return { ok: false, message: r.error.message };
101
+ }
81
102
  return { ok: r.status === 0, status: r.status };
82
103
  }
83
104
 
84
105
  if (target === 'claude') {
85
- if (!commandOk('claude', ['--version'])) {
106
+ if (!commandOk('claude', ['--version'], env)) {
86
107
  return { ok: false, message: 'claude not on PATH' };
87
108
  }
88
109
  const args = streamProgress
@@ -93,7 +114,7 @@ export function runProfilerAgent(cwd, { target, projectName, cursorTrust = true,
93
114
  }
94
115
 
95
116
  if (target === 'codex') {
96
- if (!commandOk('codex', ['--version'])) {
117
+ if (!commandOk('codex', ['--version'], env)) {
97
118
  return { ok: false, message: 'codex not on PATH' };
98
119
  }
99
120
  const args = streamProgress ? ['exec', '--json', prompt] : ['exec', prompt];