@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 +1 -1
- package/src/cli.js +1 -1
- package/src/commands/init.js +6 -1
- package/src/generators/rules/guardrails-init-profile.md +2 -2
- package/src/generators/rules/guardrails-profiler/SKILL.md +2 -2
- package/src/utils/cursor-agent-path.js +67 -0
- package/src/utils/ide-cli-install.js +24 -9
- package/src/utils/profiler-agent.js +33 -12
package/package.json
CHANGED
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',
|
package/src/commands/init.js
CHANGED
|
@@ -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
|
-
`
|
|
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
|
|
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 (
|
|
27
|
+
if (resolveCursorAgentExecutable()) {
|
|
27
28
|
return { target, ok: true, already: true };
|
|
28
29
|
}
|
|
29
30
|
if (skipInstall) {
|
|
30
|
-
return {
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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
|
|
36
|
-
|
|
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
|
|
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
|
-
|
|
71
|
-
|
|
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(
|
|
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];
|