@praneeth_54/agentdoctor 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/CHANGELOG.md +57 -3
  2. package/README.md +489 -290
  3. package/dist/agent/chat/deterministic.d.ts +6 -0
  4. package/dist/agent/chat/deterministic.js +180 -0
  5. package/dist/agent/chat/service.js +8 -14
  6. package/dist/agent/index.d.ts +2 -0
  7. package/dist/agent/index.js +1 -0
  8. package/dist/agent/loop.d.ts +2 -0
  9. package/dist/agent/loop.js +31 -0
  10. package/dist/agent/roles.d.ts +12 -0
  11. package/dist/agent/roles.js +138 -0
  12. package/dist/agent/runtime.d.ts +21 -5
  13. package/dist/agent/runtime.js +141 -28
  14. package/dist/agent/tools/execute.d.ts +2 -0
  15. package/dist/agent/tools/execute.js +10 -0
  16. package/dist/ai/config.js +1 -0
  17. package/dist/ai/redact.d.ts +1 -0
  18. package/dist/ai/redact.js +47 -7
  19. package/dist/ai/types.d.ts +1 -1
  20. package/dist/cli/commands/agent.d.ts +2 -0
  21. package/dist/cli/commands/agent.js +19 -8
  22. package/dist/cli/commands/chat.js +2 -4
  23. package/dist/cli/commands/product.d.ts +19 -0
  24. package/dist/cli/commands/product.js +147 -0
  25. package/dist/cli/commands/start.d.ts +15 -0
  26. package/dist/cli/commands/start.js +80 -0
  27. package/dist/cli/program.js +141 -0
  28. package/dist/constants.d.ts +1 -1
  29. package/dist/constants.js +1 -1
  30. package/dist/dashboard/server.js +255 -53
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.js +2 -0
  33. package/dist/intelligence/graph/build.js +36 -8
  34. package/dist/intelligence/resolve/imports.js +1 -1
  35. package/dist/languages/dart.d.ts +10 -0
  36. package/dist/languages/dart.js +99 -0
  37. package/dist/languages/go.d.ts +13 -7
  38. package/dist/languages/go.js +84 -24
  39. package/dist/languages/index.d.ts +5 -1
  40. package/dist/languages/index.js +13 -36
  41. package/dist/languages/java.d.ts +10 -0
  42. package/dist/languages/java.js +80 -0
  43. package/dist/languages/kotlin.d.ts +10 -0
  44. package/dist/languages/kotlin.js +85 -0
  45. package/dist/languages/rust.d.ts +10 -0
  46. package/dist/languages/rust.js +93 -0
  47. package/dist/languages/types.d.ts +4 -3
  48. package/dist/mcp/agent/registry.d.ts +1 -1
  49. package/dist/mcp/agent/registry.js +109 -23
  50. package/dist/mcp/intelligence/handlers.d.ts +3 -0
  51. package/dist/mcp/intelligence/handlers.js +28 -0
  52. package/dist/mcp/intelligence/registry.d.ts +1 -1
  53. package/dist/mcp/intelligence/registry.js +30 -1
  54. package/dist/product/api/doctor.d.ts +14 -0
  55. package/dist/product/api/doctor.js +185 -0
  56. package/dist/product/api/openapi.d.ts +7 -0
  57. package/dist/product/api/openapi.js +122 -0
  58. package/dist/product/approval/model.d.ts +30 -0
  59. package/dist/product/approval/model.js +64 -0
  60. package/dist/product/approval/session.d.ts +49 -0
  61. package/dist/product/approval/session.js +134 -0
  62. package/dist/product/database/doctor.d.ts +30 -0
  63. package/dist/product/database/doctor.js +184 -0
  64. package/dist/product/decisions/ledger.d.ts +24 -0
  65. package/dist/product/decisions/ledger.js +110 -0
  66. package/dist/product/deps/analyze.d.ts +46 -0
  67. package/dist/product/deps/analyze.js +137 -0
  68. package/dist/product/deps/lockfiles.d.ts +25 -0
  69. package/dist/product/deps/lockfiles.js +200 -0
  70. package/dist/product/discovery/roots.d.ts +42 -0
  71. package/dist/product/discovery/roots.js +215 -0
  72. package/dist/product/dna/build.d.ts +50 -0
  73. package/dist/product/dna/build.js +255 -0
  74. package/dist/product/eval/lab.d.ts +17 -0
  75. package/dist/product/eval/lab.js +218 -0
  76. package/dist/product/events/doctor.d.ts +21 -0
  77. package/dist/product/events/doctor.js +147 -0
  78. package/dist/product/evidence-scan.d.ts +12 -0
  79. package/dist/product/evidence-scan.js +46 -0
  80. package/dist/product/evolution/timeline.d.ts +29 -0
  81. package/dist/product/evolution/timeline.js +123 -0
  82. package/dist/product/features/intelligence.d.ts +23 -0
  83. package/dist/product/features/intelligence.js +158 -0
  84. package/dist/product/forensic/mode.d.ts +25 -0
  85. package/dist/product/forensic/mode.js +70 -0
  86. package/dist/product/graph/enrich-languages.d.ts +20 -0
  87. package/dist/product/graph/enrich-languages.js +193 -0
  88. package/dist/product/health/code-health.d.ts +20 -0
  89. package/dist/product/health/code-health.js +149 -0
  90. package/dist/product/index.d.ts +69 -0
  91. package/dist/product/index.js +35 -0
  92. package/dist/product/ledger/change-ledger.d.ts +23 -0
  93. package/dist/product/ledger/change-ledger.js +64 -0
  94. package/dist/product/map/software-map.d.ts +17 -0
  95. package/dist/product/map/software-map.js +95 -0
  96. package/dist/product/memory/institutional.d.ts +16 -0
  97. package/dist/product/memory/institutional.js +87 -0
  98. package/dist/product/ops/incident.d.ts +20 -0
  99. package/dist/product/ops/incident.js +61 -0
  100. package/dist/product/ops/infra.d.ts +15 -0
  101. package/dist/product/ops/infra.js +112 -0
  102. package/dist/product/org/model.d.ts +34 -0
  103. package/dist/product/org/model.js +195 -0
  104. package/dist/product/privacy/doctor.d.ts +13 -0
  105. package/dist/product/privacy/doctor.js +90 -0
  106. package/dist/product/requirements/trace.d.ts +21 -0
  107. package/dist/product/requirements/trace.js +166 -0
  108. package/dist/product/search/index.d.ts +29 -0
  109. package/dist/product/search/index.js +116 -0
  110. package/dist/product/search/software-search.d.ts +19 -0
  111. package/dist/product/search/software-search.js +100 -0
  112. package/dist/product/security/doctor.d.ts +23 -0
  113. package/dist/product/security/doctor.js +124 -0
  114. package/dist/product/self/diagnose.d.ts +15 -0
  115. package/dist/product/self/diagnose.js +82 -0
  116. package/dist/product/techdebt/roadmap.d.ts +20 -0
  117. package/dist/product/techdebt/roadmap.js +118 -0
  118. package/dist/product/testbrain/analyze.d.ts +29 -0
  119. package/dist/product/testbrain/analyze.js +129 -0
  120. package/dist/product/truth.d.ts +12 -0
  121. package/dist/product/truth.js +30 -0
  122. package/dist/product/twin/digital-twin.d.ts +23 -0
  123. package/dist/product/twin/digital-twin.js +52 -0
  124. package/dist/product/twin/store.d.ts +21 -0
  125. package/dist/product/twin/store.js +70 -0
  126. package/dist/product/whatif/engine.d.ts +28 -0
  127. package/dist/product/whatif/engine.js +70 -0
  128. package/dist/utils/fs.js +10 -2
  129. package/package.json +1 -1
@@ -0,0 +1,69 @@
1
+ export type { TruthLabel, ProductEvidence } from "./truth.js";
2
+ export { minProductTruth, truthLabelDescription } from "./truth.js";
3
+ export { traceRequirements } from "./requirements/trace.js";
4
+ export type { RequirementTrace, RequirementsTraceReport } from "./requirements/trace.js";
5
+ export { analyzeApiSurface } from "./api/doctor.js";
6
+ export type { ApiDoctorReport, ApiEndpointFinding } from "./api/doctor.js";
7
+ export { analyzeDatabaseSchema } from "./database/doctor.js";
8
+ export type { DatabaseDoctorReport, SchemaObjectFinding, SchemaDriftFinding, SchemaRelationFinding, } from "./database/doctor.js";
9
+ export { analyzeEvents } from "./events/doctor.js";
10
+ export type { EventsDoctorReport, EventFlowNode, EventFlowEdge } from "./events/doctor.js";
11
+ export { analyzeDependencies } from "./deps/analyze.js";
12
+ export type { DependencyAnalysisReport, DirectDependency, DuplicatePackageName, UpgradeImpactHint, } from "./deps/analyze.js";
13
+ export { analyzeCodeHealth } from "./health/code-health.js";
14
+ export type { CodeHealthReport, CodeHealthIndicator } from "./health/code-health.js";
15
+ export { buildSoftwareMap } from "./map/software-map.js";
16
+ export type { SoftwareMap, SoftwareMapNode, SoftwareMapNodeKind } from "./map/software-map.js";
17
+ export { appendChangeLedgerEntry, readChangeLedger, listChangeLedgerEntries, } from "./ledger/change-ledger.js";
18
+ export type { ChangeLedgerEntry, ChangeLedgerListResult } from "./ledger/change-ledger.js";
19
+ export { analyzeWhatIf } from "./whatif/engine.js";
20
+ export type { WhatIfReport, WhatIfAffectedItem } from "./whatif/engine.js";
21
+ export { loadDecisionLedger, appendDecisionLedgerEntry, parseAdrDecisions, resolveDecisionPath, } from "./decisions/ledger.js";
22
+ export type { DecisionRecord, DecisionLedgerResult } from "./decisions/ledger.js";
23
+ export { runForensicAnalysis, assertForensicReadOnly, ForensicWriteRefusedError, } from "./forensic/mode.js";
24
+ export type { ForensicAnalysisReport } from "./forensic/mode.js";
25
+ export { buildSoftwareDigitalTwinSnapshot } from "./twin/digital-twin.js";
26
+ export type { SoftwareDigitalTwin } from "./twin/digital-twin.js";
27
+ export { buildSoftwareDigitalTwin, updateTwin, loadTwinSnapshot, saveTwinSnapshot, invalidateTwinSnapshot, } from "./twin/store.js";
28
+ export type { StoredTwinSnapshot, TwinSnapshotMeta } from "./twin/store.js";
29
+ export { parsePackageLockJson, parseYarnLock, parsePnpmLockYaml, parseAllLockfiles, } from "./deps/lockfiles.js";
30
+ export type { LockfilePackageVersion, ParsedLockfile } from "./deps/lockfiles.js";
31
+ export { discoverOpenApiEndpoints } from "./api/openapi.js";
32
+ export { buildIndex, searchIndex, searchHybrid, documentId } from "./search/index.js";
33
+ export type { TfidfIndex, IndexDocument } from "./search/index.js";
34
+ export { runEvalLab } from "./eval/lab.js";
35
+ export type { EvalLabReport, EvalCheckResult } from "./eval/lab.js";
36
+ export { diagnoseAgentDoctorSelf } from "./self/diagnose.js";
37
+ export type { SelfDiagnoseReport, SelfDiagnoseFinding } from "./self/diagnose.js";
38
+ export { analyzeInfra } from "./ops/infra.js";
39
+ export type { InfraReport, InfraArtifact } from "./ops/infra.js";
40
+ export { buildIncidentHypotheses } from "./ops/incident.js";
41
+ export type { IncidentReport, IncidentTimelineItem } from "./ops/incident.js";
42
+ export { discoverProjectRoots, resolveStartedProjectRoot } from "./discovery/roots.js";
43
+ export type { ProjectDiscoveryReport, ProjectCandidate, DiscoverProjectOptions, } from "./discovery/roots.js";
44
+ export { buildProjectDna, persistProjectDna } from "./dna/build.js";
45
+ export type { ProjectDna } from "./dna/build.js";
46
+ export { enrichGraphWithLanguageAdapters } from "./graph/enrich-languages.js";
47
+ export type { LanguageGraphEnrichmentStats } from "./graph/enrich-languages.js";
48
+ export { analyzeSecuritySurface } from "./security/doctor.js";
49
+ export type { SecurityDoctorReport, SecurityPatternFinding } from "./security/doctor.js";
50
+ export { analyzeTestBrain, persistTestBrainReport, mapTestsFromGraph, } from "./testbrain/analyze.js";
51
+ export type { TestBrainReport, FileTestMapping, TestBrainGraphStats, FileTestMappingEvidence, } from "./testbrain/analyze.js";
52
+ export { analyzePrivacySurface } from "./privacy/doctor.js";
53
+ export type { PrivacyDoctorReport, PrivacyFinding } from "./privacy/doctor.js";
54
+ export { searchSymbolsAndConcepts } from "./search/software-search.js";
55
+ export type { SoftwareSearchReport, SoftwareSearchHit } from "./search/software-search.js";
56
+ export { buildTechnicalDebtRoadmap } from "./techdebt/roadmap.js";
57
+ export type { TechnicalDebtRoadmap, TechnicalDebtItem } from "./techdebt/roadmap.js";
58
+ export { loadOrganizationModel, saveOrganizationModel, linkProjectToOrgModel, resolveLinkedProjectRoot, } from "./org/model.js";
59
+ export type { OrganizationModel, OrgEntityRef, OrgProjectLink } from "./org/model.js";
60
+ export { analyzeFeatureIntelligence } from "./features/intelligence.js";
61
+ export type { FeatureIntelligenceReport, FeatureFlow, FeatureFlowLink, } from "./features/intelligence.js";
62
+ export { buildSoftwareEvolutionTimeline } from "./evolution/timeline.js";
63
+ export type { SoftwareEvolutionReport, EvolutionTimelineEvent, EvolutionTrend, } from "./evolution/timeline.js";
64
+ export { queryMemory } from "./memory/institutional.js";
65
+ export type { InstitutionalMemoryQueryResult, MemoryHit } from "./memory/institutional.js";
66
+ export { evaluateApprovalRecord, formatApprovalRecordSummary } from "./approval/model.js";
67
+ export type { ApprovalRecord, ApprovalState, ApprovalRisk, ApprovalEvaluationInput, ApprovalEvaluationResult, } from "./approval/model.js";
68
+ export { issueApprovalGrant, consumeApprovalGrant, hashPlanPayload, hashFileWritePlan, } from "./approval/session.js";
69
+ export type { ApprovalGrant, ConsumeApprovalResult } from "./approval/session.js";
@@ -0,0 +1,35 @@
1
+ export { minProductTruth, truthLabelDescription } from "./truth.js";
2
+ export { traceRequirements } from "./requirements/trace.js";
3
+ export { analyzeApiSurface } from "./api/doctor.js";
4
+ export { analyzeDatabaseSchema } from "./database/doctor.js";
5
+ export { analyzeEvents } from "./events/doctor.js";
6
+ export { analyzeDependencies } from "./deps/analyze.js";
7
+ export { analyzeCodeHealth } from "./health/code-health.js";
8
+ export { buildSoftwareMap } from "./map/software-map.js";
9
+ export { appendChangeLedgerEntry, readChangeLedger, listChangeLedgerEntries, } from "./ledger/change-ledger.js";
10
+ export { analyzeWhatIf } from "./whatif/engine.js";
11
+ export { loadDecisionLedger, appendDecisionLedgerEntry, parseAdrDecisions, resolveDecisionPath, } from "./decisions/ledger.js";
12
+ export { runForensicAnalysis, assertForensicReadOnly, ForensicWriteRefusedError, } from "./forensic/mode.js";
13
+ export { buildSoftwareDigitalTwinSnapshot } from "./twin/digital-twin.js";
14
+ export { buildSoftwareDigitalTwin, updateTwin, loadTwinSnapshot, saveTwinSnapshot, invalidateTwinSnapshot, } from "./twin/store.js";
15
+ export { parsePackageLockJson, parseYarnLock, parsePnpmLockYaml, parseAllLockfiles, } from "./deps/lockfiles.js";
16
+ export { discoverOpenApiEndpoints } from "./api/openapi.js";
17
+ export { buildIndex, searchIndex, searchHybrid, documentId } from "./search/index.js";
18
+ export { runEvalLab } from "./eval/lab.js";
19
+ export { diagnoseAgentDoctorSelf } from "./self/diagnose.js";
20
+ export { analyzeInfra } from "./ops/infra.js";
21
+ export { buildIncidentHypotheses } from "./ops/incident.js";
22
+ export { discoverProjectRoots, resolveStartedProjectRoot } from "./discovery/roots.js";
23
+ export { buildProjectDna, persistProjectDna } from "./dna/build.js";
24
+ export { enrichGraphWithLanguageAdapters } from "./graph/enrich-languages.js";
25
+ export { analyzeSecuritySurface } from "./security/doctor.js";
26
+ export { analyzeTestBrain, persistTestBrainReport, mapTestsFromGraph, } from "./testbrain/analyze.js";
27
+ export { analyzePrivacySurface } from "./privacy/doctor.js";
28
+ export { searchSymbolsAndConcepts } from "./search/software-search.js";
29
+ export { buildTechnicalDebtRoadmap } from "./techdebt/roadmap.js";
30
+ export { loadOrganizationModel, saveOrganizationModel, linkProjectToOrgModel, resolveLinkedProjectRoot, } from "./org/model.js";
31
+ export { analyzeFeatureIntelligence } from "./features/intelligence.js";
32
+ export { buildSoftwareEvolutionTimeline } from "./evolution/timeline.js";
33
+ export { queryMemory } from "./memory/institutional.js";
34
+ export { evaluateApprovalRecord, formatApprovalRecordSummary } from "./approval/model.js";
35
+ export { issueApprovalGrant, consumeApprovalGrant, hashPlanPayload, hashFileWritePlan, } from "./approval/session.js";
@@ -0,0 +1,23 @@
1
+ export interface ChangeLedgerEntry {
2
+ id: string;
3
+ recordedAt: string;
4
+ task?: string;
5
+ plan?: string;
6
+ approval?: string;
7
+ files?: string[];
8
+ tests?: string[];
9
+ proofIds?: string[];
10
+ note?: string;
11
+ }
12
+ export interface ChangeLedgerListResult {
13
+ root: string;
14
+ ledgerDir: string;
15
+ entries: ChangeLedgerEntry[];
16
+ limitations: string[];
17
+ }
18
+ export declare function appendChangeLedgerEntry(rootInput: string, entry: Omit<ChangeLedgerEntry, "id" | "recordedAt"> & {
19
+ id?: string;
20
+ recordedAt?: string;
21
+ }): Promise<ChangeLedgerEntry>;
22
+ export declare function readChangeLedger(rootInput: string): Promise<ChangeLedgerListResult>;
23
+ export declare function listChangeLedgerEntries(rootInput: string): Promise<ChangeLedgerEntry[]>;
@@ -0,0 +1,64 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { safeRelPath } from "../../security/paths.js";
4
+ import { resolveRepoRoot } from "../../utils/path.js";
5
+ import { pathExists } from "../../utils/fs.js";
6
+ function ledgerDirFor(root) {
7
+ return path.join(resolveRepoRoot(root), ".agentdoctor", "change-ledger");
8
+ }
9
+ function ledgerFileFor(root) {
10
+ return path.join(ledgerDirFor(root), "entries.jsonl");
11
+ }
12
+ async function ensureLedgerDir(root) {
13
+ const dir = ledgerDirFor(root);
14
+ await fs.mkdir(dir, { recursive: true });
15
+ return dir;
16
+ }
17
+ function newEntryId() {
18
+ return `cle_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
19
+ }
20
+ export async function appendChangeLedgerEntry(rootInput, entry) {
21
+ const root = resolveRepoRoot(rootInput);
22
+ await ensureLedgerDir(root);
23
+ const record = {
24
+ id: entry.id ?? newEntryId(),
25
+ recordedAt: entry.recordedAt ?? new Date().toISOString(),
26
+ ...(entry.task !== undefined ? { task: entry.task } : {}),
27
+ ...(entry.plan !== undefined ? { plan: entry.plan } : {}),
28
+ ...(entry.approval !== undefined ? { approval: entry.approval } : {}),
29
+ ...(entry.files !== undefined ? { files: entry.files.map((f) => safeRelPath(root, f)) } : {}),
30
+ ...(entry.tests !== undefined ? { tests: entry.tests } : {}),
31
+ ...(entry.proofIds !== undefined ? { proofIds: entry.proofIds } : {}),
32
+ ...(entry.note !== undefined ? { note: entry.note } : {}),
33
+ };
34
+ const line = `${JSON.stringify(record)}\n`;
35
+ await fs.appendFile(ledgerFileFor(root), line, "utf8");
36
+ return record;
37
+ }
38
+ export async function readChangeLedger(rootInput) {
39
+ const root = resolveRepoRoot(rootInput);
40
+ const ledgerDir = ledgerDirFor(root);
41
+ const limitations = ["Append-only JSONL; entries are not cryptographically signed."];
42
+ const file = ledgerFileFor(root);
43
+ if (!(await pathExists(file))) {
44
+ return { root, ledgerDir, entries: [], limitations };
45
+ }
46
+ const text = await fs.readFile(file, "utf8");
47
+ const entries = [];
48
+ for (const line of text.split(/\r?\n/)) {
49
+ const trimmed = line.trim();
50
+ if (!trimmed)
51
+ continue;
52
+ try {
53
+ entries.push(JSON.parse(trimmed));
54
+ }
55
+ catch {
56
+ limitations.push("Skipped malformed JSONL line in change ledger.");
57
+ }
58
+ }
59
+ return { root, ledgerDir, entries, limitations };
60
+ }
61
+ export async function listChangeLedgerEntries(rootInput) {
62
+ const result = await readChangeLedger(rootInput);
63
+ return result.entries;
64
+ }
@@ -0,0 +1,17 @@
1
+ import type { ProductEvidence, TruthLabel } from "../truth.js";
2
+ export type SoftwareMapNodeKind = "root" | "package" | "directory" | "workspace" | "docs" | "tests" | "unknown";
3
+ export interface SoftwareMapNode {
4
+ id: string;
5
+ kind: SoftwareMapNodeKind;
6
+ label: string;
7
+ path?: string;
8
+ truth: TruthLabel;
9
+ evidence: ProductEvidence[];
10
+ children?: SoftwareMapNode[];
11
+ }
12
+ export interface SoftwareMap {
13
+ root: string;
14
+ tree: SoftwareMapNode;
15
+ limitations: string[];
16
+ }
17
+ export declare function buildSoftwareMap(rootInput: string, maxFileSizeBytes?: number): Promise<SoftwareMap>;
@@ -0,0 +1,95 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { DEFAULT_MAX_FILE_SIZE_BYTES } from "../../constants.js";
4
+ import { detectProject } from "../../detectors/project.js";
5
+ import { detectMonorepo } from "../../detectors/monorepo.js";
6
+ import { resolveRepoRoot } from "../../utils/path.js";
7
+ function nodeId(prefix, p) {
8
+ return `${prefix}:${p.replace(/[^\w./-]/g, "_")}`;
9
+ }
10
+ async function topLevelEntries(root) {
11
+ try {
12
+ const entries = await fs.readdir(root, { withFileTypes: true });
13
+ return entries
14
+ .filter((e) => e.isDirectory() && !e.name.startsWith("."))
15
+ .map((e) => e.name)
16
+ .sort();
17
+ }
18
+ catch {
19
+ return [];
20
+ }
21
+ }
22
+ export async function buildSoftwareMap(rootInput, maxFileSizeBytes = DEFAULT_MAX_FILE_SIZE_BYTES) {
23
+ const root = resolveRepoRoot(rootInput);
24
+ const detection = await detectProject(root, maxFileSizeBytes);
25
+ const relativePaths = detection.discovery.files.map((f) => f.relativePath.replace(/\\/g, "/"));
26
+ let rootWorkspaces;
27
+ const rootPkgPath = path.join(root, "package.json");
28
+ try {
29
+ const raw = await fs.readFile(rootPkgPath, "utf8");
30
+ rootWorkspaces = JSON.parse(raw).workspaces;
31
+ }
32
+ catch {
33
+ rootWorkspaces = undefined;
34
+ }
35
+ const monorepo = detectMonorepo({
36
+ relativePaths,
37
+ packageJsonWorkspaces: rootWorkspaces,
38
+ }).monorepo;
39
+ const limitations = [
40
+ "Map is structural (directories/packages), not a full module dependency graph.",
41
+ `Monorepo signal: ${monorepo}`,
42
+ ];
43
+ const packageJsonPaths = relativePaths.filter((p) => p.endsWith("package.json"));
44
+ const packageNodes = packageJsonPaths.map((p) => ({
45
+ id: nodeId("pkg", p),
46
+ kind: p === "package.json" ? "root" : "package",
47
+ label: path.dirname(p) === "." ? "root package" : path.dirname(p),
48
+ path: p,
49
+ truth: "VERIFIED",
50
+ evidence: [{ path: p }],
51
+ }));
52
+ const dirs = await topLevelEntries(root);
53
+ const dirNodes = dirs.map((name) => {
54
+ let kind = "directory";
55
+ if (name === "docs")
56
+ kind = "docs";
57
+ if (name === "tests" || name === "test")
58
+ kind = "tests";
59
+ return {
60
+ id: nodeId("dir", name),
61
+ kind,
62
+ label: name,
63
+ path: name,
64
+ truth: "VERIFIED",
65
+ evidence: [{ path: name, excerpt: "top-level directory listing" }],
66
+ };
67
+ });
68
+ const tree = {
69
+ id: nodeId("root", root),
70
+ kind: "root",
71
+ label: path.basename(root) || root,
72
+ path: ".",
73
+ truth: "VERIFIED",
74
+ evidence: [{ path: ".", excerpt: "detectProject root" }],
75
+ children: [
76
+ {
77
+ id: "packages-section",
78
+ kind: "workspace",
79
+ label: "packages",
80
+ truth: packageNodes.length ? "VERIFIED" : "UNKNOWN",
81
+ evidence: packageNodes.slice(0, 3).map((n) => ({ path: n.path ?? "." })),
82
+ children: packageNodes,
83
+ },
84
+ {
85
+ id: "directories-section",
86
+ kind: "directory",
87
+ label: "top-level directories",
88
+ truth: dirNodes.length ? "VERIFIED" : "PARTIAL",
89
+ evidence: dirNodes.slice(0, 5).map((n) => ({ path: n.path ?? n.label })),
90
+ children: dirNodes,
91
+ },
92
+ ],
93
+ };
94
+ return { root, tree, limitations };
95
+ }
@@ -0,0 +1,16 @@
1
+ import type { ProductEvidence, TruthLabel } from "../truth.js";
2
+ export interface MemoryHit {
3
+ source: "brain" | "decisions" | "change-ledger" | "dna";
4
+ id: string;
5
+ title: string;
6
+ excerpt: string;
7
+ truth: TruthLabel;
8
+ evidence: ProductEvidence[];
9
+ }
10
+ export interface InstitutionalMemoryQueryResult {
11
+ root: string;
12
+ query: string;
13
+ hits: MemoryHit[];
14
+ limitations: string[];
15
+ }
16
+ export declare function queryMemory(rootInput: string, query: string): Promise<InstitutionalMemoryQueryResult>;
@@ -0,0 +1,87 @@
1
+ import { loadLatestBrain, searchBrain } from "../../core/brain-cli/service.js";
2
+ import { resolveRepoRoot } from "../../utils/path.js";
3
+ import { buildProjectDna } from "../dna/build.js";
4
+ import { loadDecisionLedger } from "../decisions/ledger.js";
5
+ import { readChangeLedger } from "../ledger/change-ledger.js";
6
+ export async function queryMemory(rootInput, query) {
7
+ const root = resolveRepoRoot(rootInput);
8
+ const q = query.trim();
9
+ const limitations = [
10
+ "Institutional memory merges repo-local Brain, ADR/decision ledger, change ledger, and DNA — not a vector DB.",
11
+ "Ranking is substring match with simple scoring — not semantic search.",
12
+ "Cross-repo memory is not supported; query is scoped to the resolved catalog root.",
13
+ ];
14
+ if (!q) {
15
+ return { root, query: q, hits: [], limitations: [...limitations, "Empty query — no hits."] };
16
+ }
17
+ const hits = [];
18
+ const qLower = q.toLowerCase();
19
+ const brain = await loadLatestBrain(root);
20
+ if (brain) {
21
+ for (const bh of searchBrain(brain, q).slice(0, 15)) {
22
+ hits.push({
23
+ source: "brain",
24
+ id: bh.id,
25
+ title: bh.kind,
26
+ excerpt: bh.text.slice(0, 280),
27
+ truth: "VERIFIED",
28
+ evidence: [{ path: ".agentdoctor/project-brain", excerpt: bh.text.slice(0, 120) }],
29
+ });
30
+ }
31
+ }
32
+ else {
33
+ limitations.push("No Project Brain snapshot — brain hits skipped.");
34
+ }
35
+ const decisions = await loadDecisionLedger(root);
36
+ for (const d of decisions.decisions) {
37
+ const hay = `${d.title} ${d.bodyExcerpt ?? ""}`.toLowerCase();
38
+ if (!hay.includes(qLower))
39
+ continue;
40
+ hits.push({
41
+ source: "decisions",
42
+ id: d.id,
43
+ title: d.title,
44
+ excerpt: (d.bodyExcerpt ?? d.title).slice(0, 280),
45
+ truth: d.truth,
46
+ evidence: d.evidence,
47
+ });
48
+ }
49
+ const changeLedger = await readChangeLedger(root);
50
+ for (const entry of changeLedger.entries) {
51
+ const hay = `${entry.task ?? ""} ${entry.plan ?? ""} ${entry.note ?? ""} ${(entry.files ?? []).join(" ")}`.toLowerCase();
52
+ if (!hay.includes(qLower))
53
+ continue;
54
+ hits.push({
55
+ source: "change-ledger",
56
+ id: entry.id,
57
+ title: entry.task ?? entry.id,
58
+ excerpt: (entry.note ?? entry.plan ?? entry.task ?? "").slice(0, 280),
59
+ truth: "VERIFIED",
60
+ evidence: [{ path: ".agentdoctor/change-ledger/entries.jsonl", excerpt: entry.id }],
61
+ });
62
+ }
63
+ const dna = await buildProjectDna(root);
64
+ const dnaHay = JSON.stringify({
65
+ name: dna.name,
66
+ frameworks: dna.frameworks,
67
+ languages: dna.languages,
68
+ services: dna.services,
69
+ }).toLowerCase();
70
+ if (dnaHay.includes(qLower)) {
71
+ hits.push({
72
+ source: "dna",
73
+ id: dna.fingerprint,
74
+ title: `Project DNA: ${dna.name}`,
75
+ excerpt: `Languages: ${dna.languages.join(", ")}; frameworks: ${dna.frameworks.join(", ")}`,
76
+ truth: dna.truth,
77
+ evidence: [{ path: ".agentdoctor/dna/project-dna.json", excerpt: dna.fingerprint }],
78
+ });
79
+ }
80
+ hits.sort((a, b) => b.excerpt.length - a.excerpt.length);
81
+ return {
82
+ root,
83
+ query: q,
84
+ hits: hits.slice(0, 40),
85
+ limitations,
86
+ };
87
+ }
@@ -0,0 +1,20 @@
1
+ import type { TruthLabel } from "../truth.js";
2
+ export interface IncidentTimelineItem {
3
+ id: string;
4
+ hypothesis: string;
5
+ truth: TruthLabel;
6
+ relatedPaths: string[];
7
+ method: string;
8
+ }
9
+ export interface IncidentReport {
10
+ root: string;
11
+ timeline: IncidentTimelineItem[];
12
+ limitations: string[];
13
+ }
14
+ export declare function buildIncidentHypotheses(rootInput: string, options?: {
15
+ changedFiles?: string[];
16
+ gitHotspots?: Array<{
17
+ path: string;
18
+ commits: number;
19
+ }>;
20
+ }): Promise<IncidentReport>;
@@ -0,0 +1,61 @@
1
+ import { analyzeGitIntelligence } from "../../intelligence/git/analyze.js";
2
+ import { resolveRepoRoot } from "../../utils/path.js";
3
+ function normalizePaths(paths) {
4
+ return [...new Set(paths.map((p) => p.replace(/\\/g, "/")))].sort();
5
+ }
6
+ export async function buildIncidentHypotheses(rootInput, options) {
7
+ const root = resolveRepoRoot(rootInput);
8
+ const limitations = [
9
+ "Timeline items are hypotheses for investigation, not verified incident root cause.",
10
+ "VERIFIED items require explicit changedFiles input or git hotspot file paths.",
11
+ ];
12
+ const changed = normalizePaths(options?.changedFiles ?? []);
13
+ const timeline = [];
14
+ if (changed.length) {
15
+ timeline.push({
16
+ id: "changed-files",
17
+ hypothesis: "Recent changes concentrated in supplied changedFiles list",
18
+ truth: "VERIFIED",
19
+ relatedPaths: changed,
20
+ method: "caller-supplied changedFiles",
21
+ });
22
+ }
23
+ let hotspots = options?.gitHotspots;
24
+ if (!hotspots) {
25
+ const git = await analyzeGitIntelligence(root);
26
+ if (git.gitAvailable) {
27
+ hotspots = git.hotspots.map((h) => ({ path: h.path, commits: h.commits }));
28
+ }
29
+ else {
30
+ limitations.push("Git unavailable; hotspot hypotheses skipped.");
31
+ }
32
+ }
33
+ if (hotspots?.length) {
34
+ const top = [...hotspots].sort((a, b) => b.commits - a.commits).slice(0, 5);
35
+ for (const h of top) {
36
+ const overlapsChange = changed.some((c) => c === h.path || h.path.endsWith(c) || c.endsWith(h.path));
37
+ timeline.push({
38
+ id: `hotspot-${h.path.replace(/[^\w]/g, "_")}`,
39
+ hypothesis: "Frequent historical churn (git hotspot) may relate to instability",
40
+ truth: overlapsChange ? "VERIFIED" : "INFERRED",
41
+ relatedPaths: [h.path.replace(/\\/g, "/")],
42
+ method: `git log file appearances (commits=${h.commits})`,
43
+ });
44
+ }
45
+ }
46
+ if (changed.length && hotspots?.length) {
47
+ const hotSet = new Set(hotspots.map((h) => h.path.replace(/\\/g, "/")));
48
+ const overlap = changed.filter((c) => hotSet.has(c));
49
+ if (overlap.length) {
50
+ timeline.push({
51
+ id: "change-hotspot-overlap",
52
+ hypothesis: "Changed files overlap historical hotspots",
53
+ truth: "VERIFIED",
54
+ relatedPaths: overlap,
55
+ method: "intersection(changedFiles, gitHotspots)",
56
+ });
57
+ }
58
+ }
59
+ timeline.sort((a, b) => a.id.localeCompare(b.id));
60
+ return { root, timeline, limitations };
61
+ }
@@ -0,0 +1,15 @@
1
+ import type { ProductEvidence, TruthLabel } from "../truth.js";
2
+ export interface InfraArtifact {
3
+ kind: "docker" | "kubernetes" | "terraform" | "ci" | "compose-service" | "unknown";
4
+ path: string;
5
+ label: string;
6
+ truth: TruthLabel;
7
+ evidence: ProductEvidence[];
8
+ }
9
+ export interface InfraReport {
10
+ root: string;
11
+ artifacts: InfraArtifact[];
12
+ services: string[];
13
+ limitations: string[];
14
+ }
15
+ export declare function analyzeInfra(rootInput: string, maxFileSizeBytes?: number): Promise<InfraReport>;
@@ -0,0 +1,112 @@
1
+ import path from "node:path";
2
+ import { DEFAULT_MAX_FILE_SIZE_BYTES } from "../../constants.js";
3
+ import { detectProject } from "../../detectors/project.js";
4
+ import { readTextFile } from "../../utils/fs.js";
5
+ import { resolveRepoRoot } from "../../utils/path.js";
6
+ function classifyPath(relativePath) {
7
+ const norm = relativePath.replace(/\\/g, "/");
8
+ const base = norm.split("/").pop()?.toLowerCase() ?? "";
9
+ if (base === "dockerfile" || base.startsWith("dockerfile.")) {
10
+ return {
11
+ kind: "docker",
12
+ path: norm,
13
+ label: "Dockerfile",
14
+ truth: "VERIFIED",
15
+ evidence: [{ path: norm }],
16
+ };
17
+ }
18
+ if (base === "docker-compose.yml" || base === "docker-compose.yaml" || base === "compose.yml") {
19
+ return {
20
+ kind: "docker",
21
+ path: norm,
22
+ label: "Docker Compose",
23
+ truth: "VERIFIED",
24
+ evidence: [{ path: norm }],
25
+ };
26
+ }
27
+ if (norm.includes(".github/workflows/") && (base.endsWith(".yml") || base.endsWith(".yaml"))) {
28
+ return {
29
+ kind: "ci",
30
+ path: norm,
31
+ label: "GitHub Actions workflow",
32
+ truth: "VERIFIED",
33
+ evidence: [{ path: norm }],
34
+ };
35
+ }
36
+ if (base.endsWith(".tf")) {
37
+ return {
38
+ kind: "terraform",
39
+ path: norm,
40
+ label: "Terraform",
41
+ truth: "VERIFIED",
42
+ evidence: [{ path: norm }],
43
+ };
44
+ }
45
+ if (norm.includes("kubernetes/") || base.includes("deployment") || base.includes("service")) {
46
+ if (base.endsWith(".yaml") || base.endsWith(".yml")) {
47
+ return {
48
+ kind: "kubernetes",
49
+ path: norm,
50
+ label: "Kubernetes manifest (heuristic)",
51
+ truth: "INFERRED",
52
+ evidence: [{ path: norm }],
53
+ };
54
+ }
55
+ }
56
+ return null;
57
+ }
58
+ function parseComposeServices(content, relativePath) {
59
+ const services = [];
60
+ let inServices = false;
61
+ for (const line of content.split(/\r?\n/)) {
62
+ const trimmed = line.trim();
63
+ if (/^services:\s*$/i.test(trimmed)) {
64
+ inServices = true;
65
+ continue;
66
+ }
67
+ if (inServices) {
68
+ if (/^[a-zA-Z0-9_-]+:\s*$/.test(trimmed) && !trimmed.startsWith("version:")) {
69
+ const name = trimmed.replace(":", "");
70
+ if (name !== "services" && !name.includes(" "))
71
+ services.push(name);
72
+ }
73
+ if (/^[a-zA-Z_]+:/.test(trimmed) &&
74
+ !line.startsWith(" ") &&
75
+ !line.startsWith("\t") &&
76
+ trimmed !== "services:") {
77
+ if (!trimmed.startsWith("services:"))
78
+ inServices = false;
79
+ }
80
+ }
81
+ }
82
+ return services.map((s) => `${s}@${relativePath}`);
83
+ }
84
+ export async function analyzeInfra(rootInput, maxFileSizeBytes = DEFAULT_MAX_FILE_SIZE_BYTES) {
85
+ const root = resolveRepoRoot(rootInput);
86
+ const detection = await detectProject(root, maxFileSizeBytes);
87
+ const limitations = [
88
+ "Compose service names use simple line heuristics, not a full YAML parser.",
89
+ "Kubernetes detection is path/name heuristic.",
90
+ ];
91
+ const artifacts = [];
92
+ const services = [];
93
+ for (const entry of detection.discovery.files) {
94
+ const rel = entry.relativePath.replace(/\\/g, "/");
95
+ const artifact = classifyPath(rel);
96
+ if (artifact)
97
+ artifacts.push(artifact);
98
+ const base = rel.split("/").pop()?.toLowerCase() ?? "";
99
+ if (base === "docker-compose.yml" || base === "docker-compose.yaml" || base === "compose.yml") {
100
+ const text = await readTextFile(path.join(root, rel), maxFileSizeBytes);
101
+ if (text)
102
+ services.push(...parseComposeServices(text, rel));
103
+ }
104
+ }
105
+ artifacts.sort((a, b) => a.path.localeCompare(b.path));
106
+ return {
107
+ root,
108
+ artifacts,
109
+ services: [...new Set(services)].sort(),
110
+ limitations,
111
+ };
112
+ }
@@ -0,0 +1,34 @@
1
+ import type { TruthLabel } from "../truth.js";
2
+ export interface OrgEntityRef {
3
+ id: string;
4
+ name: string;
5
+ description?: string;
6
+ }
7
+ export interface OrgProjectLink {
8
+ id: string;
9
+ name: string;
10
+ /** Absolute or repo-relative root path for this project within the workspace catalog */
11
+ rootPath: string;
12
+ teamIds: string[];
13
+ serviceIds: string[];
14
+ ownerIds: string[];
15
+ }
16
+ export interface OrganizationModel {
17
+ schemaVersion: 1;
18
+ updatedAt: string;
19
+ /** Catalog root this model was loaded from (not shared across repos automatically) */
20
+ catalogRoot: string;
21
+ teams: OrgEntityRef[];
22
+ projects: OrgProjectLink[];
23
+ services: OrgEntityRef[];
24
+ owners: OrgEntityRef[];
25
+ truth: TruthLabel;
26
+ limitations: string[];
27
+ }
28
+ export declare function loadOrganizationModel(rootInput: string): Promise<OrganizationModel>;
29
+ export declare function saveOrganizationModel(rootInput: string, model: Omit<OrganizationModel, "schemaVersion" | "updatedAt" | "catalogRoot" | "truth" | "limitations">): Promise<OrganizationModel>;
30
+ /** Register or update a project link under the catalog root (path must stay within catalogRoot). */
31
+ export declare function linkProjectToOrgModel(rootInput: string, link: Omit<OrgProjectLink, "rootPath"> & {
32
+ rootPath?: string;
33
+ }): Promise<OrganizationModel>;
34
+ export declare function resolveLinkedProjectRoot(model: OrganizationModel, projectId: string): string | null;