solidity-argus 0.2.0 → 0.3.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 (167) hide show
  1. package/AGENTS.md +3 -3
  2. package/README.md +93 -37
  3. package/package.json +33 -7
  4. package/skills/INVENTORY.md +88 -57
  5. package/skills/README.md +26 -23
  6. package/skills/case-studies/beanstalk-governance/SKILL.md +52 -0
  7. package/skills/case-studies/bzx-flash-loan/SKILL.md +53 -0
  8. package/skills/case-studies/cream-finance/SKILL.md +52 -0
  9. package/skills/case-studies/curve-reentrancy/SKILL.md +52 -0
  10. package/skills/case-studies/dao-hack/SKILL.md +51 -0
  11. package/skills/case-studies/euler-finance/SKILL.md +52 -0
  12. package/skills/case-studies/harvest-finance/SKILL.md +52 -0
  13. package/skills/case-studies/level-finance/SKILL.md +51 -0
  14. package/skills/case-studies/mango-markets/SKILL.md +53 -0
  15. package/skills/case-studies/nomad-bridge/SKILL.md +51 -0
  16. package/skills/case-studies/parity-multisig/SKILL.md +55 -0
  17. package/skills/case-studies/poly-network/SKILL.md +51 -0
  18. package/skills/case-studies/rari-fuse/SKILL.md +51 -0
  19. package/skills/case-studies/ronin-bridge/SKILL.md +52 -0
  20. package/skills/case-studies/wormhole-bridge/SKILL.md +51 -0
  21. package/skills/manifests/smartbugs.json +1 -3
  22. package/skills/manifests/sunweb3sec.json +1 -3
  23. package/skills/vulnerability-patterns/access-control/SKILL.md +14 -0
  24. package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +13 -1
  25. package/skills/vulnerability-patterns/assert-violation/SKILL.md +8 -1
  26. package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +12 -1
  27. package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +2 -1
  28. package/skills/vulnerability-patterns/cross-chain-bridge-vulnerabilities/SKILL.md +217 -0
  29. package/skills/vulnerability-patterns/default-visibility/SKILL.md +13 -1
  30. package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +2 -1
  31. package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +8 -1
  32. package/skills/vulnerability-patterns/dos-revert/SKILL.md +1 -0
  33. package/skills/vulnerability-patterns/erc4626-exchange-rate-manipulation/SKILL.md +64 -0
  34. package/skills/vulnerability-patterns/fee-on-transfer-tokens/SKILL.md +93 -0
  35. package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +1 -0
  36. package/skills/vulnerability-patterns/floating-pragma/SKILL.md +8 -1
  37. package/skills/vulnerability-patterns/front-running-attacks/SKILL.md +209 -0
  38. package/skills/vulnerability-patterns/gas-optimization-patterns/SKILL.md +203 -0
  39. package/skills/vulnerability-patterns/governance-attacks/SKILL.md +208 -0
  40. package/skills/vulnerability-patterns/hash-collision/SKILL.md +8 -1
  41. package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +12 -1
  42. package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +8 -1
  43. package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +8 -1
  44. package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +12 -1
  45. package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +7 -1
  46. package/skills/vulnerability-patterns/logic-errors/SKILL.md +10 -0
  47. package/skills/vulnerability-patterns/missing-parameter-bounds/SKILL.md +44 -0
  48. package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +17 -1
  49. package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +12 -1
  50. package/skills/vulnerability-patterns/off-by-one/SKILL.md +7 -1
  51. package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +9 -0
  52. package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +8 -1
  53. package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +1 -0
  54. package/skills/vulnerability-patterns/proxy-vulnerabilities/SKILL.md +209 -0
  55. package/skills/vulnerability-patterns/reentrancy/SKILL.md +9 -0
  56. package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +8 -1
  57. package/skills/vulnerability-patterns/share-accounting-desynchronization/SKILL.md +44 -0
  58. package/skills/vulnerability-patterns/signature-malleability/SKILL.md +2 -1
  59. package/skills/vulnerability-patterns/stateful-parameter-update-drift/SKILL.md +44 -0
  60. package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +12 -1
  61. package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +2 -1
  62. package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +8 -1
  63. package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +8 -1
  64. package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +8 -1
  65. package/skills/vulnerability-patterns/unsafe-erc20-transfers/SKILL.md +132 -0
  66. package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +12 -1
  67. package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +12 -1
  68. package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +11 -1
  69. package/skills/vulnerability-patterns/unused-variables/SKILL.md +8 -1
  70. package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +8 -1
  71. package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +8 -1
  72. package/skills/vulnerability-patterns/weird-tokens/SKILL.md +10 -0
  73. package/skills/vulnerability-patterns/zero-address-misconfiguration/SKILL.md +48 -0
  74. package/src/agents/argus-prompt.ts +24 -7
  75. package/src/agents/pythia-prompt.ts +3 -4
  76. package/src/agents/scribe-prompt.ts +7 -2
  77. package/src/agents/sentinel-prompt.ts +32 -3
  78. package/src/cli/cli-program.ts +29 -26
  79. package/src/cli/commands/check-skills.ts +135 -0
  80. package/src/cli/commands/doctor.ts +48 -26
  81. package/src/cli/commands/init.ts +5 -3
  82. package/src/cli/commands/install.ts +7 -5
  83. package/src/cli/commands/lint-skills.ts +16 -12
  84. package/src/cli/index.ts +5 -5
  85. package/src/cli/types.ts +3 -3
  86. package/src/config/index.ts +1 -1
  87. package/src/config/loader.ts +4 -6
  88. package/src/config/schema.ts +4 -5
  89. package/src/config/types.ts +2 -2
  90. package/src/constants/defaults.ts +2 -0
  91. package/src/create-hooks.ts +145 -34
  92. package/src/create-managers.ts +10 -8
  93. package/src/create-tools.ts +13 -9
  94. package/src/features/background-agent/background-manager.ts +93 -87
  95. package/src/features/background-agent/index.ts +1 -1
  96. package/src/features/context-monitor/context-monitor.ts +3 -3
  97. package/src/features/context-monitor/index.ts +2 -2
  98. package/src/features/error-recovery/session-recovery.ts +2 -4
  99. package/src/features/error-recovery/tool-error-recovery.ts +12 -7
  100. package/src/features/index.ts +5 -5
  101. package/src/features/persistent-state/audit-state-manager.ts +143 -60
  102. package/src/features/persistent-state/global-run-index.ts +38 -0
  103. package/src/features/persistent-state/index.ts +1 -1
  104. package/src/features/persistent-state/run-journal.ts +86 -0
  105. package/src/hooks/config-handler.ts +28 -11
  106. package/src/hooks/context-budget.ts +2 -5
  107. package/src/hooks/event-hook.ts +47 -23
  108. package/src/hooks/hook-system.ts +4 -4
  109. package/src/hooks/index.ts +5 -5
  110. package/src/hooks/knowledge-sync-hook.ts +18 -21
  111. package/src/hooks/recon-context-builder.ts +2 -2
  112. package/src/hooks/safe-create-hook.ts +6 -7
  113. package/src/hooks/tool-tracking-hook.ts +104 -50
  114. package/src/hooks/types.ts +2 -1
  115. package/src/index.ts +23 -36
  116. package/src/knowledge/retry.ts +22 -22
  117. package/src/knowledge/scvd-client.ts +88 -95
  118. package/src/knowledge/scvd-errors.ts +35 -35
  119. package/src/knowledge/scvd-index.ts +78 -80
  120. package/src/knowledge/scvd-sync.ts +106 -101
  121. package/src/managers/index.ts +1 -1
  122. package/src/managers/types.ts +19 -14
  123. package/src/plugin-interface.ts +7 -9
  124. package/src/shared/binary-utils.ts +44 -35
  125. package/src/shared/deep-merge.ts +55 -36
  126. package/src/shared/file-utils.ts +21 -19
  127. package/src/shared/index.ts +11 -5
  128. package/src/shared/jsonc-parser.ts +123 -28
  129. package/src/shared/logger.ts +16 -3
  130. package/src/shared/project-utils.ts +30 -0
  131. package/src/skills/analysis/cluster.ts +414 -0
  132. package/src/skills/analysis/gates.ts +227 -0
  133. package/src/skills/analysis/index.ts +33 -0
  134. package/src/skills/analysis/normalize.ts +217 -0
  135. package/src/skills/analysis/similarity.ts +224 -0
  136. package/src/skills/argus-skill-resolver.ts +17 -6
  137. package/src/skills/skill-schema.ts +11 -10
  138. package/src/solodit-lifecycle.ts +202 -0
  139. package/src/state/audit-state.ts +8 -8
  140. package/src/state/finding-store.ts +68 -55
  141. package/src/state/types.ts +88 -67
  142. package/src/tools/argus-skill-load-tool.ts +12 -7
  143. package/src/tools/contract-analyzer-tool.ts +60 -77
  144. package/src/tools/forge-coverage-tool.ts +226 -0
  145. package/src/tools/forge-fuzz-tool.ts +127 -127
  146. package/src/tools/forge-test-tool.ts +153 -157
  147. package/src/tools/gas-analysis-tool.ts +264 -0
  148. package/src/tools/pattern-checker-tool.ts +185 -190
  149. package/src/tools/pattern-loader.ts +5 -111
  150. package/src/tools/proxy-detection-tool.ts +224 -0
  151. package/src/tools/report-generator-tool.ts +268 -200
  152. package/src/tools/slither-tool.ts +266 -218
  153. package/src/tools/solodit-search-tool.ts +216 -119
  154. package/src/tools/sync-knowledge-tool.ts +7 -11
  155. package/src/utils/audit-artifact-detector.ts +28 -29
  156. package/src/utils/dependency-scanner.ts +37 -37
  157. package/src/utils/project-detector.ts +111 -124
  158. package/src/utils/solidity-parser.ts +103 -74
  159. package/skills/patterns/access-control.yaml +0 -31
  160. package/skills/patterns/erc4626.yaml +0 -29
  161. package/skills/patterns/flash-loan.yaml +0 -20
  162. package/skills/patterns/oracle.yaml +0 -30
  163. package/skills/patterns/proxy.yaml +0 -30
  164. package/skills/patterns/reentrancy.yaml +0 -30
  165. package/skills/patterns/signature.yaml +0 -31
  166. package/src/hooks/event-hook-v2.ts +0 -99
  167. package/src/state/plugin-state.ts +0 -14
@@ -1,64 +1,11 @@
1
- import { readdirSync, readFileSync, existsSync } from "node:fs"
2
- import { join, extname } from "node:path"
3
- import { parse as parseYaml } from "yaml"
4
- import { PatternPackSchema, type PatternDefinition } from "./pattern-schema"
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs"
2
+ import { join } from "node:path"
5
3
  import { createLogger } from "../shared/logger"
6
4
  import { parseFrontmatter, SkillFrontmatterSchema } from "../skills/skill-schema"
5
+ import type { PatternDefinition } from "./pattern-schema"
7
6
 
8
7
  const logger = createLogger()
9
8
 
10
- const YAML_EXTENSIONS = new Set([".yaml", ".yml"])
11
-
12
- const SKILL_NAME_TO_PATTERN_CATEGORY: Record<string, PatternDefinition["category"]> = {
13
- "reentrancy": "reentrancy",
14
- "access-control": "access-control",
15
- "oracle-manipulation": "oracle-manipulation",
16
- "flash-loan-attacks": "flash-loan",
17
- "delegatecall-untrusted-callee": "proxy",
18
- "authorization-txorigin": "access-control",
19
- "unchecked-return-values": "logic-error",
20
- "dos-revert": "dos",
21
- "overflow-underflow": "logic-error",
22
- "signature-malleability": "signature",
23
- }
24
-
25
- export function loadPatternPacks(patternsDir: string): PatternDefinition[] {
26
- if (!existsSync(patternsDir)) {
27
- logger.warn(`Patterns directory does not exist: ${patternsDir}`)
28
- return []
29
- }
30
-
31
- const entries = readdirSync(patternsDir).filter((f) =>
32
- YAML_EXTENSIONS.has(extname(f).toLowerCase())
33
- )
34
-
35
- const allPatterns: PatternDefinition[] = []
36
-
37
- for (const filename of entries) {
38
- const filePath = join(patternsDir, filename)
39
- try {
40
- const raw = readFileSync(filePath, "utf-8")
41
- const parsed = parseYaml(raw)
42
- const result = PatternPackSchema.safeParse(parsed)
43
-
44
- if (!result.success) {
45
- logger.warn(
46
- `Skipping ${filename}: schema validation failed — ${result.error.issues[0]?.message ?? "unknown"}`
47
- )
48
- continue
49
- }
50
-
51
- allPatterns.push(...result.data.patterns)
52
- } catch (err) {
53
- logger.warn(
54
- `Skipping ${filename}: ${err instanceof Error ? err.message : "parse error"}`
55
- )
56
- }
57
- }
58
-
59
- return allPatterns
60
- }
61
-
62
9
  function listSkillMarkdownFiles(skillsDir: string): string[] {
63
10
  if (!existsSync(skillsDir)) {
64
11
  logger.warn(`Skills directory does not exist: ${skillsDir}`)
@@ -103,7 +50,7 @@ export function extractDetectionRulesFromSkills(skillsDir: string): PatternDefin
103
50
  if (!parsed.success) continue
104
51
 
105
52
  const skillName = parsed.data.name
106
- const category = SKILL_NAME_TO_PATTERN_CATEGORY[skillName]
53
+ const category = parsed.data.pattern_category
107
54
  if (!category) continue
108
55
 
109
56
  const rules = parsed.data.detection_rules
@@ -122,62 +69,9 @@ export function extractDetectionRulesFromSkills(skillsDir: string): PatternDefin
122
69
  })
123
70
  }
124
71
  } catch (err) {
125
- logger.warn(
126
- `Skipping ${filePath}: ${err instanceof Error ? err.message : "parse error"}`
127
- )
72
+ logger.warn(`Skipping ${filePath}: ${err instanceof Error ? err.message : "parse error"}`)
128
73
  }
129
74
  }
130
75
 
131
76
  return extracted
132
77
  }
133
-
134
- type BuiltinPattern = {
135
- name: string
136
- category: string
137
- severity: string
138
- regex: RegExp
139
- description: string
140
- exploitReference?: string
141
- }
142
-
143
- function isValidUrl(s: string): boolean {
144
- try {
145
- new URL(s)
146
- return true
147
- } catch {
148
- return false
149
- }
150
- }
151
-
152
- function builtinToDefinition(b: BuiltinPattern): PatternDefinition {
153
- return {
154
- name: b.name,
155
- category: b.category as PatternDefinition["category"],
156
- severity: b.severity as PatternDefinition["severity"],
157
- confidence: "Medium",
158
- version: "1.0",
159
- regex: b.regex.source,
160
- description: b.description,
161
- ...(b.exploitReference && isValidUrl(b.exploitReference)
162
- ? { exploit_ref: b.exploitReference }
163
- : {}),
164
- }
165
- }
166
-
167
- export function mergeWithBuiltins(
168
- yamlPatterns: PatternDefinition[],
169
- builtins: BuiltinPattern[],
170
- skillDetectionRules: PatternDefinition[] = []
171
- ): PatternDefinition[] {
172
- const mergedInputs = [...yamlPatterns, ...skillDetectionRules]
173
- const yamlByName = new Map(mergedInputs.map((p) => [p.name, p]))
174
- const merged: PatternDefinition[] = [...mergedInputs]
175
-
176
- for (const builtin of builtins) {
177
- if (!yamlByName.has(builtin.name)) {
178
- merged.push(builtinToDefinition(builtin))
179
- }
180
- }
181
-
182
- return merged
183
- }
@@ -0,0 +1,224 @@
1
+ import { isAbsolute, join } from "node:path"
2
+ import { type ToolContext, tool } from "@opencode-ai/plugin"
3
+
4
+ type ProxyDetectionArgs = {
5
+ file_path: string
6
+ project_dir?: string
7
+ }
8
+
9
+ type ProxyType = "diamond" | "uups" | "beacon" | "transparent" | "erc1967"
10
+
11
+ type Confidence = "high" | "medium" | "low"
12
+
13
+ export type ProxyDetectionResult = {
14
+ file: string
15
+ isProxy: boolean
16
+ proxyType: ProxyType | null
17
+ indicators: string[]
18
+ confidence: Confidence
19
+ error?: string
20
+ }
21
+
22
+ export type ReadFileFn = (path: string) => Promise<string>
23
+
24
+ const ERC1967_IMPLEMENTATION_SLOT =
25
+ "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"
26
+ const ERC1967_ADMIN_SLOT = "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103"
27
+ const ERC1967_BEACON_SLOT = "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50"
28
+
29
+ const readWithBunFile: ReadFileFn = async (path) => Bun.file(path).text()
30
+
31
+ function hasMatch(source: string, pattern: RegExp): boolean {
32
+ return pattern.test(source)
33
+ }
34
+
35
+ function computeConfidence(indicatorCount: number): Confidence {
36
+ if (indicatorCount >= 3) {
37
+ return "high"
38
+ }
39
+ if (indicatorCount === 2) {
40
+ return "medium"
41
+ }
42
+ return "low"
43
+ }
44
+
45
+ function collectIndicators(source: string): Set<string> {
46
+ const indicators = new Set<string>()
47
+ const lower = source.toLowerCase()
48
+
49
+ if (lower.includes(ERC1967_IMPLEMENTATION_SLOT)) {
50
+ indicators.add("erc1967-implementation-slot")
51
+ }
52
+ if (lower.includes(ERC1967_ADMIN_SLOT)) {
53
+ indicators.add("erc1967-admin-slot")
54
+ }
55
+ if (lower.includes(ERC1967_BEACON_SLOT)) {
56
+ indicators.add("erc1967-beacon-slot")
57
+ }
58
+
59
+ if (hasMatch(source, /\b_implementation\s*\(/i)) {
60
+ indicators.add("transparent-implementation-getter")
61
+ }
62
+ if (hasMatch(source, /\b_admin\s*\(/i) || hasMatch(source, /\b_admin\b/i)) {
63
+ indicators.add("transparent-admin-getter")
64
+ }
65
+ if (hasMatch(source, /\b_setImplementation\s*\(/i)) {
66
+ indicators.add("transparent-set-implementation")
67
+ }
68
+
69
+ if (hasMatch(source, /\b_authorizeUpgrade\s*\(/i)) {
70
+ indicators.add("uups-authorize-upgrade")
71
+ }
72
+ if (hasMatch(source, /\bupgradeToAndCall\s*\(/i)) {
73
+ indicators.add("uups-upgrade-to-and-call")
74
+ }
75
+ if (hasMatch(source, /\bUUPSUpgradeable\b/)) {
76
+ indicators.add("uups-upgradeable")
77
+ }
78
+
79
+ if (hasMatch(source, /\bIBeacon\b/)) {
80
+ indicators.add("beacon-interface")
81
+ }
82
+ if (hasMatch(source, /\bBeaconProxy\b/)) {
83
+ indicators.add("beacon-proxy")
84
+ }
85
+ if (hasMatch(source, /\bUpgradeableBeacon\b/)) {
86
+ indicators.add("upgradeable-beacon")
87
+ }
88
+
89
+ if (hasMatch(source, /\bDiamondCut\b/)) {
90
+ indicators.add("diamond-cut")
91
+ }
92
+ if (hasMatch(source, /\bIDiamondCut\b/)) {
93
+ indicators.add("diamond-cut-interface")
94
+ }
95
+ if (hasMatch(source, /\bfacetAddress\s*\(/i)) {
96
+ indicators.add("facet-address")
97
+ }
98
+ if (hasMatch(source, /\bIDiamondLoupe\b/)) {
99
+ indicators.add("diamond-loupe")
100
+ }
101
+
102
+ if (hasMatch(source, /\bdelegatecall\b/)) {
103
+ indicators.add("delegatecall")
104
+ }
105
+ if (hasMatch(source, /\bfallback\s*\(/i)) {
106
+ indicators.add("fallback-function")
107
+ }
108
+ if (hasMatch(source, /\bProxy\b/)) {
109
+ indicators.add("proxy-keyword")
110
+ }
111
+ if (hasMatch(source, /\bERC1967\b/)) {
112
+ indicators.add("erc1967-keyword")
113
+ }
114
+
115
+ return indicators
116
+ }
117
+
118
+ function hasAny(indicators: Set<string>, candidates: string[]): boolean {
119
+ return candidates.some((candidate) => indicators.has(candidate))
120
+ }
121
+
122
+ function classifyProxyType(indicators: Set<string>): ProxyType | null {
123
+ if (
124
+ hasAny(indicators, ["diamond-cut", "diamond-cut-interface", "facet-address", "diamond-loupe"])
125
+ ) {
126
+ return "diamond"
127
+ }
128
+
129
+ if (
130
+ hasAny(indicators, ["uups-authorize-upgrade", "uups-upgrade-to-and-call", "uups-upgradeable"])
131
+ ) {
132
+ return "uups"
133
+ }
134
+
135
+ if (hasAny(indicators, ["beacon-interface", "beacon-proxy", "upgradeable-beacon"])) {
136
+ return "beacon"
137
+ }
138
+
139
+ if (
140
+ hasAny(indicators, [
141
+ "transparent-implementation-getter",
142
+ "transparent-admin-getter",
143
+ "transparent-set-implementation",
144
+ ])
145
+ ) {
146
+ return "transparent"
147
+ }
148
+
149
+ if (
150
+ hasAny(indicators, [
151
+ "erc1967-implementation-slot",
152
+ "erc1967-admin-slot",
153
+ "erc1967-beacon-slot",
154
+ "delegatecall",
155
+ ])
156
+ ) {
157
+ return "erc1967"
158
+ }
159
+
160
+ return null
161
+ }
162
+
163
+ export async function executeProxyDetection(
164
+ args: ProxyDetectionArgs,
165
+ context: ToolContext,
166
+ readFile: ReadFileFn = readWithBunFile,
167
+ ): Promise<ProxyDetectionResult> {
168
+ context.metadata({ title: `Detect proxy patterns: ${args.file_path}` })
169
+
170
+ const fileToRead =
171
+ args.project_dir && !isAbsolute(args.file_path)
172
+ ? join(args.project_dir, args.file_path)
173
+ : args.file_path
174
+
175
+ try {
176
+ const source = await readFile(fileToRead)
177
+ const indicators = collectIndicators(source)
178
+ const proxyType = classifyProxyType(indicators)
179
+ const indicatorList = [...indicators]
180
+ const isProxy = proxyType !== null
181
+
182
+ return {
183
+ file: args.file_path,
184
+ isProxy,
185
+ proxyType,
186
+ indicators: isProxy ? indicatorList : [],
187
+ confidence: computeConfidence(isProxy ? indicatorList.length : 0),
188
+ }
189
+ } catch (error) {
190
+ const maybeError = error as Error & { code?: string }
191
+ if (maybeError.code === "ENOENT") {
192
+ return {
193
+ file: args.file_path,
194
+ isProxy: false,
195
+ proxyType: null,
196
+ indicators: [],
197
+ confidence: "low",
198
+ error: `File not found: ${args.file_path}`,
199
+ }
200
+ }
201
+
202
+ return {
203
+ file: args.file_path,
204
+ isProxy: false,
205
+ proxyType: null,
206
+ indicators: [],
207
+ confidence: "low",
208
+ error: maybeError.message || "proxy detection failed",
209
+ }
210
+ }
211
+ }
212
+
213
+ export const proxyDetectionTool = tool({
214
+ description:
215
+ "Detects proxy patterns in Solidity contracts (ERC1967, UUPS, transparent, beacon, diamond) with confidence scoring.",
216
+ args: {
217
+ file_path: tool.schema.string(),
218
+ project_dir: tool.schema.string().optional(),
219
+ },
220
+ async execute(args, context) {
221
+ const result = await executeProxyDetection(args, context)
222
+ return JSON.stringify(result)
223
+ },
224
+ })