@praneeth_54/agentdoctor 2.1.0 → 3.0.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.
Files changed (167) hide show
  1. package/CHANGELOG.md +86 -3
  2. package/README.md +490 -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/context/retrieve.d.ts +2 -1
  7. package/dist/agent/context/retrieve.js +14 -1
  8. package/dist/agent/index.d.ts +2 -0
  9. package/dist/agent/index.js +1 -0
  10. package/dist/agent/loop.d.ts +2 -0
  11. package/dist/agent/loop.js +31 -0
  12. package/dist/agent/roles.d.ts +12 -0
  13. package/dist/agent/roles.js +138 -0
  14. package/dist/agent/runtime.d.ts +21 -5
  15. package/dist/agent/runtime.js +141 -28
  16. package/dist/agent/tools/execute.d.ts +2 -0
  17. package/dist/agent/tools/execute.js +49 -43
  18. package/dist/agent/tools/write.d.ts +1 -1
  19. package/dist/agent/tools/write.js +18 -4
  20. package/dist/ai/config.js +1 -0
  21. package/dist/ai/providers/adversarial-local.d.ts +7 -0
  22. package/dist/ai/providers/adversarial-local.js +146 -0
  23. package/dist/ai/redact.d.ts +1 -0
  24. package/dist/ai/redact.js +47 -7
  25. package/dist/ai/types.d.ts +1 -1
  26. package/dist/cli/commands/agent.d.ts +2 -0
  27. package/dist/cli/commands/agent.js +32 -11
  28. package/dist/cli/commands/architecture.js +7 -2
  29. package/dist/cli/commands/brain.js +7 -2
  30. package/dist/cli/commands/chat.js +15 -7
  31. package/dist/cli/commands/complete.js +13 -3
  32. package/dist/cli/commands/fix.js +6 -0
  33. package/dist/cli/commands/learn.js +7 -2
  34. package/dist/cli/commands/mcp.js +7 -3
  35. package/dist/cli/commands/platform.js +7 -2
  36. package/dist/cli/commands/policy-graph-run.js +7 -0
  37. package/dist/cli/commands/product.d.ts +19 -0
  38. package/dist/cli/commands/product.js +157 -0
  39. package/dist/cli/commands/scan.js +6 -0
  40. package/dist/cli/commands/start.d.ts +15 -0
  41. package/dist/cli/commands/start.js +86 -0
  42. package/dist/cli/commands/v2.js +61 -11
  43. package/dist/cli/commands/verify.js +6 -0
  44. package/dist/cli/program.js +141 -0
  45. package/dist/cli/safe-root.d.ts +17 -0
  46. package/dist/cli/safe-root.js +32 -0
  47. package/dist/constants.d.ts +1 -1
  48. package/dist/constants.js +1 -1
  49. package/dist/core/brain-cli/service.js +5 -2
  50. package/dist/core/monorepo/detect.js +10 -0
  51. package/dist/core/secrets/scan.js +9 -0
  52. package/dist/core/understanding/brain/storage/store.d.ts +4 -0
  53. package/dist/core/understanding/brain/storage/store.js +22 -2
  54. package/dist/dashboard/page.d.ts +2 -0
  55. package/dist/dashboard/page.js +53 -0
  56. package/dist/dashboard/server.js +117 -140
  57. package/dist/dashboard/ui/client.d.ts +2 -0
  58. package/dist/dashboard/ui/client.js +2 -0
  59. package/dist/dashboard/ui/styles.d.ts +2 -0
  60. package/dist/dashboard/ui/styles.js +138 -0
  61. package/dist/discovery/files.js +13 -0
  62. package/dist/index.d.ts +4 -0
  63. package/dist/index.js +2 -0
  64. package/dist/intelligence/graph/build.js +46 -38
  65. package/dist/intelligence/graph/incremental.d.ts +2 -0
  66. package/dist/intelligence/graph/incremental.js +9 -0
  67. package/dist/intelligence/resolve/imports.js +1 -1
  68. package/dist/languages/dart.d.ts +10 -0
  69. package/dist/languages/dart.js +99 -0
  70. package/dist/languages/go.d.ts +13 -7
  71. package/dist/languages/go.js +84 -24
  72. package/dist/languages/index.d.ts +5 -1
  73. package/dist/languages/index.js +13 -36
  74. package/dist/languages/java.d.ts +10 -0
  75. package/dist/languages/java.js +80 -0
  76. package/dist/languages/kotlin.d.ts +10 -0
  77. package/dist/languages/kotlin.js +85 -0
  78. package/dist/languages/rust.d.ts +10 -0
  79. package/dist/languages/rust.js +93 -0
  80. package/dist/languages/types.d.ts +4 -3
  81. package/dist/mcp/agent/registry.d.ts +1 -1
  82. package/dist/mcp/agent/registry.js +109 -23
  83. package/dist/mcp/intelligence/handlers.d.ts +3 -0
  84. package/dist/mcp/intelligence/handlers.js +28 -0
  85. package/dist/mcp/intelligence/path-safety.d.ts +6 -1
  86. package/dist/mcp/intelligence/path-safety.js +55 -2
  87. package/dist/mcp/intelligence/registry.d.ts +1 -1
  88. package/dist/mcp/intelligence/registry.js +30 -1
  89. package/dist/platform/graph/build.js +9 -0
  90. package/dist/product/api/doctor.d.ts +14 -0
  91. package/dist/product/api/doctor.js +185 -0
  92. package/dist/product/api/openapi.d.ts +7 -0
  93. package/dist/product/api/openapi.js +122 -0
  94. package/dist/product/approval/model.d.ts +30 -0
  95. package/dist/product/approval/model.js +64 -0
  96. package/dist/product/approval/session.d.ts +49 -0
  97. package/dist/product/approval/session.js +134 -0
  98. package/dist/product/database/doctor.d.ts +30 -0
  99. package/dist/product/database/doctor.js +184 -0
  100. package/dist/product/decisions/ledger.d.ts +36 -0
  101. package/dist/product/decisions/ledger.js +141 -0
  102. package/dist/product/deps/analyze.d.ts +46 -0
  103. package/dist/product/deps/analyze.js +137 -0
  104. package/dist/product/deps/lockfiles.d.ts +25 -0
  105. package/dist/product/deps/lockfiles.js +200 -0
  106. package/dist/product/discovery/roots.d.ts +52 -0
  107. package/dist/product/discovery/roots.js +231 -0
  108. package/dist/product/dna/build.d.ts +50 -0
  109. package/dist/product/dna/build.js +255 -0
  110. package/dist/product/eval/lab.d.ts +17 -0
  111. package/dist/product/eval/lab.js +218 -0
  112. package/dist/product/events/doctor.d.ts +21 -0
  113. package/dist/product/events/doctor.js +147 -0
  114. package/dist/product/evidence-scan.d.ts +12 -0
  115. package/dist/product/evidence-scan.js +46 -0
  116. package/dist/product/evolution/timeline.d.ts +29 -0
  117. package/dist/product/evolution/timeline.js +123 -0
  118. package/dist/product/features/intelligence.d.ts +23 -0
  119. package/dist/product/features/intelligence.js +158 -0
  120. package/dist/product/forensic/mode.d.ts +25 -0
  121. package/dist/product/forensic/mode.js +70 -0
  122. package/dist/product/graph/enrich-languages.d.ts +20 -0
  123. package/dist/product/graph/enrich-languages.js +168 -0
  124. package/dist/product/health/code-health.d.ts +20 -0
  125. package/dist/product/health/code-health.js +149 -0
  126. package/dist/product/index.d.ts +72 -0
  127. package/dist/product/index.js +36 -0
  128. package/dist/product/ledger/change-ledger.d.ts +23 -0
  129. package/dist/product/ledger/change-ledger.js +64 -0
  130. package/dist/product/map/software-map.d.ts +17 -0
  131. package/dist/product/map/software-map.js +96 -0
  132. package/dist/product/memory/institutional.d.ts +16 -0
  133. package/dist/product/memory/institutional.js +92 -0
  134. package/dist/product/ops/incident.d.ts +20 -0
  135. package/dist/product/ops/incident.js +61 -0
  136. package/dist/product/ops/infra.d.ts +15 -0
  137. package/dist/product/ops/infra.js +112 -0
  138. package/dist/product/org/model.d.ts +34 -0
  139. package/dist/product/org/model.js +195 -0
  140. package/dist/product/privacy/doctor.d.ts +13 -0
  141. package/dist/product/privacy/doctor.js +90 -0
  142. package/dist/product/requirements/trace.d.ts +21 -0
  143. package/dist/product/requirements/trace.js +166 -0
  144. package/dist/product/search/index.d.ts +29 -0
  145. package/dist/product/search/index.js +116 -0
  146. package/dist/product/search/software-search.d.ts +19 -0
  147. package/dist/product/search/software-search.js +105 -0
  148. package/dist/product/security/doctor.d.ts +23 -0
  149. package/dist/product/security/doctor.js +124 -0
  150. package/dist/product/self/diagnose.d.ts +15 -0
  151. package/dist/product/self/diagnose.js +82 -0
  152. package/dist/product/techdebt/roadmap.d.ts +20 -0
  153. package/dist/product/techdebt/roadmap.js +118 -0
  154. package/dist/product/testbrain/analyze.d.ts +29 -0
  155. package/dist/product/testbrain/analyze.js +129 -0
  156. package/dist/product/truth.d.ts +12 -0
  157. package/dist/product/truth.js +30 -0
  158. package/dist/product/twin/digital-twin.d.ts +23 -0
  159. package/dist/product/twin/digital-twin.js +52 -0
  160. package/dist/product/twin/store.d.ts +21 -0
  161. package/dist/product/twin/store.js +78 -0
  162. package/dist/product/whatif/engine.d.ts +28 -0
  163. package/dist/product/whatif/engine.js +91 -0
  164. package/dist/project/ownership.d.ts +65 -0
  165. package/dist/project/ownership.js +169 -0
  166. package/dist/utils/fs.js +10 -2
  167. package/package.json +1 -1
@@ -0,0 +1,20 @@
1
+ import type { GitIntelligenceReport } from "../../intelligence/git/analyze.js";
2
+ import type { ArchitectureCheckResult } from "../../architecture/contract.js";
3
+ import type { ProductEvidence, TruthLabel } from "../truth.js";
4
+ export interface CodeHealthIndicator {
5
+ id: string;
6
+ label: string;
7
+ value: number | string;
8
+ truth: TruthLabel;
9
+ evidence: ProductEvidence[];
10
+ }
11
+ export interface CodeHealthReport {
12
+ root: string;
13
+ indicators: CodeHealthIndicator[];
14
+ limitations: string[];
15
+ }
16
+ export declare function analyzeCodeHealth(rootInput: string, options?: {
17
+ gitReport?: GitIntelligenceReport;
18
+ architectureCheck?: ArchitectureCheckResult;
19
+ maxFileSizeBytes?: number;
20
+ }): Promise<CodeHealthReport>;
@@ -0,0 +1,149 @@
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 isTestPath(relativePath) {
7
+ const p = relativePath.toLowerCase();
8
+ return (p.includes(".test.") ||
9
+ p.includes(".spec.") ||
10
+ p.startsWith("tests/") ||
11
+ p.startsWith("test/") ||
12
+ p.includes("__tests__/"));
13
+ }
14
+ function isSourcePath(relativePath) {
15
+ const p = relativePath.toLowerCase();
16
+ if (isTestPath(p))
17
+ return false;
18
+ return (p.endsWith(".ts") ||
19
+ p.endsWith(".tsx") ||
20
+ p.endsWith(".js") ||
21
+ p.endsWith(".jsx") ||
22
+ p.endsWith(".py") ||
23
+ p.endsWith(".php") ||
24
+ p.endsWith(".go") ||
25
+ p.endsWith(".rs"));
26
+ }
27
+ async function countTodoFixme(root, files, maxBytes) {
28
+ let todo = 0;
29
+ let fixme = 0;
30
+ const evidence = [];
31
+ const re = /\b(TODO|FIXME)\b/g;
32
+ for (const f of files) {
33
+ const text = await readTextFile(path.join(root, f.relativePath), maxBytes);
34
+ if (!text)
35
+ continue;
36
+ re.lastIndex = 0;
37
+ let m;
38
+ while ((m = re.exec(text)) !== null) {
39
+ if (m[1] === "TODO")
40
+ todo++;
41
+ else
42
+ fixme++;
43
+ if (evidence.length < 8) {
44
+ const line = text.slice(0, m.index).split("\n").length;
45
+ const excerpt = text.split(/\r?\n/)[line - 1]?.trim().slice(0, 100);
46
+ evidence.push({
47
+ path: f.relativePath.replace(/\\/g, "/"),
48
+ line,
49
+ ...(excerpt !== undefined ? { excerpt } : {}),
50
+ });
51
+ }
52
+ }
53
+ }
54
+ return { todo, fixme, evidence };
55
+ }
56
+ export async function analyzeCodeHealth(rootInput, options) {
57
+ const maxFileSizeBytes = options?.maxFileSizeBytes ?? DEFAULT_MAX_FILE_SIZE_BYTES;
58
+ const root = resolveRepoRoot(rootInput);
59
+ const detection = await detectProject(root, maxFileSizeBytes);
60
+ const paths = detection.discovery.files.map((f) => f.relativePath.replace(/\\/g, "/"));
61
+ const limitations = [
62
+ "Scores are descriptive indicators with evidence; not a single authoritative debt score.",
63
+ "Hotspot overlap requires git intelligence input.",
64
+ ];
65
+ const sourceFiles = paths.filter(isSourcePath);
66
+ const testFiles = paths.filter(isTestPath);
67
+ const { todo, fixme, evidence: todoEvidence, } = await countTodoFixme(root, detection.discovery.files, maxFileSizeBytes);
68
+ const indicators = [
69
+ {
70
+ id: "file-count",
71
+ label: "Discovered files",
72
+ value: paths.length,
73
+ truth: "VERIFIED",
74
+ evidence: [{ path: ".", excerpt: `discovery.files.length=${paths.length}` }],
75
+ },
76
+ {
77
+ id: "source-file-count",
78
+ label: "Source-like files",
79
+ value: sourceFiles.length,
80
+ truth: "INFERRED",
81
+ evidence: [{ path: ".", excerpt: `extension/heuristic count=${sourceFiles.length}` }],
82
+ },
83
+ {
84
+ id: "test-file-count",
85
+ label: "Test-like files",
86
+ value: testFiles.length,
87
+ truth: "INFERRED",
88
+ evidence: [{ path: ".", excerpt: `test path heuristic count=${testFiles.length}` }],
89
+ },
90
+ {
91
+ id: "test-ratio",
92
+ label: "Test file ratio (test / (test+source))",
93
+ value: sourceFiles.length + testFiles.length === 0
94
+ ? "n/a"
95
+ : (testFiles.length / (sourceFiles.length + testFiles.length)).toFixed(3),
96
+ truth: "INFERRED",
97
+ evidence: [
98
+ {
99
+ path: ".",
100
+ excerpt: `tests=${testFiles.length}, sources=${sourceFiles.length}`,
101
+ },
102
+ ],
103
+ },
104
+ {
105
+ id: "todo-count",
106
+ label: "TODO markers",
107
+ value: todo,
108
+ truth: "VERIFIED",
109
+ evidence: todoEvidence.filter((e) => e.excerpt?.includes("TODO")),
110
+ },
111
+ {
112
+ id: "fixme-count",
113
+ label: "FIXME markers",
114
+ value: fixme,
115
+ truth: "VERIFIED",
116
+ evidence: todoEvidence.filter((e) => e.excerpt?.includes("FIXME")),
117
+ },
118
+ ];
119
+ if (options?.architectureCheck) {
120
+ indicators.push({
121
+ id: "architecture-violations",
122
+ label: "Architecture contract violations",
123
+ value: options.architectureCheck.violations.length,
124
+ truth: "VERIFIED",
125
+ evidence: options.architectureCheck.violations.slice(0, 5).map((v) => ({
126
+ path: v.fromPath || v.toPath || ".",
127
+ excerpt: `${v.ruleId}: ${v.description}`.slice(0, 120),
128
+ })),
129
+ });
130
+ }
131
+ if (options?.gitReport?.gitAvailable) {
132
+ const hotspotPaths = new Set(options.gitReport.hotspots.map((h) => h.path.replace(/\\/g, "/")));
133
+ const overlap = sourceFiles.filter((p) => hotspotPaths.has(p)).length;
134
+ indicators.push({
135
+ id: "hotspot-source-overlap",
136
+ label: "Source files overlapping git hotspots",
137
+ value: overlap,
138
+ truth: "INFERRED",
139
+ evidence: options.gitReport.hotspots.slice(0, 5).map((h) => ({
140
+ path: h.path,
141
+ excerpt: `commits=${h.commits}`,
142
+ })),
143
+ });
144
+ }
145
+ else {
146
+ limitations.push("Git hotspot overlap skipped (git unavailable or not provided).");
147
+ }
148
+ return { root, indicators, limitations };
149
+ }
@@ -0,0 +1,72 @@
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, classifyBroadUserScanRoot, } 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";
70
+ export { classifyRelativePathOwnership, decideDirectoryTraversal, isNestedRepositoryRoot, isProjectOwnedRelativePath, isUnderNestedRepository, assertProjectOwnedRepoPath, ProjectOwnershipError, OWNERSHIP_BOUNDARY_VERSION, verifiedDecisionTruthMeaning, } from "../project/ownership.js";
71
+ export type { ProjectOwnershipClass, DirectoryTraversalDecision } from "../project/ownership.js";
72
+ export type { DecisionSourceKind } from "./decisions/ledger.js";
@@ -0,0 +1,36 @@
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, classifyBroadUserScanRoot, } 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";
36
+ export { classifyRelativePathOwnership, decideDirectoryTraversal, isNestedRepositoryRoot, isProjectOwnedRelativePath, isUnderNestedRepository, assertProjectOwnedRepoPath, ProjectOwnershipError, OWNERSHIP_BOUNDARY_VERSION, verifiedDecisionTruthMeaning, } from "../project/ownership.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,96 @@
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 { isProjectOwnedRelativePath } from "../../project/ownership.js";
7
+ import { resolveRepoRoot } from "../../utils/path.js";
8
+ function nodeId(prefix, p) {
9
+ return `${prefix}:${p.replace(/[^\w./-]/g, "_")}`;
10
+ }
11
+ async function topLevelEntries(root) {
12
+ try {
13
+ const entries = await fs.readdir(root, { withFileTypes: true });
14
+ return entries
15
+ .filter((e) => e.isDirectory() && !e.name.startsWith(".") && isProjectOwnedRelativePath(e.name))
16
+ .map((e) => e.name)
17
+ .sort();
18
+ }
19
+ catch {
20
+ return [];
21
+ }
22
+ }
23
+ export async function buildSoftwareMap(rootInput, maxFileSizeBytes = DEFAULT_MAX_FILE_SIZE_BYTES) {
24
+ const root = resolveRepoRoot(rootInput);
25
+ const detection = await detectProject(root, maxFileSizeBytes);
26
+ const relativePaths = detection.discovery.files.map((f) => f.relativePath.replace(/\\/g, "/"));
27
+ let rootWorkspaces;
28
+ const rootPkgPath = path.join(root, "package.json");
29
+ try {
30
+ const raw = await fs.readFile(rootPkgPath, "utf8");
31
+ rootWorkspaces = JSON.parse(raw).workspaces;
32
+ }
33
+ catch {
34
+ rootWorkspaces = undefined;
35
+ }
36
+ const monorepo = detectMonorepo({
37
+ relativePaths,
38
+ packageJsonWorkspaces: rootWorkspaces,
39
+ }).monorepo;
40
+ const limitations = [
41
+ "Map is structural (directories/packages), not a full module dependency graph.",
42
+ `Monorepo signal: ${monorepo}`,
43
+ ];
44
+ const packageJsonPaths = relativePaths.filter((p) => p.endsWith("package.json"));
45
+ const packageNodes = packageJsonPaths.map((p) => ({
46
+ id: nodeId("pkg", p),
47
+ kind: p === "package.json" ? "root" : "package",
48
+ label: path.dirname(p) === "." ? "root package" : path.dirname(p),
49
+ path: p,
50
+ truth: "VERIFIED",
51
+ evidence: [{ path: p }],
52
+ }));
53
+ const dirs = await topLevelEntries(root);
54
+ const dirNodes = dirs.map((name) => {
55
+ let kind = "directory";
56
+ if (name === "docs")
57
+ kind = "docs";
58
+ if (name === "tests" || name === "test")
59
+ kind = "tests";
60
+ return {
61
+ id: nodeId("dir", name),
62
+ kind,
63
+ label: name,
64
+ path: name,
65
+ truth: "VERIFIED",
66
+ evidence: [{ path: name, excerpt: "top-level directory listing" }],
67
+ };
68
+ });
69
+ const tree = {
70
+ id: nodeId("root", root),
71
+ kind: "root",
72
+ label: path.basename(root) || root,
73
+ path: ".",
74
+ truth: "VERIFIED",
75
+ evidence: [{ path: ".", excerpt: "detectProject root" }],
76
+ children: [
77
+ {
78
+ id: "packages-section",
79
+ kind: "workspace",
80
+ label: "packages",
81
+ truth: packageNodes.length ? "VERIFIED" : "UNKNOWN",
82
+ evidence: packageNodes.slice(0, 3).map((n) => ({ path: n.path ?? "." })),
83
+ children: packageNodes,
84
+ },
85
+ {
86
+ id: "directories-section",
87
+ kind: "directory",
88
+ label: "top-level directories",
89
+ truth: dirNodes.length ? "VERIFIED" : "PARTIAL",
90
+ evidence: dirNodes.slice(0, 5).map((n) => ({ path: n.path ?? n.label })),
91
+ children: dirNodes,
92
+ },
93
+ ],
94
+ };
95
+ return { root, tree, limitations };
96
+ }
@@ -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,92 @@
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
+ if (bh.text.includes(".private/") ||
23
+ bh.text.includes("AgentDoctorOS/") ||
24
+ bh.text.includes("oss-validation/")) {
25
+ continue;
26
+ }
27
+ hits.push({
28
+ source: "brain",
29
+ id: bh.id,
30
+ title: bh.kind,
31
+ excerpt: bh.text.slice(0, 280),
32
+ truth: "VERIFIED",
33
+ evidence: [{ path: ".agentdoctor/project-brain", excerpt: bh.text.slice(0, 120) }],
34
+ });
35
+ }
36
+ }
37
+ else {
38
+ limitations.push("No Project Brain snapshot — brain hits skipped.");
39
+ }
40
+ const decisions = await loadDecisionLedger(root);
41
+ for (const d of decisions.decisions) {
42
+ const hay = `${d.title} ${d.bodyExcerpt ?? ""}`.toLowerCase();
43
+ if (!hay.includes(qLower))
44
+ continue;
45
+ hits.push({
46
+ source: "decisions",
47
+ id: d.id,
48
+ title: d.title,
49
+ excerpt: (d.bodyExcerpt ?? d.title).slice(0, 280),
50
+ truth: d.truth,
51
+ evidence: d.evidence,
52
+ });
53
+ }
54
+ const changeLedger = await readChangeLedger(root);
55
+ for (const entry of changeLedger.entries) {
56
+ const hay = `${entry.task ?? ""} ${entry.plan ?? ""} ${entry.note ?? ""} ${(entry.files ?? []).join(" ")}`.toLowerCase();
57
+ if (!hay.includes(qLower))
58
+ continue;
59
+ hits.push({
60
+ source: "change-ledger",
61
+ id: entry.id,
62
+ title: entry.task ?? entry.id,
63
+ excerpt: (entry.note ?? entry.plan ?? entry.task ?? "").slice(0, 280),
64
+ truth: "VERIFIED",
65
+ evidence: [{ path: ".agentdoctor/change-ledger/entries.jsonl", excerpt: entry.id }],
66
+ });
67
+ }
68
+ const dna = await buildProjectDna(root);
69
+ const dnaHay = JSON.stringify({
70
+ name: dna.name,
71
+ frameworks: dna.frameworks,
72
+ languages: dna.languages,
73
+ services: dna.services,
74
+ }).toLowerCase();
75
+ if (dnaHay.includes(qLower)) {
76
+ hits.push({
77
+ source: "dna",
78
+ id: dna.fingerprint,
79
+ title: `Project DNA: ${dna.name}`,
80
+ excerpt: `Languages: ${dna.languages.join(", ")}; frameworks: ${dna.frameworks.join(", ")}`,
81
+ truth: dna.truth,
82
+ evidence: [{ path: ".agentdoctor/dna/project-dna.json", excerpt: dna.fingerprint }],
83
+ });
84
+ }
85
+ hits.sort((a, b) => b.excerpt.length - a.excerpt.length);
86
+ return {
87
+ root,
88
+ query: q,
89
+ hits: hits.slice(0, 40),
90
+ limitations,
91
+ };
92
+ }
@@ -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>;