@praneeth_54/agentdoctor 1.0.0 → 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 (208) hide show
  1. package/CHANGELOG.md +34 -5
  2. package/README.md +15 -12
  3. package/dist/cli/commands/brain-mcp.d.ts +9 -0
  4. package/dist/cli/commands/brain-mcp.js +38 -0
  5. package/dist/cli/program.js +13 -0
  6. package/dist/constants.d.ts +1 -1
  7. package/dist/constants.js +1 -1
  8. package/dist/core/understanding/architecture/index.d.ts +4 -0
  9. package/dist/core/understanding/architecture/index.js +3 -0
  10. package/dist/core/understanding/architecture/infer.d.ts +6 -0
  11. package/dist/core/understanding/architecture/infer.js +39 -0
  12. package/dist/core/understanding/architecture/models.d.ts +23 -0
  13. package/dist/core/understanding/architecture/models.js +127 -0
  14. package/dist/core/understanding/architecture/patterns/distribution.d.ts +2 -0
  15. package/dist/core/understanding/architecture/patterns/distribution.js +115 -0
  16. package/dist/core/understanding/architecture/patterns/index.d.ts +2 -0
  17. package/dist/core/understanding/architecture/patterns/index.js +8 -0
  18. package/dist/core/understanding/architecture/patterns/layered.d.ts +2 -0
  19. package/dist/core/understanding/architecture/patterns/layered.js +279 -0
  20. package/dist/core/understanding/architecture/patterns/structural.d.ts +2 -0
  21. package/dist/core/understanding/architecture/patterns/structural.js +197 -0
  22. package/dist/core/understanding/architecture/rules.d.ts +4 -0
  23. package/dist/core/understanding/architecture/rules.js +6 -0
  24. package/dist/core/understanding/architecture/types.d.ts +52 -0
  25. package/dist/core/understanding/architecture/types.js +1 -0
  26. package/dist/core/understanding/brain/build.d.ts +13 -0
  27. package/dist/core/understanding/brain/build.js +158 -0
  28. package/dist/core/understanding/brain/claims/index.d.ts +3 -0
  29. package/dist/core/understanding/brain/claims/index.js +2 -0
  30. package/dist/core/understanding/brain/claims/lifecycle.d.ts +16 -0
  31. package/dist/core/understanding/brain/claims/lifecycle.js +78 -0
  32. package/dist/core/understanding/brain/claims/types.d.ts +32 -0
  33. package/dist/core/understanding/brain/claims/types.js +25 -0
  34. package/dist/core/understanding/brain/components/index.d.ts +17 -0
  35. package/dist/core/understanding/brain/components/index.js +97 -0
  36. package/dist/core/understanding/brain/components/types.d.ts +16 -0
  37. package/dist/core/understanding/brain/components/types.js +8 -0
  38. package/dist/core/understanding/brain/confidence.d.ts +19 -0
  39. package/dist/core/understanding/brain/confidence.js +39 -0
  40. package/dist/core/understanding/brain/contract.d.ts +17 -0
  41. package/dist/core/understanding/brain/contract.js +100 -0
  42. package/dist/core/understanding/brain/contradictions/index.d.ts +12 -0
  43. package/dist/core/understanding/brain/contradictions/index.js +57 -0
  44. package/dist/core/understanding/brain/contradictions/types.d.ts +12 -0
  45. package/dist/core/understanding/brain/contradictions/types.js +8 -0
  46. package/dist/core/understanding/brain/delta.d.ts +25 -0
  47. package/dist/core/understanding/brain/delta.js +116 -0
  48. package/dist/core/understanding/brain/evidence/index.d.ts +3 -0
  49. package/dist/core/understanding/brain/evidence/index.js +2 -0
  50. package/dist/core/understanding/brain/evidence/redact.d.ts +4 -0
  51. package/dist/core/understanding/brain/evidence/redact.js +36 -0
  52. package/dist/core/understanding/brain/evidence/types.d.ts +34 -0
  53. package/dist/core/understanding/brain/evidence/types.js +35 -0
  54. package/dist/core/understanding/brain/explain.d.ts +17 -0
  55. package/dist/core/understanding/brain/explain.js +33 -0
  56. package/dist/core/understanding/brain/index.d.ts +28 -0
  57. package/dist/core/understanding/brain/index.js +16 -0
  58. package/dist/core/understanding/brain/migrate.d.ts +21 -0
  59. package/dist/core/understanding/brain/migrate.js +47 -0
  60. package/dist/core/understanding/brain/query.d.ts +60 -0
  61. package/dist/core/understanding/brain/query.js +92 -0
  62. package/dist/core/understanding/brain/security.d.ts +3 -0
  63. package/dist/core/understanding/brain/security.js +43 -0
  64. package/dist/core/understanding/brain/storage/index.d.ts +2 -0
  65. package/dist/core/understanding/brain/storage/index.js +1 -0
  66. package/dist/core/understanding/brain/storage/store.d.ts +53 -0
  67. package/dist/core/understanding/brain/storage/store.js +305 -0
  68. package/dist/core/understanding/brain/trace.d.ts +24 -0
  69. package/dist/core/understanding/brain/trace.js +207 -0
  70. package/dist/core/understanding/brain/types.d.ts +32 -0
  71. package/dist/core/understanding/brain/types.js +8 -0
  72. package/dist/core/understanding/brain/version.d.ts +13 -0
  73. package/dist/core/understanding/brain/version.js +7 -0
  74. package/dist/core/understanding/delta/compare.d.ts +26 -0
  75. package/dist/core/understanding/delta/compare.js +74 -0
  76. package/dist/core/understanding/delta/index.d.ts +2 -0
  77. package/dist/core/understanding/delta/index.js +1 -0
  78. package/dist/core/understanding/dependencies/discover.d.ts +7 -0
  79. package/dist/core/understanding/dependencies/discover.js +550 -0
  80. package/dist/core/understanding/dependencies/extract.d.ts +10 -0
  81. package/dist/core/understanding/dependencies/extract.js +189 -0
  82. package/dist/core/understanding/dependencies/index.d.ts +4 -0
  83. package/dist/core/understanding/dependencies/index.js +3 -0
  84. package/dist/core/understanding/dependencies/models.d.ts +12 -0
  85. package/dist/core/understanding/dependencies/models.js +125 -0
  86. package/dist/core/understanding/dependencies/types.d.ts +27 -0
  87. package/dist/core/understanding/dependencies/types.js +1 -0
  88. package/dist/core/understanding/domain/discover.d.ts +14 -0
  89. package/dist/core/understanding/domain/discover.js +90 -0
  90. package/dist/core/understanding/domain/index.d.ts +2 -0
  91. package/dist/core/understanding/domain/index.js +1 -0
  92. package/dist/core/understanding/entrypoints/discover.d.ts +6 -0
  93. package/dist/core/understanding/entrypoints/discover.js +62 -0
  94. package/dist/core/understanding/entrypoints/extract.d.ts +19 -0
  95. package/dist/core/understanding/entrypoints/extract.js +43 -0
  96. package/dist/core/understanding/entrypoints/index.d.ts +4 -0
  97. package/dist/core/understanding/entrypoints/index.js +3 -0
  98. package/dist/core/understanding/entrypoints/models.d.ts +26 -0
  99. package/dist/core/understanding/entrypoints/models.js +216 -0
  100. package/dist/core/understanding/entrypoints/types.d.ts +20 -0
  101. package/dist/core/understanding/entrypoints/types.js +1 -0
  102. package/dist/core/understanding/index.d.ts +32 -0
  103. package/dist/core/understanding/index.js +18 -0
  104. package/dist/core/understanding/mind/build.d.ts +16 -0
  105. package/dist/core/understanding/mind/build.js +134 -0
  106. package/dist/core/understanding/mind/index.d.ts +6 -0
  107. package/dist/core/understanding/mind/index.js +3 -0
  108. package/dist/core/understanding/mind/query.d.ts +33 -0
  109. package/dist/core/understanding/mind/query.js +39 -0
  110. package/dist/core/understanding/mind/types.d.ts +24 -0
  111. package/dist/core/understanding/mind/types.js +7 -0
  112. package/dist/core/understanding/model/builder.d.ts +9 -0
  113. package/dist/core/understanding/model/builder.js +209 -0
  114. package/dist/core/understanding/model/ids.d.ts +7 -0
  115. package/dist/core/understanding/model/ids.js +48 -0
  116. package/dist/core/understanding/model/index.d.ts +7 -0
  117. package/dist/core/understanding/model/index.js +6 -0
  118. package/dist/core/understanding/model/schema.d.ts +12 -0
  119. package/dist/core/understanding/model/schema.js +33 -0
  120. package/dist/core/understanding/model/serializer.d.ts +9 -0
  121. package/dist/core/understanding/model/serializer.js +46 -0
  122. package/dist/core/understanding/model/types.d.ts +110 -0
  123. package/dist/core/understanding/model/types.js +1 -0
  124. package/dist/core/understanding/model/validator.d.ts +5 -0
  125. package/dist/core/understanding/model/validator.js +126 -0
  126. package/dist/core/understanding/model/version.d.ts +9 -0
  127. package/dist/core/understanding/model/version.js +7 -0
  128. package/dist/core/understanding/ownership/discover.d.ts +12 -0
  129. package/dist/core/understanding/ownership/discover.js +242 -0
  130. package/dist/core/understanding/ownership/index.d.ts +2 -0
  131. package/dist/core/understanding/ownership/index.js +1 -0
  132. package/dist/core/understanding/ownership/types.d.ts +23 -0
  133. package/dist/core/understanding/ownership/types.js +1 -0
  134. package/dist/core/understanding/query/engine.d.ts +24 -0
  135. package/dist/core/understanding/query/engine.js +35 -0
  136. package/dist/core/understanding/query/errors.d.ts +13 -0
  137. package/dist/core/understanding/query/errors.js +26 -0
  138. package/dist/core/understanding/query/executor.d.ts +8 -0
  139. package/dist/core/understanding/query/executor.js +34 -0
  140. package/dist/core/understanding/query/index.d.ts +7 -0
  141. package/dist/core/understanding/query/index.js +5 -0
  142. package/dist/core/understanding/query/models.d.ts +102 -0
  143. package/dist/core/understanding/query/models.js +1 -0
  144. package/dist/core/understanding/query/query.d.ts +68 -0
  145. package/dist/core/understanding/query/query.js +145 -0
  146. package/dist/core/understanding/query/registry.d.ts +8 -0
  147. package/dist/core/understanding/query/registry.js +340 -0
  148. package/dist/core/understanding/query/types.d.ts +63 -0
  149. package/dist/core/understanding/query/types.js +1 -0
  150. package/dist/core/understanding/relationships/discover.d.ts +11 -0
  151. package/dist/core/understanding/relationships/discover.js +517 -0
  152. package/dist/core/understanding/relationships/extract.d.ts +14 -0
  153. package/dist/core/understanding/relationships/extract.js +165 -0
  154. package/dist/core/understanding/relationships/index.d.ts +4 -0
  155. package/dist/core/understanding/relationships/index.js +3 -0
  156. package/dist/core/understanding/relationships/models.d.ts +21 -0
  157. package/dist/core/understanding/relationships/models.js +185 -0
  158. package/dist/core/understanding/relationships/types.d.ts +42 -0
  159. package/dist/core/understanding/relationships/types.js +1 -0
  160. package/dist/core/understanding/risks/discover.d.ts +8 -0
  161. package/dist/core/understanding/risks/discover.js +155 -0
  162. package/dist/core/understanding/risks/index.d.ts +2 -0
  163. package/dist/core/understanding/risks/index.js +1 -0
  164. package/dist/core/understanding/risks/types.d.ts +26 -0
  165. package/dist/core/understanding/risks/types.js +1 -0
  166. package/dist/core/understanding/shared/domain-lexicon.d.ts +9 -0
  167. package/dist/core/understanding/shared/domain-lexicon.js +107 -0
  168. package/dist/core/understanding/shared/index.d.ts +2 -0
  169. package/dist/core/understanding/shared/index.js +2 -0
  170. package/dist/core/understanding/shared/tokens.d.ts +7 -0
  171. package/dist/core/understanding/shared/tokens.js +141 -0
  172. package/dist/core/understanding/snapshot/identity.d.ts +24 -0
  173. package/dist/core/understanding/snapshot/identity.js +79 -0
  174. package/dist/core/understanding/snapshot/index.d.ts +2 -0
  175. package/dist/core/understanding/snapshot/index.js +1 -0
  176. package/dist/core/understanding/types/index.d.ts +22 -0
  177. package/dist/core/understanding/types/index.js +1 -0
  178. package/dist/core/understanding/understand/formatter.d.ts +10 -0
  179. package/dist/core/understanding/understand/formatter.js +115 -0
  180. package/dist/core/understanding/understand/index.d.ts +4 -0
  181. package/dist/core/understanding/understand/index.js +3 -0
  182. package/dist/core/understanding/understand/service.d.ts +17 -0
  183. package/dist/core/understanding/understand/service.js +76 -0
  184. package/dist/core/understanding/understand/summary.d.ts +14 -0
  185. package/dist/core/understanding/understand/summary.js +39 -0
  186. package/dist/core/understanding/understand/types.d.ts +31 -0
  187. package/dist/core/understanding/understand/types.js +1 -0
  188. package/dist/mcp/brain/compile.d.ts +9 -0
  189. package/dist/mcp/brain/compile.js +42 -0
  190. package/dist/mcp/brain/errors.d.ts +12 -0
  191. package/dist/mcp/brain/errors.js +23 -0
  192. package/dist/mcp/brain/index.d.ts +11 -0
  193. package/dist/mcp/brain/index.js +8 -0
  194. package/dist/mcp/brain/provenance.d.ts +29 -0
  195. package/dist/mcp/brain/provenance.js +59 -0
  196. package/dist/mcp/brain/schemas.d.ts +13 -0
  197. package/dist/mcp/brain/schemas.js +105 -0
  198. package/dist/mcp/brain/security/root.d.ts +6 -0
  199. package/dist/mcp/brain/security/root.js +66 -0
  200. package/dist/mcp/brain/server.d.ts +12 -0
  201. package/dist/mcp/brain/server.js +46 -0
  202. package/dist/mcp/brain/session.d.ts +33 -0
  203. package/dist/mcp/brain/session.js +117 -0
  204. package/dist/mcp/brain/tools/handlers.d.ts +13 -0
  205. package/dist/mcp/brain/tools/handlers.js +483 -0
  206. package/dist/mcp/brain/tools/registry.d.ts +9 -0
  207. package/dist/mcp/brain/tools/registry.js +191 -0
  208. package/package.json +5 -1
package/CHANGELOG.md CHANGED
@@ -9,8 +9,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Notes
11
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).
12
+ - Owner-only after this tree is green: `npm publish`, git tag `v1.1.0`, GitHub Release.
13
+ Do **not** auto-publish from CI agents.
14
+
15
+ ## [1.1.0] — 2026-08-13
16
+
17
+ Minor release: Brain → MCP → Agent consumption for Project Brain (Safety V1 unchanged).
18
+
19
+ ### Added
20
+
21
+ - Project Brain MCP bridge (`agentdoctor brain-mcp --root <path>`): STDIO MCP server
22
+ exposing evidence-backed Brain tools (`brain_overview`, `brain_query`, `brain_explain`,
23
+ `brain_trace`, `brain_claims`, `brain_evidence`, `brain_ownership`, `brain_risk`,
24
+ `brain_delta`, `brain_snapshot`) with provenance envelopes. Local-only; no API key.
25
+ Docs: [docs/mcp/brain-mcp.md](docs/mcp/brain-mcp.md). Examples: [examples/mcp/](examples/mcp/).
26
+ Demo: [docs/demo/brain-mcp-demo.md](docs/demo/brain-mcp-demo.md).
27
+ Validation: [validation/mcp-agent/](validation/mcp-agent/).
28
+
29
+ ### Changed
30
+
31
+ - Build packaging now includes `src/core/understanding/**` and `src/mcp/**` so the
32
+ `brain-mcp` CLI command can run from `dist/` (Safety public API export remains
33
+ Scan/Fix/Verify-focused).
34
+ - MCP tool descriptions explicitly mark READ vs CONTROLLED WRITE (`brain_snapshot` rebuild
35
+ only under `.agentdoctor/project-brain/`).
36
+
37
+ ### Notes
38
+
39
+ - Product promise: help AI coding agents understand what is in a repository, what can be
40
+ trusted, and why. Not a generic coding assistant, not RAG, not chatbot memory.
41
+ - Authenticated third-party agent LLM smoke (Cursor / Claude Code / Codex Q1–Q7) remains an
42
+ environment/auth gate; engineering MCP + Brain contracts pass without it.
14
43
 
15
44
  ## [1.0.0] — 2026-08-12
16
45
 
@@ -78,8 +107,7 @@ First production release: Scan → Fix → Verify → CI contract frozen for v1.
78
107
  ### Compatibility
79
108
 
80
109
  - 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
110
+ - Action `version` input default is `1.0.0` (bumped after npm published `@praneeth_54/agentdoctor@1.0.0`)
83
111
 
84
112
  ## [0.3.0-beta] — 2026-08-07
85
113
 
@@ -245,7 +273,8 @@ First public beta.
245
273
  - Not a complete secret scanner
246
274
  - Git “tracked secret” detection deferred
247
275
 
248
- [Unreleased]: https://github.com/pranee54/AgentDoctor/compare/v1.0.0...HEAD
276
+ [Unreleased]: https://github.com/pranee54/AgentDoctor/compare/v1.1.0...HEAD
277
+ [1.1.0]: https://github.com/pranee54/AgentDoctor/releases/tag/v1.1.0
249
278
  [1.0.0]: https://github.com/pranee54/AgentDoctor/releases/tag/v1.0.0
250
279
  [0.3.0-beta]: https://github.com/pranee54/AgentDoctor/releases/tag/v0.3.0-beta
251
280
  [0.2.0-beta]: https://github.com/pranee54/AgentDoctor/releases/tag/v0.2.0-beta
package/README.md CHANGED
@@ -16,7 +16,9 @@ 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 release (`1.0.0`). Scan → Fix → Verify → CI. Deterministic scores in the terminal and JSON.
19
+ Public release track: Safety V1 (`1.0.0`) plus Project Brain MCP for agents (`1.1.0`).
20
+ Scan → Fix → Verify → CI remains the Safety contract. Brain MCP helps agents understand
21
+ what is in a repository, what can be trusted, and why — not a generic coding assistant.
20
22
 
21
23
  ---
22
24
 
@@ -301,8 +303,7 @@ steps:
301
303
 
302
304
  Policy inputs: `minimum-score`, `fail-on-severity`, `fail-on-rule`, `fail-on-new`,
303
305
  `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
+ policy input. The Action default `version` is `1.0.0`. For local CI against this repo, use
306
307
  `version: workspace` after `npm run build`. The action installs `@praneeth_54/agentdoctor`,
307
308
  runs scan (or `verify` when `verify-baseline` is set) with `--json`, and writes the report
308
309
  inside the workspace.
@@ -378,7 +379,9 @@ Details: [docs/architecture.md](docs/architecture.md)
378
379
  | [docs/rules.md](docs/rules.md) | Stable rule IDs |
379
380
  | [docs/exit-codes.md](docs/exit-codes.md) | Process exit codes |
380
381
  | [docs/scoring.md](docs/scoring.md) | Readiness scoring specification |
381
- | [docs/compatibility.md](docs/compatibility.md) | Beta compatibility promises |
382
+ | [docs/compatibility.md](docs/compatibility.md) | v1 compatibility promises |
383
+ | [docs/project-brain.md](docs/project-brain.md) | Project Brain understanding |
384
+ | [docs/mcp/brain-mcp.md](docs/mcp/brain-mcp.md) | Brain → MCP → Agent bridge |
382
385
  | [docs/development.md](docs/development.md) | Local development |
383
386
  | [docs/github-launch-checklist.md](docs/github-launch-checklist.md) | GitHub About / topics / launch |
384
387
  | [ROADMAP.md](ROADMAP.md) | Near- and medium-term plans |
@@ -404,17 +407,17 @@ node dist/cli/index.js ./fixtures/clean-configured-project
404
407
 
405
408
  ---
406
409
 
407
- ## Project Brain (separate laboratory capability)
410
+ ## Project Brain MCP (agent context)
408
411
 
409
- AgentDoctor V1 ships the **safety** product: Scan → Fix → Verify → Policy → CI.
412
+ Evidence-backed repository understanding for agents (not a search MCP):
410
413
 
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)
414
+ ```bash
415
+ agentdoctor brain-mcp --root /absolute/path/to/project
416
+ ```
416
417
 
417
- See [PROJECT_AUDIT.txt](PROJECT_AUDIT.txt) and [RELEASE_CHECKLIST.txt](RELEASE_CHECKLIST.txt).
418
+ Tools: overview, query, explain, trace, claims, evidence, ownership, risk, delta, snapshot.
419
+ Local STDIO only — no API key, no upload. Docs: [docs/mcp/brain-mcp.md](docs/mcp/brain-mcp.md).
420
+ Examples: [examples/mcp/](examples/mcp/).
418
421
 
419
422
  ---
420
423
 
@@ -0,0 +1,9 @@
1
+ export interface BrainMcpCommandOptions {
2
+ root: string;
3
+ buildIfMissing?: boolean;
4
+ generatedAt?: string;
5
+ }
6
+ /**
7
+ * Start Brain MCP over STDIO. Must not write protocol noise to stdout.
8
+ */
9
+ export declare function runBrainMcpCommand(options: BrainMcpCommandOptions): Promise<number>;
@@ -0,0 +1,38 @@
1
+ import { EXIT_CODES } from "../../types/index.js";
2
+ import { BrainMcpError, runBrainMcpStdio } from "../../mcp/brain/index.js";
3
+ /**
4
+ * Start Brain MCP over STDIO. Must not write protocol noise to stdout.
5
+ */
6
+ export async function runBrainMcpCommand(options) {
7
+ try {
8
+ if (!options.root || options.root.trim().length === 0) {
9
+ process.stderr.write("Error: --root <path> is required (never scans process.cwd() implicitly)\n");
10
+ return EXIT_CODES.USAGE_ERROR;
11
+ }
12
+ await runBrainMcpStdio({
13
+ root: options.root,
14
+ buildIfMissing: options.buildIfMissing !== false,
15
+ ...(options.generatedAt !== undefined ? { generatedAt: options.generatedAt } : {}),
16
+ log: (message) => process.stderr.write(`${message}\n`),
17
+ });
18
+ // STDIO server runs until stdin closes; resolve when transport ends.
19
+ await new Promise((resolve) => {
20
+ const onClose = () => resolve();
21
+ process.stdin.on("end", onClose);
22
+ process.stdin.on("close", onClose);
23
+ });
24
+ return EXIT_CODES.SUCCESS;
25
+ }
26
+ catch (error) {
27
+ if (error instanceof BrainMcpError) {
28
+ process.stderr.write(`Error: ${error.message}\n`);
29
+ if (error.code === "invalid_root" || error.code === "invalid_argument") {
30
+ return EXIT_CODES.USAGE_ERROR;
31
+ }
32
+ return EXIT_CODES.INTERNAL_ERROR;
33
+ }
34
+ const message = error instanceof Error ? error.message : "brain-mcp failed";
35
+ process.stderr.write(`Error: ${message}\n`);
36
+ return EXIT_CODES.INTERNAL_ERROR;
37
+ }
38
+ }
@@ -2,6 +2,7 @@ import { Command, InvalidArgumentError } from "commander";
2
2
  import { PACKAGE_VERSION } from "../constants.js";
3
3
  import { parseFailOnRules, parseSeverityGate } from "../core/policy/evaluate.js";
4
4
  import { EXIT_CODES } from "../types/index.js";
5
+ import { runBrainMcpCommand } from "./commands/brain-mcp.js";
5
6
  import { runDoctorCommand } from "./commands/doctor.js";
6
7
  import { runExplainCommand } from "./commands/explain.js";
7
8
  import { runFixCommand } from "./commands/fix.js";
@@ -153,6 +154,18 @@ export function createProgram() {
153
154
  const code = await runDoctorCommand();
154
155
  process.exitCode = code;
155
156
  });
157
+ program
158
+ .command("brain-mcp")
159
+ .description("Start the local Project Brain MCP server over STDIO (evidence-backed agent context; no API key)")
160
+ .requiredOption("--root <path>", "Absolute or relative project root (required; never uses process.cwd() implicitly)")
161
+ .option("--no-build-if-missing", "Fail when no snapshot exists instead of compiling Project Brain")
162
+ .action(async (options) => {
163
+ const code = await runBrainMcpCommand({
164
+ root: options.root,
165
+ buildIfMissing: options.buildIfMissing !== false,
166
+ });
167
+ process.exitCode = code;
168
+ });
156
169
  program.configureOutput({
157
170
  outputError: (str, write) => write(str),
158
171
  });
@@ -1,4 +1,4 @@
1
- export declare const PACKAGE_VERSION = "1.0.0";
1
+ export declare const PACKAGE_VERSION = "1.1.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 = "1.0.0";
1
+ export const PACKAGE_VERSION = "1.1.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,4 @@
1
+ export { inferArchitectures } from "./infer.js";
2
+ export { ARCHITECTURE_RULES, listArchitecturePatterns } from "./rules.js";
3
+ export { scorePatternConfidence, clampConfidence } from "./models.js";
4
+ export type { ArchitectureInferenceInput, ArchitectureInferenceOptions, ArchitectureInferenceResult, ArchitectureMatch, ArchitecturePattern, PatternRuleResult, } from "./types.js";
@@ -0,0 +1,3 @@
1
+ export { inferArchitectures } from "./infer.js";
2
+ export { ARCHITECTURE_RULES, listArchitecturePatterns } from "./rules.js";
3
+ export { scorePatternConfidence, clampConfidence } from "./models.js";
@@ -0,0 +1,6 @@
1
+ import type { ArchitectureInferenceInput, ArchitectureInferenceOptions, ArchitectureInferenceResult } from "./types.js";
2
+ /**
3
+ * Infer architectural patterns from prior discovery outputs only.
4
+ * Does not scan the filesystem or repository.
5
+ */
6
+ export declare function inferArchitectures(input: ArchitectureInferenceInput, options?: ArchitectureInferenceOptions): ArchitectureInferenceResult;
@@ -0,0 +1,39 @@
1
+ import { finalizeMatch } from "./models.js";
2
+ import { ARCHITECTURE_RULES } from "./rules.js";
3
+ function assertInput(input) {
4
+ if (!input.domains || !input.entrypoints || !input.dependencies || !input.relationships) {
5
+ throw new Error("inferArchitectures requires domains, entrypoints, dependencies, and relationships inputs");
6
+ }
7
+ }
8
+ /**
9
+ * Infer architectural patterns from prior discovery outputs only.
10
+ * Does not scan the filesystem or repository.
11
+ */
12
+ export function inferArchitectures(input, options = {}) {
13
+ const started = performance.now();
14
+ assertInput(input);
15
+ const minConfidence = options.minConfidence ?? 0.55;
16
+ const architectures = [];
17
+ for (const definition of ARCHITECTURE_RULES) {
18
+ const evaluated = definition.evaluate({ input });
19
+ const match = finalizeMatch(definition.pattern, evaluated);
20
+ if (!match) {
21
+ continue;
22
+ }
23
+ if (match.confidence < minConfidence) {
24
+ continue;
25
+ }
26
+ architectures.push(match);
27
+ }
28
+ architectures.sort((a, b) => {
29
+ if (b.confidence !== a.confidence) {
30
+ return b.confidence - a.confidence;
31
+ }
32
+ return a.pattern.localeCompare(b.pattern);
33
+ });
34
+ return {
35
+ architectures,
36
+ timingMs: Math.round(performance.now() - started),
37
+ patternsEvaluated: ARCHITECTURE_RULES.length,
38
+ };
39
+ }
@@ -0,0 +1,23 @@
1
+ import type { DependencyMatch } from "../dependencies/types.js";
2
+ import type { EntrypointMatch } from "../entrypoints/types.js";
3
+ import type { RelationshipMatch } from "../relationships/types.js";
4
+ import type { ArchitectureInferenceInput, ArchitectureMatch, PatternRuleResult, RelationPredicate } from "./types.js";
5
+ export declare function clampConfidence(value: number): number;
6
+ export declare function scorePatternConfidence(options: {
7
+ supportScore: number;
8
+ conflictScore: number;
9
+ matchedRuleCount: number;
10
+ }): number;
11
+ export declare function findRelationships(input: ArchitectureInferenceInput, predicate: RelationPredicate): RelationshipMatch[];
12
+ export declare function hasRelationship(input: ArchitectureInferenceInput, predicate: RelationPredicate): boolean;
13
+ export declare function relationshipEvidenceLines(rels: RelationshipMatch[], limit?: number): string[];
14
+ export declare function findDependencies(input: ArchitectureInferenceInput, options?: {
15
+ type?: DependencyMatch["type"] | DependencyMatch["type"][];
16
+ evidenceIncludes?: string | string[];
17
+ }): DependencyMatch[];
18
+ export declare function findEntrypoints(input: ArchitectureInferenceInput, options?: {
19
+ framework?: EntrypointMatch["framework"] | EntrypointMatch["framework"][];
20
+ evidenceIncludes?: string | string[];
21
+ }): EntrypointMatch[];
22
+ export declare function emptyRuleResult(unknowns?: string[]): PatternRuleResult;
23
+ export declare function finalizeMatch(pattern: ArchitectureMatch["pattern"], result: PatternRuleResult): ArchitectureMatch | null;
@@ -0,0 +1,127 @@
1
+ export function clampConfidence(value) {
2
+ if (value < 0) {
3
+ return 0;
4
+ }
5
+ // Never claim certainty.
6
+ if (value > 0.95) {
7
+ return 0.95;
8
+ }
9
+ return Math.round(value * 100) / 100;
10
+ }
11
+ export function scorePatternConfidence(options) {
12
+ const { supportScore, conflictScore, matchedRuleCount } = options;
13
+ if (matchedRuleCount <= 0 || supportScore <= 0) {
14
+ return 0;
15
+ }
16
+ const raw = supportScore / (supportScore + conflictScore * 1.25 + 0.35);
17
+ const ruleBonus = Math.min(matchedRuleCount, 4) * 0.03;
18
+ return clampConfidence(raw + ruleBonus);
19
+ }
20
+ function asList(value) {
21
+ if (!value) {
22
+ return [];
23
+ }
24
+ return Array.isArray(value) ? value : [value];
25
+ }
26
+ function includesAny(haystack, needles) {
27
+ if (needles.length === 0) {
28
+ return true;
29
+ }
30
+ const lower = haystack.toLowerCase();
31
+ return needles.some((n) => lower.includes(n.toLowerCase()));
32
+ }
33
+ export function findRelationships(input, predicate) {
34
+ const kinds = asList(predicate.relationship);
35
+ const sources = asList(predicate.sourceIncludes);
36
+ const targets = asList(predicate.targetIncludes);
37
+ const evidenceNeedles = asList(predicate.evidenceIncludes);
38
+ return input.relationships.relationships.filter((rel) => {
39
+ if (kinds.length > 0 && !kinds.includes(rel.relationship)) {
40
+ return false;
41
+ }
42
+ if (sources.length > 0 && !includesAny(rel.source, sources)) {
43
+ return false;
44
+ }
45
+ if (targets.length > 0 && !includesAny(rel.target, targets)) {
46
+ return false;
47
+ }
48
+ if (evidenceNeedles.length > 0) {
49
+ const blob = `${rel.source} ${rel.target} ${rel.evidence.join(" ")}`;
50
+ if (!includesAny(blob, evidenceNeedles)) {
51
+ return false;
52
+ }
53
+ }
54
+ return true;
55
+ });
56
+ }
57
+ export function hasRelationship(input, predicate) {
58
+ return findRelationships(input, predicate).length > 0;
59
+ }
60
+ export function relationshipEvidenceLines(rels, limit = 3) {
61
+ return rels
62
+ .slice(0, limit)
63
+ .flatMap((r) => r.evidence.slice(0, 1).map((e) => `${r.source} ${r.relationship} ${r.target}: ${e}`));
64
+ }
65
+ export function findDependencies(input, options = {}) {
66
+ const types = asList(options.type);
67
+ const needles = asList(options.evidenceIncludes);
68
+ return input.dependencies.dependencies.filter((dep) => {
69
+ if (types.length > 0 && !types.includes(dep.type)) {
70
+ return false;
71
+ }
72
+ if (needles.length > 0) {
73
+ const blob = `${dep.from} ${dep.to} ${dep.evidence.join(" ")}`;
74
+ if (!includesAny(blob, needles)) {
75
+ return false;
76
+ }
77
+ }
78
+ return true;
79
+ });
80
+ }
81
+ export function findEntrypoints(input, options = {}) {
82
+ const frameworks = asList(options.framework);
83
+ const needles = asList(options.evidenceIncludes);
84
+ return input.entrypoints.entrypoints.filter((entry) => {
85
+ if (frameworks.length > 0 && !frameworks.includes(entry.framework)) {
86
+ return false;
87
+ }
88
+ if (needles.length > 0) {
89
+ const blob = `${entry.file} ${entry.evidence.join(" ")}`;
90
+ if (!includesAny(blob, needles)) {
91
+ return false;
92
+ }
93
+ }
94
+ return true;
95
+ });
96
+ }
97
+ export function emptyRuleResult(unknowns = []) {
98
+ return {
99
+ matchedRules: [],
100
+ evidence: [],
101
+ conflictingEvidence: [],
102
+ unknowns,
103
+ supportScore: 0,
104
+ conflictScore: 0,
105
+ };
106
+ }
107
+ export function finalizeMatch(pattern, result) {
108
+ if (result.matchedRules.length === 0 || result.supportScore <= 0) {
109
+ return null;
110
+ }
111
+ const confidence = scorePatternConfidence({
112
+ supportScore: result.supportScore,
113
+ conflictScore: result.conflictScore,
114
+ matchedRuleCount: result.matchedRules.length,
115
+ });
116
+ if (confidence <= 0) {
117
+ return null;
118
+ }
119
+ return {
120
+ pattern,
121
+ confidence,
122
+ evidence: [...result.evidence].sort((a, b) => a.localeCompare(b)),
123
+ matchedRules: [...result.matchedRules].sort((a, b) => a.localeCompare(b)),
124
+ conflictingEvidence: [...result.conflictingEvidence].sort((a, b) => a.localeCompare(b)),
125
+ unknowns: [...result.unknowns].sort((a, b) => a.localeCompare(b)),
126
+ };
127
+ }
@@ -0,0 +1,2 @@
1
+ import type { ArchitecturePatternDefinition } from "../types.js";
2
+ export declare const distributionPatterns: ArchitecturePatternDefinition[];
@@ -0,0 +1,115 @@
1
+ import { emptyRuleResult, findDependencies, findEntrypoints, findRelationships, relationshipEvidenceLines, } from "../models.js";
2
+ function evaluateMicroservice(ctx) {
3
+ const result = emptyRuleResult();
4
+ const frameworks = new Set(ctx.input.entrypoints.entrypoints.map((e) => e.framework));
5
+ const packageDeps = findDependencies(ctx.input, { type: "package" });
6
+ if (frameworks.size >= 2) {
7
+ result.matchedRules.push("Multiple runtime entrypoint frameworks");
8
+ result.evidence.push(...ctx.input.entrypoints.entrypoints
9
+ .slice(0, 5)
10
+ .map((e) => `entrypoint:${e.framework}:${e.file}`));
11
+ result.supportScore += frameworks.size * 0.7;
12
+ }
13
+ if (packageDeps.length >= 2) {
14
+ result.matchedRules.push("Multiple independently versioned package boundaries");
15
+ result.evidence.push(...packageDeps.slice(0, 4).map((d) => `${d.from} package-depends ${d.to}`));
16
+ result.supportScore += 0.8;
17
+ }
18
+ const moduleConfig = findRelationships(ctx.input, { relationship: "CONFIGURES" });
19
+ if (frameworks.size >= 2 && moduleConfig.length > 0 && packageDeps.length <= 1) {
20
+ result.conflictingEvidence.push("Shared module configuration suggests modular monolith more than pure microservices");
21
+ result.conflictScore += 0.7;
22
+ }
23
+ if (result.matchedRules.length > 0 && frameworks.size < 2) {
24
+ result.unknowns.push("Package splits without multi-runtime entrypoint proof");
25
+ }
26
+ return result;
27
+ }
28
+ function evaluateModularMonolith(ctx) {
29
+ const result = emptyRuleResult();
30
+ const modules = findRelationships(ctx.input, {
31
+ relationship: ["PROVIDES", "CONFIGURES"],
32
+ sourceIncludes: "Module",
33
+ });
34
+ const nest = findEntrypoints(ctx.input, { framework: "NestJS" });
35
+ const packageDeps = findDependencies(ctx.input, { type: "package" });
36
+ const frameworks = new Set(ctx.input.entrypoints.entrypoints.map((e) => e.framework));
37
+ if (modules.length > 0) {
38
+ result.matchedRules.push("Internal modules provide/configure application parts");
39
+ result.evidence.push(...relationshipEvidenceLines(modules));
40
+ result.supportScore += 1.4;
41
+ }
42
+ if (nest.length > 0 || (frameworks.size === 1 && modules.length > 0)) {
43
+ result.matchedRules.push("Single primary application runtime hosts modules");
44
+ result.evidence.push(...[...frameworks].map((f) => `runtime:${f}`), ...nest.slice(0, 2).map((e) => `entrypoint:${e.file}`));
45
+ result.supportScore += 1;
46
+ }
47
+ if (packageDeps.length > 0 && modules.length > 0) {
48
+ result.matchedRules.push("Workspace packages compose one deployable system");
49
+ result.evidence.push(...packageDeps.slice(0, 3).map((d) => `${d.from}->${d.to}`));
50
+ result.supportScore += 0.8;
51
+ }
52
+ if (frameworks.size >= 3) {
53
+ result.conflictingEvidence.push("Many independent runtimes weaken modular-monolith claim");
54
+ result.conflictScore += 0.9;
55
+ }
56
+ if (result.matchedRules.length > 0 && modules.length === 0) {
57
+ result.unknowns.push("Monolith packaging without module wiring evidence");
58
+ }
59
+ return result;
60
+ }
61
+ function evaluatePlugin(ctx) {
62
+ const result = emptyRuleResult();
63
+ const dynamic = findDependencies(ctx.input, { type: "dynamic-import" });
64
+ const moduleProvides = findRelationships(ctx.input, {
65
+ relationship: "PROVIDES",
66
+ sourceIncludes: "Module",
67
+ });
68
+ if (dynamic.length > 0) {
69
+ result.matchedRules.push("Dynamic imports indicate pluggable extension points");
70
+ result.evidence.push(...dynamic.slice(0, 4).map((d) => `dynamic:${d.from}->${d.to} (${d.evidence[0] ?? ""})`));
71
+ result.supportScore += 1.5;
72
+ }
73
+ if (moduleProvides.length > 0 && dynamic.length > 0) {
74
+ result.matchedRules.push("Modules expose extension surfaces");
75
+ result.evidence.push(...relationshipEvidenceLines(moduleProvides));
76
+ result.supportScore += 0.9;
77
+ }
78
+ if (dynamic.length > 0 && moduleProvides.length === 0) {
79
+ result.unknowns.push("Dynamic loading without explicit module/plugin registry evidence");
80
+ }
81
+ return result;
82
+ }
83
+ function evaluateMonorepo(ctx) {
84
+ const result = emptyRuleResult();
85
+ const packageDeps = findDependencies(ctx.input, {
86
+ type: "package",
87
+ evidenceIncludes: ["declares dependency", "package"],
88
+ });
89
+ const owns = findRelationships(ctx.input, { relationship: "OWNS" });
90
+ const depends = findDependencies(ctx.input, { type: "package" });
91
+ if (depends.length > 0) {
92
+ result.matchedRules.push("Workspace package dependency edges");
93
+ result.evidence.push(...depends.slice(0, 5).map((d) => `${d.from} DEPENDS_ON ${d.to}: ${d.evidence[0] ?? d.type}`));
94
+ result.supportScore += Math.min(depends.length, 4) * 0.55;
95
+ }
96
+ if (packageDeps.length > 0) {
97
+ result.matchedRules.push("Manifest-declared workspace references");
98
+ result.supportScore += 0.8;
99
+ }
100
+ if (owns.length > 0) {
101
+ result.matchedRules.push("Packages own feature/module slices");
102
+ result.evidence.push(...relationshipEvidenceLines(owns));
103
+ result.supportScore += 0.7;
104
+ }
105
+ if (result.matchedRules.length > 0 && depends.length < 1) {
106
+ result.unknowns.push("Monorepo layout cues without package dependency edges");
107
+ }
108
+ return result;
109
+ }
110
+ export const distributionPatterns = [
111
+ { pattern: "Microservice", evaluate: evaluateMicroservice },
112
+ { pattern: "Modular Monolith", evaluate: evaluateModularMonolith },
113
+ { pattern: "Plugin Architecture", evaluate: evaluatePlugin },
114
+ { pattern: "Monorepo Workspace", evaluate: evaluateMonorepo },
115
+ ];
@@ -0,0 +1,2 @@
1
+ import type { ArchitecturePatternDefinition } from "../types.js";
2
+ export declare const ARCHITECTURE_PATTERNS: readonly ArchitecturePatternDefinition[];
@@ -0,0 +1,8 @@
1
+ import { distributionPatterns } from "./distribution.js";
2
+ import { layeredPatterns } from "./layered.js";
3
+ import { structuralPatterns } from "./structural.js";
4
+ export const ARCHITECTURE_PATTERNS = [
5
+ ...layeredPatterns,
6
+ ...structuralPatterns,
7
+ ...distributionPatterns,
8
+ ];
@@ -0,0 +1,2 @@
1
+ import type { ArchitecturePatternDefinition } from "../types.js";
2
+ export declare const layeredPatterns: ArchitecturePatternDefinition[];