@securityreviewai/securityreview-kit 0.1.27 → 0.1.28
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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { delimiter, 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
|
+
* Executable that responds to `cursor-agent --version`, or null.
|
|
29
|
+
*/
|
|
30
|
+
export function resolveCursorAgentExecutable() {
|
|
31
|
+
const env = augmentPathEnv();
|
|
32
|
+
const candidates = [
|
|
33
|
+
'cursor-agent',
|
|
34
|
+
join(homedir(), '.local', 'bin', 'cursor-agent'),
|
|
35
|
+
join(homedir(), '.cursor', 'bin', 'cursor-agent'),
|
|
36
|
+
];
|
|
37
|
+
for (const cmd of candidates) {
|
|
38
|
+
if (cmd !== 'cursor-agent' && !existsSync(cmd)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
const r = spawnSync(cmd, ['--version'], { stdio: 'ignore', env });
|
|
42
|
+
if (r.status === 0) {
|
|
43
|
+
return cmd;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
@@ -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 not found (install from https://cursor.com/cli; Node may need ~/.local/bin — kit augments PATH when running the profiler)',
|
|
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 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 not found (install from https://cursor.com/cli and ensure ~/.local/bin is on 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 not found. Install via Cursor CLI (https://cursor.com/cli) or run init Step 1b without --skip-ide-cli-install; ensure your shell PATH includes ~/.local/bin (Node may not inherit it).',
|
|
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];
|