@praneeth_54/agentdoctor 1.0.0 → 1.1.1
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 +60 -5
- package/README.md +462 -251
- 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 +11 -4
|
@@ -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.1";
|
|
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.1";
|
|
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
|
+
];
|