@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,65 @@
1
+ /**
2
+ * Semantic project ownership — filesystem containment alone is not ownership.
3
+ *
4
+ * VERIFIED file evidence still means "we read this path under the scan root";
5
+ * ownership classification decides whether that path may feed current-project
6
+ * intelligence (DNA, decisions, search corpus, etc.).
7
+ */
8
+ export type ProjectOwnershipClass = "project_owned" | "nested_repository" | "private_workspace" | "internal_docs" | "fixture_tree" | "validation_checkout" | "generated" | "unknown";
9
+ /** Top-level directory names that are never current-project source by default. */
10
+ export declare const NON_OWNED_TOP_LEVEL_DIRECTORIES: Set<string>;
11
+ /** Relative path prefixes (posix) treated as non-owned validation/fixture trees. */
12
+ export declare const NON_OWNED_PATH_PREFIXES: readonly ["validation/real-world/repositories/checkouts/", "fixtures/nested-repos/"];
13
+ /** Bump when ownership/traversal rules change so cached twins/brain consumers invalidate. */
14
+ export declare const OWNERSHIP_BOUNDARY_VERSION = 4;
15
+ export declare class ProjectOwnershipError extends Error {
16
+ readonly code = "PROJECT_OWNERSHIP";
17
+ readonly ownership: ProjectOwnershipClass;
18
+ constructor(ownership: ProjectOwnershipClass, message?: string);
19
+ }
20
+ export interface DirectoryTraversalDecision {
21
+ traverse: boolean;
22
+ ownership: ProjectOwnershipClass;
23
+ reason: string;
24
+ }
25
+ /**
26
+ * Classify a relative path for current-project ownership (no I/O).
27
+ * Nested git detection is handled separately during traversal.
28
+ */
29
+ export declare function classifyRelativePathOwnership(relativePath: string): ProjectOwnershipClass;
30
+ export declare function isProjectOwnedRelativePath(relativePath: string): boolean;
31
+ /**
32
+ * True when `dir` is a nested VCS root (has `.git` file or directory) and is
33
+ * not the canonical project root itself.
34
+ */
35
+ export declare function isNestedRepositoryRoot(projectRootInput: string, dirAbsolute: string): Promise<boolean>;
36
+ /**
37
+ * Decide whether discovery should descend into a directory under the project root.
38
+ */
39
+ export declare function decideDirectoryTraversal(options: {
40
+ projectRoot: string;
41
+ absoluteDir: string;
42
+ relativeDir: string;
43
+ }): Promise<DirectoryTraversalDecision>;
44
+ /**
45
+ * True when any ancestor directory under `projectRoot` (excluding the root) is a nested VCS root.
46
+ * Used for path-targeted access (agent/MCP) where discovery traversal never ran.
47
+ */
48
+ export declare function isUnderNestedRepository(projectRootInput: string, absolutePath: string): Promise<boolean>;
49
+ /**
50
+ * Assert a user-supplied path is both inside the repo root AND project-owned.
51
+ * Containment alone is not ownership (.private, AgentDoctorOS, nested repos, fixtures).
52
+ *
53
+ * Ownership is classified from the realpath-relative path under the real project root
54
+ * so symlink aliases (e.g. link-to-private → .private) cannot launder ownership.
55
+ * Lexical hints are also rejected when they themselves name a non-owned tree.
56
+ */
57
+ export declare function assertProjectOwnedRepoPath(projectRootInput: string, absolutePath: string, relativeHint?: string): Promise<{
58
+ absolutePath: string;
59
+ relativePath: string;
60
+ ownership: ProjectOwnershipClass;
61
+ }>;
62
+ /**
63
+ * Human-readable meaning for decision truth labels in product UI.
64
+ */
65
+ export declare function verifiedDecisionTruthMeaning(): string;
@@ -0,0 +1,169 @@
1
+ import path from "node:path";
2
+ import fs from "node:fs";
3
+ import { pathExists } from "../utils/fs.js";
4
+ import { resolveRepoRoot, toPosixRelative } from "../utils/path.js";
5
+ /** Top-level directory names that are never current-project source by default. */
6
+ export const NON_OWNED_TOP_LEVEL_DIRECTORIES = new Set([".private", "AgentDoctorOS"]);
7
+ /** Relative path prefixes (posix) treated as non-owned validation/fixture trees. */
8
+ export const NON_OWNED_PATH_PREFIXES = [
9
+ "validation/real-world/repositories/checkouts/",
10
+ "fixtures/nested-repos/",
11
+ ];
12
+ /** Bump when ownership/traversal rules change so cached twins/brain consumers invalidate. */
13
+ export const OWNERSHIP_BOUNDARY_VERSION = 4;
14
+ export class ProjectOwnershipError extends Error {
15
+ code = "PROJECT_OWNERSHIP";
16
+ ownership;
17
+ constructor(ownership, message) {
18
+ super(message ?? `path is outside project ownership (${ownership})`);
19
+ this.name = "ProjectOwnershipError";
20
+ this.ownership = ownership;
21
+ }
22
+ }
23
+ function firstSegment(relativePosix) {
24
+ const norm = relativePosix.replace(/\\/g, "/").replace(/^\.\//, "");
25
+ if (!norm || norm === ".")
26
+ return "";
27
+ return norm.split("/")[0] ?? "";
28
+ }
29
+ /**
30
+ * Classify a relative path for current-project ownership (no I/O).
31
+ * Nested git detection is handled separately during traversal.
32
+ */
33
+ export function classifyRelativePathOwnership(relativePath) {
34
+ const norm = relativePath.replace(/\\/g, "/").replace(/^\.\//, "");
35
+ if (!norm || norm === ".")
36
+ return "project_owned";
37
+ const top = firstSegment(norm);
38
+ if (top === ".private")
39
+ return "private_workspace";
40
+ if (top === "AgentDoctorOS")
41
+ return "internal_docs";
42
+ if (top === "fixtures")
43
+ return "fixture_tree";
44
+ for (const prefix of NON_OWNED_PATH_PREFIXES) {
45
+ if (norm === prefix.slice(0, -1) || norm.startsWith(prefix)) {
46
+ return "validation_checkout";
47
+ }
48
+ }
49
+ if (norm.startsWith("validation/") && norm.includes("/checkouts/")) {
50
+ return "validation_checkout";
51
+ }
52
+ return "project_owned";
53
+ }
54
+ export function isProjectOwnedRelativePath(relativePath) {
55
+ return classifyRelativePathOwnership(relativePath) === "project_owned";
56
+ }
57
+ /**
58
+ * True when `dir` is a nested VCS root (has `.git` file or directory) and is
59
+ * not the canonical project root itself.
60
+ */
61
+ export async function isNestedRepositoryRoot(projectRootInput, dirAbsolute) {
62
+ const root = resolveRepoRoot(projectRootInput);
63
+ const abs = path.resolve(dirAbsolute);
64
+ if (abs === root)
65
+ return false;
66
+ return pathExists(path.join(abs, ".git"));
67
+ }
68
+ /**
69
+ * Decide whether discovery should descend into a directory under the project root.
70
+ */
71
+ export async function decideDirectoryTraversal(options) {
72
+ const root = resolveRepoRoot(options.projectRoot);
73
+ const abs = path.resolve(options.absoluteDir);
74
+ const rel = toPosixRelative(root, abs) || options.relativeDir.replace(/\\/g, "/");
75
+ if (abs === root) {
76
+ return {
77
+ traverse: true,
78
+ ownership: "project_owned",
79
+ reason: "canonical project root",
80
+ };
81
+ }
82
+ const pathClass = classifyRelativePathOwnership(rel);
83
+ if (pathClass !== "project_owned") {
84
+ return {
85
+ traverse: false,
86
+ ownership: pathClass,
87
+ reason: `non-owned tree (${pathClass})`,
88
+ };
89
+ }
90
+ if (await isNestedRepositoryRoot(root, abs)) {
91
+ return {
92
+ traverse: false,
93
+ ownership: "nested_repository",
94
+ reason: "nested .git repository boundary",
95
+ };
96
+ }
97
+ return {
98
+ traverse: true,
99
+ ownership: "project_owned",
100
+ reason: "inside project-owned tree",
101
+ };
102
+ }
103
+ /**
104
+ * True when any ancestor directory under `projectRoot` (excluding the root) is a nested VCS root.
105
+ * Used for path-targeted access (agent/MCP) where discovery traversal never ran.
106
+ */
107
+ export async function isUnderNestedRepository(projectRootInput, absolutePath) {
108
+ const root = resolveRepoRoot(projectRootInput);
109
+ let current = path.resolve(absolutePath);
110
+ if (current === root)
111
+ return false;
112
+ // Walk ancestors including the path itself when it is a directory.
113
+ for (;;) {
114
+ if (current !== root && (await pathExists(path.join(current, ".git")))) {
115
+ return true;
116
+ }
117
+ if (current === root)
118
+ return false;
119
+ const parent = path.dirname(current);
120
+ if (parent === current)
121
+ return false;
122
+ current = parent;
123
+ if (current === root)
124
+ return false;
125
+ }
126
+ }
127
+ function realpathOrResolve(candidate) {
128
+ try {
129
+ return fs.realpathSync(candidate);
130
+ }
131
+ catch {
132
+ return path.resolve(candidate);
133
+ }
134
+ }
135
+ /**
136
+ * Assert a user-supplied path is both inside the repo root AND project-owned.
137
+ * Containment alone is not ownership (.private, AgentDoctorOS, nested repos, fixtures).
138
+ *
139
+ * Ownership is classified from the realpath-relative path under the real project root
140
+ * so symlink aliases (e.g. link-to-private → .private) cannot launder ownership.
141
+ * Lexical hints are also rejected when they themselves name a non-owned tree.
142
+ */
143
+ export async function assertProjectOwnedRepoPath(projectRootInput, absolutePath, relativeHint) {
144
+ const root = resolveRepoRoot(projectRootInput);
145
+ const abs = path.resolve(absolutePath);
146
+ const realRoot = realpathOrResolve(root);
147
+ const realAbs = realpathOrResolve(abs);
148
+ const resolvedRelative = toPosixRelative(realRoot, realAbs) || ".";
149
+ if (relativeHint) {
150
+ const hintClass = classifyRelativePathOwnership(relativeHint.replace(/\\/g, "/"));
151
+ if (hintClass !== "project_owned") {
152
+ throw new ProjectOwnershipError(hintClass);
153
+ }
154
+ }
155
+ const pathClass = classifyRelativePathOwnership(resolvedRelative);
156
+ if (pathClass !== "project_owned") {
157
+ throw new ProjectOwnershipError(pathClass);
158
+ }
159
+ if (await isUnderNestedRepository(root, realAbs)) {
160
+ throw new ProjectOwnershipError("nested_repository");
161
+ }
162
+ return { absolutePath: realAbs, relativePath: resolvedRelative, ownership: "project_owned" };
163
+ }
164
+ /**
165
+ * Human-readable meaning for decision truth labels in product UI.
166
+ */
167
+ export function verifiedDecisionTruthMeaning() {
168
+ return "File evidence was found and parsed for the current project. This does not prove the architectural decision is correct.";
169
+ }
package/dist/utils/fs.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { randomBytes } from "node:crypto";
1
2
  import fs from "node:fs/promises";
2
3
  import path from "node:path";
3
4
  export async function pathExists(target) {
@@ -68,8 +69,15 @@ export async function listDirectorySafe(dirPath) {
68
69
  export async function atomicWriteTextFile(filePath, content) {
69
70
  const dir = path.dirname(filePath);
70
71
  await fs.mkdir(dir, { recursive: true });
71
- const tmp = path.join(dir, `.${path.basename(filePath)}.agentdoctor.${process.pid}.${Date.now()}.tmp`);
72
- await fs.writeFile(tmp, content, "utf8");
72
+ const tmp = path.join(dir, `.${path.basename(filePath)}.${randomBytes(16).toString("hex")}.tmp`);
73
+ // Exclusive create + owner-only mode — closes CodeQL js/insecure-temporary-file.
74
+ const handle = await fs.open(tmp, "wx", 0o600);
75
+ try {
76
+ await handle.writeFile(content, "utf8");
77
+ }
78
+ finally {
79
+ await handle.close();
80
+ }
73
81
  try {
74
82
  await replaceFile(tmp, filePath);
75
83
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praneeth_54/agentdoctor",
3
- "version": "2.1.0",
3
+ "version": "3.0.1",
4
4
  "description": "Engineering assurance for AI coding agents — repository intelligence, change evidence, safety controls, and MCP tools.",
5
5
  "type": "module",
6
6
  "bin": {