@praneeth_54/agentdoctor 0.2.0-beta → 1.0.0
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/CHANGELOG.md +107 -3
- package/README.md +86 -35
- package/dist/agents/inspect.js +58 -51
- package/dist/cli/commands/doctor.js +1 -1
- package/dist/cli/commands/explain.js +2 -2
- package/dist/cli/commands/fix.d.ts +7 -5
- package/dist/cli/commands/fix.js +73 -12
- package/dist/cli/commands/scan.d.ts +5 -0
- package/dist/cli/commands/scan.js +25 -6
- package/dist/cli/commands/verify.d.ts +19 -0
- package/dist/cli/commands/verify.js +70 -0
- package/dist/cli/program.js +97 -33
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/fix/apply.d.ts +14 -0
- package/dist/core/fix/apply.js +47 -0
- package/dist/core/fix/patterns.d.ts +7 -0
- package/dist/core/fix/patterns.js +21 -0
- package/dist/core/fix/plan.d.ts +9 -0
- package/dist/core/fix/plan.js +247 -0
- package/dist/core/fix/render.d.ts +8 -0
- package/dist/core/fix/render.js +116 -0
- package/dist/core/fix/run.d.ts +15 -0
- package/dist/core/fix/run.js +58 -0
- package/dist/core/fix/types.d.ts +34 -0
- package/dist/core/fix/types.js +6 -0
- package/dist/core/fix/writers/claude-settings.d.ts +16 -0
- package/dist/core/fix/writers/claude-settings.js +100 -0
- package/dist/core/fix/writers/codex-config.d.ts +22 -0
- package/dist/core/fix/writers/codex-config.js +183 -0
- package/dist/core/fix/writers/cursorignore.d.ts +13 -0
- package/dist/core/fix/writers/cursorignore.js +68 -0
- package/dist/core/path-resolution/index.d.ts +5 -0
- package/dist/core/path-resolution/index.js +5 -0
- package/dist/core/path-resolution/prepare.d.ts +33 -0
- package/dist/core/path-resolution/prepare.js +98 -0
- package/dist/core/policy/evaluate.d.ts +44 -0
- package/dist/core/policy/evaluate.js +120 -0
- package/dist/core/rules/claude-deny.d.ts +8 -0
- package/dist/core/rules/claude-deny.js +39 -0
- package/dist/core/rules/codex-deny.d.ts +5 -0
- package/dist/core/rules/codex-deny.js +42 -0
- package/dist/core/rules/context/generated-directory.js +52 -8
- package/dist/core/rules/context/large-log-file.js +27 -10
- package/dist/core/rules/ignore.js +2 -5
- package/dist/core/rules/instructions/missing-path-reference.d.ts +5 -1
- package/dist/core/rules/instructions/missing-path-reference.js +114 -7
- package/dist/core/rules/path-kind.d.ts +27 -0
- package/dist/core/rules/path-kind.js +180 -0
- package/dist/core/rules/security/env-file-exposure.js +23 -38
- package/dist/core/rules/security/private-key-file.js +4 -0
- package/dist/core/rules/text-cache.js +46 -40
- package/dist/core/verify/compare.d.ts +29 -0
- package/dist/core/verify/compare.js +56 -0
- package/dist/core/verify/load-baseline.d.ts +15 -0
- package/dist/core/verify/load-baseline.js +71 -0
- package/dist/core/verify/verify.d.ts +26 -0
- package/dist/core/verify/verify.js +40 -0
- package/dist/discovery/files.js +7 -2
- package/dist/discovery/log-like.d.ts +2 -0
- package/dist/discovery/log-like.js +7 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +6 -0
- package/dist/reporters/github/annotations.d.ts +8 -0
- package/dist/reporters/github/annotations.js +42 -0
- package/dist/reporters/github/emit.d.ts +23 -0
- package/dist/reporters/github/emit.js +31 -0
- package/dist/reporters/github/summary.d.ts +20 -0
- package/dist/reporters/github/summary.js +111 -0
- package/dist/reporters/terminal/report.d.ts +5 -0
- package/dist/reporters/terminal/report.js +73 -4
- package/dist/reporters/verify/json.d.ts +5 -0
- package/dist/reporters/verify/json.js +51 -0
- package/dist/reporters/verify/terminal.d.ts +6 -0
- package/dist/reporters/verify/terminal.js +71 -0
- package/dist/utils/fs.d.ts +5 -0
- package/dist/utils/fs.js +34 -0
- package/dist/utils/path.d.ts +5 -0
- package/dist/utils/path.js +16 -0
- package/package.json +11 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { evaluateScanPolicy } from "../../core/policy/evaluate.js";
|
|
2
3
|
import { scan } from "../../core/scanner/scan.js";
|
|
4
|
+
import { emitGithubReports } from "../../reporters/github/emit.js";
|
|
3
5
|
import { renderJsonReport } from "../../reporters/json/report.js";
|
|
4
6
|
import { renderTerminalReport } from "../../reporters/terminal/report.js";
|
|
5
7
|
import { EXIT_CODES } from "../../types/index.js";
|
|
6
|
-
import { resolveRepoRoot } from "../../utils/path.js";
|
|
7
8
|
import { isDirectory } from "../../utils/fs.js";
|
|
9
|
+
import { resolveRepoRoot } from "../../utils/path.js";
|
|
8
10
|
export async function runScanCommand(options) {
|
|
9
11
|
const target = resolveRepoRoot(options.targetPath ?? process.cwd());
|
|
10
12
|
if (!(await isDirectory(target))) {
|
|
@@ -24,13 +26,30 @@ export async function runScanCommand(options) {
|
|
|
24
26
|
verbose: options.verbose === true,
|
|
25
27
|
}));
|
|
26
28
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const failOnSeverity = options.failOnSeverity ?? (options.ci === true ? "critical" : undefined);
|
|
30
|
+
const policy = {
|
|
31
|
+
...(options.minScore !== undefined ? { minimumScore: options.minScore } : {}),
|
|
32
|
+
...(failOnSeverity !== undefined ? { failOnSeverity } : {}),
|
|
33
|
+
...(options.failOnRules && options.failOnRules.length > 0
|
|
34
|
+
? { failOnRules: options.failOnRules }
|
|
35
|
+
: {}),
|
|
36
|
+
};
|
|
37
|
+
const violations = evaluateScanPolicy(result, policy);
|
|
38
|
+
await emitGithubReports({
|
|
39
|
+
mode: "scan",
|
|
40
|
+
findings: result.findings,
|
|
41
|
+
overallScore: result.scores?.overall ?? null,
|
|
42
|
+
violations,
|
|
43
|
+
summary: options.summary === true,
|
|
44
|
+
annotations: options.annotations === true,
|
|
45
|
+
});
|
|
46
|
+
if (violations.length > 0) {
|
|
47
|
+
if (!options.json) {
|
|
48
|
+
for (const violation of violations) {
|
|
49
|
+
console.error(`\n${violation.message}`);
|
|
31
50
|
}
|
|
32
|
-
return EXIT_CODES.ISSUES_OR_THRESHOLD;
|
|
33
51
|
}
|
|
52
|
+
return EXIT_CODES.ISSUES_OR_THRESHOLD;
|
|
34
53
|
}
|
|
35
54
|
return EXIT_CODES.SUCCESS;
|
|
36
55
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type PolicyOptions } from "../../core/policy/evaluate.js";
|
|
2
|
+
import { type ExitCode } from "../../types/index.js";
|
|
3
|
+
export interface VerifyCommandOptions {
|
|
4
|
+
targetPath?: string;
|
|
5
|
+
baselinePath?: string;
|
|
6
|
+
json?: boolean;
|
|
7
|
+
ci?: boolean;
|
|
8
|
+
verbose?: boolean;
|
|
9
|
+
minScore?: number;
|
|
10
|
+
failOnSeverity?: PolicyOptions["failOnSeverity"];
|
|
11
|
+
failOnRules?: string[];
|
|
12
|
+
failOnNew?: boolean;
|
|
13
|
+
summary?: boolean;
|
|
14
|
+
annotations?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Verify: re-scan after Fix and compare against a prior scan JSON baseline.
|
|
18
|
+
*/
|
|
19
|
+
export declare function runVerifyCommand(options: VerifyCommandOptions): Promise<ExitCode>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { evaluateVerifyPolicy } from "../../core/policy/evaluate.js";
|
|
2
|
+
import { verify } from "../../core/verify/verify.js";
|
|
3
|
+
import { emitGithubReports } from "../../reporters/github/emit.js";
|
|
4
|
+
import { renderVerifyJsonReport } from "../../reporters/verify/json.js";
|
|
5
|
+
import { renderVerifyTerminalReport } from "../../reporters/verify/terminal.js";
|
|
6
|
+
import { EXIT_CODES } from "../../types/index.js";
|
|
7
|
+
import { isDirectory } from "../../utils/fs.js";
|
|
8
|
+
import { resolveRepoRoot } from "../../utils/path.js";
|
|
9
|
+
/**
|
|
10
|
+
* Verify: re-scan after Fix and compare against a prior scan JSON baseline.
|
|
11
|
+
*/
|
|
12
|
+
export async function runVerifyCommand(options) {
|
|
13
|
+
const target = resolveRepoRoot(options.targetPath ?? process.cwd());
|
|
14
|
+
if (!(await isDirectory(target))) {
|
|
15
|
+
console.error(`Error: not a directory: ${target}`);
|
|
16
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
const result = await verify({
|
|
20
|
+
cwd: target,
|
|
21
|
+
verbose: options.verbose ?? false,
|
|
22
|
+
...(options.baselinePath !== undefined ? { baselinePath: options.baselinePath } : {}),
|
|
23
|
+
});
|
|
24
|
+
if (options.json) {
|
|
25
|
+
process.stdout.write(renderVerifyJsonReport(result));
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
process.stdout.write(renderVerifyTerminalReport(result, options.verbose === true));
|
|
29
|
+
}
|
|
30
|
+
const failOnNew = options.failOnNew === true || options.ci === true;
|
|
31
|
+
const policy = {
|
|
32
|
+
...(options.minScore !== undefined ? { minimumScore: options.minScore } : {}),
|
|
33
|
+
...(options.failOnSeverity !== undefined ? { failOnSeverity: options.failOnSeverity } : {}),
|
|
34
|
+
...(options.failOnRules && options.failOnRules.length > 0
|
|
35
|
+
? { failOnRules: options.failOnRules }
|
|
36
|
+
: {}),
|
|
37
|
+
...(failOnNew ? { failOnNew: true } : {}),
|
|
38
|
+
};
|
|
39
|
+
const violations = evaluateVerifyPolicy(result, policy);
|
|
40
|
+
await emitGithubReports({
|
|
41
|
+
mode: "verify",
|
|
42
|
+
findings: result.after.findings,
|
|
43
|
+
overallScore: result.scores?.overall ?? null,
|
|
44
|
+
violations,
|
|
45
|
+
verifySummary: result.summary,
|
|
46
|
+
summary: options.summary === true,
|
|
47
|
+
annotations: options.annotations === true,
|
|
48
|
+
});
|
|
49
|
+
if (violations.length > 0) {
|
|
50
|
+
if (!options.json) {
|
|
51
|
+
for (const violation of violations) {
|
|
52
|
+
console.error(`\n${violation.message}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return EXIT_CODES.ISSUES_OR_THRESHOLD;
|
|
56
|
+
}
|
|
57
|
+
return EXIT_CODES.SUCCESS;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
61
|
+
console.error(`Error: ${message}`);
|
|
62
|
+
if (options.verbose && error instanceof Error && error.stack) {
|
|
63
|
+
console.error(error.stack);
|
|
64
|
+
}
|
|
65
|
+
if (message.includes("baseline") || message.includes("No verify baseline")) {
|
|
66
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
67
|
+
}
|
|
68
|
+
return EXIT_CODES.INTERNAL_ERROR;
|
|
69
|
+
}
|
|
70
|
+
}
|
package/dist/cli/program.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
1
|
+
import { Command, InvalidArgumentError } from "commander";
|
|
2
2
|
import { PACKAGE_VERSION } from "../constants.js";
|
|
3
|
+
import { parseFailOnRules, parseSeverityGate } from "../core/policy/evaluate.js";
|
|
4
|
+
import { EXIT_CODES } from "../types/index.js";
|
|
3
5
|
import { runDoctorCommand } from "./commands/doctor.js";
|
|
4
6
|
import { runExplainCommand } from "./commands/explain.js";
|
|
5
7
|
import { runFixCommand } from "./commands/fix.js";
|
|
6
8
|
import { resolveTargetArgument, runScanCommand } from "./commands/scan.js";
|
|
7
|
-
import {
|
|
9
|
+
import { runVerifyCommand } from "./commands/verify.js";
|
|
8
10
|
function parseMinScore(value) {
|
|
9
11
|
const parsed = Number(value);
|
|
10
12
|
if (!Number.isFinite(parsed) || parsed < 0 || parsed > 100) {
|
|
11
|
-
throw new
|
|
13
|
+
throw new InvalidArgumentError("--min-score must be a number between 0 and 100");
|
|
12
14
|
}
|
|
13
15
|
return parsed;
|
|
14
16
|
}
|
|
@@ -21,56 +23,118 @@ function readMinScore(options) {
|
|
|
21
23
|
}
|
|
22
24
|
return undefined;
|
|
23
25
|
}
|
|
26
|
+
function collectFailOnRules(value, previous) {
|
|
27
|
+
return [...previous, ...parseFailOnRules(value)];
|
|
28
|
+
}
|
|
29
|
+
function readFailOnSeverity(options) {
|
|
30
|
+
if (typeof options.failOnSeverity !== "string" || options.failOnSeverity.length === 0) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
return parseSeverityGate(options.failOnSeverity);
|
|
34
|
+
}
|
|
35
|
+
function readFailOnRules(options) {
|
|
36
|
+
if (Array.isArray(options.failOnRule)) {
|
|
37
|
+
return options.failOnRule.filter((item) => typeof item === "string");
|
|
38
|
+
}
|
|
39
|
+
if (typeof options.failOnRule === "string") {
|
|
40
|
+
return parseFailOnRules(options.failOnRule);
|
|
41
|
+
}
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Scan flags are declared on both the root program and the `scan` subcommand
|
|
46
|
+
* so `--help` stays accurate. Commander stores overlapping flags on the parent
|
|
47
|
+
* when `scan` is invoked, so callers must read `optsWithGlobals()`.
|
|
48
|
+
*/
|
|
49
|
+
function addScanOptions(command) {
|
|
50
|
+
return command
|
|
51
|
+
.option("--json", "Emit machine-readable JSON (no decorative output)", false)
|
|
52
|
+
.option("--ci", "CI mode: exit 1 when any critical finding exists (override with --fail-on-severity)", false)
|
|
53
|
+
.option("--verbose", "Show timing and extra diagnostics", false)
|
|
54
|
+
.option("--min-score <number>", "Exit 1 when overall readiness score is below this (0-100)", parseMinScore)
|
|
55
|
+
.option("--fail-on-severity <level>", "Exit 1 when any finding has this severity or higher (critical|warning|info)", parseSeverityGate)
|
|
56
|
+
.option("--fail-on-rule <id>", "Exit 1 when a finding matches this rule id (repeatable or comma-separated)", collectFailOnRules, [])
|
|
57
|
+
.option("--summary", "Write a GitHub Actions step summary when GITHUB_STEP_SUMMARY is set", false)
|
|
58
|
+
.option("--annotations", "Emit GitHub Actions annotations for findings (stderr)", false);
|
|
59
|
+
}
|
|
60
|
+
async function runScanFromCli(pathArg, command) {
|
|
61
|
+
const options = command.optsWithGlobals();
|
|
62
|
+
const minScore = readMinScore(options);
|
|
63
|
+
const failOnSeverity = readFailOnSeverity(options);
|
|
64
|
+
const failOnRules = readFailOnRules(options);
|
|
65
|
+
const code = await runScanCommand({
|
|
66
|
+
targetPath: resolveTargetArgument(pathArg),
|
|
67
|
+
json: Boolean(options.json),
|
|
68
|
+
ci: Boolean(options.ci),
|
|
69
|
+
verbose: Boolean(options.verbose),
|
|
70
|
+
summary: Boolean(options.summary),
|
|
71
|
+
annotations: Boolean(options.annotations),
|
|
72
|
+
...(minScore !== undefined ? { minScore } : {}),
|
|
73
|
+
...(failOnSeverity !== undefined ? { failOnSeverity } : {}),
|
|
74
|
+
...(failOnRules.length > 0 ? { failOnRules } : {}),
|
|
75
|
+
});
|
|
76
|
+
process.exitCode = code;
|
|
77
|
+
}
|
|
24
78
|
export function createProgram() {
|
|
25
79
|
const program = new Command();
|
|
26
|
-
program
|
|
80
|
+
addScanOptions(program
|
|
27
81
|
.name("agentdoctor")
|
|
28
82
|
.description("Audit AI coding agent configuration in a repository (local, deterministic, no API key).")
|
|
29
83
|
.version(PACKAGE_VERSION, "-V, --version", "Print AgentDoctor version")
|
|
30
|
-
.argument("[path]", "Repository path to scan (default: current directory)")
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.
|
|
84
|
+
.argument("[path]", "Repository path to scan (default: current directory)")).action(async (pathArg, _options, command) => {
|
|
85
|
+
await runScanFromCli(pathArg, command);
|
|
86
|
+
});
|
|
87
|
+
addScanOptions(program
|
|
88
|
+
.command("scan")
|
|
89
|
+
.description("Scan a repository for AI coding agent configuration issues (default command)")
|
|
90
|
+
.argument("[path]", "Repository path to scan")).action(async (pathArg, _options, command) => {
|
|
91
|
+
await runScanFromCli(pathArg, command);
|
|
92
|
+
});
|
|
93
|
+
program
|
|
94
|
+
.command("fix")
|
|
95
|
+
.description("Apply safe automatic fixes (Cursor .cursorignore, Claude Code Read deny, and Codex filesystem deny for safe context findings)")
|
|
96
|
+
.argument("[path]", "Repository path (default: current directory)")
|
|
97
|
+
.option("--dry-run", "Show proposed fixes without writing files", false)
|
|
98
|
+
.option("-y, --yes", "Skip confirmation prompts", false)
|
|
35
99
|
.action(async (pathArg, options) => {
|
|
36
|
-
const
|
|
37
|
-
const code = await runScanCommand({
|
|
100
|
+
const code = await runFixCommand({
|
|
38
101
|
targetPath: resolveTargetArgument(pathArg),
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
verbose: Boolean(options.verbose),
|
|
42
|
-
...(minScore !== undefined ? { minScore } : {}),
|
|
102
|
+
dryRun: Boolean(options.dryRun),
|
|
103
|
+
yes: Boolean(options.yes),
|
|
43
104
|
});
|
|
44
105
|
process.exitCode = code;
|
|
45
106
|
});
|
|
46
107
|
program
|
|
47
|
-
.command("
|
|
48
|
-
.description("
|
|
49
|
-
.argument("[path]", "Repository path
|
|
108
|
+
.command("verify")
|
|
109
|
+
.description("Re-scan and compare against a prior scan JSON baseline (Scan → Fix → Verify)")
|
|
110
|
+
.argument("[path]", "Repository path (default: current directory)")
|
|
50
111
|
.option("--json", "Emit machine-readable JSON", false)
|
|
51
|
-
.option("--ci", "CI mode (
|
|
112
|
+
.option("--ci", "CI mode: exit 1 when new findings appear (also honors other policy flags)", false)
|
|
113
|
+
.option("--fail-on-new", "Exit 1 when new findings appear relative to the baseline", false)
|
|
52
114
|
.option("--verbose", "Show timing and extra diagnostics", false)
|
|
115
|
+
.option("--baseline <file>", "Prior scan JSON report (default: agentdoctor-report.json or .agentdoctor-baseline.json)")
|
|
53
116
|
.option("--min-score <number>", "Exit 1 when overall readiness score is below this (0-100)", parseMinScore)
|
|
54
|
-
.
|
|
117
|
+
.option("--fail-on-severity <level>", "Exit 1 when any finding has this severity or higher (critical|warning|info)", parseSeverityGate)
|
|
118
|
+
.option("--fail-on-rule <id>", "Exit 1 when a finding matches this rule id (repeatable or comma-separated)", collectFailOnRules, [])
|
|
119
|
+
.option("--summary", "Write a GitHub Actions step summary when GITHUB_STEP_SUMMARY is set", false)
|
|
120
|
+
.option("--annotations", "Emit GitHub Actions annotations for findings (stderr)", false)
|
|
121
|
+
.action(async (pathArg, _options, command) => {
|
|
122
|
+
const options = command.optsWithGlobals();
|
|
55
123
|
const minScore = readMinScore(options);
|
|
56
|
-
const
|
|
124
|
+
const failOnSeverity = readFailOnSeverity(options);
|
|
125
|
+
const failOnRules = readFailOnRules(options);
|
|
126
|
+
const code = await runVerifyCommand({
|
|
57
127
|
targetPath: resolveTargetArgument(pathArg),
|
|
58
128
|
json: Boolean(options.json),
|
|
59
129
|
ci: Boolean(options.ci),
|
|
130
|
+
failOnNew: Boolean(options.failOnNew),
|
|
60
131
|
verbose: Boolean(options.verbose),
|
|
132
|
+
summary: Boolean(options.summary),
|
|
133
|
+
annotations: Boolean(options.annotations),
|
|
134
|
+
...(typeof options.baseline === "string" ? { baselinePath: options.baseline } : {}),
|
|
61
135
|
...(minScore !== undefined ? { minScore } : {}),
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
program
|
|
66
|
-
.command("fix")
|
|
67
|
-
.description("Apply safe automatic fixes (not implemented yet)")
|
|
68
|
-
.option("--dry-run", "Show proposed fixes without writing files", false)
|
|
69
|
-
.option("-y, --yes", "Skip confirmation prompts", false)
|
|
70
|
-
.action(async (options) => {
|
|
71
|
-
const code = await runFixCommand({
|
|
72
|
-
dryRun: Boolean(options.dryRun),
|
|
73
|
-
yes: Boolean(options.yes),
|
|
136
|
+
...(failOnSeverity !== undefined ? { failOnSeverity } : {}),
|
|
137
|
+
...(failOnRules.length > 0 ? { failOnRules } : {}),
|
|
74
138
|
});
|
|
75
139
|
process.exitCode = code;
|
|
76
140
|
});
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "0.
|
|
1
|
+
export declare const PACKAGE_VERSION = "1.0.0";
|
|
2
2
|
export declare const DEFAULT_MAX_FILE_SIZE_BYTES: number;
|
|
3
3
|
/** Directories skipped during normal discovery (unless a rule needs them later). */
|
|
4
4
|
export declare const DEFAULT_IGNORE_DIRECTORIES: Set<string>;
|
package/dist/constants.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = "0.
|
|
1
|
+
export const PACKAGE_VERSION = "1.0.0";
|
|
2
2
|
export const DEFAULT_MAX_FILE_SIZE_BYTES = 2 * 1024 * 1024; // 2 MiB
|
|
3
3
|
/** Directories skipped during normal discovery (unless a rule needs them later). */
|
|
4
4
|
export const DEFAULT_IGNORE_DIRECTORIES = new Set([
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FixApplyResult, FixPlan } from "./types.js";
|
|
2
|
+
import { previewClaudeSettingsActions, readClaudeSettings } from "./writers/claude-settings.js";
|
|
3
|
+
import { previewCodexConfigActions, readCodexConfig } from "./writers/codex-config.js";
|
|
4
|
+
import { previewCursorignoreActions, readCursorignore } from "./writers/cursorignore.js";
|
|
5
|
+
/**
|
|
6
|
+
* Apply (or dry-run) a fix plan.
|
|
7
|
+
* Safe writers: `.cursorignore`, Claude settings deny, Codex config.toml deny.
|
|
8
|
+
*/
|
|
9
|
+
export declare function applyFixPlan(plan: FixPlan, options: {
|
|
10
|
+
dryRun: boolean;
|
|
11
|
+
}): Promise<FixApplyResult>;
|
|
12
|
+
export { previewCursorignoreActions, readCursorignore };
|
|
13
|
+
export { previewClaudeSettingsActions, readClaudeSettings };
|
|
14
|
+
export { previewCodexConfigActions, readCodexConfig };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { previewClaudeSettingsActions, readClaudeSettings, writeClaudeSettings, } from "./writers/claude-settings.js";
|
|
2
|
+
import { previewCodexConfigActions, readCodexConfig, writeCodexConfig, } from "./writers/codex-config.js";
|
|
3
|
+
import { previewCursorignoreActions, readCursorignore, writeCursorignore, } from "./writers/cursorignore.js";
|
|
4
|
+
/**
|
|
5
|
+
* Apply (or dry-run) a fix plan.
|
|
6
|
+
* Safe writers: `.cursorignore`, Claude settings deny, Codex config.toml deny.
|
|
7
|
+
*/
|
|
8
|
+
export async function applyFixPlan(plan, options) {
|
|
9
|
+
const cursorContent = await readCursorignore(plan.root);
|
|
10
|
+
const claudeContent = await readClaudeSettings(plan.root);
|
|
11
|
+
const codexContent = await readCodexConfig(plan.root);
|
|
12
|
+
const cursorPreview = previewCursorignoreActions(cursorContent, plan.actions);
|
|
13
|
+
const claudePreview = previewClaudeSettingsActions(claudeContent, plan.actions);
|
|
14
|
+
const codexPreview = previewCodexConfigActions(codexContent, plan.actions);
|
|
15
|
+
const writtenFiles = [];
|
|
16
|
+
const changedFiles = [];
|
|
17
|
+
if (cursorPreview) {
|
|
18
|
+
changedFiles.push(cursorPreview.targetRelativePath);
|
|
19
|
+
if (!options.dryRun) {
|
|
20
|
+
await writeCursorignore(plan.root, cursorPreview.after);
|
|
21
|
+
writtenFiles.push(cursorPreview.targetRelativePath);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (claudePreview) {
|
|
25
|
+
changedFiles.push(claudePreview.targetRelativePath);
|
|
26
|
+
if (!options.dryRun) {
|
|
27
|
+
await writeClaudeSettings(plan.root, claudePreview.after);
|
|
28
|
+
writtenFiles.push(claudePreview.targetRelativePath);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (codexPreview) {
|
|
32
|
+
changedFiles.push(codexPreview.targetRelativePath);
|
|
33
|
+
if (!options.dryRun) {
|
|
34
|
+
await writeCodexConfig(plan.root, codexPreview.after);
|
|
35
|
+
writtenFiles.push(codexPreview.targetRelativePath);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
plan,
|
|
40
|
+
dryRun: options.dryRun,
|
|
41
|
+
writtenFiles,
|
|
42
|
+
changedFiles,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export { previewCursorignoreActions, readCursorignore };
|
|
46
|
+
export { previewClaudeSettingsActions, readClaudeSettings };
|
|
47
|
+
export { previewCodexConfigActions, readCodexConfig };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Finding } from "../../types/index.js";
|
|
2
|
+
export declare function isSafeContextFixRule(ruleId: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Build a gitignore-style pattern that excludes the finding evidence path.
|
|
5
|
+
* Directories (generated-directory) get a trailing slash.
|
|
6
|
+
*/
|
|
7
|
+
export declare function patternForFinding(finding: Finding): string | null;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const SAFE_CONTEXT_RULES = new Set(["context/generated-directory", "context/large-log-file"]);
|
|
2
|
+
export function isSafeContextFixRule(ruleId) {
|
|
3
|
+
return SAFE_CONTEXT_RULES.has(ruleId);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Build a gitignore-style pattern that excludes the finding evidence path.
|
|
7
|
+
* Directories (generated-directory) get a trailing slash.
|
|
8
|
+
*/
|
|
9
|
+
export function patternForFinding(finding) {
|
|
10
|
+
const evidencePath = finding.evidence?.path?.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
11
|
+
if (!evidencePath) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
if (finding.ruleId === "context/generated-directory") {
|
|
15
|
+
return evidencePath.endsWith("/") ? evidencePath : `${evidencePath}/`;
|
|
16
|
+
}
|
|
17
|
+
if (finding.ruleId === "context/large-log-file") {
|
|
18
|
+
return evidencePath;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ScanResult } from "../../types/index.js";
|
|
2
|
+
import type { FixPlan } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Build a fix plan from a scan result.
|
|
5
|
+
* Safe context exclusions: Cursor `.cursorignore`, Claude Code Read deny, Codex filesystem deny.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildFixPlan(result: ScanResult): Promise<FixPlan>;
|
|
8
|
+
/** Patterns that would be newly appended given current file contents. */
|
|
9
|
+
export declare function missingPatternsForCursorignore(currentContent: string | null, patterns: string[]): string[];
|