@praneeth_54/agentdoctor 0.3.0-beta → 1.1.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.
Files changed (267) hide show
  1. package/CHANGELOG.md +105 -1
  2. package/README.md +64 -31
  3. package/dist/agents/inspect.js +58 -51
  4. package/dist/cli/commands/brain-mcp.d.ts +9 -0
  5. package/dist/cli/commands/brain-mcp.js +38 -0
  6. package/dist/cli/commands/explain.js +1 -1
  7. package/dist/cli/commands/fix.js +22 -6
  8. package/dist/cli/commands/scan.d.ts +5 -0
  9. package/dist/cli/commands/scan.js +25 -6
  10. package/dist/cli/commands/verify.d.ts +6 -0
  11. package/dist/cli/commands/verify.js +25 -11
  12. package/dist/cli/program.js +61 -7
  13. package/dist/constants.d.ts +1 -1
  14. package/dist/constants.js +1 -1
  15. package/dist/core/fix/apply.d.ts +5 -1
  16. package/dist/core/fix/apply.js +34 -14
  17. package/dist/core/fix/plan.d.ts +1 -1
  18. package/dist/core/fix/plan.js +110 -3
  19. package/dist/core/fix/render.d.ts +2 -0
  20. package/dist/core/fix/render.js +57 -9
  21. package/dist/core/fix/run.js +5 -1
  22. package/dist/core/fix/types.d.ts +1 -1
  23. package/dist/core/fix/types.js +5 -1
  24. package/dist/core/fix/writers/claude-settings.d.ts +16 -0
  25. package/dist/core/fix/writers/claude-settings.js +100 -0
  26. package/dist/core/fix/writers/codex-config.d.ts +22 -0
  27. package/dist/core/fix/writers/codex-config.js +183 -0
  28. package/dist/core/fix/writers/cursorignore.js +2 -8
  29. package/dist/core/path-resolution/index.d.ts +5 -0
  30. package/dist/core/path-resolution/index.js +5 -0
  31. package/dist/core/path-resolution/prepare.d.ts +33 -0
  32. package/dist/core/path-resolution/prepare.js +98 -0
  33. package/dist/core/policy/evaluate.d.ts +44 -0
  34. package/dist/core/policy/evaluate.js +120 -0
  35. package/dist/core/rules/claude-deny.d.ts +8 -0
  36. package/dist/core/rules/claude-deny.js +39 -0
  37. package/dist/core/rules/codex-deny.d.ts +5 -0
  38. package/dist/core/rules/codex-deny.js +42 -0
  39. package/dist/core/rules/context/generated-directory.js +43 -9
  40. package/dist/core/rules/context/large-log-file.js +27 -10
  41. package/dist/core/rules/ignore.js +2 -5
  42. package/dist/core/rules/instructions/missing-path-reference.d.ts +5 -1
  43. package/dist/core/rules/instructions/missing-path-reference.js +24 -6
  44. package/dist/core/rules/path-kind.d.ts +15 -2
  45. package/dist/core/rules/path-kind.js +113 -10
  46. package/dist/core/rules/security/env-file-exposure.js +9 -24
  47. package/dist/core/rules/text-cache.js +46 -40
  48. package/dist/core/understanding/architecture/index.d.ts +4 -0
  49. package/dist/core/understanding/architecture/index.js +3 -0
  50. package/dist/core/understanding/architecture/infer.d.ts +6 -0
  51. package/dist/core/understanding/architecture/infer.js +39 -0
  52. package/dist/core/understanding/architecture/models.d.ts +23 -0
  53. package/dist/core/understanding/architecture/models.js +127 -0
  54. package/dist/core/understanding/architecture/patterns/distribution.d.ts +2 -0
  55. package/dist/core/understanding/architecture/patterns/distribution.js +115 -0
  56. package/dist/core/understanding/architecture/patterns/index.d.ts +2 -0
  57. package/dist/core/understanding/architecture/patterns/index.js +8 -0
  58. package/dist/core/understanding/architecture/patterns/layered.d.ts +2 -0
  59. package/dist/core/understanding/architecture/patterns/layered.js +279 -0
  60. package/dist/core/understanding/architecture/patterns/structural.d.ts +2 -0
  61. package/dist/core/understanding/architecture/patterns/structural.js +197 -0
  62. package/dist/core/understanding/architecture/rules.d.ts +4 -0
  63. package/dist/core/understanding/architecture/rules.js +6 -0
  64. package/dist/core/understanding/architecture/types.d.ts +52 -0
  65. package/dist/core/understanding/architecture/types.js +1 -0
  66. package/dist/core/understanding/brain/build.d.ts +13 -0
  67. package/dist/core/understanding/brain/build.js +158 -0
  68. package/dist/core/understanding/brain/claims/index.d.ts +3 -0
  69. package/dist/core/understanding/brain/claims/index.js +2 -0
  70. package/dist/core/understanding/brain/claims/lifecycle.d.ts +16 -0
  71. package/dist/core/understanding/brain/claims/lifecycle.js +78 -0
  72. package/dist/core/understanding/brain/claims/types.d.ts +32 -0
  73. package/dist/core/understanding/brain/claims/types.js +25 -0
  74. package/dist/core/understanding/brain/components/index.d.ts +17 -0
  75. package/dist/core/understanding/brain/components/index.js +97 -0
  76. package/dist/core/understanding/brain/components/types.d.ts +16 -0
  77. package/dist/core/understanding/brain/components/types.js +8 -0
  78. package/dist/core/understanding/brain/confidence.d.ts +19 -0
  79. package/dist/core/understanding/brain/confidence.js +39 -0
  80. package/dist/core/understanding/brain/contract.d.ts +17 -0
  81. package/dist/core/understanding/brain/contract.js +100 -0
  82. package/dist/core/understanding/brain/contradictions/index.d.ts +12 -0
  83. package/dist/core/understanding/brain/contradictions/index.js +57 -0
  84. package/dist/core/understanding/brain/contradictions/types.d.ts +12 -0
  85. package/dist/core/understanding/brain/contradictions/types.js +8 -0
  86. package/dist/core/understanding/brain/delta.d.ts +25 -0
  87. package/dist/core/understanding/brain/delta.js +116 -0
  88. package/dist/core/understanding/brain/evidence/index.d.ts +3 -0
  89. package/dist/core/understanding/brain/evidence/index.js +2 -0
  90. package/dist/core/understanding/brain/evidence/redact.d.ts +4 -0
  91. package/dist/core/understanding/brain/evidence/redact.js +36 -0
  92. package/dist/core/understanding/brain/evidence/types.d.ts +34 -0
  93. package/dist/core/understanding/brain/evidence/types.js +35 -0
  94. package/dist/core/understanding/brain/explain.d.ts +17 -0
  95. package/dist/core/understanding/brain/explain.js +33 -0
  96. package/dist/core/understanding/brain/index.d.ts +28 -0
  97. package/dist/core/understanding/brain/index.js +16 -0
  98. package/dist/core/understanding/brain/migrate.d.ts +21 -0
  99. package/dist/core/understanding/brain/migrate.js +47 -0
  100. package/dist/core/understanding/brain/query.d.ts +60 -0
  101. package/dist/core/understanding/brain/query.js +92 -0
  102. package/dist/core/understanding/brain/security.d.ts +3 -0
  103. package/dist/core/understanding/brain/security.js +43 -0
  104. package/dist/core/understanding/brain/storage/index.d.ts +2 -0
  105. package/dist/core/understanding/brain/storage/index.js +1 -0
  106. package/dist/core/understanding/brain/storage/store.d.ts +53 -0
  107. package/dist/core/understanding/brain/storage/store.js +305 -0
  108. package/dist/core/understanding/brain/trace.d.ts +24 -0
  109. package/dist/core/understanding/brain/trace.js +207 -0
  110. package/dist/core/understanding/brain/types.d.ts +32 -0
  111. package/dist/core/understanding/brain/types.js +8 -0
  112. package/dist/core/understanding/brain/version.d.ts +13 -0
  113. package/dist/core/understanding/brain/version.js +7 -0
  114. package/dist/core/understanding/delta/compare.d.ts +26 -0
  115. package/dist/core/understanding/delta/compare.js +74 -0
  116. package/dist/core/understanding/delta/index.d.ts +2 -0
  117. package/dist/core/understanding/delta/index.js +1 -0
  118. package/dist/core/understanding/dependencies/discover.d.ts +7 -0
  119. package/dist/core/understanding/dependencies/discover.js +550 -0
  120. package/dist/core/understanding/dependencies/extract.d.ts +10 -0
  121. package/dist/core/understanding/dependencies/extract.js +189 -0
  122. package/dist/core/understanding/dependencies/index.d.ts +4 -0
  123. package/dist/core/understanding/dependencies/index.js +3 -0
  124. package/dist/core/understanding/dependencies/models.d.ts +12 -0
  125. package/dist/core/understanding/dependencies/models.js +125 -0
  126. package/dist/core/understanding/dependencies/types.d.ts +27 -0
  127. package/dist/core/understanding/dependencies/types.js +1 -0
  128. package/dist/core/understanding/domain/discover.d.ts +14 -0
  129. package/dist/core/understanding/domain/discover.js +90 -0
  130. package/dist/core/understanding/domain/index.d.ts +2 -0
  131. package/dist/core/understanding/domain/index.js +1 -0
  132. package/dist/core/understanding/entrypoints/discover.d.ts +6 -0
  133. package/dist/core/understanding/entrypoints/discover.js +62 -0
  134. package/dist/core/understanding/entrypoints/extract.d.ts +19 -0
  135. package/dist/core/understanding/entrypoints/extract.js +43 -0
  136. package/dist/core/understanding/entrypoints/index.d.ts +4 -0
  137. package/dist/core/understanding/entrypoints/index.js +3 -0
  138. package/dist/core/understanding/entrypoints/models.d.ts +26 -0
  139. package/dist/core/understanding/entrypoints/models.js +216 -0
  140. package/dist/core/understanding/entrypoints/types.d.ts +20 -0
  141. package/dist/core/understanding/entrypoints/types.js +1 -0
  142. package/dist/core/understanding/index.d.ts +32 -0
  143. package/dist/core/understanding/index.js +18 -0
  144. package/dist/core/understanding/mind/build.d.ts +16 -0
  145. package/dist/core/understanding/mind/build.js +134 -0
  146. package/dist/core/understanding/mind/index.d.ts +6 -0
  147. package/dist/core/understanding/mind/index.js +3 -0
  148. package/dist/core/understanding/mind/query.d.ts +33 -0
  149. package/dist/core/understanding/mind/query.js +39 -0
  150. package/dist/core/understanding/mind/types.d.ts +24 -0
  151. package/dist/core/understanding/mind/types.js +7 -0
  152. package/dist/core/understanding/model/builder.d.ts +9 -0
  153. package/dist/core/understanding/model/builder.js +209 -0
  154. package/dist/core/understanding/model/ids.d.ts +7 -0
  155. package/dist/core/understanding/model/ids.js +48 -0
  156. package/dist/core/understanding/model/index.d.ts +7 -0
  157. package/dist/core/understanding/model/index.js +6 -0
  158. package/dist/core/understanding/model/schema.d.ts +12 -0
  159. package/dist/core/understanding/model/schema.js +33 -0
  160. package/dist/core/understanding/model/serializer.d.ts +9 -0
  161. package/dist/core/understanding/model/serializer.js +46 -0
  162. package/dist/core/understanding/model/types.d.ts +110 -0
  163. package/dist/core/understanding/model/types.js +1 -0
  164. package/dist/core/understanding/model/validator.d.ts +5 -0
  165. package/dist/core/understanding/model/validator.js +126 -0
  166. package/dist/core/understanding/model/version.d.ts +9 -0
  167. package/dist/core/understanding/model/version.js +7 -0
  168. package/dist/core/understanding/ownership/discover.d.ts +12 -0
  169. package/dist/core/understanding/ownership/discover.js +242 -0
  170. package/dist/core/understanding/ownership/index.d.ts +2 -0
  171. package/dist/core/understanding/ownership/index.js +1 -0
  172. package/dist/core/understanding/ownership/types.d.ts +23 -0
  173. package/dist/core/understanding/ownership/types.js +1 -0
  174. package/dist/core/understanding/query/engine.d.ts +24 -0
  175. package/dist/core/understanding/query/engine.js +35 -0
  176. package/dist/core/understanding/query/errors.d.ts +13 -0
  177. package/dist/core/understanding/query/errors.js +26 -0
  178. package/dist/core/understanding/query/executor.d.ts +8 -0
  179. package/dist/core/understanding/query/executor.js +34 -0
  180. package/dist/core/understanding/query/index.d.ts +7 -0
  181. package/dist/core/understanding/query/index.js +5 -0
  182. package/dist/core/understanding/query/models.d.ts +102 -0
  183. package/dist/core/understanding/query/models.js +1 -0
  184. package/dist/core/understanding/query/query.d.ts +68 -0
  185. package/dist/core/understanding/query/query.js +145 -0
  186. package/dist/core/understanding/query/registry.d.ts +8 -0
  187. package/dist/core/understanding/query/registry.js +340 -0
  188. package/dist/core/understanding/query/types.d.ts +63 -0
  189. package/dist/core/understanding/query/types.js +1 -0
  190. package/dist/core/understanding/relationships/discover.d.ts +11 -0
  191. package/dist/core/understanding/relationships/discover.js +517 -0
  192. package/dist/core/understanding/relationships/extract.d.ts +14 -0
  193. package/dist/core/understanding/relationships/extract.js +165 -0
  194. package/dist/core/understanding/relationships/index.d.ts +4 -0
  195. package/dist/core/understanding/relationships/index.js +3 -0
  196. package/dist/core/understanding/relationships/models.d.ts +21 -0
  197. package/dist/core/understanding/relationships/models.js +185 -0
  198. package/dist/core/understanding/relationships/types.d.ts +42 -0
  199. package/dist/core/understanding/relationships/types.js +1 -0
  200. package/dist/core/understanding/risks/discover.d.ts +8 -0
  201. package/dist/core/understanding/risks/discover.js +155 -0
  202. package/dist/core/understanding/risks/index.d.ts +2 -0
  203. package/dist/core/understanding/risks/index.js +1 -0
  204. package/dist/core/understanding/risks/types.d.ts +26 -0
  205. package/dist/core/understanding/risks/types.js +1 -0
  206. package/dist/core/understanding/shared/domain-lexicon.d.ts +9 -0
  207. package/dist/core/understanding/shared/domain-lexicon.js +107 -0
  208. package/dist/core/understanding/shared/index.d.ts +2 -0
  209. package/dist/core/understanding/shared/index.js +2 -0
  210. package/dist/core/understanding/shared/tokens.d.ts +7 -0
  211. package/dist/core/understanding/shared/tokens.js +141 -0
  212. package/dist/core/understanding/snapshot/identity.d.ts +24 -0
  213. package/dist/core/understanding/snapshot/identity.js +79 -0
  214. package/dist/core/understanding/snapshot/index.d.ts +2 -0
  215. package/dist/core/understanding/snapshot/index.js +1 -0
  216. package/dist/core/understanding/types/index.d.ts +22 -0
  217. package/dist/core/understanding/types/index.js +1 -0
  218. package/dist/core/understanding/understand/formatter.d.ts +10 -0
  219. package/dist/core/understanding/understand/formatter.js +115 -0
  220. package/dist/core/understanding/understand/index.d.ts +4 -0
  221. package/dist/core/understanding/understand/index.js +3 -0
  222. package/dist/core/understanding/understand/service.d.ts +17 -0
  223. package/dist/core/understanding/understand/service.js +76 -0
  224. package/dist/core/understanding/understand/summary.d.ts +14 -0
  225. package/dist/core/understanding/understand/summary.js +39 -0
  226. package/dist/core/understanding/understand/types.d.ts +31 -0
  227. package/dist/core/understanding/understand/types.js +1 -0
  228. package/dist/discovery/files.js +7 -2
  229. package/dist/discovery/log-like.d.ts +2 -0
  230. package/dist/discovery/log-like.js +7 -0
  231. package/dist/index.d.ts +2 -0
  232. package/dist/index.js +1 -0
  233. package/dist/mcp/brain/compile.d.ts +9 -0
  234. package/dist/mcp/brain/compile.js +42 -0
  235. package/dist/mcp/brain/errors.d.ts +12 -0
  236. package/dist/mcp/brain/errors.js +23 -0
  237. package/dist/mcp/brain/index.d.ts +11 -0
  238. package/dist/mcp/brain/index.js +8 -0
  239. package/dist/mcp/brain/provenance.d.ts +29 -0
  240. package/dist/mcp/brain/provenance.js +59 -0
  241. package/dist/mcp/brain/schemas.d.ts +13 -0
  242. package/dist/mcp/brain/schemas.js +105 -0
  243. package/dist/mcp/brain/security/root.d.ts +6 -0
  244. package/dist/mcp/brain/security/root.js +66 -0
  245. package/dist/mcp/brain/server.d.ts +12 -0
  246. package/dist/mcp/brain/server.js +46 -0
  247. package/dist/mcp/brain/session.d.ts +33 -0
  248. package/dist/mcp/brain/session.js +117 -0
  249. package/dist/mcp/brain/tools/handlers.d.ts +13 -0
  250. package/dist/mcp/brain/tools/handlers.js +483 -0
  251. package/dist/mcp/brain/tools/registry.d.ts +9 -0
  252. package/dist/mcp/brain/tools/registry.js +191 -0
  253. package/dist/reporters/github/annotations.d.ts +8 -0
  254. package/dist/reporters/github/annotations.js +42 -0
  255. package/dist/reporters/github/emit.d.ts +23 -0
  256. package/dist/reporters/github/emit.js +31 -0
  257. package/dist/reporters/github/summary.d.ts +20 -0
  258. package/dist/reporters/github/summary.js +111 -0
  259. package/dist/reporters/terminal/report.d.ts +5 -0
  260. package/dist/reporters/terminal/report.js +68 -5
  261. package/dist/reporters/verify/terminal.d.ts +4 -0
  262. package/dist/reporters/verify/terminal.js +26 -0
  263. package/dist/utils/fs.d.ts +5 -0
  264. package/dist/utils/fs.js +34 -0
  265. package/dist/utils/path.d.ts +5 -0
  266. package/dist/utils/path.js +16 -0
  267. package/package.json +15 -1
@@ -0,0 +1,42 @@
1
+ /**
2
+ * GitHub Actions workflow commands for file annotations.
3
+ * Emitted to stderr so `--json` stdout stays pure JSON.
4
+ *
5
+ * @see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
6
+ */
7
+ export function renderGithubAnnotations(findings) {
8
+ const lines = [];
9
+ for (const finding of findings) {
10
+ lines.push(formatAnnotation(finding));
11
+ }
12
+ return lines.length > 0 ? `${lines.join("\n")}\n` : "";
13
+ }
14
+ function formatAnnotation(finding) {
15
+ const level = annotationLevel(finding.severity);
16
+ const properties = [];
17
+ const file = finding.evidence?.path?.replace(/\\/g, "/");
18
+ if (file) {
19
+ properties.push(`file=${escapeProperty(file)}`);
20
+ }
21
+ if (finding.evidence?.line !== undefined && Number.isFinite(finding.evidence.line)) {
22
+ properties.push(`line=${finding.evidence.line}`);
23
+ }
24
+ properties.push(`title=${escapeProperty(`${finding.ruleId}: ${finding.title}`)}`);
25
+ const message = escapeMessage(`${finding.message} (${finding.ruleId})`);
26
+ const props = properties.length > 0 ? ` ${properties.join(",")}` : "";
27
+ return `::${level}${props}::${message}`;
28
+ }
29
+ function annotationLevel(severity) {
30
+ return severity === "critical" ? "error" : severity === "warning" ? "warning" : "notice";
31
+ }
32
+ function escapeProperty(value) {
33
+ return value
34
+ .replace(/%/g, "%25")
35
+ .replace(/\r/g, "%0D")
36
+ .replace(/\n/g, "%0A")
37
+ .replace(/:/g, "%3A")
38
+ .replace(/,/g, "%2C");
39
+ }
40
+ function escapeMessage(value) {
41
+ return value.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
42
+ }
@@ -0,0 +1,23 @@
1
+ import type { PolicyViolation } from "../../core/policy/evaluate.js";
2
+ import type { Finding } from "../../types/index.js";
3
+ export interface GithubReportOptions {
4
+ summary?: boolean;
5
+ annotations?: boolean;
6
+ }
7
+ export declare function emitGithubReports(options: {
8
+ mode: "scan" | "verify";
9
+ findings: Finding[];
10
+ overallScore: number | null;
11
+ violations: PolicyViolation[];
12
+ verifySummary?: {
13
+ fixed: number;
14
+ remaining: number;
15
+ new: number;
16
+ unchanged: number;
17
+ };
18
+ summary?: boolean;
19
+ annotations?: boolean;
20
+ /** Override for tests */
21
+ stepSummaryPath?: string | null;
22
+ annotationsWrite?: (text: string) => void;
23
+ }): Promise<void>;
@@ -0,0 +1,31 @@
1
+ import fs from "node:fs/promises";
2
+ import { renderGithubAnnotations } from "./annotations.js";
3
+ import { renderGithubStepSummary } from "./summary.js";
4
+ export async function emitGithubReports(options) {
5
+ if (options.annotations) {
6
+ const text = renderGithubAnnotations(options.findings);
7
+ if (text.length > 0) {
8
+ const write = options.annotationsWrite ?? ((chunk) => process.stderr.write(chunk));
9
+ write(text);
10
+ }
11
+ }
12
+ if (!options.summary) {
13
+ return;
14
+ }
15
+ const markdown = renderGithubStepSummary({
16
+ title: options.mode === "verify" ? "AgentDoctor Verify" : "AgentDoctor Scan",
17
+ overallScore: options.overallScore,
18
+ findings: options.findings,
19
+ violations: options.violations,
20
+ mode: options.mode,
21
+ ...(options.verifySummary ? { verifySummary: options.verifySummary } : {}),
22
+ });
23
+ const summaryPath = options.stepSummaryPath !== undefined
24
+ ? options.stepSummaryPath
25
+ : (process.env.GITHUB_STEP_SUMMARY ?? null);
26
+ if (summaryPath) {
27
+ await fs.appendFile(summaryPath, markdown, "utf8");
28
+ return;
29
+ }
30
+ process.stderr.write("Note: --summary set but GITHUB_STEP_SUMMARY is unset; skipping step summary write.\n");
31
+ }
@@ -0,0 +1,20 @@
1
+ import type { Finding, ScanResult } from "../../types/index.js";
2
+ import type { PolicyViolation } from "../../core/policy/evaluate.js";
3
+ export interface GithubSummaryInput {
4
+ title: string;
5
+ overallScore: number | null;
6
+ findings: Finding[];
7
+ violations: PolicyViolation[];
8
+ mode: "scan" | "verify";
9
+ verifySummary?: {
10
+ fixed: number;
11
+ remaining: number;
12
+ new: number;
13
+ unchanged: number;
14
+ };
15
+ }
16
+ /**
17
+ * Markdown for `$GITHUB_STEP_SUMMARY`.
18
+ */
19
+ export declare function renderGithubStepSummary(input: GithubSummaryInput): string;
20
+ export declare function renderGithubStepSummaryForScan(result: ScanResult, violations: PolicyViolation[]): string;
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Markdown for `$GITHUB_STEP_SUMMARY`.
3
+ */
4
+ export function renderGithubStepSummary(input) {
5
+ const lines = [];
6
+ lines.push(`# AgentDoctor ${input.mode === "verify" ? "Verify" : "Scan"}`);
7
+ lines.push("");
8
+ if (input.overallScore !== null) {
9
+ lines.push(`**Readiness:** ${input.overallScore}/100`);
10
+ lines.push("");
11
+ }
12
+ if (input.violations.length > 0) {
13
+ lines.push("## Policy violations");
14
+ lines.push("");
15
+ for (const violation of input.violations) {
16
+ lines.push(`- ${violation.message}`);
17
+ }
18
+ lines.push("");
19
+ }
20
+ else {
21
+ lines.push("Policy gates: **passed**");
22
+ lines.push("");
23
+ }
24
+ if (input.verifySummary) {
25
+ lines.push("## Verify delta");
26
+ lines.push("");
27
+ lines.push(`| Fixed | Remaining | New | Unchanged |`);
28
+ lines.push(`| ---: | ---: | ---: | ---: |`);
29
+ lines.push(`| ${input.verifySummary.fixed} | ${input.verifySummary.remaining} | ${input.verifySummary.new} | ${input.verifySummary.unchanged} |`);
30
+ lines.push("");
31
+ }
32
+ const critical = input.findings.filter((f) => f.severity === "critical").length;
33
+ const warning = input.findings.filter((f) => f.severity === "warning").length;
34
+ const info = input.findings.filter((f) => f.severity === "info").length;
35
+ lines.push("## Findings");
36
+ lines.push("");
37
+ lines.push(`| Severity | Count |`);
38
+ lines.push(`| --- | ---: |`);
39
+ lines.push(`| Critical | ${critical} |`);
40
+ lines.push(`| Warning | ${warning} |`);
41
+ lines.push(`| Info | ${info} |`);
42
+ lines.push(`| **Total** | **${input.findings.length}** |`);
43
+ lines.push("");
44
+ if (input.findings.length > 0) {
45
+ lines.push("### Top findings");
46
+ lines.push("");
47
+ const top = [...input.findings]
48
+ .sort((a, b) => severityOrder(b.severity) - severityOrder(a.severity))
49
+ .slice(0, 20);
50
+ for (const finding of top) {
51
+ const path = finding.evidence?.path ? ` \`${finding.evidence.path}\`` : "";
52
+ lines.push(`- **${finding.severity}** \`${finding.ruleId}\`${path} — ${finding.title}`);
53
+ }
54
+ lines.push("");
55
+ }
56
+ if (input.violations.length > 0) {
57
+ lines.push(...renderGithubNextSteps(input));
58
+ }
59
+ return `${lines.join("\n")}\n`;
60
+ }
61
+ /**
62
+ * Shortest path back to green when the Action failed a policy gate.
63
+ * Shown only on failure — success summaries stay quiet.
64
+ */
65
+ function renderGithubNextSteps(input) {
66
+ const lines = [];
67
+ lines.push("## Next");
68
+ lines.push("");
69
+ lines.push("Policy gate failed. Shortest path back to green:");
70
+ lines.push("");
71
+ const hasSafe = input.findings.some((f) => f.fixability === "safe");
72
+ const topRule = [...input.findings].sort((a, b) => severityOrder(b.severity) - severityOrder(a.severity))[0]?.ruleId;
73
+ if (input.mode === "verify") {
74
+ lines.push("1. Reproduce locally: `npx @praneeth_54/agentdoctor verify`");
75
+ if (hasSafe) {
76
+ lines.push("2. Apply safe fixes: `npx @praneeth_54/agentdoctor fix -y`");
77
+ lines.push("3. Re-check: `npx @praneeth_54/agentdoctor verify`");
78
+ }
79
+ else {
80
+ lines.push(`2. Inspect a finding: \`npx @praneeth_54/agentdoctor explain ${topRule ?? "<rule-id>"}\``);
81
+ lines.push("3. Fix or baseline, then re-run this Action");
82
+ }
83
+ }
84
+ else {
85
+ lines.push("1. Reproduce locally: `npx @praneeth_54/agentdoctor scan --ci`");
86
+ if (hasSafe) {
87
+ lines.push("2. Apply safe fixes: `npx @praneeth_54/agentdoctor fix -y`");
88
+ lines.push("3. Confirm: `npx @praneeth_54/agentdoctor scan --ci`");
89
+ }
90
+ else {
91
+ lines.push(`2. Inspect a finding: \`npx @praneeth_54/agentdoctor explain ${topRule ?? "<rule-id>"}\``);
92
+ lines.push("3. Fix the gated findings, then re-run this Action");
93
+ }
94
+ }
95
+ lines.push("");
96
+ lines.push("Preview before applying: `npx @praneeth_54/agentdoctor fix --dry-run`");
97
+ lines.push("");
98
+ return lines;
99
+ }
100
+ export function renderGithubStepSummaryForScan(result, violations) {
101
+ return renderGithubStepSummary({
102
+ title: "AgentDoctor Scan",
103
+ overallScore: result.scores?.overall ?? null,
104
+ findings: result.findings,
105
+ violations,
106
+ mode: "scan",
107
+ });
108
+ }
109
+ function severityOrder(severity) {
110
+ return severity === "critical" ? 3 : severity === "warning" ? 2 : 1;
111
+ }
@@ -2,6 +2,11 @@ import type { ScanResult } from "../../types/index.js";
2
2
  export interface TerminalReportOptions {
3
3
  verbose?: boolean;
4
4
  }
5
+ /**
6
+ * Close the Scan → Fix → Verify loop on first run.
7
+ * Empty / agentless scans already print a Next line; findings used to dead-end.
8
+ */
9
+ export declare function renderNextSteps(result: ScanResult): string[];
5
10
  /**
6
11
  * Terminal report: agents and actionable findings.
7
12
  */
@@ -42,7 +42,12 @@ function renderFindingBlock(finding, verbose) {
42
42
  : finding.severity === "warning"
43
43
  ? symbolWarn()
44
44
  : symbolInfo();
45
- lines.push(` ${icon} ${sanitizeTerminalText(finding.title)}`);
45
+ const fixTag = finding.fixability === "safe"
46
+ ? colors.green("[safe]")
47
+ : finding.fixability === "review"
48
+ ? colors.yellow("[review]")
49
+ : colors.dim("[manual]");
50
+ lines.push(` ${icon} ${fixTag} ${sanitizeTerminalText(finding.title)}`);
46
51
  if (finding.evidence?.path) {
47
52
  lines.push(colors.dim(` ${sanitizeTerminalText(finding.evidence.path)}`));
48
53
  }
@@ -59,6 +64,50 @@ function renderFindingBlock(finding, verbose) {
59
64
  lines.push("");
60
65
  return lines;
61
66
  }
67
+ function countByFixability(findings) {
68
+ let safe = 0;
69
+ let review = 0;
70
+ let manual = 0;
71
+ for (const finding of findings) {
72
+ if (finding.fixability === "safe") {
73
+ safe += 1;
74
+ }
75
+ else if (finding.fixability === "review") {
76
+ review += 1;
77
+ }
78
+ else {
79
+ manual += 1;
80
+ }
81
+ }
82
+ return { safe, review, manual };
83
+ }
84
+ /**
85
+ * Close the Scan → Fix → Verify loop on first run.
86
+ * Empty / agentless scans already print a Next line; findings used to dead-end.
87
+ */
88
+ export function renderNextSteps(result) {
89
+ const lines = [];
90
+ const total = result.summary.total;
91
+ if (total === 0) {
92
+ return lines;
93
+ }
94
+ const { safe, review, manual } = countByFixability(result.findings);
95
+ const reviewLike = review + manual;
96
+ lines.push(colors.bold("Next"));
97
+ lines.push("");
98
+ if (safe > 0) {
99
+ lines.push(` ${safe} finding(s) are auto-fixable. Preview: ${colors.bold("agentdoctor fix --dry-run")}`);
100
+ lines.push(` Apply: ${colors.bold("agentdoctor fix -y")}`);
101
+ }
102
+ else if (reviewLike > 0) {
103
+ lines.push(` ${reviewLike} finding(s) need review or manual action — ${colors.bold("agentdoctor fix")} will not change them automatically.`);
104
+ lines.push(colors.dim(" Inspect a rule: agentdoctor explain <rule-id> (ids are in --json output)"));
105
+ }
106
+ lines.push(colors.dim(" Save a verify baseline: agentdoctor scan --json > agentdoctor-report.json"));
107
+ lines.push(colors.dim(" After changes: agentdoctor verify --baseline agentdoctor-report.json"));
108
+ lines.push("");
109
+ return lines;
110
+ }
62
111
  /**
63
112
  * Terminal report: agents and actionable findings.
64
113
  */
@@ -115,7 +164,10 @@ export function renderTerminalReport(result, options = {}) {
115
164
  }
116
165
  if (total === 0) {
117
166
  if (limited) {
118
- lines.push(` ${symbolOk()} No agent-configuration findings`);
167
+ lines.push(` ${symbolWarn()} Nothing to audit yet — no Cursor, Claude Code, or Codex config found`);
168
+ lines.push("");
169
+ lines.push(colors.dim(" Next: add project agent config (for example `.cursor/`, `CLAUDE.md` / `.claude/`, or `AGENTS.md`),"));
170
+ lines.push(colors.dim(" then re-run `agentdoctor` in this repository."));
119
171
  }
120
172
  else {
121
173
  lines.push(` ${symbolOk()} No findings`);
@@ -149,7 +201,7 @@ export function renderTerminalReport(result, options = {}) {
149
201
  }
150
202
  }
151
203
  if (total > shown) {
152
- lines.push(colors.dim(` … and ${total - shown} more. Re-run with --verbose to see additional detail.`));
204
+ lines.push(colors.dim(` … and ${total - shown} more. Re-run with --verbose for detail, or --json for the full list.`));
153
205
  lines.push("");
154
206
  }
155
207
  }
@@ -158,15 +210,26 @@ export function renderTerminalReport(result, options = {}) {
158
210
  lines.push(` ${result.summary.critical} critical`);
159
211
  lines.push(` ${result.summary.warning} warning`);
160
212
  lines.push(` ${result.summary.info} info`);
213
+ if (total > 0) {
214
+ const { safe, review, manual } = countByFixability(result.findings);
215
+ lines.push(colors.dim(` Fixability: ${safe} safe · ${review} review · ${manual} manual`));
216
+ }
161
217
  lines.push("");
162
218
  if (result.scoringAvailable && result.scores) {
163
- lines.push(` Readiness: ${result.scores.overall}/100`);
164
- lines.push(colors.dim(" Category and agent scores: agentdoctor scan --json"));
219
+ if (limited) {
220
+ lines.push(` Readiness: n/a — configure Cursor, Claude Code, or Codex before treating scores as agent readiness`);
221
+ lines.push(colors.dim(` Numeric scores remain in JSON (overall ${result.scores.overall}/100) for repository-risk findings only`));
222
+ }
223
+ else {
224
+ lines.push(` Readiness: ${result.scores.overall}/100`);
225
+ lines.push(colors.dim(" Category and agent scores: agentdoctor scan --json"));
226
+ }
165
227
  }
166
228
  else {
167
229
  lines.push(colors.dim(" Readiness scoring unavailable for this scan"));
168
230
  }
169
231
  lines.push("");
232
+ lines.push(...renderNextSteps(result));
170
233
  if (verbose) {
171
234
  lines.push(colors.dim("Timing"));
172
235
  lines.push(` Discovery: ${result.timing.discoveryMs}ms`);
@@ -1,2 +1,6 @@
1
1
  import type { VerifyResult } from "../../core/verify/verify.js";
2
2
  export declare function renderVerifyTerminalReport(result: VerifyResult, verbose?: boolean): string;
3
+ /**
4
+ * Close the verify step: tell a first-time user what to do with Fixed / Remaining / New.
5
+ */
6
+ export declare function renderVerifyNextSteps(result: VerifyResult): string[];
@@ -41,5 +41,31 @@ export function renderVerifyTerminalReport(result, verbose = false) {
41
41
  lines.push(colors.dim(` Timing scan=${result.timing.scanMs}ms compare=${result.timing.compareMs}ms total=${result.timing.totalMs}ms`));
42
42
  }
43
43
  lines.push("");
44
+ lines.push(...renderVerifyNextSteps(result));
44
45
  return `${lines.join("\n")}\n`;
45
46
  }
47
+ /**
48
+ * Close the verify step: tell a first-time user what to do with Fixed / Remaining / New.
49
+ */
50
+ export function renderVerifyNextSteps(result) {
51
+ const lines = [];
52
+ const { fixed, remaining, new: newly } = result.summary;
53
+ lines.push(colors.bold("Next"));
54
+ if (remaining === 0 && newly === 0) {
55
+ lines.push(fixed > 0
56
+ ? " All tracked findings cleared. Optional: gate CI with agentdoctor scan --ci"
57
+ : " No regressions vs baseline. Optional: gate CI with agentdoctor scan --ci");
58
+ }
59
+ else {
60
+ if (newly > 0) {
61
+ lines.push(` ${newly} new finding(s) appeared since the baseline — inspect: agentdoctor scan`);
62
+ }
63
+ if (remaining > 0) {
64
+ lines.push(` ${remaining} finding(s) still open — auto-fixable ones: agentdoctor fix --dry-run`);
65
+ lines.push(colors.dim(" Review/manual leftovers: agentdoctor explain <rule-id> (ids in scan --json)"));
66
+ }
67
+ lines.push(colors.dim(" After more fixes, re-check: agentdoctor verify --baseline agentdoctor-report.json"));
68
+ }
69
+ lines.push("");
70
+ return lines;
71
+ }
@@ -15,3 +15,8 @@ export declare function listDirectorySafe(dirPath: string): Promise<{
15
15
  ok: false;
16
16
  error: string;
17
17
  }>;
18
+ /**
19
+ * Write text via a same-directory temp file, then replace the target.
20
+ * On Windows, `rename` cannot overwrite an existing destination — remove first.
21
+ */
22
+ export declare function atomicWriteTextFile(filePath: string, content: string): Promise<void>;
package/dist/utils/fs.js CHANGED
@@ -61,3 +61,37 @@ export async function listDirectorySafe(dirPath) {
61
61
  return { ok: false, error: message };
62
62
  }
63
63
  }
64
+ /**
65
+ * Write text via a same-directory temp file, then replace the target.
66
+ * On Windows, `rename` cannot overwrite an existing destination — remove first.
67
+ */
68
+ export async function atomicWriteTextFile(filePath, content) {
69
+ const dir = path.dirname(filePath);
70
+ await fs.mkdir(dir, { recursive: true });
71
+ const tmp = path.join(dir, `.${path.basename(filePath)}.agentdoctor.${process.pid}.${Date.now()}.tmp`);
72
+ await fs.writeFile(tmp, content, "utf8");
73
+ try {
74
+ await replaceFile(tmp, filePath);
75
+ }
76
+ catch (error) {
77
+ await fs.rm(tmp, { force: true }).catch(() => undefined);
78
+ throw error;
79
+ }
80
+ }
81
+ async function replaceFile(tmpPath, targetPath) {
82
+ try {
83
+ await fs.rename(tmpPath, targetPath);
84
+ }
85
+ catch (error) {
86
+ const code = error && typeof error === "object" && "code" in error
87
+ ? String(error.code)
88
+ : "";
89
+ // Windows (and some locked-file cases) cannot rename over an existing path.
90
+ if (process.platform === "win32" || code === "EEXIST" || code === "EPERM") {
91
+ await fs.rm(targetPath, { force: true });
92
+ await fs.rename(tmpPath, targetPath);
93
+ return;
94
+ }
95
+ throw error;
96
+ }
97
+ }
@@ -7,6 +7,11 @@ export declare function resolveRepoRoot(inputPath: string): string;
7
7
  * Produce a POSIX-style relative path for stable cross-platform reporting.
8
8
  */
9
9
  export declare function toPosixRelative(from: string, to: string): string;
10
+ /**
11
+ * Normalize a repository-relative path to POSIX form without regex backtracking.
12
+ * Converts `\` to `/` and strips leading/trailing `/`.
13
+ */
14
+ export declare function toRepoRelativePosix(relativePath: string): string;
10
15
  /**
11
16
  * Ensure a candidate path stays within a root (defense against traversal).
12
17
  */
@@ -16,6 +16,22 @@ export function resolveRepoRoot(inputPath) {
16
16
  export function toPosixRelative(from, to) {
17
17
  return path.relative(from, to).split(path.sep).join("/");
18
18
  }
19
+ /**
20
+ * Normalize a repository-relative path to POSIX form without regex backtracking.
21
+ * Converts `\` to `/` and strips leading/trailing `/`.
22
+ */
23
+ export function toRepoRelativePosix(relativePath) {
24
+ const value = relativePath.includes("\\") ? relativePath.split("\\").join("/") : relativePath;
25
+ let start = 0;
26
+ let end = value.length;
27
+ while (start < end && value.charCodeAt(start) === 47) {
28
+ start += 1;
29
+ }
30
+ while (end > start && value.charCodeAt(end - 1) === 47) {
31
+ end -= 1;
32
+ }
33
+ return start === 0 && end === value.length ? value : value.slice(start, end);
34
+ }
19
35
  /**
20
36
  * Ensure a candidate path stays within a root (defense against traversal).
21
37
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praneeth_54/agentdoctor",
3
- "version": "0.3.0-beta",
3
+ "version": "1.1.0",
4
4
  "description": "Audit AI coding agent configuration in a repository — local, deterministic, no API key.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,7 +33,17 @@
33
33
  "format:check": "prettier --check .",
34
34
  "test": "vitest run",
35
35
  "test:watch": "vitest",
36
+ "test:understanding": "vitest run --config vitest.understanding.config.ts",
37
+ "test:mcp": "vitest run tests/unit/mcp",
38
+ "validate:understanding": "tsx validation/software-understanding/runner/run.ts",
39
+ "validate:mcp-agent": "tsx validation/mcp-agent/run.ts",
40
+ "validate:project-brain": "tsx validation/project-brain/run.ts",
41
+ "validate:project-brain:real-world": "tsx validation/project-brain/real-world-run.ts",
42
+ "benchmark:project-brain": "tsx validation/project-brain/benchmark.ts",
36
43
  "verify": "npm run typecheck && npm run lint && npm run format:check && npm test && npm run build",
44
+ "verify:understanding": "npm run typecheck && npm run test:understanding",
45
+ "verify:project-brain": "npm run typecheck && npm run test:understanding && npm run validate:project-brain && npm run benchmark:project-brain",
46
+ "verify:mcp": "npm run typecheck && npm run test:mcp",
37
47
  "prepare": "node scripts/ensure-cli-bin.mjs",
38
48
  "prepublishOnly": "npm run verify"
39
49
  },
@@ -65,6 +75,7 @@
65
75
  "access": "public"
66
76
  },
67
77
  "dependencies": {
78
+ "@modelcontextprotocol/sdk": "^1.30.0",
68
79
  "commander": "^14.0.3",
69
80
  "picocolors": "^1.1.1"
70
81
  },
@@ -77,5 +88,8 @@
77
88
  "typescript": "^5.9.3",
78
89
  "typescript-eslint": "^8.50.0",
79
90
  "vitest": "^3.2.4"
91
+ },
92
+ "overrides": {
93
+ "js-yaml": "4.3.1"
80
94
  }
81
95
  }