@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.
- package/CHANGELOG.md +34 -5
- package/README.md +15 -12
- package/dist/cli/commands/brain-mcp.d.ts +9 -0
- package/dist/cli/commands/brain-mcp.js +38 -0
- package/dist/cli/program.js +13 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/core/understanding/architecture/index.d.ts +4 -0
- package/dist/core/understanding/architecture/index.js +3 -0
- package/dist/core/understanding/architecture/infer.d.ts +6 -0
- package/dist/core/understanding/architecture/infer.js +39 -0
- package/dist/core/understanding/architecture/models.d.ts +23 -0
- package/dist/core/understanding/architecture/models.js +127 -0
- package/dist/core/understanding/architecture/patterns/distribution.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/distribution.js +115 -0
- package/dist/core/understanding/architecture/patterns/index.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/index.js +8 -0
- package/dist/core/understanding/architecture/patterns/layered.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/layered.js +279 -0
- package/dist/core/understanding/architecture/patterns/structural.d.ts +2 -0
- package/dist/core/understanding/architecture/patterns/structural.js +197 -0
- package/dist/core/understanding/architecture/rules.d.ts +4 -0
- package/dist/core/understanding/architecture/rules.js +6 -0
- package/dist/core/understanding/architecture/types.d.ts +52 -0
- package/dist/core/understanding/architecture/types.js +1 -0
- package/dist/core/understanding/brain/build.d.ts +13 -0
- package/dist/core/understanding/brain/build.js +158 -0
- package/dist/core/understanding/brain/claims/index.d.ts +3 -0
- package/dist/core/understanding/brain/claims/index.js +2 -0
- package/dist/core/understanding/brain/claims/lifecycle.d.ts +16 -0
- package/dist/core/understanding/brain/claims/lifecycle.js +78 -0
- package/dist/core/understanding/brain/claims/types.d.ts +32 -0
- package/dist/core/understanding/brain/claims/types.js +25 -0
- package/dist/core/understanding/brain/components/index.d.ts +17 -0
- package/dist/core/understanding/brain/components/index.js +97 -0
- package/dist/core/understanding/brain/components/types.d.ts +16 -0
- package/dist/core/understanding/brain/components/types.js +8 -0
- package/dist/core/understanding/brain/confidence.d.ts +19 -0
- package/dist/core/understanding/brain/confidence.js +39 -0
- package/dist/core/understanding/brain/contract.d.ts +17 -0
- package/dist/core/understanding/brain/contract.js +100 -0
- package/dist/core/understanding/brain/contradictions/index.d.ts +12 -0
- package/dist/core/understanding/brain/contradictions/index.js +57 -0
- package/dist/core/understanding/brain/contradictions/types.d.ts +12 -0
- package/dist/core/understanding/brain/contradictions/types.js +8 -0
- package/dist/core/understanding/brain/delta.d.ts +25 -0
- package/dist/core/understanding/brain/delta.js +116 -0
- package/dist/core/understanding/brain/evidence/index.d.ts +3 -0
- package/dist/core/understanding/brain/evidence/index.js +2 -0
- package/dist/core/understanding/brain/evidence/redact.d.ts +4 -0
- package/dist/core/understanding/brain/evidence/redact.js +36 -0
- package/dist/core/understanding/brain/evidence/types.d.ts +34 -0
- package/dist/core/understanding/brain/evidence/types.js +35 -0
- package/dist/core/understanding/brain/explain.d.ts +17 -0
- package/dist/core/understanding/brain/explain.js +33 -0
- package/dist/core/understanding/brain/index.d.ts +28 -0
- package/dist/core/understanding/brain/index.js +16 -0
- package/dist/core/understanding/brain/migrate.d.ts +21 -0
- package/dist/core/understanding/brain/migrate.js +47 -0
- package/dist/core/understanding/brain/query.d.ts +60 -0
- package/dist/core/understanding/brain/query.js +92 -0
- package/dist/core/understanding/brain/security.d.ts +3 -0
- package/dist/core/understanding/brain/security.js +43 -0
- package/dist/core/understanding/brain/storage/index.d.ts +2 -0
- package/dist/core/understanding/brain/storage/index.js +1 -0
- package/dist/core/understanding/brain/storage/store.d.ts +53 -0
- package/dist/core/understanding/brain/storage/store.js +305 -0
- package/dist/core/understanding/brain/trace.d.ts +24 -0
- package/dist/core/understanding/brain/trace.js +207 -0
- package/dist/core/understanding/brain/types.d.ts +32 -0
- package/dist/core/understanding/brain/types.js +8 -0
- package/dist/core/understanding/brain/version.d.ts +13 -0
- package/dist/core/understanding/brain/version.js +7 -0
- package/dist/core/understanding/delta/compare.d.ts +26 -0
- package/dist/core/understanding/delta/compare.js +74 -0
- package/dist/core/understanding/delta/index.d.ts +2 -0
- package/dist/core/understanding/delta/index.js +1 -0
- package/dist/core/understanding/dependencies/discover.d.ts +7 -0
- package/dist/core/understanding/dependencies/discover.js +550 -0
- package/dist/core/understanding/dependencies/extract.d.ts +10 -0
- package/dist/core/understanding/dependencies/extract.js +189 -0
- package/dist/core/understanding/dependencies/index.d.ts +4 -0
- package/dist/core/understanding/dependencies/index.js +3 -0
- package/dist/core/understanding/dependencies/models.d.ts +12 -0
- package/dist/core/understanding/dependencies/models.js +125 -0
- package/dist/core/understanding/dependencies/types.d.ts +27 -0
- package/dist/core/understanding/dependencies/types.js +1 -0
- package/dist/core/understanding/domain/discover.d.ts +14 -0
- package/dist/core/understanding/domain/discover.js +90 -0
- package/dist/core/understanding/domain/index.d.ts +2 -0
- package/dist/core/understanding/domain/index.js +1 -0
- package/dist/core/understanding/entrypoints/discover.d.ts +6 -0
- package/dist/core/understanding/entrypoints/discover.js +62 -0
- package/dist/core/understanding/entrypoints/extract.d.ts +19 -0
- package/dist/core/understanding/entrypoints/extract.js +43 -0
- package/dist/core/understanding/entrypoints/index.d.ts +4 -0
- package/dist/core/understanding/entrypoints/index.js +3 -0
- package/dist/core/understanding/entrypoints/models.d.ts +26 -0
- package/dist/core/understanding/entrypoints/models.js +216 -0
- package/dist/core/understanding/entrypoints/types.d.ts +20 -0
- package/dist/core/understanding/entrypoints/types.js +1 -0
- package/dist/core/understanding/index.d.ts +32 -0
- package/dist/core/understanding/index.js +18 -0
- package/dist/core/understanding/mind/build.d.ts +16 -0
- package/dist/core/understanding/mind/build.js +134 -0
- package/dist/core/understanding/mind/index.d.ts +6 -0
- package/dist/core/understanding/mind/index.js +3 -0
- package/dist/core/understanding/mind/query.d.ts +33 -0
- package/dist/core/understanding/mind/query.js +39 -0
- package/dist/core/understanding/mind/types.d.ts +24 -0
- package/dist/core/understanding/mind/types.js +7 -0
- package/dist/core/understanding/model/builder.d.ts +9 -0
- package/dist/core/understanding/model/builder.js +209 -0
- package/dist/core/understanding/model/ids.d.ts +7 -0
- package/dist/core/understanding/model/ids.js +48 -0
- package/dist/core/understanding/model/index.d.ts +7 -0
- package/dist/core/understanding/model/index.js +6 -0
- package/dist/core/understanding/model/schema.d.ts +12 -0
- package/dist/core/understanding/model/schema.js +33 -0
- package/dist/core/understanding/model/serializer.d.ts +9 -0
- package/dist/core/understanding/model/serializer.js +46 -0
- package/dist/core/understanding/model/types.d.ts +110 -0
- package/dist/core/understanding/model/types.js +1 -0
- package/dist/core/understanding/model/validator.d.ts +5 -0
- package/dist/core/understanding/model/validator.js +126 -0
- package/dist/core/understanding/model/version.d.ts +9 -0
- package/dist/core/understanding/model/version.js +7 -0
- package/dist/core/understanding/ownership/discover.d.ts +12 -0
- package/dist/core/understanding/ownership/discover.js +242 -0
- package/dist/core/understanding/ownership/index.d.ts +2 -0
- package/dist/core/understanding/ownership/index.js +1 -0
- package/dist/core/understanding/ownership/types.d.ts +23 -0
- package/dist/core/understanding/ownership/types.js +1 -0
- package/dist/core/understanding/query/engine.d.ts +24 -0
- package/dist/core/understanding/query/engine.js +35 -0
- package/dist/core/understanding/query/errors.d.ts +13 -0
- package/dist/core/understanding/query/errors.js +26 -0
- package/dist/core/understanding/query/executor.d.ts +8 -0
- package/dist/core/understanding/query/executor.js +34 -0
- package/dist/core/understanding/query/index.d.ts +7 -0
- package/dist/core/understanding/query/index.js +5 -0
- package/dist/core/understanding/query/models.d.ts +102 -0
- package/dist/core/understanding/query/models.js +1 -0
- package/dist/core/understanding/query/query.d.ts +68 -0
- package/dist/core/understanding/query/query.js +145 -0
- package/dist/core/understanding/query/registry.d.ts +8 -0
- package/dist/core/understanding/query/registry.js +340 -0
- package/dist/core/understanding/query/types.d.ts +63 -0
- package/dist/core/understanding/query/types.js +1 -0
- package/dist/core/understanding/relationships/discover.d.ts +11 -0
- package/dist/core/understanding/relationships/discover.js +517 -0
- package/dist/core/understanding/relationships/extract.d.ts +14 -0
- package/dist/core/understanding/relationships/extract.js +165 -0
- package/dist/core/understanding/relationships/index.d.ts +4 -0
- package/dist/core/understanding/relationships/index.js +3 -0
- package/dist/core/understanding/relationships/models.d.ts +21 -0
- package/dist/core/understanding/relationships/models.js +185 -0
- package/dist/core/understanding/relationships/types.d.ts +42 -0
- package/dist/core/understanding/relationships/types.js +1 -0
- package/dist/core/understanding/risks/discover.d.ts +8 -0
- package/dist/core/understanding/risks/discover.js +155 -0
- package/dist/core/understanding/risks/index.d.ts +2 -0
- package/dist/core/understanding/risks/index.js +1 -0
- package/dist/core/understanding/risks/types.d.ts +26 -0
- package/dist/core/understanding/risks/types.js +1 -0
- package/dist/core/understanding/shared/domain-lexicon.d.ts +9 -0
- package/dist/core/understanding/shared/domain-lexicon.js +107 -0
- package/dist/core/understanding/shared/index.d.ts +2 -0
- package/dist/core/understanding/shared/index.js +2 -0
- package/dist/core/understanding/shared/tokens.d.ts +7 -0
- package/dist/core/understanding/shared/tokens.js +141 -0
- package/dist/core/understanding/snapshot/identity.d.ts +24 -0
- package/dist/core/understanding/snapshot/identity.js +79 -0
- package/dist/core/understanding/snapshot/index.d.ts +2 -0
- package/dist/core/understanding/snapshot/index.js +1 -0
- package/dist/core/understanding/types/index.d.ts +22 -0
- package/dist/core/understanding/types/index.js +1 -0
- package/dist/core/understanding/understand/formatter.d.ts +10 -0
- package/dist/core/understanding/understand/formatter.js +115 -0
- package/dist/core/understanding/understand/index.d.ts +4 -0
- package/dist/core/understanding/understand/index.js +3 -0
- package/dist/core/understanding/understand/service.d.ts +17 -0
- package/dist/core/understanding/understand/service.js +76 -0
- package/dist/core/understanding/understand/summary.d.ts +14 -0
- package/dist/core/understanding/understand/summary.js +39 -0
- package/dist/core/understanding/understand/types.d.ts +31 -0
- package/dist/core/understanding/understand/types.js +1 -0
- package/dist/mcp/brain/compile.d.ts +9 -0
- package/dist/mcp/brain/compile.js +42 -0
- package/dist/mcp/brain/errors.d.ts +12 -0
- package/dist/mcp/brain/errors.js +23 -0
- package/dist/mcp/brain/index.d.ts +11 -0
- package/dist/mcp/brain/index.js +8 -0
- package/dist/mcp/brain/provenance.d.ts +29 -0
- package/dist/mcp/brain/provenance.js +59 -0
- package/dist/mcp/brain/schemas.d.ts +13 -0
- package/dist/mcp/brain/schemas.js +105 -0
- package/dist/mcp/brain/security/root.d.ts +6 -0
- package/dist/mcp/brain/security/root.js +66 -0
- package/dist/mcp/brain/server.d.ts +12 -0
- package/dist/mcp/brain/server.js +46 -0
- package/dist/mcp/brain/session.d.ts +33 -0
- package/dist/mcp/brain/session.js +117 -0
- package/dist/mcp/brain/tools/handlers.d.ts +13 -0
- package/dist/mcp/brain/tools/handlers.js +483 -0
- package/dist/mcp/brain/tools/registry.d.ts +9 -0
- package/dist/mcp/brain/tools/registry.js +191 -0
- 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
|
|
13
|
-
|
|
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
|
|
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.
|
|
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`)
|
|
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.
|
|
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) |
|
|
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 (
|
|
410
|
+
## Project Brain MCP (agent context)
|
|
408
411
|
|
|
409
|
-
|
|
412
|
+
Evidence-backed repository understanding for agents (not a search MCP):
|
|
410
413
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/dist/cli/program.js
CHANGED
|
@@ -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
|
});
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "1.
|
|
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.
|
|
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,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,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,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
|
+
];
|