@securityreviewai/securityreview-kit 0.1.48 → 0.1.49
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/api.js +44 -0
- package/dist/commands/guardrails.js +13 -0
- package/dist/commands/init.js +88 -0
- package/dist/commands/profile.js +14 -0
- package/dist/commands/status.js +27 -0
- package/dist/commands/sync.js +6 -0
- package/dist/config.js +18 -0
- package/dist/fs.js +43 -0
- package/dist/index.js +44 -0
- package/dist/profile.js +113 -0
- package/dist/scaffold/claude-code.js +37 -0
- package/dist/scaffold/codex.js +35 -0
- package/dist/scaffold/cursor.js +39 -0
- package/dist/scaffold/gemini.js +10 -0
- package/dist/scaffold/index.js +22 -0
- package/dist/scaffold/mcp.js +15 -0
- package/dist/scaffold/rules.js +165 -0
- package/dist/scaffold/vibreview.js +24 -0
- package/dist/scaffold/vscode.js +22 -0
- package/dist/scaffold/windsurf.js +10 -0
- package/dist/sync/index.js +34 -0
- package/dist/sync/payload.js +23 -0
- package/dist/sync/state.js +12 -0
- package/dist/types.js +1 -0
- package/package.json +24 -30
- package/templates/claude/CLAUDE.md +13 -0
- package/templates/claude/agents/guardrail_profiler.md +12 -0
- package/templates/claude/agents/threat_modeler.md +5 -0
- package/templates/claude/skills/vibreview/SKILL.md +21 -0
- package/templates/claude/skills/vibreview/guardrail_patterns.md +12 -0
- package/templates/cursor/rules/vibreview-security.mdc +8 -0
- package/README.md +0 -105
- package/bin/securityreview-kit.js +0 -5
- package/src/cli.js +0 -109
- package/src/commands/init.js +0 -851
- package/src/commands/status.js +0 -99
- package/src/commands/switch-project.js +0 -207
- package/src/generators/mcp/claude.js +0 -85
- package/src/generators/mcp/claude.test.js +0 -64
- package/src/generators/mcp/codex.js +0 -70
- package/src/generators/mcp/codex.test.js +0 -43
- package/src/generators/mcp/cursor.js +0 -29
- package/src/generators/mcp/cursor.test.js +0 -50
- package/src/generators/mcp/gemini.js +0 -28
- package/src/generators/mcp/vscode.js +0 -29
- package/src/generators/mcp/windsurf.js +0 -27
- package/src/generators/rules/antigravity.js +0 -22
- package/src/generators/rules/claude.js +0 -87
- package/src/generators/rules/claude.test.js +0 -60
- package/src/generators/rules/codex.js +0 -141
- package/src/generators/rules/codex.test.js +0 -59
- package/src/generators/rules/content.js +0 -110
- package/src/generators/rules/content.md +0 -57
- package/src/generators/rules/cursor.js +0 -128
- package/src/generators/rules/gemini.js +0 -13
- package/src/generators/rules/guardrails-init-profile.md +0 -56
- package/src/generators/rules/guardrails-profiler/SKILL.md +0 -130
- package/src/generators/rules/guardrails-profiler/references/signal-registry.json +0 -514
- package/src/generators/rules/guardrails-selection/SKILL.md +0 -187
- package/src/generators/rules/guardrails-selection/references/category-threat-map.md +0 -232
- package/src/generators/rules/guardrails_rule.md +0 -94
- package/src/generators/rules/hooks.json +0 -11
- package/src/generators/rules/skill.md +0 -256
- package/src/generators/rules/srai-profile.md +0 -32
- package/src/generators/rules/vibereview-sync/SKILL.md +0 -378
- package/src/generators/rules/vscode.js +0 -101
- package/src/generators/rules/vscode.test.js +0 -54
- package/src/generators/rules/windsurf.js +0 -13
- package/src/utils/constants.js +0 -95
- package/src/utils/cursor-agent-path.js +0 -67
- package/src/utils/cursor-cli-permissions.js +0 -28
- package/src/utils/detect.js +0 -27
- package/src/utils/fs-helpers.js +0 -82
- package/src/utils/guardrails-profiler-bundle.js +0 -84
- package/src/utils/ide-cli-install.js +0 -138
- package/src/utils/profiler-agent.js +0 -446
- package/src/utils/profiler-agent.test.js +0 -81
- package/src/utils/srai.js +0 -252
package/src/commands/init.js
DELETED
|
@@ -1,851 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { input, checkbox, confirm, password, select } from '@inquirer/prompts';
|
|
3
|
-
import { TARGETS, TARGET_NAMES } from '../utils/constants.js';
|
|
4
|
-
import { detectTargets } from '../utils/detect.js';
|
|
5
|
-
import { ensureIdeClisForTargets } from '../utils/ide-cli-install.js';
|
|
6
|
-
import { writeGuardrailsSkillBundles } from '../utils/guardrails-profiler-bundle.js';
|
|
7
|
-
import {
|
|
8
|
-
pickProfilerAgentTarget,
|
|
9
|
-
runClaudeAuthLogin,
|
|
10
|
-
runClaudeSetupToken,
|
|
11
|
-
runCodexLogin,
|
|
12
|
-
runCopilotLogin,
|
|
13
|
-
runCursorAgentLogin,
|
|
14
|
-
runProfilerAgent,
|
|
15
|
-
} from '../utils/profiler-agent.js';
|
|
16
|
-
import { fetchVibeReviewProjectNames, getStoredCredentials, normalizeApiUrl } from '../utils/srai.js';
|
|
17
|
-
|
|
18
|
-
// Dynamic imports for generators (avoids loading all at startup)
|
|
19
|
-
const mcpGenerators = {
|
|
20
|
-
cursor: () => import('../generators/mcp/cursor.js'),
|
|
21
|
-
claude: () => import('../generators/mcp/claude.js'),
|
|
22
|
-
vscode: () => import('../generators/mcp/vscode.js'),
|
|
23
|
-
windsurf: () => import('../generators/mcp/windsurf.js'),
|
|
24
|
-
codex: () => import('../generators/mcp/codex.js'),
|
|
25
|
-
gemini: () => import('../generators/mcp/gemini.js'),
|
|
26
|
-
antigravity: () => import('../generators/mcp/gemini.js'),
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const ruleGenerators = {
|
|
30
|
-
cursor: () => import('../generators/rules/cursor.js'),
|
|
31
|
-
claude: () => import('../generators/rules/claude.js'),
|
|
32
|
-
vscode: () => import('../generators/rules/vscode.js'),
|
|
33
|
-
windsurf: () => import('../generators/rules/windsurf.js'),
|
|
34
|
-
codex: () => import('../generators/rules/codex.js'),
|
|
35
|
-
gemini: () => import('../generators/rules/gemini.js'),
|
|
36
|
-
antigravity: () => import('../generators/rules/antigravity.js'),
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
function normalizeRuleResults(rawResult) {
|
|
40
|
-
const entries = Array.isArray(rawResult) ? rawResult : [rawResult];
|
|
41
|
-
|
|
42
|
-
return entries.map((entry) => {
|
|
43
|
-
if (typeof entry === 'string') {
|
|
44
|
-
return { filePath: entry, action: 'created', kind: 'rule' };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (entry && typeof entry.filePath === 'string') {
|
|
48
|
-
const allowedKinds = new Set(['rule', 'command', 'agent', 'skill', 'hooks', 'config']);
|
|
49
|
-
const kind = allowedKinds.has(entry.kind) ? entry.kind : 'rule';
|
|
50
|
-
return { filePath: entry.filePath, action: entry.action || 'created', kind };
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
throw new Error('Rule generator returned an invalid result.');
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
async function resolveClaudeProfilerAuth(options, interactive, cwd) {
|
|
58
|
-
const providerModel = String(options.claudeProviderModel || process.env.ANTHROPIC_MODEL || '').trim();
|
|
59
|
-
let mode = String(options.claudeAuthMode || process.env.SECURITY_REVIEW_CLAUDE_AUTH_MODE || '').trim();
|
|
60
|
-
|
|
61
|
-
if (!mode && options.profilerClaudeLogin) {
|
|
62
|
-
mode = 'claudeai';
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (!mode && interactive) {
|
|
66
|
-
mode = await select({
|
|
67
|
-
message: 'How should Claude Code authenticate for this profiling run?',
|
|
68
|
-
default: 'current',
|
|
69
|
-
choices: [
|
|
70
|
-
{ name: 'Use current Claude Code auth/environment', value: 'current' },
|
|
71
|
-
{ name: 'Claude subscription login', value: 'claudeai' },
|
|
72
|
-
{ name: 'Anthropic Console login', value: 'console' },
|
|
73
|
-
{ name: 'Anthropic API key', value: 'api_key' },
|
|
74
|
-
{ name: 'Anthropic-compatible gateway / proxy', value: 'gateway' },
|
|
75
|
-
{ name: 'AWS Bedrock', value: 'bedrock' },
|
|
76
|
-
{ name: 'Google Vertex AI', value: 'vertex' },
|
|
77
|
-
{ name: 'Long-lived Claude token', value: 'setup_token' },
|
|
78
|
-
],
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (!mode) {
|
|
83
|
-
mode = 'current';
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const result = {
|
|
87
|
-
mode,
|
|
88
|
-
model: providerModel || 'haiku',
|
|
89
|
-
envOverrides: {},
|
|
90
|
-
loginRunner: null,
|
|
91
|
-
loginLabel: '',
|
|
92
|
-
summary: '',
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
if (mode === 'claudeai') {
|
|
96
|
-
result.loginRunner = () => runClaudeAuthLogin(cwd, { mode: 'claudeai' });
|
|
97
|
-
result.loginLabel = 'Claude subscription login';
|
|
98
|
-
result.summary = 'Claude subscription auth';
|
|
99
|
-
return result;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (mode === 'console') {
|
|
103
|
-
result.loginRunner = () => runClaudeAuthLogin(cwd, { mode: 'console' });
|
|
104
|
-
result.loginLabel = 'Anthropic Console login';
|
|
105
|
-
result.summary = 'Anthropic Console auth';
|
|
106
|
-
return result;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (mode === 'setup_token') {
|
|
110
|
-
result.loginRunner = () => runClaudeSetupToken(cwd);
|
|
111
|
-
result.loginLabel = 'Claude long-lived token setup';
|
|
112
|
-
result.summary = 'Claude subscription token auth';
|
|
113
|
-
return result;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (mode === 'api_key') {
|
|
117
|
-
let apiKey = String(options.claudeApiKey || process.env.ANTHROPIC_API_KEY || '').trim();
|
|
118
|
-
if (!apiKey && interactive) {
|
|
119
|
-
apiKey = await password({
|
|
120
|
-
message: 'Anthropic API key for this profiling run:',
|
|
121
|
-
validate: (v) => (String(v || '').trim() ? true : 'API key is required'),
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
result.envOverrides = {
|
|
125
|
-
ANTHROPIC_API_KEY: apiKey,
|
|
126
|
-
ANTHROPIC_AUTH_TOKEN: null,
|
|
127
|
-
ANTHROPIC_BASE_URL: null,
|
|
128
|
-
CLAUDE_CODE_USE_BEDROCK: null,
|
|
129
|
-
CLAUDE_CODE_USE_VERTEX: null,
|
|
130
|
-
};
|
|
131
|
-
result.summary = 'Anthropic API key auth';
|
|
132
|
-
return result;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (mode === 'gateway') {
|
|
136
|
-
let baseUrl = String(options.claudeBaseUrl || process.env.ANTHROPIC_BASE_URL || '').trim();
|
|
137
|
-
let authToken = String(options.claudeAuthToken || process.env.ANTHROPIC_AUTH_TOKEN || '').trim();
|
|
138
|
-
let model = providerModel;
|
|
139
|
-
|
|
140
|
-
if (interactive) {
|
|
141
|
-
if (!baseUrl) {
|
|
142
|
-
baseUrl = await input({
|
|
143
|
-
message: 'Anthropic-compatible base URL for this profiling run:',
|
|
144
|
-
validate: (v) => (String(v || '').trim() ? true : 'Base URL is required'),
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
if (!authToken) {
|
|
148
|
-
authToken = await password({
|
|
149
|
-
message: 'Gateway auth token for this profiling run:',
|
|
150
|
-
validate: (v) => (String(v || '').trim() ? true : 'Auth token is required'),
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
if (!providerModel) {
|
|
154
|
-
model = String(
|
|
155
|
-
await input({
|
|
156
|
-
message: 'Provider model override (leave blank to use haiku):',
|
|
157
|
-
default: '',
|
|
158
|
-
}),
|
|
159
|
-
).trim();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
result.model = model || 'haiku';
|
|
164
|
-
result.envOverrides = {
|
|
165
|
-
ANTHROPIC_BASE_URL: baseUrl,
|
|
166
|
-
ANTHROPIC_AUTH_TOKEN: authToken,
|
|
167
|
-
ANTHROPIC_API_KEY: null,
|
|
168
|
-
CLAUDE_CODE_USE_BEDROCK: null,
|
|
169
|
-
CLAUDE_CODE_USE_VERTEX: null,
|
|
170
|
-
};
|
|
171
|
-
result.summary = `Anthropic-compatible gateway (${baseUrl || 'configured base URL'})`;
|
|
172
|
-
return result;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (mode === 'bedrock') {
|
|
176
|
-
let model = providerModel;
|
|
177
|
-
if (interactive && !providerModel) {
|
|
178
|
-
model = String(
|
|
179
|
-
await input({
|
|
180
|
-
message: 'Bedrock model override (leave blank to use haiku):',
|
|
181
|
-
default: '',
|
|
182
|
-
}),
|
|
183
|
-
).trim();
|
|
184
|
-
}
|
|
185
|
-
result.model = model || 'haiku';
|
|
186
|
-
result.envOverrides = {
|
|
187
|
-
CLAUDE_CODE_USE_BEDROCK: 'true',
|
|
188
|
-
CLAUDE_CODE_USE_VERTEX: null,
|
|
189
|
-
ANTHROPIC_API_KEY: null,
|
|
190
|
-
ANTHROPIC_AUTH_TOKEN: null,
|
|
191
|
-
};
|
|
192
|
-
result.summary = 'AWS Bedrock credentials from your shell/environment';
|
|
193
|
-
return result;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (mode === 'vertex') {
|
|
197
|
-
let model = providerModel;
|
|
198
|
-
if (interactive && !providerModel) {
|
|
199
|
-
model = String(
|
|
200
|
-
await input({
|
|
201
|
-
message: 'Vertex model override (leave blank to use haiku):',
|
|
202
|
-
default: '',
|
|
203
|
-
}),
|
|
204
|
-
).trim();
|
|
205
|
-
}
|
|
206
|
-
result.model = model || 'haiku';
|
|
207
|
-
result.envOverrides = {
|
|
208
|
-
CLAUDE_CODE_USE_VERTEX: 'true',
|
|
209
|
-
CLAUDE_CODE_USE_BEDROCK: null,
|
|
210
|
-
ANTHROPIC_API_KEY: null,
|
|
211
|
-
ANTHROPIC_AUTH_TOKEN: null,
|
|
212
|
-
};
|
|
213
|
-
result.summary = 'Google Vertex AI credentials from your shell/environment';
|
|
214
|
-
return result;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
result.summary = 'current Claude Code auth/environment';
|
|
218
|
-
return result;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Resolve environment variables from flags, env, or interactive prompt.
|
|
223
|
-
*/
|
|
224
|
-
async function resolveEnvVars(options, interactive, cwd) {
|
|
225
|
-
let apiUrl = normalizeApiUrl(options.apiUrl || process.env.SECURITY_REVIEW_API_URL || '');
|
|
226
|
-
let apiToken = options.apiKey || process.env.SECURITY_REVIEW_API_TOKEN || '';
|
|
227
|
-
|
|
228
|
-
if (!apiUrl || !apiToken) {
|
|
229
|
-
const stored = getStoredCredentials(cwd);
|
|
230
|
-
if (!apiUrl && stored.apiUrl) {
|
|
231
|
-
apiUrl = stored.apiUrl;
|
|
232
|
-
}
|
|
233
|
-
if (!apiToken && stored.apiToken) {
|
|
234
|
-
apiToken = stored.apiToken;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (interactive) {
|
|
239
|
-
if (!apiUrl) {
|
|
240
|
-
apiUrl = await input({
|
|
241
|
-
message: '🔗 SRAI API URL:',
|
|
242
|
-
default: 'app.demo.securityreview.ai',
|
|
243
|
-
validate: (v) => {
|
|
244
|
-
const normalized = normalizeApiUrl(v);
|
|
245
|
-
if (!normalized) return 'Must be a valid URL';
|
|
246
|
-
|
|
247
|
-
try {
|
|
248
|
-
new URL(normalized);
|
|
249
|
-
return true;
|
|
250
|
-
} catch {
|
|
251
|
-
return 'Must be a valid URL';
|
|
252
|
-
}
|
|
253
|
-
},
|
|
254
|
-
});
|
|
255
|
-
apiUrl = normalizeApiUrl(apiUrl);
|
|
256
|
-
} else {
|
|
257
|
-
console.log(chalk.dim(` API URL: ${apiUrl} (from saved config/env/flags)`));
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (!apiToken) {
|
|
261
|
-
apiToken = await input({
|
|
262
|
-
message: '🔑 SRAI API Token:',
|
|
263
|
-
validate: (v) => (v.length > 0 ? true : 'Token is required'),
|
|
264
|
-
});
|
|
265
|
-
} else {
|
|
266
|
-
console.log(chalk.dim(` API Token: ${'•'.repeat(8)} (from saved config/env/flags)`));
|
|
267
|
-
}
|
|
268
|
-
} else {
|
|
269
|
-
if (!apiUrl || !apiToken) {
|
|
270
|
-
console.log(
|
|
271
|
-
chalk.yellow(
|
|
272
|
-
'⚠ Missing credentials. Set SECURITY_REVIEW_API_URL and SECURITY_REVIEW_API_TOKEN\n' +
|
|
273
|
-
' environment variables, pass --api-url and --api-key flags, or run in interactive mode.',
|
|
274
|
-
),
|
|
275
|
-
);
|
|
276
|
-
process.exit(1);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return { apiUrl, apiToken };
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Resolve project mapping from SRAI API.
|
|
285
|
-
*/
|
|
286
|
-
async function resolveProjectName(options, interactive, apiUrl, apiToken) {
|
|
287
|
-
const pinnedProject = (options.projectName || process.env.SECURITY_REVIEW_PROJECT_NAME || '').trim();
|
|
288
|
-
|
|
289
|
-
const projectNames = await fetchVibeReviewProjectNames(apiUrl, apiToken);
|
|
290
|
-
|
|
291
|
-
if (!interactive) {
|
|
292
|
-
if (pinnedProject) {
|
|
293
|
-
if (!projectNames.includes(pinnedProject)) {
|
|
294
|
-
console.log(
|
|
295
|
-
chalk.yellow(
|
|
296
|
-
`⚠ Project "${pinnedProject}" is not in the vibe-review-enabled list. ` +
|
|
297
|
-
'Only projects with is_vibe_review=true can be selected. Pass a valid --project-name or run interactive mode.',
|
|
298
|
-
),
|
|
299
|
-
);
|
|
300
|
-
process.exit(1);
|
|
301
|
-
}
|
|
302
|
-
return pinnedProject;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (projectNames.length === 1) {
|
|
306
|
-
return projectNames[0];
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
console.log(
|
|
310
|
-
chalk.yellow(
|
|
311
|
-
'⚠ Multiple vibe-review projects available. Pass --project-name or run interactive mode to choose one.',
|
|
312
|
-
),
|
|
313
|
-
);
|
|
314
|
-
process.exit(1);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const selected = await select({
|
|
318
|
-
message: '🧩 Select SRAI project (vibe review enabled only):',
|
|
319
|
-
choices: projectNames.map((name) => ({
|
|
320
|
-
name,
|
|
321
|
-
value: name,
|
|
322
|
-
})),
|
|
323
|
-
default: projectNames.includes(pinnedProject) ? pinnedProject : undefined,
|
|
324
|
-
pageSize: 12,
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
return selected;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Resolve targets from flags or interactive prompt.
|
|
332
|
-
*/
|
|
333
|
-
async function resolveTargets(options, interactive, cwd) {
|
|
334
|
-
// Flag mode: explicit target(s)
|
|
335
|
-
if (options.target) {
|
|
336
|
-
const targets = Array.isArray(options.target) ? options.target : [options.target];
|
|
337
|
-
for (const t of targets) {
|
|
338
|
-
if (!TARGET_NAMES.includes(t)) {
|
|
339
|
-
console.log(chalk.red(`✗ Unknown target: ${t}`));
|
|
340
|
-
console.log(chalk.dim(` Valid targets: ${TARGET_NAMES.join(', ')}`));
|
|
341
|
-
process.exit(1);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
return targets;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (options.all) {
|
|
348
|
-
return [...TARGET_NAMES];
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
if (!interactive) {
|
|
352
|
-
console.log(chalk.red('✗ No target specified. Use --target <name> or --all.'));
|
|
353
|
-
process.exit(1);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// Interactive: detect and offer choices
|
|
357
|
-
const detected = detectTargets(cwd);
|
|
358
|
-
|
|
359
|
-
console.log('');
|
|
360
|
-
if (detected.length > 0) {
|
|
361
|
-
console.log(
|
|
362
|
-
chalk.dim(` Detected IDEs in workspace: ${detected.map((d) => TARGETS[d].name).join(', ')}`),
|
|
363
|
-
);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
const selected = await checkbox({
|
|
367
|
-
message: '🎯 Select target IDE(s) / CLI(s) to configure:',
|
|
368
|
-
choices: TARGET_NAMES.map((key) => ({
|
|
369
|
-
name: `${TARGETS[key].name}`,
|
|
370
|
-
value: key,
|
|
371
|
-
checked: detected.includes(key),
|
|
372
|
-
})),
|
|
373
|
-
validate: (v) => (v.length > 0 ? true : 'Select at least one target'),
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
return selected;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Main init command handler.
|
|
381
|
-
*/
|
|
382
|
-
export async function initCommand(options) {
|
|
383
|
-
const cwd = process.cwd();
|
|
384
|
-
const interactive = !options.target && !options.all;
|
|
385
|
-
|
|
386
|
-
// Banner
|
|
387
|
-
console.log('');
|
|
388
|
-
console.log(chalk.bold.cyan(' ╔══════════════════════════════════════╗'));
|
|
389
|
-
console.log(chalk.bold.cyan(' ║') + chalk.bold(' 🛡️ Security Review Kit — Init ') + chalk.bold.cyan(' ║'));
|
|
390
|
-
console.log(chalk.bold.cyan(' ╚══════════════════════════════════════╝'));
|
|
391
|
-
console.log('');
|
|
392
|
-
|
|
393
|
-
if (interactive) {
|
|
394
|
-
console.log(chalk.dim(' Interactive setup — follow the prompts below.\n'));
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Step 1: Resolve targets
|
|
398
|
-
console.log(chalk.bold.white(' Step 1 of 5: Select Targets'));
|
|
399
|
-
console.log(chalk.dim(' ─────────────────────────────────'));
|
|
400
|
-
const targets = await resolveTargets(options, interactive, cwd);
|
|
401
|
-
console.log(chalk.green(` ✓ Targets: ${targets.map((t) => TARGETS[t].name).join(', ')}`));
|
|
402
|
-
console.log('');
|
|
403
|
-
console.log(chalk.bold.white(' Step 1b: IDE / agent CLIs'));
|
|
404
|
-
console.log(chalk.dim(' ─────────────────────────────────'));
|
|
405
|
-
const cliResults = ensureIdeClisForTargets(targets, { skipInstall: options.skipIdeCliInstall });
|
|
406
|
-
for (const r of cliResults) {
|
|
407
|
-
if (r.skipped) continue;
|
|
408
|
-
const label = TARGETS[r.target]?.name || r.target;
|
|
409
|
-
if (r.already) {
|
|
410
|
-
console.log(chalk.dim(` • ${label} CLI already available`));
|
|
411
|
-
} else if (r.ok) {
|
|
412
|
-
console.log(chalk.green(` \u2713 ${label} CLI install attempted`));
|
|
413
|
-
} else {
|
|
414
|
-
console.log(chalk.yellow(` \u26a0 ${label} CLI: ${r.message || 'install skipped or failed'}`));
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
console.log('');
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
// Step 2: What to install (rules question first, then MCP)
|
|
421
|
-
let installMcp = !options.skipMcp;
|
|
422
|
-
let installRules = !options.skipRules;
|
|
423
|
-
|
|
424
|
-
if (interactive) {
|
|
425
|
-
console.log(chalk.bold.white(' Step 2 of 5: Installation Options'));
|
|
426
|
-
console.log(chalk.dim(' ─────────────────────────────────'));
|
|
427
|
-
installRules = await confirm({
|
|
428
|
-
message: '📋 Install workspace security rules?',
|
|
429
|
-
default: true,
|
|
430
|
-
});
|
|
431
|
-
installMcp = await confirm({
|
|
432
|
-
message: '📡 Install MCP server configuration?',
|
|
433
|
-
default: true,
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (!installMcp && !installRules) {
|
|
438
|
-
console.log(chalk.yellow(' ⚠ Nothing to install. Exiting.'));
|
|
439
|
-
return;
|
|
440
|
-
}
|
|
441
|
-
console.log('');
|
|
442
|
-
|
|
443
|
-
// Step 3: Resolve credentials (only needed when MCP is being installed)
|
|
444
|
-
let envVars = { apiUrl: '', apiToken: '' };
|
|
445
|
-
if (installMcp) {
|
|
446
|
-
console.log(chalk.bold.white(' Step 3 of 5: SRAI Credentials'));
|
|
447
|
-
console.log(chalk.dim(' ─────────────────────────────────'));
|
|
448
|
-
envVars = await resolveEnvVars(options, interactive, cwd);
|
|
449
|
-
console.log(chalk.green(' ✓ Credentials configured'));
|
|
450
|
-
} else {
|
|
451
|
-
console.log(chalk.dim(' Step 3 of 5: Skipping SRAI credentials (MCP not selected).'));
|
|
452
|
-
}
|
|
453
|
-
console.log('');
|
|
454
|
-
|
|
455
|
-
// Step 4: Resolve project name (only needed when MCP is being installed)
|
|
456
|
-
let projectName = options.projectName || process.env.SECURITY_REVIEW_PROJECT_NAME || '';
|
|
457
|
-
if (installMcp) {
|
|
458
|
-
console.log(chalk.bold.white(' Step 4 of 5: SRAI Project Mapping'));
|
|
459
|
-
console.log(chalk.dim(' ─────────────────────────────────'));
|
|
460
|
-
projectName = await resolveProjectName(options, interactive, envVars.apiUrl, envVars.apiToken);
|
|
461
|
-
if (projectName) {
|
|
462
|
-
console.log(chalk.green(` ✓ Project mapped: ${projectName}`));
|
|
463
|
-
} else {
|
|
464
|
-
console.log(chalk.dim(' No project name provided. Rules will use a generic project lookup instruction.'));
|
|
465
|
-
}
|
|
466
|
-
} else {
|
|
467
|
-
console.log(chalk.dim(' Step 4 of 5: Skipping SRAI project mapping (MCP not selected).'));
|
|
468
|
-
}
|
|
469
|
-
console.log('');
|
|
470
|
-
|
|
471
|
-
const projectNameForSkill =
|
|
472
|
-
(projectName || options.projectName || process.env.SECURITY_REVIEW_PROJECT_NAME || '').trim();
|
|
473
|
-
|
|
474
|
-
console.log(chalk.bold.white(' Step 5 of 5: Installing workspace files'));
|
|
475
|
-
console.log(chalk.dim(' ─────────────────────────────────'));
|
|
476
|
-
|
|
477
|
-
const results = [];
|
|
478
|
-
|
|
479
|
-
for (const target of targets) {
|
|
480
|
-
const targetInfo = TARGETS[target];
|
|
481
|
-
console.log('');
|
|
482
|
-
console.log(chalk.bold(` ${targetInfo.name}`));
|
|
483
|
-
|
|
484
|
-
if (installMcp) {
|
|
485
|
-
try {
|
|
486
|
-
const gen = await mcpGenerators[target]();
|
|
487
|
-
const mcpPath = gen.generate(cwd, envVars);
|
|
488
|
-
console.log(chalk.green(` ✓ MCP config → ${typeof mcpPath === 'string' ? mcpPath : mcpPath}`));
|
|
489
|
-
results.push({ target, type: 'mcp', status: 'ok', path: mcpPath });
|
|
490
|
-
} catch (err) {
|
|
491
|
-
console.log(chalk.red(` ✗ MCP config failed: ${err.message}`));
|
|
492
|
-
results.push({ target, type: 'mcp', status: 'error', error: err.message });
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
if (installRules) {
|
|
497
|
-
try {
|
|
498
|
-
const gen = await ruleGenerators[target]();
|
|
499
|
-
const generatedRules = normalizeRuleResults(gen.generate(cwd, { projectName }));
|
|
500
|
-
|
|
501
|
-
for (const rule of generatedRules) {
|
|
502
|
-
const labelByKind = {
|
|
503
|
-
rule: 'Workspace rule',
|
|
504
|
-
command: 'Workspace command',
|
|
505
|
-
agent: 'Workspace agent',
|
|
506
|
-
skill: 'Workspace skill',
|
|
507
|
-
hooks: 'Hooks',
|
|
508
|
-
config: 'CLI config',
|
|
509
|
-
};
|
|
510
|
-
const label = labelByKind[rule.kind] || 'Workspace rule';
|
|
511
|
-
console.log(chalk.green(` ✓ ${label} → ${rule.filePath} (${rule.action})`));
|
|
512
|
-
results.push({ target, type: rule.kind, status: 'ok', path: rule.filePath, action: rule.action });
|
|
513
|
-
}
|
|
514
|
-
} catch (err) {
|
|
515
|
-
console.log(chalk.red(` ✗ Workspace rule failed: ${err.message}`));
|
|
516
|
-
results.push({ target, type: 'rule', status: 'error', error: err.message });
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
if (installRules) {
|
|
522
|
-
try {
|
|
523
|
-
const bundlePaths = writeGuardrailsSkillBundles(cwd, {
|
|
524
|
-
projectName: projectNameForSkill,
|
|
525
|
-
targets,
|
|
526
|
-
});
|
|
527
|
-
if (bundlePaths.length === 0) {
|
|
528
|
-
console.log(
|
|
529
|
-
chalk.dim(
|
|
530
|
-
' (Guardrails skills are only written for Cursor, VS Code Copilot, Claude Code, and Codex targets.)',
|
|
531
|
-
),
|
|
532
|
-
);
|
|
533
|
-
}
|
|
534
|
-
for (const bundlePath of bundlePaths) {
|
|
535
|
-
console.log(chalk.green(` \u2713 Guardrails skill bundle \u2192 ${bundlePath}`));
|
|
536
|
-
results.push({ target: 'kit', type: 'bundle', status: 'ok', path: bundlePath });
|
|
537
|
-
}
|
|
538
|
-
} catch (err) {
|
|
539
|
-
console.log(chalk.red(` \u2717 Guardrails skill bundle failed: ${err.message}`));
|
|
540
|
-
results.push({ target: 'kit', type: 'bundle', status: 'error', error: err.message });
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Summary
|
|
545
|
-
const ok = results.filter((r) => r.status === 'ok').length;
|
|
546
|
-
const errors = results.filter((r) => r.status === 'error').length;
|
|
547
|
-
|
|
548
|
-
console.log('');
|
|
549
|
-
console.log(chalk.dim(' ─────────────────────────────────'));
|
|
550
|
-
if (errors === 0) {
|
|
551
|
-
console.log(chalk.bold.green(` ✅ Done! ${ok} configuration(s) installed successfully.`));
|
|
552
|
-
} else {
|
|
553
|
-
console.log(
|
|
554
|
-
chalk.bold.yellow(` ⚠ Done with ${errors} error(s). ${ok} configuration(s) installed.`),
|
|
555
|
-
);
|
|
556
|
-
}
|
|
557
|
-
console.log('');
|
|
558
|
-
|
|
559
|
-
const profilerEligible =
|
|
560
|
-
installMcp && installRules && projectNameForSkill && options.profileRepo !== false;
|
|
561
|
-
|
|
562
|
-
let shouldProfile = false;
|
|
563
|
-
if (profilerEligible) {
|
|
564
|
-
if (interactive) {
|
|
565
|
-
shouldProfile = await confirm({
|
|
566
|
-
message:
|
|
567
|
-
'Profile this repository and push the default guardrail pack to SecurityReview.ai (runs your IDE agent CLI)?',
|
|
568
|
-
default: true,
|
|
569
|
-
});
|
|
570
|
-
} else {
|
|
571
|
-
shouldProfile = Boolean(options.profileRepo);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
if (shouldProfile) {
|
|
576
|
-
const agentTarget = pickProfilerAgentTarget(targets);
|
|
577
|
-
if (!agentTarget) {
|
|
578
|
-
console.log(
|
|
579
|
-
chalk.yellow(
|
|
580
|
-
' \u26a0 Profiling needs Cursor, VS Code Copilot, Claude Code, or Codex in your targets. Add one and re-run, or run the guardrails-init-profile command in your IDE.',
|
|
581
|
-
),
|
|
582
|
-
);
|
|
583
|
-
} else {
|
|
584
|
-
console.log('');
|
|
585
|
-
console.log(chalk.bold.white(` Starting profiler via ${TARGETS[agentTarget].name} CLI…`));
|
|
586
|
-
let claudeProfilerAuth = null;
|
|
587
|
-
if (agentTarget === 'cursor') {
|
|
588
|
-
console.log(
|
|
589
|
-
chalk.dim(
|
|
590
|
-
' Cursor: headless profiling uses `--trust` and `--approve-mcps` on this folder (you confirmed above).',
|
|
591
|
-
),
|
|
592
|
-
);
|
|
593
|
-
if (options.profilerNoTrust) {
|
|
594
|
-
console.log(
|
|
595
|
-
chalk.dim(
|
|
596
|
-
' You passed `--profiler-no-trust`: complete any login or workspace-trust prompts in this terminal.',
|
|
597
|
-
),
|
|
598
|
-
);
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
let runLogin = Boolean(options.profilerCursorLogin);
|
|
602
|
-
if (!runLogin && interactive) {
|
|
603
|
-
runLogin = await confirm({
|
|
604
|
-
message:
|
|
605
|
-
'Run Cursor Agent login in this terminal now? (Same init — profiling runs next. Choose No if already signed in.)',
|
|
606
|
-
default: true,
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
if (runLogin) {
|
|
610
|
-
console.log('');
|
|
611
|
-
console.log(chalk.bold.white(' Cursor Agent login'));
|
|
612
|
-
console.log(chalk.dim(' Complete the browser or code prompt, then return here.\n'));
|
|
613
|
-
const loginResult = runCursorAgentLogin(cwd);
|
|
614
|
-
if (loginResult.ok) {
|
|
615
|
-
console.log(chalk.green(' \u2713 Cursor Agent login step finished.'));
|
|
616
|
-
} else {
|
|
617
|
-
console.log(
|
|
618
|
-
chalk.yellow(
|
|
619
|
-
` \u26a0 Login exited with status ${loginResult.status ?? 'unknown'}. Profiling will still be attempted; sign in and re-run init if it fails.`,
|
|
620
|
-
),
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
console.log('');
|
|
624
|
-
}
|
|
625
|
-
} else if (agentTarget === 'vscode') {
|
|
626
|
-
console.log(
|
|
627
|
-
chalk.dim(
|
|
628
|
-
' Copilot: profiling uses the VS Code MCP config and Copilot CLI tool approvals for this folder.',
|
|
629
|
-
),
|
|
630
|
-
);
|
|
631
|
-
|
|
632
|
-
let runLogin = Boolean(options.profilerCopilotLogin);
|
|
633
|
-
if (!runLogin && interactive) {
|
|
634
|
-
runLogin = await confirm({
|
|
635
|
-
message:
|
|
636
|
-
'Run GitHub Copilot CLI login in this terminal now? (Same init — profiling runs next. Choose No if already signed in.)',
|
|
637
|
-
default: true,
|
|
638
|
-
});
|
|
639
|
-
}
|
|
640
|
-
if (runLogin) {
|
|
641
|
-
console.log('');
|
|
642
|
-
console.log(chalk.bold.white(' GitHub Copilot CLI login'));
|
|
643
|
-
console.log(chalk.dim(' Complete the browser or device-code prompt, then return here.\n'));
|
|
644
|
-
const loginResult = runCopilotLogin(cwd);
|
|
645
|
-
if (loginResult.ok) {
|
|
646
|
-
console.log(chalk.green(' \u2713 GitHub Copilot CLI login step finished.'));
|
|
647
|
-
} else {
|
|
648
|
-
console.log(
|
|
649
|
-
chalk.yellow(
|
|
650
|
-
` \u26a0 Copilot login exited with status ${loginResult.status ?? 'unknown'}. Profiling will still be attempted; sign in and re-run init if it fails.`,
|
|
651
|
-
),
|
|
652
|
-
);
|
|
653
|
-
}
|
|
654
|
-
console.log('');
|
|
655
|
-
}
|
|
656
|
-
} else if (agentTarget === 'claude') {
|
|
657
|
-
claudeProfilerAuth = await resolveClaudeProfilerAuth(options, interactive, cwd);
|
|
658
|
-
console.log(
|
|
659
|
-
chalk.dim(
|
|
660
|
-
` Claude Code: profiling uses \`.mcp.json\`, \`.claude/settings.json\`, bypassed tool prompts for this profiling pass, and \`${claudeProfilerAuth.model}\` for this run.`,
|
|
661
|
-
),
|
|
662
|
-
);
|
|
663
|
-
console.log(chalk.dim(` Auth mode: ${claudeProfilerAuth.summary}.`));
|
|
664
|
-
|
|
665
|
-
const needsSetup = typeof claudeProfilerAuth.loginRunner === 'function';
|
|
666
|
-
let runLogin = needsSetup;
|
|
667
|
-
if (needsSetup && interactive && !options.claudeAuthMode && !options.profilerClaudeLogin) {
|
|
668
|
-
runLogin = await confirm({
|
|
669
|
-
message: `${claudeProfilerAuth.loginLabel} in this terminal now? (Same init — profiling runs next.)`,
|
|
670
|
-
default: true,
|
|
671
|
-
});
|
|
672
|
-
}
|
|
673
|
-
if (runLogin && claudeProfilerAuth.loginRunner) {
|
|
674
|
-
console.log('');
|
|
675
|
-
console.log(chalk.bold.white(` ${claudeProfilerAuth.loginLabel}`));
|
|
676
|
-
console.log(chalk.dim(' Complete the prompt flow, then return here.\n'));
|
|
677
|
-
const loginResult = claudeProfilerAuth.loginRunner();
|
|
678
|
-
if (loginResult.ok) {
|
|
679
|
-
console.log(chalk.green(` \u2713 ${claudeProfilerAuth.loginLabel} finished.`));
|
|
680
|
-
} else {
|
|
681
|
-
console.log(
|
|
682
|
-
chalk.yellow(
|
|
683
|
-
` \u26a0 ${claudeProfilerAuth.loginLabel} exited with status ${loginResult.status ?? 'unknown'}. Profiling will still be attempted; configure auth and re-run init if it fails.`,
|
|
684
|
-
),
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
|
-
console.log('');
|
|
688
|
-
}
|
|
689
|
-
} else if (agentTarget === 'codex') {
|
|
690
|
-
console.log(
|
|
691
|
-
chalk.dim(
|
|
692
|
-
' Codex: profiling passes the SRAI MCP server directly to `codex exec` for this run.',
|
|
693
|
-
),
|
|
694
|
-
);
|
|
695
|
-
|
|
696
|
-
let runLogin = Boolean(options.profilerCodexLogin);
|
|
697
|
-
if (!runLogin && interactive) {
|
|
698
|
-
runLogin = await confirm({
|
|
699
|
-
message:
|
|
700
|
-
'Run Codex login in this terminal now? (Same init — profiling runs next. Choose No if already signed in.)',
|
|
701
|
-
default: true,
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
if (runLogin) {
|
|
705
|
-
console.log('');
|
|
706
|
-
console.log(chalk.bold.white(' Codex login'));
|
|
707
|
-
console.log(chalk.dim(' Complete the device-code prompt, then return here.\n'));
|
|
708
|
-
const loginResult = runCodexLogin(cwd);
|
|
709
|
-
if (loginResult.ok) {
|
|
710
|
-
console.log(chalk.green(' \u2713 Codex login step finished.'));
|
|
711
|
-
} else {
|
|
712
|
-
console.log(
|
|
713
|
-
chalk.yellow(
|
|
714
|
-
` \u26a0 Codex login exited with status ${loginResult.status ?? 'unknown'}. Profiling will still be attempted; sign in and re-run init if it fails.`,
|
|
715
|
-
),
|
|
716
|
-
);
|
|
717
|
-
}
|
|
718
|
-
console.log('');
|
|
719
|
-
}
|
|
720
|
-
} else {
|
|
721
|
-
console.log(chalk.dim(' (Sign-in or approvals may be required in your terminal.)'));
|
|
722
|
-
}
|
|
723
|
-
console.log('');
|
|
724
|
-
const profilerVerbose = Boolean(options.profilerVerbose || options.showProfilerLogs);
|
|
725
|
-
const showProfilerOutput = Boolean(
|
|
726
|
-
profilerVerbose || (agentTarget === 'cursor' && options.profilerNoTrust),
|
|
727
|
-
);
|
|
728
|
-
if (showProfilerOutput) {
|
|
729
|
-
console.log(chalk.dim(' Profiling in progress. Live profiler logs are visible for this run...'));
|
|
730
|
-
} else {
|
|
731
|
-
console.log(chalk.dim(' Profiling in progress. This can take a few minutes...'));
|
|
732
|
-
}
|
|
733
|
-
const pr = runProfilerAgent(cwd, {
|
|
734
|
-
target: agentTarget,
|
|
735
|
-
projectName: projectNameForSkill,
|
|
736
|
-
apiUrl: envVars?.apiUrl,
|
|
737
|
-
apiToken: envVars?.apiToken,
|
|
738
|
-
modelOverride: agentTarget === 'claude' ? claudeProfilerAuth?.model : undefined,
|
|
739
|
-
extraEnv: agentTarget === 'claude' ? claudeProfilerAuth?.envOverrides : undefined,
|
|
740
|
-
cursorTrust: !options.profilerNoTrust,
|
|
741
|
-
streamProgress: profilerVerbose,
|
|
742
|
-
showOutput: showProfilerOutput,
|
|
743
|
-
});
|
|
744
|
-
if (pr.ok) {
|
|
745
|
-
console.log(chalk.green(' \u2713 Profiler agent finished.'));
|
|
746
|
-
if (pr.logPath) {
|
|
747
|
-
console.log(chalk.dim(` Profiler log saved to ${pr.logPath}`));
|
|
748
|
-
}
|
|
749
|
-
} else {
|
|
750
|
-
const detail =
|
|
751
|
-
pr.message ||
|
|
752
|
-
(typeof pr.status === 'number' ? `exit status ${pr.status}` : 'unknown error');
|
|
753
|
-
console.log(
|
|
754
|
-
chalk.yellow(
|
|
755
|
-
` \u26a0 Profiler agent exited with an error: ${detail}. You can run the guardrails-init-profile workflow manually.`,
|
|
756
|
-
),
|
|
757
|
-
);
|
|
758
|
-
if (pr.logPath) {
|
|
759
|
-
console.log(chalk.dim(` Profiler log saved to ${pr.logPath}`));
|
|
760
|
-
}
|
|
761
|
-
if (agentTarget === 'cursor') {
|
|
762
|
-
console.log('');
|
|
763
|
-
console.log(chalk.dim(' Typical fixes:'));
|
|
764
|
-
console.log(
|
|
765
|
-
chalk.dim(
|
|
766
|
-
' • 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.',
|
|
767
|
-
),
|
|
768
|
-
);
|
|
769
|
-
console.log(
|
|
770
|
-
chalk.dim(
|
|
771
|
-
' • CLI missing: install from https://cursor.com/docs/cli/installation and verify `agent --version` (new installs use the `agent` command; `cursor-agent` is legacy).',
|
|
772
|
-
),
|
|
773
|
-
);
|
|
774
|
-
console.log(
|
|
775
|
-
chalk.dim(
|
|
776
|
-
' • Want interactive trust instead of `--trust`: run `securityreview-kit init ... --profiler-no-trust` and answer the prompts, then profile again.',
|
|
777
|
-
),
|
|
778
|
-
);
|
|
779
|
-
} else if (agentTarget === 'vscode') {
|
|
780
|
-
console.log('');
|
|
781
|
-
console.log(chalk.dim(' Typical fixes:'));
|
|
782
|
-
console.log(
|
|
783
|
-
chalk.dim(
|
|
784
|
-
' • Not signed in: re-run `securityreview-kit init` and choose Yes for GitHub Copilot CLI login, or pass `--profiler-copilot-login` with `--profile-repo`.',
|
|
785
|
-
),
|
|
786
|
-
);
|
|
787
|
-
console.log(
|
|
788
|
-
chalk.dim(
|
|
789
|
-
' • CLI missing: install GitHub Copilot CLI and verify `copilot --version`.',
|
|
790
|
-
),
|
|
791
|
-
);
|
|
792
|
-
console.log(
|
|
793
|
-
chalk.dim(
|
|
794
|
-
' • MCP missing: re-run init with VS Code selected and MCP installation enabled so `.vscode/mcp.json` is written.',
|
|
795
|
-
),
|
|
796
|
-
);
|
|
797
|
-
} else if (agentTarget === 'claude') {
|
|
798
|
-
console.log('');
|
|
799
|
-
console.log(chalk.dim(' Typical fixes:'));
|
|
800
|
-
console.log(
|
|
801
|
-
chalk.dim(
|
|
802
|
-
' • Choose the right auth mode: subscription, Console, API key, gateway, Bedrock, or Vertex.',
|
|
803
|
-
),
|
|
804
|
-
);
|
|
805
|
-
console.log(
|
|
806
|
-
chalk.dim(
|
|
807
|
-
' • CLI missing: install Claude Code and verify `claude --version`.',
|
|
808
|
-
),
|
|
809
|
-
);
|
|
810
|
-
console.log(
|
|
811
|
-
chalk.dim(
|
|
812
|
-
' • API key / gateway: provide `--claude-api-key`, or `--claude-base-url` plus `--claude-auth-token`, or set the matching env vars before re-running init.',
|
|
813
|
-
),
|
|
814
|
-
);
|
|
815
|
-
console.log(
|
|
816
|
-
chalk.dim(
|
|
817
|
-
' • Bedrock / Vertex: make sure your cloud credentials are already available in this shell before profiling.',
|
|
818
|
-
),
|
|
819
|
-
);
|
|
820
|
-
console.log(
|
|
821
|
-
chalk.dim(
|
|
822
|
-
' • MCP missing: re-run init with Claude Code selected and MCP installation enabled so `.mcp.json` is written and `.claude/settings.json` enables `security-review-mcp`.',
|
|
823
|
-
),
|
|
824
|
-
);
|
|
825
|
-
} else if (agentTarget === 'codex') {
|
|
826
|
-
console.log('');
|
|
827
|
-
console.log(chalk.dim(' Typical fixes:'));
|
|
828
|
-
console.log(
|
|
829
|
-
chalk.dim(
|
|
830
|
-
' • Not signed in: re-run `securityreview-kit init` and choose Yes for Codex login, or pass `--profiler-codex-login` with `--profile-repo`.',
|
|
831
|
-
),
|
|
832
|
-
);
|
|
833
|
-
console.log(
|
|
834
|
-
chalk.dim(
|
|
835
|
-
' • CLI missing: install Codex CLI and verify `codex --version`.',
|
|
836
|
-
),
|
|
837
|
-
);
|
|
838
|
-
console.log(
|
|
839
|
-
chalk.dim(
|
|
840
|
-
' • MCP missing: re-run init with Codex selected and MCP installation enabled so the SRAI credentials are available for profiling.',
|
|
841
|
-
),
|
|
842
|
-
);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
console.log('');
|
|
849
|
-
console.log(chalk.dim(' Run `securityreview-kit status` to verify your setup.'));
|
|
850
|
-
console.log('');
|
|
851
|
-
}
|