@securityreviewai/securityreview-kit 0.1.31 → 0.1.33

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.31",
3
+ "version": "0.1.33",
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
@@ -39,7 +39,11 @@ export function run() {
39
39
  )
40
40
  .option(
41
41
  '--profiler-quiet',
42
- 'When profiling, use minimal agent output (no streaming JSON / verbose progress)',
42
+ 'When profiling, use the standard progress message (default; retained for compatibility)',
43
+ )
44
+ .option(
45
+ '--profiler-verbose',
46
+ 'When profiling, show live agent output for troubleshooting',
43
47
  )
44
48
  .action(async (options) => {
45
49
  try {
@@ -457,11 +457,18 @@ export async function initCommand(options) {
457
457
  console.log(chalk.dim(' (Sign-in or approvals may be required in your terminal.)'));
458
458
  }
459
459
  console.log('');
460
+ const showProfilerOutput = Boolean(options.profilerVerbose || options.profilerNoTrust);
461
+ if (showProfilerOutput) {
462
+ console.log(chalk.dim(' Profiling in progress. Agent output is visible for this run...'));
463
+ } else {
464
+ console.log(chalk.dim(' Profiling in progress. This can take a few minutes...'));
465
+ }
460
466
  const pr = runProfilerAgent(cwd, {
461
467
  target: agentTarget,
462
468
  projectName: projectNameForSkill,
463
469
  cursorTrust: !options.profilerNoTrust,
464
- streamProgress: !options.profilerQuiet,
470
+ streamProgress: Boolean(options.profilerVerbose),
471
+ showOutput: showProfilerOutput,
465
472
  });
466
473
  if (pr.ok) {
467
474
  console.log(chalk.green(' \u2713 Profiler agent finished.'));
@@ -4,7 +4,7 @@
4
4
 
5
5
  > **Skill Reference:** Read and apply `.cursor/skills/threat-modelling/SKILL.md` for the full PWNISMS framework — the 7 threat categories, code generation rules, and post-generation checklist. You MUST walk through all 7 categories for every code task that seems security relevant.
6
6
  >
7
- > **Guardrail Selection Skill:** In IDEs that support workspace skills, read and apply `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md` when fetching guardrails. In other IDEs, follow that same workflow explicitly: analyze the task, shortlist relevant guardrails, and hydrate the exact shortlist with `get_guardrails_by_id`.
7
+ > **Guardrail Selection Skill:** In IDEs that support workspace skills, read and apply `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md` when fetching guardrails. In other IDEs, follow that same workflow explicitly: analyze the task, shortlist relevant guardrails, and hydrate the exact shortlist with `get_guardrail_by_id`.
8
8
 
9
9
  ### When NOT to apply
10
10
 
@@ -28,7 +28,7 @@ If in doubt, do a quick mental scan: *does this change touch auth, input handlin
28
28
  This workflow enforces security through two complementary pillars:
29
29
 
30
30
  1. **Secure by Design (PWNISMS)** — Universal threat-category analysis applied to every security-relevant task. Identifies risks and drives architectural/design-level mitigations.
31
- 2. **Secure by Code (Vibe Guardrails)** — Project-specific coding dos and don'ts fetched from SRAI via `get_guardrails`, shortlisted intentionally, and then hydrated via `get_guardrails_by_id`. Enforces concrete implementation rules derived from prior threat reviews, compliance requirements, and team decisions.
31
+ 2. **Secure by Code (Vibe Guardrails)** — Project-specific coding dos and don'ts fetched from SRAI via `get_guardrails`, shortlisted intentionally, and then hydrated via `get_guardrail_by_id`. Enforces concrete implementation rules derived from prior threat reviews, compliance requirements, and team decisions.
32
32
 
33
33
  Both pillars are **mandatory** for any code-generation task with a security surface. PWNISMS catches broad threats; guardrails enforce project-specific coding standards.
34
34
 
@@ -83,7 +83,7 @@ Rather than fetching the entire profile at once, walk through each reasoning blo
83
83
  10. **Fetch Vibe Guardrails** — Follow `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md`.
84
84
  - Call `get_guardrails` with `project_id` to load the broad catalog.
85
85
  - Analyze the task and infer the relevant security categories and likely threats first.
86
- - Shortlist only the guardrails relevant to the task and fetch the exact shortlist with `get_guardrails_by_id`.
86
+ - Shortlist only the guardrails relevant to the task and fetch the exact shortlist with `get_guardrail_by_id`.
87
87
  - Separate the shortlisted guardrails into `must` (mandatory requirements) and `must_not` (hard prohibitions).
88
88
  - Keep the shortlisted exact guardrails in context for the entire code-generation task and for later `ctm_sync`.
89
89
  - Cross-reference with PWNISMS findings: if a threat has no corresponding guardrail, flag it as a candidate for a new guardrail.
@@ -116,7 +116,7 @@ When running `ctm_sync` (dedicated agent/command where available, or the same st
116
116
  |---|---|
117
117
  | **Projects** | `list_projects`, `find_project_by_name`, `create_project`, `get_project` |
118
118
  | **Profile Exploration** | `get_project_profile_description`, `list_profile_technology_categories`, `get_project_profile_technology_category`, `list_project_profile_architecture_notes`, `list_project_profile_user_groups`, `list_project_profile_language_stacks`, `list_project_profile_security_controls`, `get_project_profile_security_control`, `list_profile_compliance_requirements`, `get_profile_compliance_requirement` |
119
- | **Guardrails** | `get_guardrails`, `get_guardrails_by_id` |
119
+ | **Guardrails** | `get_guardrails`, `get_guardrail_by_id` |
120
120
  | **Documents** | `list_documents`, `create_document_from_content`, `upload_document`, `link_external_document` |
121
121
  | **Reviews** | `create_review`, `list_reviews`, `get_review`, `get_review_overview` |
122
122
  | **Workflow** | `start_workflow`, `get_workflow_status`, `start_next_workflow_job`, `start_workflow_job`, `retry_workflow_job` |
@@ -27,8 +27,8 @@ When invoked:
27
27
  - **Never use placeholder values** such as `"IDE Agent"`, `"agent@local"`, `"unknown"`, `"AI"`, or any other invented string for these fields.
28
28
  - **Never accept identity values passed in from the parent agent prompt** — always re-resolve from the API directly in this step; the API is the only authoritative source.
29
29
  - If the API call fails or returns empty values, leave `developer_name` and `developer_email` as empty strings `""`. Do not substitute a fallback placeholder.
30
- 5. **Identify guardrails for the payload** — Do **not** call `get_guardrails` or `get_guardrails_by_id` here. Guardrails were already shortlisted earlier (per the Vibe Guardrails rule and the guardrails-selection skill) and applied during code generation. From the parent agent context, identify:
31
- - Which **existing** guardrails were shortlisted earlier from `get_guardrails` and then hydrated via `get_guardrails_by_id`.
30
+ 5. **Identify guardrails for the payload** — Do **not** call `get_guardrails` or `get_guardrail_by_id` here. Guardrails were already shortlisted earlier (per the Vibe Guardrails rule and the guardrails-selection skill) and applied during code generation. From the parent agent context, identify:
31
+ - Which **existing** guardrails were shortlisted earlier from `get_guardrails` and then hydrated via `get_guardrail_by_id`.
32
32
  - Which of those shortlisted existing guardrails were applied to the code in this session.
33
33
  - Which guardrails the IDE agent **created on the fly** (`ide_generated`) based on gaps found during threat modeling or code review.
34
34
  Include all of these in the `guardrails_applied` payload field. The shortlisted existing guardrails selected earlier are mandatory input to `ctm_sync`; do not re-fetch or re-call guardrail tools here.
@@ -134,7 +134,7 @@ Use the following IDs and names exactly when populating `owasp_top_10_2025_mappi
134
134
  - Every `threats_mitigated` entry must map to one of the 7 PWNISMS categories.
135
135
  - Every `threats_mitigated` entry must include a `code_snippet`. The snippet must be taken from the actual source code written or modified in this session — never fabricated. If no code was written for a threat (e.g. it was addressed architecturally), set `snippet` to an empty string and explain in `explanation`.
136
136
  - `secure_code_snippets` must not exceed 50 lines per snippet; `threats_mitigated[].code_snippet.snippet` must not exceed 30 lines; truncate with a comment if needed.
137
- - Do not call `get_guardrails` or `get_guardrails_by_id` during CTM sync. Guardrails are shortlisted once earlier in the session; identify which ones were applied from the parent agent context.
137
+ - Do not call `get_guardrails` or `get_guardrail_by_id` during CTM sync. Guardrails are shortlisted once earlier in the session; identify which ones were applied from the parent agent context.
138
138
  - Guardrails shortlisted earlier by the IDE must be included in `guardrails_applied` even when some were only partially satisfied. Use `satisfied: false` plus `notes` instead of silently dropping them.
139
139
  - `guardrails_applied` entries with `source: "existing"` must reference guardrails by the exact `title` they had when fetched at session start.
140
140
  - `guardrails_applied` entries with `source: "ide_generated"` are new guardrails the IDE agent created based on gaps found during threat modeling or code review.
@@ -38,7 +38,7 @@ This rule is **triggered** whenever the assistant:
38
38
  - "Update the data flow for the auth system"
39
39
  - "Refine the mitigations for threat X"
40
40
  - **Enforces guardrails** during code generation and has compliance data:
41
- - Guardrails were shortlisted via the guardrails-selection workflow (`get_guardrails` + `get_guardrails_by_id`) and applied to generated code
41
+ - Guardrails were shortlisted via the guardrails-selection workflow (`get_guardrails` + `get_guardrail_by_id`) and applied to generated code
42
42
  - New guardrails were created by the IDE agent based on gaps found
43
43
  - Existing guardrails were flagged as unsatisfiable or conflicting
44
44
 
@@ -21,11 +21,11 @@ Do **not** ask the user to verbally approve MCP for `security-review-mcp`. The r
21
21
 
22
22
  ## Cursor CLI (scripted)
23
23
 
24
- From the repo root, non-interactive runs should include workspace trust, MCP approval, and **streaming progress** (matches default `securityreview-kit init`):
24
+ From the repo root, non-interactive runs should include workspace trust and MCP approval:
25
25
 
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)
26
+ `agent -p "<your profiling instructions>" --trust --approve-mcps` (or `cursor-agent` if that is what your install provides)
27
27
 
28
- Omit `--output-format` / `--stream-partial-output` if you want less verbose terminal output (or use `securityreview-kit init` with `--profiler-quiet`).
28
+ Add `--output-format stream-json --stream-partial-output` only when you need verbose agent diagnostics (or use `securityreview-kit init` with `--profiler-verbose`).
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
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: guardrails-selection
3
- description: Analyze the developer request, infer the security categories and likely threats involved, shortlist the most relevant project guardrails, then hydrate the exact guardrails with get_guardrails_by_id before implementation. Use for every security-relevant code task before code is written and preserve the shortlist for CTM sync.
3
+ description: Analyze the developer request, infer the security categories and likely threats involved, shortlist the most relevant project guardrails, then hydrate the exact guardrails with get_guardrail_by_id before implementation. Use for every security-relevant code task before code is written and preserve the shortlist for CTM sync.
4
4
  ---
5
5
 
6
6
  # Guardrails Selection
@@ -15,7 +15,7 @@ This skill exists to stop the IDE from treating the full `get_guardrails` result
15
15
  2. Infer which security categories are in play.
16
16
  3. Predict the threats that might occur for this exact task.
17
17
  4. Shortlist only the guardrails that mitigate those threats.
18
- 5. Fetch the exact shortlisted guardrails with `get_guardrails_by_id`.
18
+ 5. Fetch the exact shortlisted guardrails with `get_guardrail_by_id`.
19
19
  6. Carry that same shortlist forward into implementation and `ctm_sync`.
20
20
 
21
21
  Do not skip the analysis step. Do not rely on title-matching alone. Do not dump every guardrail into the final answer.
@@ -126,13 +126,13 @@ Examples:
126
126
 
127
127
  ### Step 3: Hydrate exact shortlisted guardrails
128
128
 
129
- For every shortlisted existing guardrail, call `get_guardrails_by_id` to retrieve the exact guardrail that will govern implementation.
129
+ For every shortlisted existing guardrail, call `get_guardrail_by_id` to retrieve the exact guardrail that will govern implementation.
130
130
 
131
- - Use `get_guardrails_by_id` for the shortlisted ids only.
131
+ - Use `get_guardrail_by_id` for the shortlisted ids only.
132
132
  - If the tool supports batching, batch the shortlisted ids.
133
133
  - If the tool only supports one id at a time, call it once per shortlisted id.
134
134
 
135
- Implementation must be driven by the hydrated shortlist from `get_guardrails_by_id`, not by vague memory from the broad catalog listing.
135
+ Implementation must be driven by the hydrated shortlist from `get_guardrail_by_id`, not by vague memory from the broad catalog listing.
136
136
 
137
137
  ### Step 4: Track the active shortlist in context
138
138
 
@@ -162,7 +162,7 @@ When deciding whether a guardrail applies, prefer security-preserving inclusion
162
162
 
163
163
  ## CTM Sync Handoff Contract
164
164
 
165
- `ctm_sync` must reuse the shortlist from this skill. It must not call `get_guardrails` or `get_guardrails_by_id` again.
165
+ `ctm_sync` must reuse the shortlist from this skill. It must not call `get_guardrails` or `get_guardrail_by_id` again.
166
166
 
167
167
  Before `ctm_sync` is invoked, ensure the parent context clearly contains:
168
168
 
@@ -10,7 +10,7 @@ The intent is not to produce a full threat model here. The intent is to make sur
10
10
  2. Infer the categories involved in the implementation.
11
11
  3. Use the mappings below to identify likely threat families.
12
12
  4. Shortlist guardrails that directly or adjacently mitigate those threats.
13
- 5. Fetch those guardrails with `get_guardrails_by_id`.
13
+ 5. Fetch those guardrails with `get_guardrail_by_id`.
14
14
 
15
15
  ## STRIDE-style mappings for guardrail selection
16
16
 
@@ -33,16 +33,16 @@ That skill is mandatory because the IDE must not leave guardrail selection to ch
33
33
  - **Resolve the project** — use `find_project_by_name` with `name="<SRAI_PROJECT_NAME>"` to obtain `project_id`.
34
34
  - **Load the broad catalog** — call `get_guardrails` with `project_id`.
35
35
  - **Shortlist intentionally** — choose only the guardrails that mitigate the categories and likely threats for the current task.
36
- - **Hydrate the exact shortlist** — call `get_guardrails_by_id` for the shortlisted guardrail ids so implementation uses the exact selected guardrails, not a vague reading of the full catalog.
36
+ - **Hydrate the exact shortlist** — call `get_guardrail_by_id` for the shortlisted guardrail ids so implementation uses the exact selected guardrails, not a vague reading of the full catalog.
37
37
  - **Preserve the shortlist** — keep the selected existing guardrails in context so `ctm_sync` can include the same guardrails later without re-querying.
38
38
 
39
- The broad `get_guardrails` result is only the candidate catalog. The active implementation guardrails must come from the shortlisted `get_guardrails_by_id` result.
39
+ The broad `get_guardrails` result is only the candidate catalog. The active implementation guardrails must come from the shortlisted `get_guardrail_by_id` result.
40
40
 
41
41
  Each guardrail used for implementation should preserve these fields:
42
42
 
43
43
  | Field | Description |
44
44
  |---|---|
45
- | `id` | Stable guardrail identifier used with `get_guardrails_by_id` |
45
+ | `id` | Stable guardrail identifier used with `get_guardrail_by_id` |
46
46
  | `title` | Short name of the guardrail |
47
47
  | `rule_type` | `must` (mandatory) or `must_not` (prohibition) |
48
48
  | `category` | Grouping label (e.g. `authentication`, `input_validation`, `secrets`) or `null` |
@@ -90,5 +90,5 @@ Guardrails are living artifacts. The IDE agent can create, apply, and update the
90
90
  | Tool | Purpose |
91
91
  |---|---|
92
92
  | `get_guardrails` | Fetch the broad project guardrails catalog (requires `project_id`) |
93
- | `get_guardrails_by_id` | Fetch the exact shortlisted guardrails that will govern implementation |
93
+ | `get_guardrail_by_id` | Fetch the exact shortlisted guardrails that will govern implementation |
94
94
  | `find_project_by_name` | Resolve project by name to get `project_id` |
@@ -3,7 +3,7 @@
3
3
  "hooks": {
4
4
  "sessionStart": [
5
5
  {
6
- "command": "printf '%s\\n' '{\"additional_context\":\"## MANDATORY SECURITY GATE (Session Policy)\\n\\nFor ANY user request that has security impact (auth, crypto, input handling, secrets, network, data storage, dependencies, new APIs/endpoints, or any code handling untrusted data):\\n\\n**Plan order:**\\n1. **Fetch Vibe Guardrails (first)** - Use .cursor/skills/guardrails-selection/SKILL.md. Analyze the task, call get_guardrails from security-review-mcp to load the broad catalog, shortlist the relevant guardrails, then call get_guardrails_by_id for the shortlist. These exact shortlisted guardrails are the hard constraints on generated code.\\n2. **PWNISMS threat modeling (second)** - Run threat modeling as a guide before writing code. Use .cursor/skills/threat-modelling/SKILL.md. Cross-reference guardrails with PWNISMS findings to identify gaps. Document findings to inform secure implementation.\\n3. **Implement secure code (third)** - Use the threat model findings AND shortlisted guardrails as guides to create secure code. Every must guardrail must be satisfied; no must_not guardrail may be violated.\\n4. **ctm_sync (last)** - Push payload to SRAI only after implementation is done. Invoke ctm_sync agent (Task tool, subagent_type=ctm_sync) as the final step. Include the exact shortlisted guardrails from earlier plus any IDE-generated guardrails in the guardrails_applied payload field; do not query again during ctm_sync. See .cursor/agents/ctm_sync.md and .cursor/rules/ctm_sync_rule.mdc.\\n\\nNo blocking. No deferral. Guardrails enforce secure code; threat modeling guides the design; ctm_sync runs last to push to SRAI. Both ctm_sync and guardrail compliance are mandatory and should be part of your to-do.\"}'",
6
+ "command": "printf '%s\\n' '{\"additional_context\":\"## MANDATORY SECURITY GATE (Session Policy)\\n\\nFor ANY user request that has security impact (auth, crypto, input handling, secrets, network, data storage, dependencies, new APIs/endpoints, or any code handling untrusted data):\\n\\n**Plan order:**\\n1. **Fetch Vibe Guardrails (first)** - Use .cursor/skills/guardrails-selection/SKILL.md. Analyze the task, call get_guardrails from security-review-mcp to load the broad catalog, shortlist the relevant guardrails, then call get_guardrail_by_id for the shortlist. These exact shortlisted guardrails are the hard constraints on generated code.\\n2. **PWNISMS threat modeling (second)** - Run threat modeling as a guide before writing code. Use .cursor/skills/threat-modelling/SKILL.md. Cross-reference guardrails with PWNISMS findings to identify gaps. Document findings to inform secure implementation.\\n3. **Implement secure code (third)** - Use the threat model findings AND shortlisted guardrails as guides to create secure code. Every must guardrail must be satisfied; no must_not guardrail may be violated.\\n4. **ctm_sync (last)** - Push payload to SRAI only after implementation is done. Invoke ctm_sync agent (Task tool, subagent_type=ctm_sync) as the final step. Include the exact shortlisted guardrails from earlier plus any IDE-generated guardrails in the guardrails_applied payload field; do not query again during ctm_sync. See .cursor/agents/ctm_sync.md and .cursor/rules/ctm_sync_rule.mdc.\\n\\nNo blocking. No deferral. Guardrails enforce secure code; threat modeling guides the design; ctm_sync runs last to push to SRAI. Both ctm_sync and guardrail compliance are mandatory and should be part of your to-do.\"}'",
7
7
  "timeout": 5
8
8
  }
9
9
  ]
@@ -33,7 +33,7 @@ Before deep analysis, pull project context from `security-review-mcp` (if availa
33
33
  - `get_data_dictionaries` — sensitive data assets
34
34
  - `get_security_objectives` — compliance targets
35
35
  - `get_findings` — aggregated insights
36
- 10. **Vibe Guardrails** — Use `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md` with `project_id` to load the guardrail catalog, shortlist the relevant guardrails, and hydrate the exact implementation set with `get_guardrails_by_id`.
36
+ 10. **Vibe Guardrails** — Use `{{GUARDRAILS_SELECTION_SKILL_DIR}}/SKILL.md` with `project_id` to load the guardrail catalog, shortlist the relevant guardrails, and hydrate the exact implementation set with `get_guardrail_by_id`.
37
37
 
38
38
  If SRAI is not available, proceed with whatever context the user provides — files, diffs, PRs, architecture docs.
39
39
 
@@ -224,7 +224,7 @@ The `ctm_sync` agent builds and pushes an event payload containing:
224
224
  - **Threat model findings**: threats mitigated, PWNISMS categories, severities, mitigations applied
225
225
  - **Best practices achieved**: security patterns followed during implementation
226
226
  - **Secure code snippets**: security-relevant code with explanations
227
- - **Guardrails applied**: all guardrails enforced during this session — both existing ones shortlisted earlier via `get_guardrails` + `get_guardrails_by_id` (`source: "existing"`) and new ones the IDE agent created on the fly (`source: "ide_generated"`), each with satisfaction status
227
+ - **Guardrails applied**: all guardrails enforced during this session — both existing ones shortlisted earlier via `get_guardrails` + `get_guardrail_by_id` (`source: "existing"`) and new ones the IDE agent created on the fly (`source: "ide_generated"`), each with satisfaction status
228
228
  - **Project profile updates**: architecture notes, tech categories, user groups, compliance requirements, language stacks
229
229
 
230
230
  ### How to invoke
@@ -64,18 +64,24 @@ export function pickProfilerAgentTarget(targets) {
64
64
  * @param {boolean} [opts.cursorTrust=true] When true, passes `--trust` and `--approve-mcps` so headless init is not blocked by
65
65
  * workspace trust or MCP approval (user confirmed profiling in the kit). Set false with `--profiler-no-trust`
66
66
  * if you need an interactive trust/login/MCP flow in the same terminal.
67
- * @param {boolean} [opts.streamProgress=true] When true, pass each CLI’s streaming / verbose flags so the terminal shows live progress
68
- * (JSON event lines on Cursor/Codex; stream-json + partial messages + verbose on Claude). Disable with `--profiler-quiet`.
67
+ * @param {boolean} [opts.streamProgress=false] When true, pass each CLI’s streaming / verbose flags.
68
+ * @param {boolean} [opts.showOutput=false] When true, inherit stdio from the child process.
69
+ * Keep both false for init-time profiling so the agent does not flood the terminal with JSON/progress logs.
69
70
  */
70
- export function runProfilerAgent(cwd, { target, projectName, cursorTrust = true, streamProgress = true }) {
71
+ export function runProfilerAgent(
72
+ cwd,
73
+ { target, projectName, cursorTrust = true, streamProgress = false, showOutput = streamProgress },
74
+ ) {
71
75
  const prompt = buildProfilerAgentPrompt(projectName, target);
72
76
  const env = augmentPathEnv(process.env);
73
- const opts = { cwd, stdio: 'inherit', env };
77
+ const opts = showOutput
78
+ ? { cwd, stdio: 'inherit', env }
79
+ : { cwd, stdio: ['ignore', 'pipe', 'pipe'], env, encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 };
74
80
 
75
81
  if (streamProgress) {
76
82
  console.error(
77
83
  '\n[securityreview-kit] Profiler live output: you should see streaming progress below ' +
78
- '(JSON lines are normal). Use --profiler-quiet for minimal output.\n',
84
+ '(JSON lines are normal). Omit --profiler-verbose for minimal output.\n',
79
85
  );
80
86
  }
81
87
 
@@ -99,7 +105,7 @@ export function runProfilerAgent(cwd, { target, projectName, cursorTrust = true,
99
105
  if (r.error) {
100
106
  return { ok: false, message: r.error.message };
101
107
  }
102
- return { ok: r.status === 0, status: r.status };
108
+ return buildProfilerResult(r);
103
109
  }
104
110
 
105
111
  if (target === 'claude') {
@@ -110,7 +116,7 @@ export function runProfilerAgent(cwd, { target, projectName, cursorTrust = true,
110
116
  ? ['-p', '--output-format', 'stream-json', '--include-partial-messages', '--verbose', prompt]
111
117
  : ['-p', prompt];
112
118
  const r = spawnSync('claude', args, opts);
113
- return { ok: r.status === 0, status: r.status };
119
+ return buildProfilerResult(r);
114
120
  }
115
121
 
116
122
  if (target === 'codex') {
@@ -119,8 +125,42 @@ export function runProfilerAgent(cwd, { target, projectName, cursorTrust = true,
119
125
  }
120
126
  const args = streamProgress ? ['exec', '--json', prompt] : ['exec', prompt];
121
127
  const r = spawnSync('codex', args, opts);
122
- return { ok: r.status === 0, status: r.status };
128
+ return buildProfilerResult(r);
123
129
  }
124
130
 
125
131
  return { ok: false, message: 'unsupported agent target' };
126
132
  }
133
+
134
+ function buildProfilerResult(result) {
135
+ if (result.error) {
136
+ return { ok: false, status: result.status, message: result.error.message };
137
+ }
138
+
139
+ if (result.status === 0) {
140
+ return { ok: true, status: result.status };
141
+ }
142
+
143
+ const outputTail = summarizeProfilerOutput(result.stderr || result.stdout || '');
144
+ const exitDetail =
145
+ typeof result.status === 'number'
146
+ ? `exit status ${result.status}`
147
+ : result.signal
148
+ ? `signal ${result.signal}`
149
+ : 'unknown error';
150
+
151
+ return {
152
+ ok: false,
153
+ status: result.status,
154
+ message: outputTail ? `${exitDetail}; last output: ${outputTail}` : exitDetail,
155
+ };
156
+ }
157
+
158
+ function summarizeProfilerOutput(output) {
159
+ return String(output || '')
160
+ .replace(/\u001b\[[0-9;]*m/g, '')
161
+ .split(/\r?\n/)
162
+ .map((line) => line.trim())
163
+ .filter(Boolean)
164
+ .slice(-3)
165
+ .join(' | ');
166
+ }