code-auditor-mcp 3.0.5 → 3.0.7
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/.claude-plugin/marketplace.json +1 -1
- package/README.md +31 -1
- package/dist/cli.js +416 -111
- package/dist/cli.js.map +1 -1
- package/dist/generators/AWSQConfigGenerator.d.ts +20 -0
- package/dist/generators/AWSQConfigGenerator.d.ts.map +1 -1
- package/dist/generators/AWSQConfigGenerator.js +48 -19
- package/dist/generators/AWSQConfigGenerator.js.map +1 -1
- package/dist/generators/AiderConfigGenerator.d.ts +18 -0
- package/dist/generators/AiderConfigGenerator.d.ts.map +1 -1
- package/dist/generators/AiderConfigGenerator.js +48 -10
- package/dist/generators/AiderConfigGenerator.js.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.d.ts +9 -3
- package/dist/generators/ClaudeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClaudeConfigGenerator.js +30 -42
- package/dist/generators/ClaudeConfigGenerator.js.map +1 -1
- package/dist/generators/ClineConfigGenerator.d.ts +13 -0
- package/dist/generators/ClineConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ClineConfigGenerator.js +44 -11
- package/dist/generators/ClineConfigGenerator.js.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.d.ts +15 -0
- package/dist/generators/CodeiumConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CodeiumConfigGenerator.js +45 -15
- package/dist/generators/CodeiumConfigGenerator.js.map +1 -1
- package/dist/generators/CodexConfigGenerator.d.ts +29 -0
- package/dist/generators/CodexConfigGenerator.d.ts.map +1 -0
- package/dist/generators/CodexConfigGenerator.js +80 -0
- package/dist/generators/CodexConfigGenerator.js.map +1 -0
- package/dist/generators/ConfigGeneratorFactory.d.ts +10 -1
- package/dist/generators/ConfigGeneratorFactory.d.ts.map +1 -1
- package/dist/generators/ConfigGeneratorFactory.js +22 -8
- package/dist/generators/ConfigGeneratorFactory.js.map +1 -1
- package/dist/generators/ContinueConfigGenerator.d.ts +16 -1
- package/dist/generators/ContinueConfigGenerator.d.ts.map +1 -1
- package/dist/generators/ContinueConfigGenerator.js +41 -73
- package/dist/generators/ContinueConfigGenerator.js.map +1 -1
- package/dist/generators/CopilotConfigGenerator.d.ts +13 -2
- package/dist/generators/CopilotConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CopilotConfigGenerator.js +31 -51
- package/dist/generators/CopilotConfigGenerator.js.map +1 -1
- package/dist/generators/CursorConfigGenerator.d.ts +8 -1
- package/dist/generators/CursorConfigGenerator.d.ts.map +1 -1
- package/dist/generators/CursorConfigGenerator.js +27 -51
- package/dist/generators/CursorConfigGenerator.js.map +1 -1
- package/dist/generators/GeminiConfigGenerator.d.ts +23 -0
- package/dist/generators/GeminiConfigGenerator.d.ts.map +1 -0
- package/dist/generators/GeminiConfigGenerator.js +59 -0
- package/dist/generators/GeminiConfigGenerator.js.map +1 -0
- package/dist/generators/JetBrainsConfigGenerator.d.ts +11 -0
- package/dist/generators/JetBrainsConfigGenerator.d.ts.map +1 -1
- package/dist/generators/JetBrainsConfigGenerator.js +39 -21
- package/dist/generators/JetBrainsConfigGenerator.js.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.d.ts +14 -0
- package/dist/generators/VSCodeConfigGenerator.d.ts.map +1 -1
- package/dist/generators/VSCodeConfigGenerator.js +43 -15
- package/dist/generators/VSCodeConfigGenerator.js.map +1 -1
- package/dist/hooks/codex.d.ts +20 -0
- package/dist/hooks/codex.d.ts.map +1 -0
- package/dist/hooks/codex.js +91 -0
- package/dist/hooks/codex.js.map +1 -0
- package/dist/hooks/core.d.ts +62 -0
- package/dist/hooks/core.d.ts.map +1 -0
- package/dist/hooks/core.js +119 -0
- package/dist/hooks/core.js.map +1 -0
- package/dist/hooks/cursor.d.ts +72 -0
- package/dist/hooks/cursor.d.ts.map +1 -0
- package/dist/hooks/cursor.js +163 -0
- package/dist/hooks/cursor.js.map +1 -0
- package/dist/installer.d.ts +30 -0
- package/dist/installer.d.ts.map +1 -0
- package/dist/installer.js +355 -0
- package/dist/installer.js.map +1 -0
- package/dist/mcp-tools/whitelistTools.d.ts +1 -1
- package/package.json +3 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/skills/code-auditor/SKILL.md +13 -5
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Codex hook adapter — `code-audit codex-hook`.
|
|
4
|
+
*
|
|
5
|
+
* Reads Codex's PostToolUse stdin JSON and runs a diff-scoped audit.
|
|
6
|
+
*
|
|
7
|
+
* Phase 0 verification (2026-07-19):
|
|
8
|
+
* - Source: learn.chatgpt.com/docs/hooks
|
|
9
|
+
* - PostToolUse is BLOCKING: exit 2 replaces the tool result with feedback
|
|
10
|
+
* - Matcher in hooks.json: "apply_patch" (Codex's file-editing tool)
|
|
11
|
+
* - stdin shape: { session_id, tool_name, tool_use_id, tool_input, tool_response }
|
|
12
|
+
* - tool_input.file_path contains the edited file
|
|
13
|
+
*
|
|
14
|
+
* Exit codes:
|
|
15
|
+
* 0 — no critical violations found
|
|
16
|
+
* 2 — critical violations found (blocking feedback loop)
|
|
17
|
+
* 1 — internal error (adapter crash, not violation-related)
|
|
18
|
+
*/
|
|
19
|
+
import { readStdin, runHookAudit } from './core.js';
|
|
20
|
+
import chalk from 'chalk';
|
|
21
|
+
async function main() {
|
|
22
|
+
const raw = await readStdin();
|
|
23
|
+
if (!raw) {
|
|
24
|
+
// No stdin data — nothing to audit
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
let event;
|
|
28
|
+
try {
|
|
29
|
+
event = JSON.parse(raw);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
console.error(chalk.red('[code-auditor codex-hook] Failed to parse stdin JSON'));
|
|
33
|
+
process.exit(0); // Degrade gracefully
|
|
34
|
+
}
|
|
35
|
+
const filePath = event.tool_input?.file_path || event.tool_input?.path;
|
|
36
|
+
if (!filePath) {
|
|
37
|
+
// No file path — nothing to audit
|
|
38
|
+
process.exit(0);
|
|
39
|
+
}
|
|
40
|
+
const projectRoot = process.cwd();
|
|
41
|
+
try {
|
|
42
|
+
const output = await runHookAudit({
|
|
43
|
+
filePaths: [filePath],
|
|
44
|
+
projectRoot,
|
|
45
|
+
failOn: 'critical',
|
|
46
|
+
});
|
|
47
|
+
if (output.violations.length > 0) {
|
|
48
|
+
// Codex PostToolUse: exit 2 replaces tool result with feedback
|
|
49
|
+
// Write violations as JSON to stdout — Codex feeds this back to the agent
|
|
50
|
+
const criticals = output.violations.filter((v) => v.severity === 'critical');
|
|
51
|
+
if (criticals.length > 0) {
|
|
52
|
+
const feedback = {
|
|
53
|
+
decision: 'block',
|
|
54
|
+
reason: `code-auditor found ${criticals.length} critical violation(s)`,
|
|
55
|
+
violations: output.violations.map((v) => ({
|
|
56
|
+
severity: v.severity,
|
|
57
|
+
message: v.message,
|
|
58
|
+
file: v.file,
|
|
59
|
+
line: v.line,
|
|
60
|
+
rule: v.rule,
|
|
61
|
+
suggestion: v.suggestion || null,
|
|
62
|
+
})),
|
|
63
|
+
};
|
|
64
|
+
process.stdout.write(JSON.stringify(feedback, null, 2) + '\n');
|
|
65
|
+
process.exit(2);
|
|
66
|
+
}
|
|
67
|
+
// Non-critical violations: report but don't block
|
|
68
|
+
const warnings = output.violations.filter((v) => v.severity === 'warning');
|
|
69
|
+
if (warnings.length > 0) {
|
|
70
|
+
const advisory = {
|
|
71
|
+
decision: 'allow',
|
|
72
|
+
warnings: warnings.map((v) => ({
|
|
73
|
+
message: v.message,
|
|
74
|
+
file: v.file,
|
|
75
|
+
line: v.line,
|
|
76
|
+
suggestion: v.suggestion || null,
|
|
77
|
+
})),
|
|
78
|
+
};
|
|
79
|
+
process.stdout.write(JSON.stringify(advisory, null, 2) + '\n');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
process.exit(0);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
// Internal error — report and exit 0 (never wedge the agent loop on errors)
|
|
86
|
+
console.error(chalk.red(`[code-auditor codex-hook] Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
87
|
+
process.exit(0);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
main();
|
|
91
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/hooks/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared hook adapter core — reads stdin, runs a diff-scoped audit, writes stdout.
|
|
3
|
+
*
|
|
4
|
+
* Each tool's hook adapter (cursor.ts, codex.ts) is a thin payload mapper that:
|
|
5
|
+
* 1. Reads the tool-specific stdin JSON
|
|
6
|
+
* 2. Extracts edited file paths
|
|
7
|
+
* 3. Calls runHookAudit() with those paths
|
|
8
|
+
* 4. Maps audit results back to the tool's stdout contract
|
|
9
|
+
*
|
|
10
|
+
* Phase 0 live-docs verification (2026-07-20):
|
|
11
|
+
* - Cursor postToolUse (Write matcher): BLOCKING (exit 2, additional_context)
|
|
12
|
+
* Verified: cursor.com/docs/hooks + cursor.com/docs/reference/third-party-hooks
|
|
13
|
+
* - Cursor afterFileEdit: EXISTS but observation-only — NOT used for blocking
|
|
14
|
+
* - Codex PostToolUse: blocking (exit 2 replaces tool result with feedback)
|
|
15
|
+
* Verified: learn.chatgpt.com/docs/extend/hooks
|
|
16
|
+
* - Claude Code PostToolUse: blocking (exit 2 blocks the edit)
|
|
17
|
+
* Cursor reads .claude/settings.json natively via third-party hooks compat
|
|
18
|
+
*/
|
|
19
|
+
import type { Severity } from '../types.js';
|
|
20
|
+
export interface HookAuditInput {
|
|
21
|
+
filePaths: string[];
|
|
22
|
+
projectRoot: string;
|
|
23
|
+
failOn: Severity;
|
|
24
|
+
}
|
|
25
|
+
export interface HookViolation {
|
|
26
|
+
analyzer: string;
|
|
27
|
+
rule: string;
|
|
28
|
+
severity: Severity;
|
|
29
|
+
message: string;
|
|
30
|
+
file: string;
|
|
31
|
+
line?: number;
|
|
32
|
+
column?: number;
|
|
33
|
+
endLine?: number;
|
|
34
|
+
endColumn?: number;
|
|
35
|
+
enclosingSymbol?: string;
|
|
36
|
+
suggestion?: string;
|
|
37
|
+
details?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface HookAuditOutput {
|
|
40
|
+
violations: HookViolation[];
|
|
41
|
+
summary: {
|
|
42
|
+
total: number;
|
|
43
|
+
critical: number;
|
|
44
|
+
warning: number;
|
|
45
|
+
suggestion: number;
|
|
46
|
+
};
|
|
47
|
+
filesAnalyzed: number;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Read all of stdin as a string. Returns empty string on EOF/error.
|
|
51
|
+
*/
|
|
52
|
+
export declare function readStdin(): Promise<string>;
|
|
53
|
+
/**
|
|
54
|
+
* Run a diff-scoped audit on the given file paths.
|
|
55
|
+
* This is the shared core — all hook adapters call this.
|
|
56
|
+
*/
|
|
57
|
+
export declare function runHookAudit(input: HookAuditInput): Promise<HookAuditOutput>;
|
|
58
|
+
/**
|
|
59
|
+
* Check if violations at or above failOn severity exist.
|
|
60
|
+
*/
|
|
61
|
+
export declare function hasViolationsAtOrAbove(violations: HookViolation[], failOn: Severity): boolean;
|
|
62
|
+
//# sourceMappingURL=core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../src/hooks/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,KAAK,EAAE,QAAQ,EAAc,MAAM,aAAa,CAAC;AAExD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,QAAQ,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,OAAO,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CA+BjD;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CA4DlF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,aAAa,EAAE,EAC3B,MAAM,EAAE,QAAQ,GACf,OAAO,CAOT"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared hook adapter core — reads stdin, runs a diff-scoped audit, writes stdout.
|
|
3
|
+
*
|
|
4
|
+
* Each tool's hook adapter (cursor.ts, codex.ts) is a thin payload mapper that:
|
|
5
|
+
* 1. Reads the tool-specific stdin JSON
|
|
6
|
+
* 2. Extracts edited file paths
|
|
7
|
+
* 3. Calls runHookAudit() with those paths
|
|
8
|
+
* 4. Maps audit results back to the tool's stdout contract
|
|
9
|
+
*
|
|
10
|
+
* Phase 0 live-docs verification (2026-07-20):
|
|
11
|
+
* - Cursor postToolUse (Write matcher): BLOCKING (exit 2, additional_context)
|
|
12
|
+
* Verified: cursor.com/docs/hooks + cursor.com/docs/reference/third-party-hooks
|
|
13
|
+
* - Cursor afterFileEdit: EXISTS but observation-only — NOT used for blocking
|
|
14
|
+
* - Codex PostToolUse: blocking (exit 2 replaces tool result with feedback)
|
|
15
|
+
* Verified: learn.chatgpt.com/docs/extend/hooks
|
|
16
|
+
* - Claude Code PostToolUse: blocking (exit 2 blocks the edit)
|
|
17
|
+
* Cursor reads .claude/settings.json natively via third-party hooks compat
|
|
18
|
+
*/
|
|
19
|
+
import { createInterface } from 'node:readline';
|
|
20
|
+
import { resolve } from 'node:path';
|
|
21
|
+
import { createAuditRunner } from '../auditRunner.js';
|
|
22
|
+
import { initParsers } from '../languages/index.js';
|
|
23
|
+
/**
|
|
24
|
+
* Read all of stdin as a string. Returns empty string on EOF/error.
|
|
25
|
+
*/
|
|
26
|
+
export async function readStdin() {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
// If stdin is a TTY, there's nothing to read
|
|
29
|
+
if (process.stdin.isTTY) {
|
|
30
|
+
resolve('');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const rl = createInterface({
|
|
34
|
+
input: process.stdin,
|
|
35
|
+
output: undefined,
|
|
36
|
+
terminal: false,
|
|
37
|
+
});
|
|
38
|
+
const lines = [];
|
|
39
|
+
rl.on('line', (line) => {
|
|
40
|
+
lines.push(line);
|
|
41
|
+
});
|
|
42
|
+
rl.on('close', () => {
|
|
43
|
+
resolve(lines.join('\n'));
|
|
44
|
+
});
|
|
45
|
+
rl.on('error', () => {
|
|
46
|
+
resolve(lines.join('\n'));
|
|
47
|
+
});
|
|
48
|
+
// Timeout: if nothing arrives in 5s, assume no input
|
|
49
|
+
setTimeout(() => {
|
|
50
|
+
rl.close();
|
|
51
|
+
resolve(lines.join('\n'));
|
|
52
|
+
}, 5000).unref();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Run a diff-scoped audit on the given file paths.
|
|
57
|
+
* This is the shared core — all hook adapters call this.
|
|
58
|
+
*/
|
|
59
|
+
export async function runHookAudit(input) {
|
|
60
|
+
// Initialize parsers (idempotent once loaded)
|
|
61
|
+
await initParsers();
|
|
62
|
+
if (input.filePaths.length === 0) {
|
|
63
|
+
return {
|
|
64
|
+
violations: [],
|
|
65
|
+
summary: { total: 0, critical: 0, warning: 0, suggestion: 0 },
|
|
66
|
+
filesAnalyzed: 0,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// Convert to absolute paths
|
|
70
|
+
const resolvedPaths = [...new Set(input.filePaths.map((f) => resolve(input.projectRoot, f)))];
|
|
71
|
+
// Create runner with the resolved scope
|
|
72
|
+
const runner = createAuditRunner({
|
|
73
|
+
projectRoot: input.projectRoot,
|
|
74
|
+
scope: resolvedPaths,
|
|
75
|
+
analyzerConcurrency: 4,
|
|
76
|
+
});
|
|
77
|
+
const result = await runner.run();
|
|
78
|
+
// Collect all violations
|
|
79
|
+
const allViolations = Object.values(result.analyzerResults).flatMap((r) => r.violations || []);
|
|
80
|
+
const violations = allViolations.map((v) => ({
|
|
81
|
+
analyzer: v.analyzer || '',
|
|
82
|
+
rule: v.rule || v.type || '',
|
|
83
|
+
severity: v.severity,
|
|
84
|
+
message: v.message,
|
|
85
|
+
file: v.file || '',
|
|
86
|
+
line: v.line ?? v.start?.line,
|
|
87
|
+
column: v.column ?? v.start?.column ?? 1,
|
|
88
|
+
endLine: v.end?.line,
|
|
89
|
+
endColumn: v.end?.column,
|
|
90
|
+
enclosingSymbol: v.symbol || v.enclosingFunction || '',
|
|
91
|
+
suggestion: v.suggestion || '',
|
|
92
|
+
details: v.details || '',
|
|
93
|
+
}));
|
|
94
|
+
const criticalCount = violations.filter((v) => v.severity === 'critical').length;
|
|
95
|
+
const warningCount = violations.filter((v) => v.severity === 'warning').length;
|
|
96
|
+
const suggestionCount = violations.filter((v) => v.severity === 'suggestion').length;
|
|
97
|
+
return {
|
|
98
|
+
violations,
|
|
99
|
+
summary: {
|
|
100
|
+
total: violations.length,
|
|
101
|
+
critical: criticalCount,
|
|
102
|
+
warning: warningCount,
|
|
103
|
+
suggestion: suggestionCount,
|
|
104
|
+
},
|
|
105
|
+
filesAnalyzed: result.metadata.filesAnalyzed,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Check if violations at or above failOn severity exist.
|
|
110
|
+
*/
|
|
111
|
+
export function hasViolationsAtOrAbove(violations, failOn) {
|
|
112
|
+
const severityOrder = ['critical', 'warning', 'suggestion'];
|
|
113
|
+
const failIndex = severityOrder.indexOf(failOn);
|
|
114
|
+
return violations.some((v) => {
|
|
115
|
+
const vIndex = severityOrder.indexOf(v.severity);
|
|
116
|
+
return vIndex >= 0 && vIndex <= failIndex;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core.js","sourceRoot":"","sources":["../../src/hooks/core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cursor hook adapter — `code-audit cursor-hook`.
|
|
4
|
+
*
|
|
5
|
+
* Reads Cursor's postToolUse stdin JSON (native or Claude Code compat) and
|
|
6
|
+
* runs a scoped audit on edited files. Blocks edits with critical violations.
|
|
7
|
+
*
|
|
8
|
+
* Phase 0 verification (2026-07-20):
|
|
9
|
+
* - Source: cursor.com/docs/hooks + cursor.com/docs/reference/third-party-hooks
|
|
10
|
+
* - postToolUse with Write matcher IS BLOCKING: exit 2 blocks the action
|
|
11
|
+
* - additional_context is injected into the conversation after the tool result
|
|
12
|
+
* - afterFileEdit EXISTS but is observation-only ("No output fields defined")
|
|
13
|
+
* - Cursor natively reads .claude/settings.json hooks (PostToolUse→postToolUse)
|
|
14
|
+
* - Tool name mapping: Edit→Write, Bash→Shell
|
|
15
|
+
* - Dual response format support: both Claude nested format and Cursor flat format
|
|
16
|
+
*
|
|
17
|
+
* Input formats (both accepted):
|
|
18
|
+
* Cursor native: { tool_name, tool_input, tool_output, tool_use_id, cwd, ... }
|
|
19
|
+
* Claude Code compat: { tool_name, tool_input, tool_output, session_id, cwd, ... }
|
|
20
|
+
* (CLAUDE_PROJECT_DIR env var set in Claude Code compat mode)
|
|
21
|
+
*
|
|
22
|
+
* Exit codes:
|
|
23
|
+
* 0 — no critical violations found
|
|
24
|
+
* 2 — critical violations found (blocks the action, additional_context injected)
|
|
25
|
+
* 1 — internal error (adapter crash, not violation-related)
|
|
26
|
+
*/
|
|
27
|
+
import type { HookAuditOutput } from './core.js';
|
|
28
|
+
export interface CursorPostToolUse {
|
|
29
|
+
tool_name?: string;
|
|
30
|
+
tool_input?: Record<string, unknown>;
|
|
31
|
+
tool_output?: unknown;
|
|
32
|
+
tool_use_id?: string;
|
|
33
|
+
toolName?: string;
|
|
34
|
+
toolInput?: Record<string, unknown>;
|
|
35
|
+
toolOutput?: unknown;
|
|
36
|
+
session_id?: string;
|
|
37
|
+
transcript_path?: string;
|
|
38
|
+
cwd?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface CursorHookResult {
|
|
41
|
+
exitCode: number;
|
|
42
|
+
stdout: string;
|
|
43
|
+
stderr: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Detect whether this is a Write/Edit tool invocation we should audit.
|
|
47
|
+
*/
|
|
48
|
+
export declare function isWriteOrEdit(event: CursorPostToolUse): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Extract the edited file path from tool_input.
|
|
51
|
+
* Cursor Write: tool_input.file_path
|
|
52
|
+
* Claude Code Edit: tool_input.file_path
|
|
53
|
+
*/
|
|
54
|
+
export declare function extractFilePath(event: CursorPostToolUse): string | null;
|
|
55
|
+
/**
|
|
56
|
+
* Build the additional_context string for violations.
|
|
57
|
+
*/
|
|
58
|
+
export declare function formatViolationContext(output: HookAuditOutput): {
|
|
59
|
+
context: string;
|
|
60
|
+
isBlocking: boolean;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Process a Cursor postToolUse event and return the hook result.
|
|
64
|
+
* Extracted from main() for testability — tests inject a mock audit function.
|
|
65
|
+
*/
|
|
66
|
+
export declare function processCursorEvent(rawStdin: string, auditFn?: (filePaths: string[], projectRoot: string) => Promise<HookAuditOutput>): Promise<CursorHookResult>;
|
|
67
|
+
/**
|
|
68
|
+
* CLI entry point — reads stdin, processes, writes result, exits.
|
|
69
|
+
* Exported so the CLI can call it explicitly after importing.
|
|
70
|
+
*/
|
|
71
|
+
export declare function main(): Promise<void>;
|
|
72
|
+
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/hooks/cursor.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAGjD,MAAM,WAAW,iBAAiB;IAEhC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAK/D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAQvE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,eAAe,GAAG;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;CACrB,CAgDA;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,eAAe,CAChB,GAC7D,OAAO,CAAC,gBAAgB,CAAC,CAyD3B;AAED;;;GAGG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAW1C"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Cursor hook adapter — `code-audit cursor-hook`.
|
|
4
|
+
*
|
|
5
|
+
* Reads Cursor's postToolUse stdin JSON (native or Claude Code compat) and
|
|
6
|
+
* runs a scoped audit on edited files. Blocks edits with critical violations.
|
|
7
|
+
*
|
|
8
|
+
* Phase 0 verification (2026-07-20):
|
|
9
|
+
* - Source: cursor.com/docs/hooks + cursor.com/docs/reference/third-party-hooks
|
|
10
|
+
* - postToolUse with Write matcher IS BLOCKING: exit 2 blocks the action
|
|
11
|
+
* - additional_context is injected into the conversation after the tool result
|
|
12
|
+
* - afterFileEdit EXISTS but is observation-only ("No output fields defined")
|
|
13
|
+
* - Cursor natively reads .claude/settings.json hooks (PostToolUse→postToolUse)
|
|
14
|
+
* - Tool name mapping: Edit→Write, Bash→Shell
|
|
15
|
+
* - Dual response format support: both Claude nested format and Cursor flat format
|
|
16
|
+
*
|
|
17
|
+
* Input formats (both accepted):
|
|
18
|
+
* Cursor native: { tool_name, tool_input, tool_output, tool_use_id, cwd, ... }
|
|
19
|
+
* Claude Code compat: { tool_name, tool_input, tool_output, session_id, cwd, ... }
|
|
20
|
+
* (CLAUDE_PROJECT_DIR env var set in Claude Code compat mode)
|
|
21
|
+
*
|
|
22
|
+
* Exit codes:
|
|
23
|
+
* 0 — no critical violations found
|
|
24
|
+
* 2 — critical violations found (blocks the action, additional_context injected)
|
|
25
|
+
* 1 — internal error (adapter crash, not violation-related)
|
|
26
|
+
*/
|
|
27
|
+
import { readStdin, runHookAudit } from './core.js';
|
|
28
|
+
import chalk from 'chalk';
|
|
29
|
+
/**
|
|
30
|
+
* Detect whether this is a Write/Edit tool invocation we should audit.
|
|
31
|
+
*/
|
|
32
|
+
export function isWriteOrEdit(event) {
|
|
33
|
+
const toolName = (event.tool_name || event.toolName || '').toLowerCase();
|
|
34
|
+
// Cursor native: "Write" (camelCase mapped). Claude Code compat: "Edit".
|
|
35
|
+
// Also accept "edit", "write" for robustness.
|
|
36
|
+
return toolName === 'write' || toolName === 'edit';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract the edited file path from tool_input.
|
|
40
|
+
* Cursor Write: tool_input.file_path
|
|
41
|
+
* Claude Code Edit: tool_input.file_path
|
|
42
|
+
*/
|
|
43
|
+
export function extractFilePath(event) {
|
|
44
|
+
const input = event.tool_input || event.toolInput;
|
|
45
|
+
if (!input)
|
|
46
|
+
return null;
|
|
47
|
+
const filePath = input.file_path;
|
|
48
|
+
if (filePath && typeof filePath === 'string')
|
|
49
|
+
return filePath;
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build the additional_context string for violations.
|
|
54
|
+
*/
|
|
55
|
+
export function formatViolationContext(output) {
|
|
56
|
+
const criticals = output.violations.filter((v) => v.severity === 'critical');
|
|
57
|
+
const warnings = output.violations.filter((v) => v.severity === 'warning');
|
|
58
|
+
const suggestions = output.violations.filter((v) => v.severity === 'suggestion');
|
|
59
|
+
if (criticals.length > 0) {
|
|
60
|
+
const lines = [
|
|
61
|
+
`🚨 **Code Auditor found ${criticals.length} critical violation(s)**`,
|
|
62
|
+
'',
|
|
63
|
+
...criticals.map((v) => `- **${v.file}${v.line ? `:${v.line}` : ''}** — ${v.message}` +
|
|
64
|
+
(v.suggestion ? `\n Suggestion: ${v.suggestion}` : '')),
|
|
65
|
+
];
|
|
66
|
+
if (warnings.length > 0 || suggestions.length > 0) {
|
|
67
|
+
lines.push('', `Also found: ${warnings.length} warning(s), ${suggestions.length} suggestion(s).`);
|
|
68
|
+
}
|
|
69
|
+
lines.push('', 'Fix the critical violations above and retry. The edit has been blocked.');
|
|
70
|
+
return { context: lines.join('\n'), isBlocking: true };
|
|
71
|
+
}
|
|
72
|
+
if (warnings.length > 0 || suggestions.length > 0) {
|
|
73
|
+
const notes = [];
|
|
74
|
+
if (warnings.length > 0) {
|
|
75
|
+
notes.push(`${warnings.length} warning(s):`);
|
|
76
|
+
warnings.forEach((v) => {
|
|
77
|
+
notes.push(` - ${v.file}${v.line ? `:${v.line}` : ''} — ${v.message}`);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (suggestions.length > 0) {
|
|
81
|
+
notes.push(`${suggestions.length} suggestion(s):`);
|
|
82
|
+
suggestions.forEach((v) => {
|
|
83
|
+
notes.push(` - ${v.file}${v.line ? `:${v.line}` : ''} — ${v.message}`);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
context: `ℹ️ **Code Auditor notes:**\n${notes.join('\n')}`,
|
|
88
|
+
isBlocking: false,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { context: '', isBlocking: false };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Process a Cursor postToolUse event and return the hook result.
|
|
95
|
+
* Extracted from main() for testability — tests inject a mock audit function.
|
|
96
|
+
*/
|
|
97
|
+
export async function processCursorEvent(rawStdin, auditFn = async (filePaths, projectRoot) => runHookAudit({ filePaths, projectRoot, failOn: 'critical' })) {
|
|
98
|
+
const result = { exitCode: 0, stdout: '', stderr: '' };
|
|
99
|
+
if (!rawStdin) {
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
let event;
|
|
103
|
+
try {
|
|
104
|
+
event = JSON.parse(rawStdin);
|
|
105
|
+
}
|
|
106
|
+
catch (err) {
|
|
107
|
+
result.stderr = chalk.red(`[code-auditor cursor-hook] Failed to parse stdin JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
108
|
+
// Degrade gracefully — never wedge the agent on parse errors
|
|
109
|
+
return result;
|
|
110
|
+
}
|
|
111
|
+
// Guard: only audit Write/Edit tool calls
|
|
112
|
+
if (!isWriteOrEdit(event)) {
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
const filePath = extractFilePath(event);
|
|
116
|
+
if (!filePath) {
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
// Determine project root:
|
|
120
|
+
// 1. cwd from the event (native Cursor)
|
|
121
|
+
// 2. CLAUDE_PROJECT_DIR env var (Claude Code compat mode)
|
|
122
|
+
// 3. process.cwd() fallback
|
|
123
|
+
const projectRoot = (event.cwd && event.cwd.trim()) ||
|
|
124
|
+
process.env.CLAUDE_PROJECT_DIR ||
|
|
125
|
+
process.cwd();
|
|
126
|
+
try {
|
|
127
|
+
const output = await auditFn([filePath], projectRoot);
|
|
128
|
+
const { context, isBlocking } = formatViolationContext(output);
|
|
129
|
+
if (context) {
|
|
130
|
+
const response = { additional_context: context };
|
|
131
|
+
result.stdout = JSON.stringify(response) + '\n';
|
|
132
|
+
if (isBlocking) {
|
|
133
|
+
result.exitCode = 2;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
result.stderr = chalk.red(`[code-auditor cursor-hook] Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
139
|
+
// Internal error — never wedge the agent loop
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* CLI entry point — reads stdin, processes, writes result, exits.
|
|
145
|
+
* Exported so the CLI can call it explicitly after importing.
|
|
146
|
+
*/
|
|
147
|
+
export async function main() {
|
|
148
|
+
const raw = await readStdin();
|
|
149
|
+
const result = await processCursorEvent(raw);
|
|
150
|
+
if (result.stderr) {
|
|
151
|
+
console.error(result.stderr);
|
|
152
|
+
}
|
|
153
|
+
if (result.stdout) {
|
|
154
|
+
process.stdout.write(result.stdout);
|
|
155
|
+
}
|
|
156
|
+
process.exit(result.exitCode);
|
|
157
|
+
}
|
|
158
|
+
// Auto-run when executed as an entry point (not imported by tests)
|
|
159
|
+
// vitest sets process.env.VITEST; when set, tests call processCursorEvent directly
|
|
160
|
+
if (!process.env.VITEST) {
|
|
161
|
+
main();
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/hooks/cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Installer — code-audit install subcommand logic.
|
|
3
|
+
*
|
|
4
|
+
* Copies the skill folder to agent-specific paths and optionally wires hooks.
|
|
5
|
+
* Phase 0 live-docs verification (2026-07-19):
|
|
6
|
+
* - Cursor: project-only skills, ~/.cursor/skills/ does NOT exist
|
|
7
|
+
* - Codex: both ~/.codex/skills/ (user) and .codex/skills/ (project)
|
|
8
|
+
* - Cursor afterFileEdit is advisory only (cannot block retroactively)
|
|
9
|
+
* - Codex PostToolUse is blocking (exit 2 replaces tool result)
|
|
10
|
+
*/
|
|
11
|
+
export interface InstallOptions {
|
|
12
|
+
agent: string;
|
|
13
|
+
scope: 'user' | 'project';
|
|
14
|
+
hooks: boolean;
|
|
15
|
+
list: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface InstallResult {
|
|
18
|
+
agent: string;
|
|
19
|
+
displayName: string;
|
|
20
|
+
skillPath: string;
|
|
21
|
+
installed: boolean;
|
|
22
|
+
hooksWired: boolean;
|
|
23
|
+
skipped: boolean;
|
|
24
|
+
skipReason?: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Main entry point for `code-audit install`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function runInstall(options: InstallOptions): Promise<void>;
|
|
30
|
+
//# sourceMappingURL=installer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAqEH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA2CvE"}
|