@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
package/CHANGELOG.md
CHANGED
|
@@ -7,10 +7,112 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Notes
|
|
11
|
+
|
|
12
|
+
- Owner release sequence for `1.0.0`: `npm publish` → `git tag v1.0.0` → GitHub Release → then bump Action `version` default `0.3.0-beta` → `1.0.0` (only after npm serves `1.0.0`).
|
|
13
|
+
- Project Brain V1 remains a parallel laboratory under `src/core/understanding/brain` and is excluded from the published npm pack. Docs: [docs/project-brain.md](docs/project-brain.md).
|
|
14
|
+
|
|
15
|
+
## [1.0.0] — 2026-08-12
|
|
16
|
+
|
|
17
|
+
First production release: Scan → Fix → Verify → CI contract frozen for v1.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Claude Code safe-context Fix writer: `agentdoctor fix` appends allowlisted
|
|
22
|
+
`permissions.deny` Read rules to `.claude/settings.json` for
|
|
23
|
+
`context/generated-directory` and `context/large-log-file` when Claude Code is
|
|
24
|
+
configured (alongside existing Cursor `.cursorignore` fixes).
|
|
25
|
+
- Codex safe-context Fix writer: `agentdoctor fix` merges allowlisted filesystem
|
|
26
|
+
`deny` keys into `.codex/config.toml` permission profiles for the same safe
|
|
27
|
+
context findings when Codex is detected. Skips when `sandbox_mode` is set or
|
|
28
|
+
`default_permissions` selects a built-in `:…` profile.
|
|
29
|
+
- GitHub Action / CLI CI policy enforcement: `minimum-score` / `--min-score`,
|
|
30
|
+
`fail-on-severity` / `--fail-on-severity`, `fail-on-rule` / `--fail-on-rule`,
|
|
31
|
+
`fail-on-new` / `--fail-on-new`, `verify-baseline`, `json-output`, `summary` /
|
|
32
|
+
`--summary`, and `annotations` / `--annotations`. Action `version: workspace`
|
|
33
|
+
runs the checked-out `dist/cli` for local CI.
|
|
34
|
+
- Guided Next steps on failed `scan` / `fix` / `verify` terminal output, and on
|
|
35
|
+
GitHub Step Summary when a policy gate fails — shortest path back to green
|
|
36
|
+
(reproduce → fix or explain → verify).
|
|
37
|
+
- Windows CI quality job (Node 20) and Windows-safe Fix/Action overwrite writes.
|
|
38
|
+
- Action smoke coverage for `fail-on-rule`, `verify-baseline`, and baseline
|
|
39
|
+
symlink escape rejection.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- `instructions/missing-path-reference` also resolves non-`./` paths relative to the
|
|
44
|
+
instruction file directory (monorepo package docs), while root-level instruction
|
|
45
|
+
files still require repository-root paths. Corpus-100: 121 → 88 findings for this
|
|
46
|
+
rule (−33); other rules unchanged.
|
|
47
|
+
- `context/generated-directory` and `context/large-log-file` honor Claude Code Read
|
|
48
|
+
deny exclusions when computing `affectedAgents`, so Fix → Verify clears Claude
|
|
49
|
+
context findings after a deny rule is applied.
|
|
50
|
+
- The same rules honor Codex filesystem deny keys in `.codex/config.toml` when
|
|
51
|
+
computing `affectedAgents`.
|
|
52
|
+
- Action writes the JSON report even when a policy gate fails (exit `1`), so
|
|
53
|
+
artifacts remain available for triage.
|
|
54
|
+
- `--min-score` / Action `minimum-score` fail when no supported agents are
|
|
55
|
+
configured (`agentSecurityAnalysis: limited`) instead of passing on a vacuous 100.
|
|
56
|
+
- Terminal readiness prints `n/a` when analysis is limited (no agents).
|
|
57
|
+
- Agentless first scan no longer shows a green “No agent-configuration findings”
|
|
58
|
+
success line; it tells the user to add Cursor / Claude Code / Codex config and
|
|
59
|
+
re-run.
|
|
60
|
+
- Invalid `--min-score` values exit `2` (usage) instead of `3` (internal).
|
|
61
|
+
- Codex Fix refuses invalid `.codex/config.toml` during planning (same as Claude
|
|
62
|
+
invalid JSON) instead of silently skipping Codex and writing Cursor-only fixes.
|
|
63
|
+
- Codex Fix refuses unrecognizable / invalid `.codex/config.toml` content instead of
|
|
64
|
+
appending permission profiles into garbage TOML.
|
|
65
|
+
- `agentdoctor fix` exits `2` when confirmation is cancelled (non-TTY without `--yes`)
|
|
66
|
+
or when Fix refuses invalid settings / cannot write due to permissions.
|
|
67
|
+
- `scan --ci` now fails (exit `1`) when any **critical** finding exists. Omit `--ci` for
|
|
68
|
+
report-only scans. The GitHub Action stays report-only unless policy inputs are set
|
|
69
|
+
(it no longer passes a bare `--ci`).
|
|
70
|
+
- Discovery keeps oversized log/dump-like paths as size metadata so
|
|
71
|
+
`context/large-log-file` flags files above the content-read limit (previously silent
|
|
72
|
+
false negatives for the largest logs).
|
|
73
|
+
- Action `verify-baseline` re-checks workspace containment after `realpath` so a
|
|
74
|
+
workspace-relative symlink cannot escape to an outside file.
|
|
75
|
+
- Fix writers and Action report overwrite use Windows-safe replace (rename cannot
|
|
76
|
+
overwrite an existing destination on Windows).
|
|
77
|
+
|
|
78
|
+
### Compatibility
|
|
79
|
+
|
|
80
|
+
- CLI + JSON + rule ID contracts frozen for v1 (see [docs/compatibility.md](docs/compatibility.md))
|
|
81
|
+
- Action `version` input default remains `0.3.0-beta` until `1.0.0` is published to npm;
|
|
82
|
+
pin `1.0.0` or use `version: workspace` after the release is cut
|
|
83
|
+
|
|
84
|
+
## [0.3.0-beta] — 2026-08-07
|
|
85
|
+
|
|
86
|
+
Minor beta: completes the Scan → Fix → Verify CLI loop and corrects release-facing honesty.
|
|
87
|
+
|
|
88
|
+
### Added
|
|
89
|
+
|
|
90
|
+
- `agentdoctor verify` — re-scan and compare against a prior `scan --json` baseline
|
|
91
|
+
(`fixed` / `remaining` / `new` / `unchanged`). Supports `--json`, `--ci` (fails on new
|
|
92
|
+
findings), `--baseline`, and `--min-score`. Completes the Scan → Fix → Verify CLI loop.
|
|
93
|
+
- Terminal summary prints overall readiness (`N/100`); category/agent scores remain in JSON.
|
|
94
|
+
|
|
95
|
+
### Fixed
|
|
96
|
+
|
|
97
|
+
- `agentdoctor scan --json` (and `--ci` / `--verbose` / `--min-score` on the `scan`
|
|
98
|
+
subcommand) now honor flags correctly. Overlapping root/subcommand options are read via
|
|
99
|
+
Commander `optsWithGlobals()`, so CI scripts using `scan … --json` receive JSON instead of
|
|
100
|
+
a terminal report.
|
|
101
|
+
- `instructions/missing-path-reference` no longer treats Go/npm module imports
|
|
102
|
+
(`github.com/…`, `@scope/pkg`), Go stdlib paths (`io/ioutil`), glob patterns, code tokens
|
|
103
|
+
(`try/finally`), or bare build roots (`dist/`) as missing local paths.
|
|
104
|
+
- `agentdoctor fix` now reports skip reasons for review/manual findings instead of an empty
|
|
105
|
+
“no applicable fixes” message with no explanation.
|
|
106
|
+
- Sample/test/example paths and env templates no longer inflate security/context false positives.
|
|
107
|
+
|
|
108
|
+
### Compatibility
|
|
109
|
+
|
|
110
|
+
- Default Action `version` input is `0.3.0-beta` (pin CI smoke to last published until npm ships)
|
|
111
|
+
- Fix remains Cursor `.cursorignore` safe-context only; security findings stay review/manual
|
|
112
|
+
|
|
10
113
|
### Planned
|
|
11
114
|
|
|
12
|
-
-
|
|
13
|
-
- Terminal readiness line and GitHub Action score-gate inputs (deferred; see scoring.md v2+)
|
|
115
|
+
- GitHub Action score-gate inputs (deferred; see scoring.md v2+)
|
|
14
116
|
|
|
15
117
|
## [0.2.0-beta] — 2026-08-02
|
|
16
118
|
|
|
@@ -143,7 +245,9 @@ First public beta.
|
|
|
143
245
|
- Not a complete secret scanner
|
|
144
246
|
- Git “tracked secret” detection deferred
|
|
145
247
|
|
|
146
|
-
[Unreleased]: https://github.com/pranee54/AgentDoctor/compare/
|
|
248
|
+
[Unreleased]: https://github.com/pranee54/AgentDoctor/compare/v1.0.0...HEAD
|
|
249
|
+
[1.0.0]: https://github.com/pranee54/AgentDoctor/releases/tag/v1.0.0
|
|
250
|
+
[0.3.0-beta]: https://github.com/pranee54/AgentDoctor/releases/tag/v0.3.0-beta
|
|
147
251
|
[0.2.0-beta]: https://github.com/pranee54/AgentDoctor/releases/tag/v0.2.0-beta
|
|
148
252
|
[0.1.4-beta]: https://github.com/pranee54/AgentDoctor/releases/tag/v0.1.4-beta
|
|
149
253
|
[0.1.3-beta]: https://github.com/pranee54/AgentDoctor/releases/tag/v0.1.3-beta
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# AgentDoctor
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@praneeth_54/agentdoctor)
|
|
3
|
+
[](https://www.npmjs.com/package/@praneeth_54/agentdoctor)
|
|
4
4
|
[](https://www.npmjs.com/package/@praneeth_54/agentdoctor)
|
|
5
5
|
[](https://github.com/pranee54/AgentDoctor/actions)
|
|
6
6
|
[](https://nodejs.org)
|
|
@@ -16,7 +16,7 @@ AgentDoctor is a local CLI that inspects project-level AI coding agent setup —
|
|
|
16
16
|
npx @praneeth_54/agentdoctor
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
Public
|
|
19
|
+
Public release (`1.0.0`). Scan → Fix → Verify → CI. Deterministic scores in the terminal and JSON.
|
|
20
20
|
|
|
21
21
|
---
|
|
22
22
|
|
|
@@ -24,12 +24,12 @@ Public beta (`0.1.x-beta`). Deterministic readiness scores ship in JSON; automat
|
|
|
24
24
|
|
|
25
25
|

|
|
26
26
|
|
|
27
|
-
_Real scan of the included `insecure-agent-project` fixture using AgentDoctor
|
|
27
|
+
_Real scan of the included `insecure-agent-project` fixture using AgentDoctor v1.0.0._
|
|
28
28
|
|
|
29
29
|
```text
|
|
30
30
|
$ npx @praneeth_54/agentdoctor
|
|
31
31
|
|
|
32
|
-
🩺 AgentDoctor
|
|
32
|
+
🩺 AgentDoctor v1.0.0
|
|
33
33
|
|
|
34
34
|
Scanning repository...
|
|
35
35
|
|
|
@@ -71,10 +71,11 @@ Summary
|
|
|
71
71
|
1 warning
|
|
72
72
|
0 info
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
Readiness: 13/100
|
|
75
|
+
Category and agent scores: agentdoctor scan --json
|
|
75
76
|
```
|
|
76
77
|
|
|
77
|
-
Abbreviated text example from the same fixture for accessibility and search. Secret values are never printed.
|
|
78
|
+
Abbreviated text example from the same fixture for accessibility and search. Secret values are never printed. Re-run the scan if counts change.
|
|
78
79
|
|
|
79
80
|
---
|
|
80
81
|
|
|
@@ -99,7 +100,7 @@ Manually reviewing all of that across Cursor, Claude Code, and Codex is slow and
|
|
|
99
100
|
| ESLint | Source code |
|
|
100
101
|
| **AgentDoctor** | **AI coding agent environments** |
|
|
101
102
|
|
|
102
|
-
AgentDoctor analyzes configuration. It does not run agents
|
|
103
|
+
AgentDoctor analyzes configuration. It does not run agents or call an LLM. `agentdoctor fix` may append safe context exclusions (Cursor `.cursorignore`, Claude Code Read deny rules, and Codex filesystem deny keys); it does not rewrite secrets, credentials, or security modes such as `bypassPermissions`.
|
|
103
104
|
|
|
104
105
|
---
|
|
105
106
|
|
|
@@ -139,8 +140,9 @@ You get deterministic findings with:
|
|
|
139
140
|
- evidence paths
|
|
140
141
|
- affected agents when exposure claims are supported
|
|
141
142
|
- conservative recommendations
|
|
143
|
+
- readiness score (`scores.overall` in JSON; overall line in the terminal)
|
|
142
144
|
|
|
143
|
-
|
|
145
|
+
Safe context exclusions (Cursor / Claude Code / Codex) can be applied with `agentdoctor fix`. Security and review findings stay manual — Fix explains why and does not invent unsafe edits.
|
|
144
146
|
|
|
145
147
|
---
|
|
146
148
|
|
|
@@ -152,10 +154,10 @@ Automatic repair is **not** included in this beta. Findings tell you what to rev
|
|
|
152
154
|
npx @praneeth_54/agentdoctor
|
|
153
155
|
```
|
|
154
156
|
|
|
155
|
-
Pin a
|
|
157
|
+
Pin a version when you need a fixed install:
|
|
156
158
|
|
|
157
159
|
```bash
|
|
158
|
-
npx @praneeth_54/agentdoctor@0.
|
|
160
|
+
npx @praneeth_54/agentdoctor@1.0.0
|
|
159
161
|
```
|
|
160
162
|
|
|
161
163
|
### Global (optional)
|
|
@@ -165,12 +167,33 @@ npm install -g @praneeth_54/agentdoctor
|
|
|
165
167
|
agentdoctor
|
|
166
168
|
```
|
|
167
169
|
|
|
170
|
+
### Scan → Fix → Verify
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
# 1. Scan (save a baseline for Verify)
|
|
174
|
+
npx @praneeth_54/agentdoctor scan . --json > agentdoctor-report.json
|
|
175
|
+
|
|
176
|
+
# 2. Fix safe context exclusions (preview first with --dry-run)
|
|
177
|
+
npx @praneeth_54/agentdoctor fix . --dry-run
|
|
178
|
+
npx @praneeth_54/agentdoctor fix . -y
|
|
179
|
+
|
|
180
|
+
# 3. Verify against the baseline
|
|
181
|
+
npx @praneeth_54/agentdoctor verify . --baseline agentdoctor-report.json
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`fix` writes safe context exclusions for Cursor (`.cursorignore`), Claude Code
|
|
185
|
+
(`permissions.deny` Read rules in `.claude/settings.json`), and Codex (filesystem `deny`
|
|
186
|
+
keys under a permissions profile in `.codex/config.toml`) for findings such as unignored
|
|
187
|
+
`build/` or large logs. Review/manual security findings are listed as skipped — address those
|
|
188
|
+
yourself, then re-run `verify`.
|
|
189
|
+
|
|
168
190
|
### Common commands
|
|
169
191
|
|
|
170
192
|
```bash
|
|
171
193
|
agentdoctor .
|
|
172
|
-
agentdoctor . --json
|
|
173
|
-
agentdoctor . --
|
|
194
|
+
agentdoctor scan . --json
|
|
195
|
+
agentdoctor fix . --dry-run
|
|
196
|
+
agentdoctor verify . --ci --baseline agentdoctor-report.json
|
|
174
197
|
agentdoctor explain security/env-file-exposure
|
|
175
198
|
agentdoctor doctor
|
|
176
199
|
```
|
|
@@ -184,10 +207,11 @@ npm install @praneeth_54/agentdoctor
|
|
|
184
207
|
```
|
|
185
208
|
|
|
186
209
|
```ts
|
|
187
|
-
import { scan } from "@praneeth_54/agentdoctor";
|
|
210
|
+
import { scan, verify, buildFixPlan, applyFixPlan } from "@praneeth_54/agentdoctor";
|
|
188
211
|
|
|
189
212
|
const result = await scan({ cwd: process.cwd() });
|
|
190
213
|
console.log(result.summary);
|
|
214
|
+
console.log(result.scores?.overall);
|
|
191
215
|
console.log(result.agentSecurityAnalysis); // "full" | "limited"
|
|
192
216
|
```
|
|
193
217
|
|
|
@@ -259,10 +283,14 @@ steps:
|
|
|
259
283
|
|
|
260
284
|
- name: Audit coding-agent configuration
|
|
261
285
|
id: agentdoctor
|
|
262
|
-
uses: pranee54/AgentDoctor@
|
|
286
|
+
uses: pranee54/AgentDoctor@v1.0.0
|
|
263
287
|
with:
|
|
264
288
|
path: .
|
|
289
|
+
version: "1.0.0"
|
|
265
290
|
output-file: agentdoctor-report.json
|
|
291
|
+
minimum-score: "70"
|
|
292
|
+
fail-on-severity: critical
|
|
293
|
+
summary: "true"
|
|
266
294
|
|
|
267
295
|
- name: Upload AgentDoctor report
|
|
268
296
|
uses: actions/upload-artifact@v4
|
|
@@ -271,10 +299,13 @@ steps:
|
|
|
271
299
|
path: ${{ steps.agentdoctor.outputs.report-path }}
|
|
272
300
|
```
|
|
273
301
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
302
|
+
Policy inputs: `minimum-score`, `fail-on-severity`, `fail-on-rule`, `fail-on-new`,
|
|
303
|
+
`verify-baseline`, `summary`, `annotations`. The Action stays report-only until you set a
|
|
304
|
+
policy input. Explicitly set `version: "1.0.0"` after npm publish (the Action default remains
|
|
305
|
+
`0.3.0-beta` until that post-publish bump). For local CI against this repo, use
|
|
306
|
+
`version: workspace` after `npm run build`. The action installs `@praneeth_54/agentdoctor`,
|
|
307
|
+
runs scan (or `verify` when `verify-baseline` is set) with `--json`, and writes the report
|
|
308
|
+
inside the workspace.
|
|
278
309
|
|
|
279
310
|
### CLI
|
|
280
311
|
|
|
@@ -282,43 +313,49 @@ Use JSON directly in other CI systems:
|
|
|
282
313
|
|
|
283
314
|
```bash
|
|
284
315
|
# Report-only (exit 0 even when findings exist; scores still in JSON)
|
|
316
|
+
npx @praneeth_54/agentdoctor --json
|
|
317
|
+
|
|
318
|
+
# Fail when any critical finding exists
|
|
285
319
|
npx @praneeth_54/agentdoctor --ci --json
|
|
286
320
|
|
|
287
|
-
# Fail
|
|
321
|
+
# Fail when overall readiness is below 70 (with --ci also fails on criticals)
|
|
288
322
|
npx @praneeth_54/agentdoctor --ci --json --min-score 70
|
|
323
|
+
|
|
324
|
+
# Fail on warning-or-higher (overrides the default critical gate from --ci)
|
|
325
|
+
npx @praneeth_54/agentdoctor --ci --json --fail-on-severity warning
|
|
289
326
|
```
|
|
290
327
|
|
|
291
|
-
`--ci`
|
|
292
|
-
|
|
293
|
-
`
|
|
328
|
+
`--ci` fails when any **critical** finding exists. Override the severity floor with
|
|
329
|
+
`--fail-on-severity`, and use `--min-score` / `--fail-on-rule` for additional gates.
|
|
330
|
+
Omit `--ci` for report-only JSON (exit `0` even when findings exist).
|
|
294
331
|
|
|
295
332
|
Exit codes: [docs/exit-codes.md](docs/exit-codes.md). Compatibility promises: [docs/compatibility.md](docs/compatibility.md).
|
|
296
333
|
|
|
297
334
|
### Readiness scoring
|
|
298
335
|
|
|
299
336
|
Scans populate `scoringAvailable: true` and a deterministic `scores` object
|
|
300
|
-
(overall, categories, agents). The terminal
|
|
301
|
-
scores are
|
|
337
|
+
(overall, categories, agents). The terminal prints overall readiness; category and agent
|
|
338
|
+
scores are in JSON (`--json`).
|
|
302
339
|
|
|
303
340
|
`--min-score N` is enforced by the CLI. Details (weights, security caps, threshold rules,
|
|
304
341
|
and deferred v2 items): [docs/scoring.md](docs/scoring.md).
|
|
305
342
|
|
|
306
343
|
---
|
|
307
344
|
|
|
308
|
-
##
|
|
345
|
+
## Known limitations
|
|
309
346
|
|
|
310
|
-
Honest limits of
|
|
347
|
+
Honest limits of v1:
|
|
311
348
|
|
|
312
|
-
| Limitation
|
|
313
|
-
|
|
|
314
|
-
|
|
|
315
|
-
|
|
|
316
|
-
|
|
|
317
|
-
|
|
|
318
|
-
|
|
|
319
|
-
|
|
|
349
|
+
| Limitation | Status |
|
|
350
|
+
| --------------------------- | ------------------------------------------------------------------------------- |
|
|
351
|
+
| Automatic fixes | Safe Cursor / Claude Code / Codex context exclusions only |
|
|
352
|
+
| Security findings | Review/manual — Fix does not rewrite secrets or security modes |
|
|
353
|
+
| Secret-content scanning | Filename / config heuristics only |
|
|
354
|
+
| Detection style | Intentionally conservative; false security findings are avoided |
|
|
355
|
+
| Agent coverage | Cursor, Claude Code, Codex project configs |
|
|
356
|
+
| Missing-path residual noise | Broad path-lattice expansion deferred; instruction-directory resolution shipped |
|
|
320
357
|
|
|
321
|
-
See [CHANGELOG.md](CHANGELOG.md) and [docs/
|
|
358
|
+
See [CHANGELOG.md](CHANGELOG.md) and [docs/compatibility.md](docs/compatibility.md).
|
|
322
359
|
|
|
323
360
|
---
|
|
324
361
|
|
|
@@ -367,6 +404,20 @@ node dist/cli/index.js ./fixtures/clean-configured-project
|
|
|
367
404
|
|
|
368
405
|
---
|
|
369
406
|
|
|
407
|
+
## Project Brain (separate laboratory capability)
|
|
408
|
+
|
|
409
|
+
AgentDoctor V1 ships the **safety** product: Scan → Fix → Verify → Policy → CI.
|
|
410
|
+
|
|
411
|
+
A parallel **Project Brain** engineering layer lives under `src/core/understanding/` (durable local claims, evidence, snapshots, query/trace/delta). It is:
|
|
412
|
+
|
|
413
|
+
- **not** part of the published npm package (`tsconfig.build` excludes it)
|
|
414
|
+
- **not** wired into the public CLI
|
|
415
|
+
- documented in [docs/project-brain.md](docs/project-brain.md)
|
|
416
|
+
|
|
417
|
+
See [PROJECT_AUDIT.txt](PROJECT_AUDIT.txt) and [RELEASE_CHECKLIST.txt](RELEASE_CHECKLIST.txt).
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
370
421
|
## Next steps
|
|
371
422
|
|
|
372
423
|
- **Try it:** `npx @praneeth_54/agentdoctor`
|
package/dist/agents/inspect.js
CHANGED
|
@@ -23,11 +23,29 @@ export async function inspectRepoFile(root, relativePath, maxFileSizeBytes = DEF
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
try {
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
26
|
+
// Open first, then inspect via the same handle (avoids TOCTOU with prior lstat/stat).
|
|
27
|
+
const handle = await fs.open(absolutePath, "r");
|
|
28
|
+
try {
|
|
29
|
+
const lstat = await fs.lstat(absolutePath);
|
|
30
|
+
const isSymlink = lstat.isSymbolicLink();
|
|
31
|
+
if (isSymlink) {
|
|
32
|
+
const real = await fs.realpath(absolutePath);
|
|
33
|
+
if (!isPathInsideRoot(root, real)) {
|
|
34
|
+
return {
|
|
35
|
+
relativePath: normalizedRelative,
|
|
36
|
+
absolutePath,
|
|
37
|
+
exists: true,
|
|
38
|
+
readable: false,
|
|
39
|
+
sizeBytes: 0,
|
|
40
|
+
empty: true,
|
|
41
|
+
isSymlink: true,
|
|
42
|
+
text: null,
|
|
43
|
+
error: "Symlink target is outside repository root",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const stat = await handle.stat();
|
|
48
|
+
if (!stat.isFile()) {
|
|
31
49
|
return {
|
|
32
50
|
relativePath: normalizedRelative,
|
|
33
51
|
absolutePath,
|
|
@@ -35,64 +53,53 @@ export async function inspectRepoFile(root, relativePath, maxFileSizeBytes = DEF
|
|
|
35
53
|
readable: false,
|
|
36
54
|
sizeBytes: 0,
|
|
37
55
|
empty: true,
|
|
38
|
-
isSymlink
|
|
56
|
+
isSymlink,
|
|
39
57
|
text: null,
|
|
40
|
-
error: "
|
|
58
|
+
error: "Not a regular file",
|
|
41
59
|
};
|
|
42
60
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
61
|
+
const sizeBytes = Number(stat.size);
|
|
62
|
+
if (sizeBytes === 0) {
|
|
63
|
+
return {
|
|
64
|
+
relativePath: normalizedRelative,
|
|
65
|
+
absolutePath,
|
|
66
|
+
exists: true,
|
|
67
|
+
readable: true,
|
|
68
|
+
sizeBytes: 0,
|
|
69
|
+
empty: true,
|
|
70
|
+
isSymlink,
|
|
71
|
+
text: "",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (sizeBytes > maxFileSizeBytes) {
|
|
75
|
+
return {
|
|
76
|
+
relativePath: normalizedRelative,
|
|
77
|
+
absolutePath,
|
|
78
|
+
exists: true,
|
|
79
|
+
readable: false,
|
|
80
|
+
sizeBytes,
|
|
81
|
+
empty: false,
|
|
82
|
+
isSymlink,
|
|
83
|
+
text: null,
|
|
84
|
+
error: "File exceeds max size limit",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const text = await handle.readFile("utf8");
|
|
88
|
+
const trimmedEmpty = text.trim().length === 0;
|
|
60
89
|
return {
|
|
61
90
|
relativePath: normalizedRelative,
|
|
62
91
|
absolutePath,
|
|
63
92
|
exists: true,
|
|
64
93
|
readable: true,
|
|
65
|
-
sizeBytes: 0,
|
|
66
|
-
empty: true,
|
|
67
|
-
isSymlink,
|
|
68
|
-
text: "",
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
if (sizeBytes > maxFileSizeBytes) {
|
|
72
|
-
return {
|
|
73
|
-
relativePath: normalizedRelative,
|
|
74
|
-
absolutePath,
|
|
75
|
-
exists: true,
|
|
76
|
-
readable: false,
|
|
77
94
|
sizeBytes,
|
|
78
|
-
empty:
|
|
95
|
+
empty: trimmedEmpty,
|
|
79
96
|
isSymlink,
|
|
80
|
-
text
|
|
81
|
-
error: "File exceeds max size limit",
|
|
97
|
+
text,
|
|
82
98
|
};
|
|
83
99
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
relativePath: normalizedRelative,
|
|
88
|
-
absolutePath,
|
|
89
|
-
exists: true,
|
|
90
|
-
readable: true,
|
|
91
|
-
sizeBytes,
|
|
92
|
-
empty: trimmedEmpty,
|
|
93
|
-
isSymlink,
|
|
94
|
-
text,
|
|
95
|
-
};
|
|
100
|
+
finally {
|
|
101
|
+
await handle.close();
|
|
102
|
+
}
|
|
96
103
|
}
|
|
97
104
|
catch (error) {
|
|
98
105
|
const message = error instanceof Error ? error.message : "Unable to read file";
|
|
@@ -15,7 +15,7 @@ export async function runDoctorCommand() {
|
|
|
15
15
|
lines.push(` ${symbolOk()} Core scan API available`);
|
|
16
16
|
lines.push("");
|
|
17
17
|
lines.push(colors.dim("Environment looks ready."));
|
|
18
|
-
lines.push(colors.dim("
|
|
18
|
+
lines.push(colors.dim("Run agentdoctor scan → fix → verify to complete the readiness loop."));
|
|
19
19
|
lines.push("");
|
|
20
20
|
process.stdout.write(lines.join("\n"));
|
|
21
21
|
return EXIT_CODES.SUCCESS;
|
|
@@ -39,9 +39,9 @@ export async function runExplainCommand(ruleId) {
|
|
|
39
39
|
lines.push("");
|
|
40
40
|
lines.push(colors.bold("Can AgentDoctor safely fix it?"));
|
|
41
41
|
lines.push(rule.fixability === "safe"
|
|
42
|
-
? "
|
|
42
|
+
? " Yes for Cursor (`.cursorignore`), Claude Code (Read deny in `.claude/settings.json`), and Codex (filesystem deny in `.codex/config.toml`)."
|
|
43
43
|
: rule.fixability === "review"
|
|
44
|
-
? "
|
|
44
|
+
? " No — requires human review. Fix reports why and leaves the file unchanged."
|
|
45
45
|
: rule.fixability === "manual"
|
|
46
46
|
? " No automatic fix. Manual remediation required."
|
|
47
47
|
: " No fix available.");
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type ExitCode } from "../../types/index.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
export declare function runFixCommand(options: {
|
|
2
|
+
export interface FixCommandOptions {
|
|
3
|
+
targetPath?: string;
|
|
6
4
|
dryRun?: boolean;
|
|
7
5
|
yes?: boolean;
|
|
8
|
-
}
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Safe Repository Mutation — apply allowlisted agent-config exclusions.
|
|
9
|
+
*/
|
|
10
|
+
export declare function runFixCommand(options: FixCommandOptions): Promise<ExitCode>;
|
package/dist/cli/commands/fix.js
CHANGED
|
@@ -1,19 +1,80 @@
|
|
|
1
1
|
import { EXIT_CODES } from "../../types/index.js";
|
|
2
|
+
import { isDirectory } from "../../utils/fs.js";
|
|
3
|
+
import { resolveRepoRoot } from "../../utils/path.js";
|
|
4
|
+
import { applyFixPlan, readClaudeSettings, readCodexConfig, readCursorignore, } from "../../core/fix/apply.js";
|
|
5
|
+
import { buildFixPlan } from "../../core/fix/plan.js";
|
|
6
|
+
import { renderFixPlanTerminal } from "../../core/fix/render.js";
|
|
7
|
+
import { runFix } from "../../core/fix/run.js";
|
|
8
|
+
import { scan } from "../../core/scanner/scan.js";
|
|
2
9
|
import { colors } from "../../utils/colors.js";
|
|
3
10
|
/**
|
|
4
|
-
* Safe
|
|
11
|
+
* Safe Repository Mutation — apply allowlisted agent-config exclusions.
|
|
5
12
|
*/
|
|
6
13
|
export async function runFixCommand(options) {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (options.dryRun) {
|
|
12
|
-
lines.push(" Dry-run mode requested.");
|
|
14
|
+
const target = resolveRepoRoot(options.targetPath ?? process.cwd());
|
|
15
|
+
if (!(await isDirectory(target))) {
|
|
16
|
+
console.error(`Error: not a directory: ${target}`);
|
|
17
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
13
18
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
const dryRun = options.dryRun === true;
|
|
20
|
+
const yes = options.yes === true;
|
|
21
|
+
try {
|
|
22
|
+
if (dryRun) {
|
|
23
|
+
const result = await scan({ cwd: target });
|
|
24
|
+
const plan = await buildFixPlan(result);
|
|
25
|
+
const cursorContent = await readCursorignore(plan.root);
|
|
26
|
+
const claudeSettingsContent = await readClaudeSettings(plan.root);
|
|
27
|
+
const codexConfigContent = await readCodexConfig(plan.root);
|
|
28
|
+
const applyResult = await applyFixPlan(plan, { dryRun: true });
|
|
29
|
+
process.stdout.write(renderFixPlanTerminal(plan, {
|
|
30
|
+
dryRun: true,
|
|
31
|
+
cursorContent,
|
|
32
|
+
claudeSettingsContent,
|
|
33
|
+
codexConfigContent,
|
|
34
|
+
applyResult,
|
|
35
|
+
}));
|
|
36
|
+
return EXIT_CODES.SUCCESS;
|
|
37
|
+
}
|
|
38
|
+
const { plan, applyResult, cancelled } = await runFix({
|
|
39
|
+
cwd: target,
|
|
40
|
+
dryRun: false,
|
|
41
|
+
yes,
|
|
42
|
+
});
|
|
43
|
+
if (cancelled) {
|
|
44
|
+
process.stdout.write("\n Cancelled. No files were modified.\n\n");
|
|
45
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
46
|
+
}
|
|
47
|
+
const cursorContentAfter = await readCursorignore(plan.root);
|
|
48
|
+
const claudeSettingsAfter = await readClaudeSettings(plan.root);
|
|
49
|
+
const codexConfigAfter = await readCodexConfig(plan.root);
|
|
50
|
+
process.stdout.write(renderFixPlanTerminal(plan, {
|
|
51
|
+
dryRun: false,
|
|
52
|
+
cursorContent: cursorContentAfter,
|
|
53
|
+
claudeSettingsContent: claudeSettingsAfter,
|
|
54
|
+
codexConfigContent: codexConfigAfter,
|
|
55
|
+
applyResult,
|
|
56
|
+
}));
|
|
57
|
+
if (applyResult.writtenFiles.length > 0) {
|
|
58
|
+
const after = await scan({ cwd: target });
|
|
59
|
+
const remainingSafeContext = after.findings.filter((f) => f.fixability === "safe" &&
|
|
60
|
+
(f.ruleId === "context/generated-directory" || f.ruleId === "context/large-log-file"));
|
|
61
|
+
process.stdout.write(colors.dim(` Re-scan: ${remainingSafeContext.length} safe context finding(s) remain.\n\n`));
|
|
62
|
+
}
|
|
63
|
+
return EXIT_CODES.SUCCESS;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
67
|
+
console.error(`Error: ${message}`);
|
|
68
|
+
if (isFixConfigOrPermissionError(message)) {
|
|
69
|
+
return EXIT_CODES.USAGE_ERROR;
|
|
70
|
+
}
|
|
71
|
+
return EXIT_CODES.INTERNAL_ERROR;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function isFixConfigOrPermissionError(message) {
|
|
75
|
+
return (message.includes("refusing") ||
|
|
76
|
+
message.includes("not valid JSON") ||
|
|
77
|
+
message.includes("EACCES") ||
|
|
78
|
+
message.includes("EPERM") ||
|
|
79
|
+
message.includes("permission denied"));
|
|
19
80
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type PolicyOptions } from "../../core/policy/evaluate.js";
|
|
1
2
|
import { type ExitCode } from "../../types/index.js";
|
|
2
3
|
export interface ScanCommandOptions {
|
|
3
4
|
targetPath?: string;
|
|
@@ -5,6 +6,10 @@ export interface ScanCommandOptions {
|
|
|
5
6
|
ci?: boolean;
|
|
6
7
|
verbose?: boolean;
|
|
7
8
|
minScore?: number;
|
|
9
|
+
failOnSeverity?: PolicyOptions["failOnSeverity"];
|
|
10
|
+
failOnRules?: string[];
|
|
11
|
+
summary?: boolean;
|
|
12
|
+
annotations?: boolean;
|
|
8
13
|
}
|
|
9
14
|
export declare function runScanCommand(options: ScanCommandOptions): Promise<ExitCode>;
|
|
10
15
|
export declare function resolveTargetArgument(pathArg: string | undefined, cwd?: string): string;
|