@praneeth_54/agentdoctor 1.1.1 → 2.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 (224) hide show
  1. package/CHANGELOG.md +164 -7
  2. package/README.md +263 -513
  3. package/dist/agents/aider/adapter.d.ts +1 -0
  4. package/dist/agents/aider/adapter.js +1 -0
  5. package/dist/agents/aider/detector.d.ts +14 -0
  6. package/dist/agents/aider/detector.js +120 -0
  7. package/dist/agents/copilot/adapter.d.ts +1 -0
  8. package/dist/agents/copilot/adapter.js +1 -0
  9. package/dist/agents/copilot/detector.d.ts +13 -0
  10. package/dist/agents/copilot/detector.js +113 -0
  11. package/dist/agents/gemini/adapter.d.ts +1 -0
  12. package/dist/agents/gemini/adapter.js +1 -0
  13. package/dist/agents/gemini/detector.d.ts +13 -0
  14. package/dist/agents/gemini/detector.js +151 -0
  15. package/dist/agents/registry.js +13 -1
  16. package/dist/agents/types.d.ts +1 -1
  17. package/dist/agents/windsurf/adapter.d.ts +1 -0
  18. package/dist/agents/windsurf/adapter.js +1 -0
  19. package/dist/agents/windsurf/detector.d.ts +14 -0
  20. package/dist/agents/windsurf/detector.js +119 -0
  21. package/dist/architecture/c4.d.ts +22 -0
  22. package/dist/architecture/c4.js +62 -0
  23. package/dist/architecture/contract.d.ts +64 -0
  24. package/dist/architecture/contract.js +373 -0
  25. package/dist/assurance/change.d.ts +211 -0
  26. package/dist/assurance/change.js +624 -0
  27. package/dist/assurance/proof.d.ts +136 -0
  28. package/dist/assurance/proof.js +338 -0
  29. package/dist/auth/index.d.ts +81 -0
  30. package/dist/auth/index.js +130 -0
  31. package/dist/auth/rbac.d.ts +9 -0
  32. package/dist/auth/rbac.js +26 -0
  33. package/dist/cli/commands/architecture.d.ts +6 -0
  34. package/dist/cli/commands/architecture.js +109 -0
  35. package/dist/cli/commands/assurance.d.ts +67 -0
  36. package/dist/cli/commands/assurance.js +210 -0
  37. package/dist/cli/commands/brain.d.ts +12 -0
  38. package/dist/cli/commands/brain.js +162 -0
  39. package/dist/cli/commands/complete.d.ts +23 -0
  40. package/dist/cli/commands/complete.js +232 -0
  41. package/dist/cli/commands/explain.js +1 -1
  42. package/dist/cli/commands/fix.js +22 -2
  43. package/dist/cli/commands/mcp.d.ts +8 -0
  44. package/dist/cli/commands/mcp.js +14 -0
  45. package/dist/cli/commands/platform.d.ts +17 -0
  46. package/dist/cli/commands/platform.js +195 -0
  47. package/dist/cli/commands/policy-graph-run.d.ts +40 -0
  48. package/dist/cli/commands/policy-graph-run.js +200 -0
  49. package/dist/cli/commands/v2.d.ts +55 -0
  50. package/dist/cli/commands/v2.js +278 -0
  51. package/dist/cli/commands/workspace.d.ts +9 -0
  52. package/dist/cli/commands/workspace.js +82 -0
  53. package/dist/cli/program.js +1328 -3
  54. package/dist/constants.d.ts +3 -2
  55. package/dist/constants.js +5 -1
  56. package/dist/contracts/adapters.d.ts +7 -0
  57. package/dist/contracts/adapters.js +60 -0
  58. package/dist/contracts/index.d.ts +162 -0
  59. package/dist/contracts/index.js +18 -0
  60. package/dist/core/baseline/store.d.ts +48 -0
  61. package/dist/core/baseline/store.js +163 -0
  62. package/dist/core/brain-cli/service.d.ts +26 -0
  63. package/dist/core/brain-cli/service.js +175 -0
  64. package/dist/core/brain-product/init.d.ts +45 -0
  65. package/dist/core/brain-product/init.js +235 -0
  66. package/dist/core/changes/analyze.d.ts +41 -0
  67. package/dist/core/changes/analyze.js +223 -0
  68. package/dist/core/context-health/analyze.d.ts +15 -0
  69. package/dist/core/context-health/analyze.js +234 -0
  70. package/dist/core/fix/apply.d.ts +21 -4
  71. package/dist/core/fix/apply.js +118 -18
  72. package/dist/core/fix/backup.d.ts +30 -0
  73. package/dist/core/fix/backup.js +193 -0
  74. package/dist/core/fix/plan.d.ts +6 -3
  75. package/dist/core/fix/plan.js +113 -9
  76. package/dist/core/fix/render.d.ts +2 -0
  77. package/dist/core/fix/render.js +36 -21
  78. package/dist/core/fix/run.js +5 -1
  79. package/dist/core/fix/safe-target.d.ts +14 -0
  80. package/dist/core/fix/safe-target.js +81 -0
  81. package/dist/core/fix/types.d.ts +12 -1
  82. package/dist/core/fix/types.js +2 -0
  83. package/dist/core/fix/writers/claude-settings.js +3 -1
  84. package/dist/core/fix/writers/codex-config.d.ts +3 -0
  85. package/dist/core/fix/writers/codex-config.js +24 -3
  86. package/dist/core/fix/writers/cursorignore.js +3 -1
  87. package/dist/core/fix/writers/simple-ignore.d.ts +16 -0
  88. package/dist/core/fix/writers/simple-ignore.js +68 -0
  89. package/dist/core/monorepo/detect.d.ts +13 -0
  90. package/dist/core/monorepo/detect.js +121 -0
  91. package/dist/core/rules/build-context.js +22 -3
  92. package/dist/core/rules/context/generated-directory.js +11 -1
  93. package/dist/core/rules/context/large-instruction-file.js +6 -0
  94. package/dist/core/rules/context/large-log-file.js +16 -1
  95. package/dist/core/rules/ignore.d.ts +11 -1
  96. package/dist/core/rules/ignore.js +22 -1
  97. package/dist/core/rules/instructions/empty-instructions.js +7 -0
  98. package/dist/core/rules/security/env-file-exposure.js +23 -2
  99. package/dist/core/rules/security/private-key-file.js +1 -1
  100. package/dist/core/scanner/scan.js +1 -1
  101. package/dist/core/schemas/validate.d.ts +6 -0
  102. package/dist/core/schemas/validate.js +62 -0
  103. package/dist/core/scoring/compute-scores.d.ts +1 -1
  104. package/dist/core/scoring/compute-scores.js +5 -1
  105. package/dist/core/scoring/placeholder.d.ts +1 -1
  106. package/dist/core/scoring/placeholder.js +5 -1
  107. package/dist/core/secrets/scan.d.ts +29 -0
  108. package/dist/core/secrets/scan.js +172 -0
  109. package/dist/coverage/cobertura.d.ts +6 -0
  110. package/dist/coverage/cobertura.js +52 -0
  111. package/dist/coverage/istanbul.d.ts +7 -0
  112. package/dist/coverage/istanbul.js +134 -0
  113. package/dist/coverage/lcov.d.ts +7 -0
  114. package/dist/coverage/lcov.js +59 -0
  115. package/dist/coverage/load.d.ts +9 -0
  116. package/dist/coverage/load.js +39 -0
  117. package/dist/coverage/types.d.ts +23 -0
  118. package/dist/coverage/types.js +5 -0
  119. package/dist/dashboard/server.d.ts +24 -0
  120. package/dist/dashboard/server.js +402 -0
  121. package/dist/enforcement/runner.d.ts +67 -0
  122. package/dist/enforcement/runner.js +460 -0
  123. package/dist/index.d.ts +34 -0
  124. package/dist/index.js +25 -0
  125. package/dist/integrations/github/pr-analyze.d.ts +28 -0
  126. package/dist/integrations/github/pr-analyze.js +98 -0
  127. package/dist/integrations/local-ai/provider.d.ts +45 -0
  128. package/dist/integrations/local-ai/provider.js +104 -0
  129. package/dist/intelligence/git/analyze.d.ts +34 -0
  130. package/dist/intelligence/git/analyze.js +136 -0
  131. package/dist/intelligence/graph/build.d.ts +15 -0
  132. package/dist/intelligence/graph/build.js +259 -0
  133. package/dist/intelligence/graph/incremental.d.ts +68 -0
  134. package/dist/intelligence/graph/incremental.js +234 -0
  135. package/dist/intelligence/resolve/imports.d.ts +36 -0
  136. package/dist/intelligence/resolve/imports.js +245 -0
  137. package/dist/knowledge/store.d.ts +25 -0
  138. package/dist/knowledge/store.js +91 -0
  139. package/dist/languages/go.d.ts +9 -0
  140. package/dist/languages/go.js +45 -0
  141. package/dist/languages/index.d.ts +11 -0
  142. package/dist/languages/index.js +76 -0
  143. package/dist/languages/php.d.ts +7 -0
  144. package/dist/languages/php.js +193 -0
  145. package/dist/languages/python.d.ts +7 -0
  146. package/dist/languages/python.js +150 -0
  147. package/dist/languages/types.d.ts +42 -0
  148. package/dist/languages/types.js +26 -0
  149. package/dist/languages/typescript.d.ts +3 -0
  150. package/dist/languages/typescript.js +95 -0
  151. package/dist/mcp/agentdoctor/server.d.ts +16 -0
  152. package/dist/mcp/agentdoctor/server.js +67 -0
  153. package/dist/mcp/brain/session.js +7 -7
  154. package/dist/mcp/intelligence/handlers.d.ts +16 -0
  155. package/dist/mcp/intelligence/handlers.js +364 -0
  156. package/dist/mcp/intelligence/path-safety.d.ts +9 -0
  157. package/dist/mcp/intelligence/path-safety.js +63 -0
  158. package/dist/mcp/intelligence/registry.d.ts +8 -0
  159. package/dist/mcp/intelligence/registry.js +228 -0
  160. package/dist/ops/health.d.ts +18 -0
  161. package/dist/ops/health.js +51 -0
  162. package/dist/platform/ai-quality/analyze.d.ts +9 -0
  163. package/dist/platform/ai-quality/analyze.js +65 -0
  164. package/dist/platform/architecture/drift.d.ts +17 -0
  165. package/dist/platform/architecture/drift.js +74 -0
  166. package/dist/platform/auth/local.d.ts +15 -0
  167. package/dist/platform/auth/local.js +58 -0
  168. package/dist/platform/context-security/analyze.d.ts +6 -0
  169. package/dist/platform/context-security/analyze.js +97 -0
  170. package/dist/platform/firewall/evaluate.d.ts +57 -0
  171. package/dist/platform/firewall/evaluate.js +359 -0
  172. package/dist/platform/graph/build.d.ts +6 -0
  173. package/dist/platform/graph/build.js +189 -0
  174. package/dist/platform/health/analyze.d.ts +7 -0
  175. package/dist/platform/health/analyze.js +192 -0
  176. package/dist/platform/index.d.ts +27 -0
  177. package/dist/platform/index.js +89 -0
  178. package/dist/platform/knowledge/analyze.d.ts +20 -0
  179. package/dist/platform/knowledge/analyze.js +96 -0
  180. package/dist/platform/provenance/build.d.ts +44 -0
  181. package/dist/platform/provenance/build.js +65 -0
  182. package/dist/platform/readiness/scorecard.d.ts +5 -0
  183. package/dist/platform/readiness/scorecard.js +98 -0
  184. package/dist/platform/refactor/impact.d.ts +20 -0
  185. package/dist/platform/refactor/impact.js +60 -0
  186. package/dist/platform/reports/export.d.ts +12 -0
  187. package/dist/platform/reports/export.js +92 -0
  188. package/dist/platform/security/redact.d.ts +15 -0
  189. package/dist/platform/security/redact.js +87 -0
  190. package/dist/platform/sessions/store.d.ts +32 -0
  191. package/dist/platform/sessions/store.js +97 -0
  192. package/dist/platform/store.d.ts +6 -0
  193. package/dist/platform/store.js +57 -0
  194. package/dist/platform/test-impact/analyze.d.ts +50 -0
  195. package/dist/platform/test-impact/analyze.js +350 -0
  196. package/dist/platform/time-machine/compare.d.ts +17 -0
  197. package/dist/platform/time-machine/compare.js +62 -0
  198. package/dist/platform/tokens/plan.d.ts +28 -0
  199. package/dist/platform/tokens/plan.js +108 -0
  200. package/dist/platform/types.d.ts +120 -0
  201. package/dist/platform/types.js +5 -0
  202. package/dist/plugins/runtime.d.ts +36 -0
  203. package/dist/plugins/runtime.js +101 -0
  204. package/dist/plugins/sdk.d.ts +29 -0
  205. package/dist/plugins/sdk.js +114 -0
  206. package/dist/policy/compose.d.ts +34 -0
  207. package/dist/policy/compose.js +118 -0
  208. package/dist/policy/packs.d.ts +9 -0
  209. package/dist/policy/packs.js +91 -0
  210. package/dist/reporters/terminal/report.js +38 -5
  211. package/dist/security/paths.d.ts +21 -0
  212. package/dist/security/paths.js +105 -0
  213. package/dist/storage/postgres.d.ts +26 -0
  214. package/dist/storage/postgres.js +90 -0
  215. package/dist/storage/provider.d.ts +30 -0
  216. package/dist/storage/provider.js +109 -0
  217. package/dist/storage/sqlite.d.ts +33 -0
  218. package/dist/storage/sqlite.js +77 -0
  219. package/dist/team/auth.d.ts +35 -0
  220. package/dist/team/auth.js +70 -0
  221. package/dist/types/index.d.ts +5 -1
  222. package/dist/workspace/index.d.ts +69 -0
  223. package/dist/workspace/index.js +220 -0
  224. package/package.json +15 -5
@@ -0,0 +1 @@
1
+ export { aiderAdapter, detectAider } from "./detector.js";
@@ -0,0 +1 @@
1
+ export { aiderAdapter, detectAider } from "./detector.js";
@@ -0,0 +1,14 @@
1
+ import type { AgentAdapter, AgentDetectionContext, AgentDetectionResult } from "../types.js";
2
+ /**
3
+ * Aider project configuration detection.
4
+ *
5
+ * Official sources (aider.chat docs):
6
+ * - Config: `.aider.conf.yml` (cwd / git root); `.aider.conf.yaml` also seen in the wild
7
+ * - Ignore: `.aiderignore` (gitignore-style; default at git root)
8
+ * - Conventions: `CONVENTIONS.md` commonly loaded via conf `read:` — only counted when
9
+ * an Aider config/ignore file is also present (avoids false positives)
10
+ *
11
+ * We do NOT inspect `~/.aider.conf.yml` — repository only.
12
+ */
13
+ export declare function detectAider(context: AgentDetectionContext): Promise<AgentDetectionResult>;
14
+ export declare const aiderAdapter: AgentAdapter;
@@ -0,0 +1,120 @@
1
+ import { AGENT_DISPLAY_NAMES } from "../../constants.js";
2
+ import { deriveStatus, inspectRepoFile, pathExistsInsideRoot, toAgentConfigFile, } from "../inspect.js";
3
+ /**
4
+ * Aider project configuration detection.
5
+ *
6
+ * Official sources (aider.chat docs):
7
+ * - Config: `.aider.conf.yml` (cwd / git root); `.aider.conf.yaml` also seen in the wild
8
+ * - Ignore: `.aiderignore` (gitignore-style; default at git root)
9
+ * - Conventions: `CONVENTIONS.md` commonly loaded via conf `read:` — only counted when
10
+ * an Aider config/ignore file is also present (avoids false positives)
11
+ *
12
+ * We do NOT inspect `~/.aider.conf.yml` — repository only.
13
+ */
14
+ export async function detectAider(context) {
15
+ const { root, maxFileSizeBytes } = context;
16
+ const configFiles = [];
17
+ const diagnostics = [];
18
+ const seen = new Set();
19
+ const metadata = {
20
+ hasConf: false,
21
+ hasAiderignore: false,
22
+ hasConventions: false,
23
+ };
24
+ async function addFile(relativePath, kind) {
25
+ if (seen.has(relativePath)) {
26
+ return;
27
+ }
28
+ seen.add(relativePath);
29
+ const inspected = await inspectRepoFile(root, relativePath, maxFileSizeBytes);
30
+ configFiles.push(toAgentConfigFile(inspected, kind, {
31
+ legacy: false,
32
+ scope: "root",
33
+ }));
34
+ if (!inspected.exists) {
35
+ return;
36
+ }
37
+ if (!inspected.readable && inspected.error) {
38
+ diagnostics.push({
39
+ code: "aider/unreadable-file",
40
+ severity: "warning",
41
+ message: `Could not read Aider file: ${inspected.error}`,
42
+ file: relativePath,
43
+ });
44
+ }
45
+ else if (inspected.empty && kind !== "aider-ignore") {
46
+ diagnostics.push({
47
+ code: "aider/empty-config",
48
+ severity: "info",
49
+ message: "Aider config/instruction file is empty",
50
+ file: relativePath,
51
+ });
52
+ }
53
+ }
54
+ const confYml = await pathExistsInsideRoot(root, ".aider.conf.yml");
55
+ const confYaml = await pathExistsInsideRoot(root, ".aider.conf.yaml");
56
+ if (confYml) {
57
+ await addFile(".aider.conf.yml", "aider-conf");
58
+ metadata.hasConf = true;
59
+ }
60
+ else if (confYaml) {
61
+ await addFile(".aider.conf.yaml", "aider-conf");
62
+ metadata.hasConf = true;
63
+ }
64
+ if (await pathExistsInsideRoot(root, ".aiderignore")) {
65
+ await addFile(".aiderignore", "aider-ignore");
66
+ metadata.hasAiderignore = true;
67
+ }
68
+ const aiderPresent = Boolean(metadata.hasConf) || Boolean(metadata.hasAiderignore);
69
+ if (aiderPresent && (await pathExistsInsideRoot(root, "CONVENTIONS.md"))) {
70
+ await addFile("CONVENTIONS.md", "aider-conventions");
71
+ metadata.hasConventions = true;
72
+ }
73
+ const usable = configFiles.some((f) => f.readable &&
74
+ !f.empty &&
75
+ (f.kind === "aider-conf" || f.kind === "aider-conventions" || f.kind === "aider-ignore"));
76
+ const detected = aiderPresent;
77
+ const configured = usable || Boolean(metadata.hasAiderignore) || Boolean(metadata.hasConf);
78
+ const status = deriveStatus({
79
+ detected,
80
+ configured,
81
+ hasErrors: diagnostics.some((d) => d.severity === "error"),
82
+ });
83
+ const parts = [];
84
+ if (metadata.hasConf) {
85
+ parts.push("conf");
86
+ }
87
+ if (metadata.hasAiderignore) {
88
+ parts.push(".aiderignore");
89
+ }
90
+ if (metadata.hasConventions) {
91
+ parts.push("CONVENTIONS.md");
92
+ }
93
+ let summary;
94
+ if (!detected) {
95
+ summary = "not configured";
96
+ }
97
+ else if (configured) {
98
+ summary = parts.length > 0 ? parts.join(", ") : "configured";
99
+ }
100
+ else {
101
+ summary = "detected but not configured";
102
+ }
103
+ return {
104
+ id: "aider",
105
+ displayName: AGENT_DISPLAY_NAMES.aider,
106
+ detected,
107
+ configured,
108
+ status,
109
+ summary,
110
+ configFiles,
111
+ configPaths: configFiles.map((f) => f.relativePath),
112
+ diagnostics,
113
+ metadata,
114
+ };
115
+ }
116
+ export const aiderAdapter = {
117
+ id: "aider",
118
+ displayName: AGENT_DISPLAY_NAMES.aider,
119
+ detect: detectAider,
120
+ };
@@ -0,0 +1 @@
1
+ export { copilotAdapter, detectCopilot } from "./detector.js";
@@ -0,0 +1 @@
1
+ export { copilotAdapter, detectCopilot } from "./detector.js";
@@ -0,0 +1,13 @@
1
+ import type { AgentAdapter, AgentDetectionContext, AgentDetectionResult } from "../types.js";
2
+ /**
3
+ * GitHub Copilot project configuration detection.
4
+ *
5
+ * Official sources (docs.github.com Copilot custom instructions):
6
+ * - Repository-wide: `.github/copilot-instructions.md`
7
+ * - Path-specific: files under `.github/instructions/` ending in `.instructions.md`
8
+ *
9
+ * We do NOT inspect `~/.copilot` or user-global preference files — repository only.
10
+ * No official project-local deny/ignore file exists; Safe Fix does not invent one.
11
+ */
12
+ export declare function detectCopilot(context: AgentDetectionContext): Promise<AgentDetectionResult>;
13
+ export declare const copilotAdapter: AgentAdapter;
@@ -0,0 +1,113 @@
1
+ import { AGENT_DISPLAY_NAMES } from "../../constants.js";
2
+ import { deriveStatus, inspectRepoFile, pathExistsInsideRoot, toAgentConfigFile, } from "../inspect.js";
3
+ import { basenameOf } from "../types.js";
4
+ /**
5
+ * GitHub Copilot project configuration detection.
6
+ *
7
+ * Official sources (docs.github.com Copilot custom instructions):
8
+ * - Repository-wide: `.github/copilot-instructions.md`
9
+ * - Path-specific: files under `.github/instructions/` ending in `.instructions.md`
10
+ *
11
+ * We do NOT inspect `~/.copilot` or user-global preference files — repository only.
12
+ * No official project-local deny/ignore file exists; Safe Fix does not invent one.
13
+ */
14
+ export async function detectCopilot(context) {
15
+ const { root, discovery, maxFileSizeBytes } = context;
16
+ const configFiles = [];
17
+ const diagnostics = [];
18
+ const seen = new Set();
19
+ const metadata = {
20
+ repoInstructionsCount: 0,
21
+ pathInstructionsCount: 0,
22
+ };
23
+ async function addInstructionFile(relativePath, kind) {
24
+ if (seen.has(relativePath)) {
25
+ return;
26
+ }
27
+ seen.add(relativePath);
28
+ const inspected = await inspectRepoFile(root, relativePath, maxFileSizeBytes);
29
+ configFiles.push(toAgentConfigFile(inspected, kind, {
30
+ legacy: false,
31
+ scope: relativePath === ".github/copilot-instructions.md" ? "root" : "nested",
32
+ }));
33
+ if (kind === "copilot-instructions") {
34
+ metadata.repoInstructionsCount = Number(metadata.repoInstructionsCount) + 1;
35
+ }
36
+ else {
37
+ metadata.pathInstructionsCount = Number(metadata.pathInstructionsCount) + 1;
38
+ }
39
+ if (!inspected.exists) {
40
+ return;
41
+ }
42
+ if (!inspected.readable && inspected.error) {
43
+ diagnostics.push({
44
+ code: "copilot/unreadable-file",
45
+ severity: "warning",
46
+ message: `Could not read Copilot instruction file: ${inspected.error}`,
47
+ file: relativePath,
48
+ });
49
+ }
50
+ else if (inspected.empty) {
51
+ diagnostics.push({
52
+ code: "copilot/empty-instruction",
53
+ severity: "info",
54
+ message: "Copilot instruction file is empty",
55
+ file: relativePath,
56
+ });
57
+ }
58
+ }
59
+ if (await pathExistsInsideRoot(root, ".github/copilot-instructions.md")) {
60
+ await addInstructionFile(".github/copilot-instructions.md", "copilot-instructions");
61
+ }
62
+ for (const file of discovery.files) {
63
+ const relative = file.relativePath;
64
+ if (!relative.startsWith(".github/instructions/")) {
65
+ continue;
66
+ }
67
+ const base = basenameOf(relative);
68
+ if (!base.endsWith(".instructions.md")) {
69
+ continue;
70
+ }
71
+ await addInstructionFile(relative, "copilot-path-instructions");
72
+ }
73
+ const usableInstruction = configFiles.some((f) => f.readable && !f.empty);
74
+ const detected = Number(metadata.repoInstructionsCount) > 0 || Number(metadata.pathInstructionsCount) > 0;
75
+ const configured = usableInstruction;
76
+ const hasErrors = diagnostics.some((d) => d.severity === "error");
77
+ const status = deriveStatus({ detected, configured, hasErrors });
78
+ const parts = [];
79
+ if (Number(metadata.repoInstructionsCount) > 0) {
80
+ parts.push("copilot-instructions.md");
81
+ }
82
+ if (Number(metadata.pathInstructionsCount) > 0) {
83
+ const count = Number(metadata.pathInstructionsCount);
84
+ parts.push(`${count} path instruction${count === 1 ? "" : "s"}`);
85
+ }
86
+ let summary;
87
+ if (!detected) {
88
+ summary = "not configured";
89
+ }
90
+ else if (configured) {
91
+ summary = parts.length > 0 ? parts.join(", ") : "configured";
92
+ }
93
+ else {
94
+ summary = "detected but not configured";
95
+ }
96
+ return {
97
+ id: "copilot",
98
+ displayName: AGENT_DISPLAY_NAMES.copilot,
99
+ detected,
100
+ configured,
101
+ status,
102
+ summary,
103
+ configFiles,
104
+ configPaths: configFiles.map((f) => f.relativePath),
105
+ diagnostics,
106
+ metadata,
107
+ };
108
+ }
109
+ export const copilotAdapter = {
110
+ id: "copilot",
111
+ displayName: AGENT_DISPLAY_NAMES.copilot,
112
+ detect: detectCopilot,
113
+ };
@@ -0,0 +1 @@
1
+ export { geminiCliAdapter, detectGeminiCli } from "./detector.js";
@@ -0,0 +1 @@
1
+ export { geminiCliAdapter, detectGeminiCli } from "./detector.js";
@@ -0,0 +1,13 @@
1
+ import type { AgentAdapter, AgentDetectionContext, AgentDetectionResult } from "../types.js";
2
+ /**
3
+ * Gemini CLI project configuration detection.
4
+ *
5
+ * Official sources (google-gemini.github.io/gemini-cli):
6
+ * - Context: `GEMINI.md` (cwd, ancestors to git root, and subdirectories)
7
+ * - Project settings: `.gemini/settings.json`
8
+ * - Ignore: `.geminiignore` (gitignore-style)
9
+ *
10
+ * We do NOT inspect `~/.gemini` — repository only.
11
+ */
12
+ export declare function detectGeminiCli(context: AgentDetectionContext): Promise<AgentDetectionResult>;
13
+ export declare const geminiCliAdapter: AgentAdapter;
@@ -0,0 +1,151 @@
1
+ import { AGENT_DISPLAY_NAMES } from "../../constants.js";
2
+ import { deriveStatus, inspectRepoFile, pathExistsInsideRoot, toAgentConfigFile, tryParseJson, } from "../inspect.js";
3
+ import { basenameOf, isRootLevel } from "../types.js";
4
+ /**
5
+ * Gemini CLI project configuration detection.
6
+ *
7
+ * Official sources (google-gemini.github.io/gemini-cli):
8
+ * - Context: `GEMINI.md` (cwd, ancestors to git root, and subdirectories)
9
+ * - Project settings: `.gemini/settings.json`
10
+ * - Ignore: `.geminiignore` (gitignore-style)
11
+ *
12
+ * We do NOT inspect `~/.gemini` — repository only.
13
+ */
14
+ export async function detectGeminiCli(context) {
15
+ const { root, discovery, maxFileSizeBytes } = context;
16
+ const configFiles = [];
17
+ const diagnostics = [];
18
+ const seen = new Set();
19
+ const metadata = {
20
+ geminiMdCount: 0,
21
+ hasGeminiDir: false,
22
+ hasSettings: false,
23
+ hasGeminiignore: false,
24
+ };
25
+ async function addGeminiMd(relativePath) {
26
+ if (seen.has(relativePath)) {
27
+ return;
28
+ }
29
+ seen.add(relativePath);
30
+ const inspected = await inspectRepoFile(root, relativePath, maxFileSizeBytes);
31
+ configFiles.push(toAgentConfigFile(inspected, "gemini-md", {
32
+ legacy: false,
33
+ scope: isRootLevel(relativePath) ? "root" : "nested",
34
+ }));
35
+ metadata.geminiMdCount = Number(metadata.geminiMdCount) + 1;
36
+ if (!inspected.exists) {
37
+ return;
38
+ }
39
+ if (!inspected.readable && inspected.error) {
40
+ diagnostics.push({
41
+ code: "gemini/unreadable-file",
42
+ severity: "warning",
43
+ message: `Could not read Gemini context file: ${inspected.error}`,
44
+ file: relativePath,
45
+ });
46
+ }
47
+ else if (inspected.empty) {
48
+ diagnostics.push({
49
+ code: "gemini/empty-instruction",
50
+ severity: "info",
51
+ message: "Gemini context file is empty",
52
+ file: relativePath,
53
+ });
54
+ }
55
+ }
56
+ metadata.hasGeminiDir = await pathExistsInsideRoot(root, ".gemini");
57
+ if (await pathExistsInsideRoot(root, ".gemini/settings.json")) {
58
+ const relative = ".gemini/settings.json";
59
+ seen.add(relative);
60
+ const inspected = await inspectRepoFile(root, relative, maxFileSizeBytes);
61
+ let parseError;
62
+ if (inspected.readable && inspected.text !== null && !inspected.empty) {
63
+ const parsed = tryParseJson(inspected.text);
64
+ if (!parsed.ok) {
65
+ parseError = parsed.error;
66
+ diagnostics.push({
67
+ code: "gemini/malformed-settings",
68
+ severity: "warning",
69
+ message: `${relative} could not be parsed: ${parsed.error}`,
70
+ file: relative,
71
+ });
72
+ }
73
+ }
74
+ else if (inspected.exists && inspected.empty) {
75
+ diagnostics.push({
76
+ code: "gemini/empty-settings",
77
+ severity: "info",
78
+ message: `${relative} is empty`,
79
+ file: relative,
80
+ });
81
+ }
82
+ configFiles.push(toAgentConfigFile(inspected, "gemini-settings", {
83
+ legacy: false,
84
+ scope: "root",
85
+ ...(parseError !== undefined ? { parseError } : {}),
86
+ }));
87
+ metadata.hasSettings = true;
88
+ }
89
+ if (await pathExistsInsideRoot(root, ".geminiignore")) {
90
+ metadata.hasGeminiignore = true;
91
+ }
92
+ for (const file of discovery.files) {
93
+ if (basenameOf(file.relativePath) === "GEMINI.md") {
94
+ await addGeminiMd(file.relativePath);
95
+ }
96
+ }
97
+ if (await pathExistsInsideRoot(root, "GEMINI.md")) {
98
+ await addGeminiMd("GEMINI.md");
99
+ }
100
+ const usableInstruction = configFiles.some((f) => f.readable &&
101
+ !f.empty &&
102
+ (f.kind === "gemini-md" || (f.kind === "gemini-settings" && f.parseError === undefined)));
103
+ const detected = Boolean(metadata.hasGeminiDir) ||
104
+ Boolean(metadata.hasGeminiignore) ||
105
+ Number(metadata.geminiMdCount) > 0 ||
106
+ Boolean(metadata.hasSettings);
107
+ const configured = usableInstruction || Boolean(metadata.hasGeminiignore);
108
+ const hasParseProblems = configFiles.some((f) => f.parseError !== undefined);
109
+ const status = deriveStatus({
110
+ detected,
111
+ configured,
112
+ hasErrors: diagnostics.some((d) => d.severity === "error") || (hasParseProblems && !configured),
113
+ });
114
+ const parts = [];
115
+ if (Number(metadata.geminiMdCount) > 0) {
116
+ parts.push("GEMINI.md");
117
+ }
118
+ if (metadata.hasSettings) {
119
+ parts.push("settings");
120
+ }
121
+ if (metadata.hasGeminiignore) {
122
+ parts.push(".geminiignore");
123
+ }
124
+ let summary;
125
+ if (!detected) {
126
+ summary = "not configured";
127
+ }
128
+ else if (configured) {
129
+ summary = parts.length > 0 ? parts.join(", ") : "configured";
130
+ }
131
+ else {
132
+ summary = "detected but not configured";
133
+ }
134
+ return {
135
+ id: "gemini-cli",
136
+ displayName: AGENT_DISPLAY_NAMES["gemini-cli"],
137
+ detected,
138
+ configured,
139
+ status,
140
+ summary,
141
+ configFiles,
142
+ configPaths: configFiles.map((f) => f.relativePath),
143
+ diagnostics,
144
+ metadata,
145
+ };
146
+ }
147
+ export const geminiCliAdapter = {
148
+ id: "gemini-cli",
149
+ displayName: AGENT_DISPLAY_NAMES["gemini-cli"],
150
+ detect: detectGeminiCli,
151
+ };
@@ -1,11 +1,23 @@
1
+ import { aiderAdapter } from "./aider/adapter.js";
1
2
  import { claudeAdapter } from "./claude/adapter.js";
2
3
  import { codexAdapter } from "./codex/adapter.js";
4
+ import { copilotAdapter } from "./copilot/adapter.js";
5
+ import { geminiCliAdapter } from "./gemini/adapter.js";
3
6
  import { cursorAdapter } from "./cursor/adapter.js";
7
+ import { windsurfAdapter } from "./windsurf/adapter.js";
4
8
  /**
5
9
  * Central registry of AI coding agent adapters.
6
10
  * Add new agents by implementing AgentAdapter and appending here.
7
11
  */
8
- export const agentRegistry = [cursorAdapter, claudeAdapter, codexAdapter];
12
+ export const agentRegistry = [
13
+ cursorAdapter,
14
+ claudeAdapter,
15
+ codexAdapter,
16
+ copilotAdapter,
17
+ windsurfAdapter,
18
+ geminiCliAdapter,
19
+ aiderAdapter,
20
+ ];
9
21
  export function getAgentAdapter(id) {
10
22
  return agentRegistry.find((adapter) => adapter.id === id);
11
23
  }
@@ -1,6 +1,6 @@
1
1
  import type { AgentId, DiscoveredFile, DiscoveryResult } from "../types/index.js";
2
2
  export type AgentConfigStatus = "absent" | "detected" | "configured" | "misconfigured";
3
- export type AgentConfigFileKind = "cursor-rule-mdc" | "cursor-rule-ignored-md" | "cursor-legacy-cursorrules" | "agents-md" | "agents-override-md" | "claude-md" | "claude-local-md" | "claude-rule-md" | "claude-settings" | "claude-settings-local" | "codex-config" | "other";
3
+ export type AgentConfigFileKind = "cursor-rule-mdc" | "cursor-rule-ignored-md" | "cursor-legacy-cursorrules" | "agents-md" | "agents-override-md" | "claude-md" | "claude-local-md" | "claude-rule-md" | "claude-settings" | "claude-settings-local" | "codex-config" | "copilot-instructions" | "copilot-path-instructions" | "windsurf-rule-md" | "windsurf-legacy-rules" | "gemini-md" | "gemini-settings" | "aider-conf" | "aider-ignore" | "aider-conventions" | "other";
4
4
  export type AgentConfigScope = "root" | "nested" | "legacy";
5
5
  export interface AgentConfigFile {
6
6
  relativePath: string;
@@ -0,0 +1 @@
1
+ export { windsurfAdapter, detectWindsurf } from "./detector.js";
@@ -0,0 +1 @@
1
+ export { windsurfAdapter, detectWindsurf } from "./detector.js";
@@ -0,0 +1,14 @@
1
+ import type { AgentAdapter, AgentDetectionContext, AgentDetectionResult } from "../types.js";
2
+ /**
3
+ * Windsurf / Cascade project rules detection.
4
+ *
5
+ * Official sources (docs.devin.ai Cascade memories & rules):
6
+ * - Preferred: `.devin/rules/*.md`
7
+ * - Fallback: `.windsurf/rules/*.md`
8
+ * - Legacy: `.windsurfrules` at workspace root
9
+ *
10
+ * We do NOT inspect `~/.codeium/windsurf` or OS system rules — repository only.
11
+ * No official project-local deny/ignore Fix writer; Safe Fix does not invent one.
12
+ */
13
+ export declare function detectWindsurf(context: AgentDetectionContext): Promise<AgentDetectionResult>;
14
+ export declare const windsurfAdapter: AgentAdapter;
@@ -0,0 +1,119 @@
1
+ import { AGENT_DISPLAY_NAMES } from "../../constants.js";
2
+ import { deriveStatus, inspectRepoFile, pathExistsInsideRoot, toAgentConfigFile, } from "../inspect.js";
3
+ import { basenameOf } from "../types.js";
4
+ /**
5
+ * Windsurf / Cascade project rules detection.
6
+ *
7
+ * Official sources (docs.devin.ai Cascade memories & rules):
8
+ * - Preferred: `.devin/rules/*.md`
9
+ * - Fallback: `.windsurf/rules/*.md`
10
+ * - Legacy: `.windsurfrules` at workspace root
11
+ *
12
+ * We do NOT inspect `~/.codeium/windsurf` or OS system rules — repository only.
13
+ * No official project-local deny/ignore Fix writer; Safe Fix does not invent one.
14
+ */
15
+ export async function detectWindsurf(context) {
16
+ const { root, discovery, maxFileSizeBytes } = context;
17
+ const configFiles = [];
18
+ const diagnostics = [];
19
+ const seen = new Set();
20
+ const metadata = {
21
+ ruleCount: 0,
22
+ hasLegacyRules: false,
23
+ hasWindsurfDir: false,
24
+ hasDevinDir: false,
25
+ };
26
+ async function addRule(relativePath, kind, legacy) {
27
+ if (seen.has(relativePath)) {
28
+ return;
29
+ }
30
+ seen.add(relativePath);
31
+ const inspected = await inspectRepoFile(root, relativePath, maxFileSizeBytes);
32
+ configFiles.push(toAgentConfigFile(inspected, kind, {
33
+ legacy,
34
+ scope: relativePath === ".windsurfrules" ? "legacy" : "nested",
35
+ }));
36
+ metadata.ruleCount = Number(metadata.ruleCount) + 1;
37
+ if (legacy) {
38
+ metadata.hasLegacyRules = true;
39
+ }
40
+ if (!inspected.exists) {
41
+ return;
42
+ }
43
+ if (!inspected.readable && inspected.error) {
44
+ diagnostics.push({
45
+ code: "windsurf/unreadable-file",
46
+ severity: "warning",
47
+ message: `Could not read Windsurf rule file: ${inspected.error}`,
48
+ file: relativePath,
49
+ });
50
+ }
51
+ else if (inspected.empty) {
52
+ diagnostics.push({
53
+ code: "windsurf/empty-rule",
54
+ severity: "info",
55
+ message: "Windsurf rule file is empty",
56
+ file: relativePath,
57
+ });
58
+ }
59
+ }
60
+ metadata.hasWindsurfDir = await pathExistsInsideRoot(root, ".windsurf");
61
+ metadata.hasDevinDir = await pathExistsInsideRoot(root, ".devin");
62
+ if (await pathExistsInsideRoot(root, ".windsurfrules")) {
63
+ await addRule(".windsurfrules", "windsurf-legacy-rules", true);
64
+ diagnostics.push({
65
+ code: "windsurf/legacy-rules",
66
+ severity: "info",
67
+ message: "Legacy .windsurfrules detected; prefer .devin/rules/ or .windsurf/rules/",
68
+ file: ".windsurfrules",
69
+ });
70
+ }
71
+ for (const file of discovery.files) {
72
+ const relative = file.relativePath;
73
+ const base = basenameOf(relative);
74
+ if (!base.endsWith(".md")) {
75
+ continue;
76
+ }
77
+ if (relative.includes(".windsurf/rules/") || relative.includes(".devin/rules/")) {
78
+ await addRule(relative, "windsurf-rule-md", false);
79
+ }
80
+ }
81
+ const usable = configFiles.some((f) => f.readable && !f.empty);
82
+ const detected = Boolean(metadata.hasWindsurfDir) ||
83
+ Boolean(metadata.hasDevinDir) ||
84
+ Number(metadata.ruleCount) > 0;
85
+ const configured = usable;
86
+ const status = deriveStatus({
87
+ detected,
88
+ configured,
89
+ hasErrors: diagnostics.some((d) => d.severity === "error"),
90
+ });
91
+ let summary;
92
+ if (!detected) {
93
+ summary = "not configured";
94
+ }
95
+ else if (configured) {
96
+ const count = Number(metadata.ruleCount);
97
+ summary = `${count} rule${count === 1 ? "" : "s"}`;
98
+ }
99
+ else {
100
+ summary = "detected but not configured";
101
+ }
102
+ return {
103
+ id: "windsurf",
104
+ displayName: AGENT_DISPLAY_NAMES.windsurf,
105
+ detected,
106
+ configured,
107
+ status,
108
+ summary,
109
+ configFiles,
110
+ configPaths: configFiles.map((f) => f.relativePath),
111
+ diagnostics,
112
+ metadata,
113
+ };
114
+ }
115
+ export const windsurfAdapter = {
116
+ id: "windsurf",
117
+ displayName: AGENT_DISPLAY_NAMES.windsurf,
118
+ detect: detectWindsurf,
119
+ };
@@ -0,0 +1,22 @@
1
+ import type { RepositoryGraph } from "../platform/types.js";
2
+ export interface C4View {
3
+ level: "system-context" | "container" | "component" | "code";
4
+ title: string;
5
+ nodes: Array<{
6
+ id: string;
7
+ label: string;
8
+ kind: string;
9
+ }>;
10
+ edges: Array<{
11
+ from: string;
12
+ to: string;
13
+ kind: string;
14
+ }>;
15
+ evidenceKind: "observed" | "inferred" | "proposed";
16
+ limitations: string[];
17
+ }
18
+ /**
19
+ * C4-style views derived from repository graph evidence.
20
+ * Always labels inferred vs observed.
21
+ */
22
+ export declare function buildC4Views(graph: RepositoryGraph): C4View[];