solidity-argus 0.2.0 → 0.3.2

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 (169) hide show
  1. package/AGENTS.md +3 -3
  2. package/README.md +93 -37
  3. package/package.json +34 -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 +34 -7
  75. package/src/agents/pythia-prompt.ts +13 -4
  76. package/src/agents/scribe-prompt.ts +20 -2
  77. package/src/agents/sentinel-prompt.ts +45 -5
  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 +6 -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/system-prompt-hook.ts +18 -1
  114. package/src/hooks/tool-tracking-hook.ts +110 -51
  115. package/src/hooks/types.ts +2 -1
  116. package/src/index.ts +24 -37
  117. package/src/knowledge/retry.ts +22 -22
  118. package/src/knowledge/scvd-client.ts +88 -95
  119. package/src/knowledge/scvd-errors.ts +35 -35
  120. package/src/knowledge/scvd-index.ts +78 -80
  121. package/src/knowledge/scvd-sync.ts +106 -101
  122. package/src/managers/index.ts +1 -1
  123. package/src/managers/types.ts +19 -14
  124. package/src/plugin-interface.ts +7 -9
  125. package/src/shared/binary-utils.ts +44 -35
  126. package/src/shared/deep-merge.ts +55 -36
  127. package/src/shared/file-utils.ts +21 -19
  128. package/src/shared/index.ts +11 -5
  129. package/src/shared/jsonc-parser.ts +123 -28
  130. package/src/shared/logger.ts +16 -3
  131. package/src/shared/project-utils.ts +30 -0
  132. package/src/skills/analysis/cluster.ts +414 -0
  133. package/src/skills/analysis/gates.ts +227 -0
  134. package/src/skills/analysis/index.ts +33 -0
  135. package/src/skills/analysis/normalize.ts +217 -0
  136. package/src/skills/analysis/similarity.ts +224 -0
  137. package/src/skills/argus-skill-resolver.ts +17 -6
  138. package/src/skills/skill-schema.ts +11 -10
  139. package/src/solodit-lifecycle.ts +203 -0
  140. package/src/state/audit-state.ts +8 -8
  141. package/src/state/finding-store.ts +68 -55
  142. package/src/state/types.ts +88 -67
  143. package/src/tools/argus-skill-load-tool.ts +12 -7
  144. package/src/tools/contract-analyzer-tool.ts +142 -77
  145. package/src/tools/forge-coverage-tool.ts +226 -0
  146. package/src/tools/forge-fuzz-tool.ts +127 -127
  147. package/src/tools/forge-test-tool.ts +201 -158
  148. package/src/tools/gas-analysis-tool.ts +264 -0
  149. package/src/tools/pattern-checker-tool.ts +203 -191
  150. package/src/tools/pattern-loader.ts +5 -111
  151. package/src/tools/pattern-schema.ts +3 -0
  152. package/src/tools/proxy-detection-tool.ts +224 -0
  153. package/src/tools/report-generator-tool.ts +305 -206
  154. package/src/tools/slither-tool.ts +266 -218
  155. package/src/tools/solodit-search-tool.ts +235 -119
  156. package/src/tools/sync-knowledge-tool.ts +7 -11
  157. package/src/utils/audit-artifact-detector.ts +28 -29
  158. package/src/utils/dependency-scanner.ts +37 -37
  159. package/src/utils/project-detector.ts +111 -124
  160. package/src/utils/solidity-parser.ts +175 -75
  161. package/skills/patterns/access-control.yaml +0 -31
  162. package/skills/patterns/erc4626.yaml +0 -29
  163. package/skills/patterns/flash-loan.yaml +0 -20
  164. package/skills/patterns/oracle.yaml +0 -30
  165. package/skills/patterns/proxy.yaml +0 -30
  166. package/skills/patterns/reentrancy.yaml +0 -30
  167. package/skills/patterns/signature.yaml +0 -31
  168. package/src/hooks/event-hook-v2.ts +0 -99
  169. package/src/state/plugin-state.ts +0 -14
@@ -1,177 +1,180 @@
1
- import { tool, type ToolContext } from "@opencode-ai/plugin";
1
+ import { type ToolContext, tool } from "@opencode-ai/plugin"
2
+ import { resolveProjectDir } from "../shared/project-utils"
2
3
 
3
4
  type ForgeFuzzArgs = {
4
- target?: string;
5
- match_test?: string;
6
- runs?: number;
7
- seed?: number;
8
- fork_url?: string;
9
- };
5
+ target?: string
6
+ match_test?: string
7
+ runs?: number
8
+ seed?: number
9
+ fork_url?: string
10
+ }
10
11
 
11
12
  type NormalizedForgeFuzzArgs = {
12
- target: string;
13
- match_test?: string;
14
- runs: number;
15
- seed?: number;
16
- fork_url?: string;
17
- };
13
+ target: string
14
+ match_test?: string
15
+ runs: number
16
+ seed?: number
17
+ fork_url?: string
18
+ }
18
19
 
19
20
  type ForgeFuzzResultItem = {
20
- testName: string;
21
- status: "pass" | "fail";
22
- runs: number;
23
- gas: number;
24
- };
21
+ testName: string
22
+ status: "pass" | "fail"
23
+ runs: number
24
+ gas: number
25
+ }
25
26
 
26
27
  type ForgeFuzzCounterexample = {
27
- testName: string;
28
- inputs: Record<string, string>;
29
- revertReason?: string;
30
- };
28
+ testName: string
29
+ inputs: Record<string, string>
30
+ revertReason?: string
31
+ }
31
32
 
32
33
  type ForgeFuzzResult = {
33
- success: boolean;
34
- results: ForgeFuzzResultItem[];
35
- counterexamples: ForgeFuzzCounterexample[];
36
- totalRuns: number;
37
- executionTime: number;
38
- error?: string;
39
- };
34
+ success: boolean
35
+ results: ForgeFuzzResultItem[]
36
+ counterexamples: ForgeFuzzCounterexample[]
37
+ totalRuns: number
38
+ executionTime: number
39
+ error?: string
40
+ }
40
41
 
41
42
  export type ForgeFuzzCommandResult = {
42
- stdout: string;
43
- stderr: string;
44
- exitCode: number;
45
- };
43
+ stdout: string
44
+ stderr: string
45
+ exitCode: number
46
+ }
46
47
 
47
48
  type RunForgeFuzzCommand = (
48
49
  command: string[],
49
50
  signal: AbortSignal,
50
51
  cwd: string,
51
- env: Record<string, string>
52
- ) => Promise<ForgeFuzzCommandResult>;
52
+ env: Record<string, string>,
53
+ ) => Promise<ForgeFuzzCommandResult>
53
54
 
54
- function normalizeArgs(args: ForgeFuzzArgs): NormalizedForgeFuzzArgs {
55
- const requestedRuns = typeof args.runs === "number" && Number.isFinite(args.runs) ? args.runs : 256;
56
- const clampedRuns = Math.max(1, Math.min(10000, Math.floor(requestedRuns)));
55
+ function normalizeArgs(args: ForgeFuzzArgs, context: ToolContext): NormalizedForgeFuzzArgs {
56
+ const requestedRuns =
57
+ typeof args.runs === "number" && Number.isFinite(args.runs) ? args.runs : 256
58
+ const clampedRuns = Math.max(1, Math.min(10000, Math.floor(requestedRuns)))
59
+ const target = args.target && args.target !== "." ? args.target : resolveProjectDir(context)
57
60
 
58
61
  return {
59
- target: args.target ?? ".",
62
+ target,
60
63
  match_test: args.match_test,
61
64
  runs: clampedRuns,
62
65
  seed: args.seed,
63
66
  fork_url: args.fork_url,
64
- };
67
+ }
65
68
  }
66
69
 
67
70
  function buildForgeFuzzCommand(args: NormalizedForgeFuzzArgs): string[] {
68
- const command = ["forge", "test", "--fuzz-runs", String(args.runs)];
71
+ const command = ["forge", "test", "--fuzz-runs", String(args.runs)]
69
72
 
70
73
  if (args.match_test) {
71
- command.push("--match-test", args.match_test);
74
+ command.push("--match-test", args.match_test)
72
75
  }
73
76
  if (typeof args.seed === "number" && Number.isFinite(args.seed)) {
74
- command.push("--fuzz-seed", String(Math.floor(args.seed)));
77
+ command.push("--fuzz-seed", String(Math.floor(args.seed)))
75
78
  }
76
79
  if (args.fork_url) {
77
- command.push("--fork-url", args.fork_url);
80
+ command.push("--fork-url", args.fork_url)
78
81
  }
79
82
 
80
- command.push("-v");
81
- return command;
83
+ command.push("-v")
84
+ return command
82
85
  }
83
86
 
84
87
  function parseNumber(input?: string): number {
85
88
  if (!input) {
86
- return 0;
89
+ return 0
87
90
  }
88
- const normalized = input.replaceAll("_", "").trim();
89
- const value = Number.parseInt(normalized, 10);
90
- return Number.isFinite(value) ? value : 0;
91
+ const normalized = input.replaceAll("_", "").trim()
92
+ const value = Number.parseInt(normalized, 10)
93
+ return Number.isFinite(value) ? value : 0
91
94
  }
92
95
 
93
96
  function splitArgsList(input: string): string[] {
94
- const values: string[] = [];
95
- let current = "";
96
- let depth = 0;
97
+ const values: string[] = []
98
+ let current = ""
99
+ let depth = 0
97
100
 
98
101
  for (let i = 0; i < input.length; i += 1) {
99
- const ch = input[i] ?? "";
102
+ const ch = input[i] ?? ""
100
103
  if (ch === "(" || ch === "[" || ch === "{") {
101
- depth += 1;
102
- current += ch;
103
- continue;
104
+ depth += 1
105
+ current += ch
106
+ continue
104
107
  }
105
108
  if (ch === ")" || ch === "]" || ch === "}") {
106
- depth = Math.max(0, depth - 1);
107
- current += ch;
108
- continue;
109
+ depth = Math.max(0, depth - 1)
110
+ current += ch
111
+ continue
109
112
  }
110
113
  if (ch === "," && depth === 0) {
111
- values.push(current.trim());
112
- current = "";
113
- continue;
114
+ values.push(current.trim())
115
+ current = ""
116
+ continue
114
117
  }
115
- current += ch;
118
+ current += ch
116
119
  }
117
120
 
118
121
  if (current.trim().length > 0) {
119
- values.push(current.trim());
122
+ values.push(current.trim())
120
123
  }
121
124
 
122
- return values.filter((value) => value.length > 0);
125
+ return values.filter((value) => value.length > 0)
123
126
  }
124
127
 
125
128
  function parseInputsFromArgs(argsBlob: string): Record<string, string> {
126
- const values = splitArgsList(argsBlob.trim());
127
- const inputs: Record<string, string> = {};
129
+ const values = splitArgsList(argsBlob.trim())
130
+ const inputs: Record<string, string> = {}
128
131
 
129
132
  values.forEach((value, index) => {
130
- inputs[`arg${index}`] = value;
131
- });
133
+ inputs[`arg${index}`] = value
134
+ })
132
135
 
133
- return inputs;
136
+ return inputs
134
137
  }
135
138
 
136
139
  function parseResultLine(line: string): ForgeFuzzResultItem | undefined {
137
140
  const match = line.match(
138
- /^\[(PASS|FAIL)[^\]]*\]\s*(.+?)\s*\(runs:\s*([\d_]+)(?:,\s*(?:\u03bc|mean):\s*([\d_]+))?/i
139
- );
141
+ /^\[(PASS|FAIL)[^\]]*\]\s*(.+?)\s*\(runs:\s*([\d_]+)(?:,\s*(?:\u03bc|mean):\s*([\d_]+))?/i,
142
+ )
140
143
  if (!match) {
141
- return undefined;
144
+ return undefined
142
145
  }
143
146
 
144
- const status = match[1]?.toUpperCase() === "PASS" ? "pass" : "fail";
147
+ const status = match[1]?.toUpperCase() === "PASS" ? "pass" : "fail"
145
148
  return {
146
149
  testName: (match[2] ?? "unknown-test").trim(),
147
150
  status,
148
151
  runs: parseNumber(match[3]),
149
152
  gas: parseNumber(match[4]),
150
- };
153
+ }
151
154
  }
152
155
 
153
156
  function parseCounterexampleLine(line: string):
154
157
  | {
155
- testName?: string;
156
- inputs: Record<string, string>;
158
+ testName?: string
159
+ inputs: Record<string, string>
157
160
  }
158
161
  | undefined {
159
162
  if (!line.includes("Counterexample:")) {
160
- return undefined;
163
+ return undefined
161
164
  }
162
165
 
163
- const argsMatch = line.match(/Counterexample:\s*.*?args=\((.*?)\)\]/);
166
+ const argsMatch = line.match(/Counterexample:\s*.*?args=\((.*?)\)\]/)
164
167
  if (!argsMatch) {
165
- return undefined;
168
+ return undefined
166
169
  }
167
170
 
168
- const trailing = line.match(/\]\s*(.+)$/);
169
- const possibleTest = trailing?.[1]?.replace(/\s*\(runs:.*$/, "").trim();
171
+ const trailing = line.match(/\]\s*(.+)$/)
172
+ const possibleTest = trailing?.[1]?.replace(/\s*\(runs:.*$/, "").trim()
170
173
 
171
174
  return {
172
175
  testName: possibleTest && possibleTest.length > 0 ? possibleTest : undefined,
173
176
  inputs: parseInputsFromArgs(argsMatch[1] ?? ""),
174
- };
177
+ }
175
178
  }
176
179
 
177
180
  const runForgeFuzzCommand: RunForgeFuzzCommand = async (command, signal, cwd, env) => {
@@ -181,29 +184,29 @@ const runForgeFuzzCommand: RunForgeFuzzCommand = async (command, signal, cwd, en
181
184
  stderr: "pipe",
182
185
  signal,
183
186
  env,
184
- });
187
+ })
185
188
 
186
189
  const [exitCode, stdout, stderr] = await Promise.all([
187
190
  child.exited,
188
191
  new Response(child.stdout).text(),
189
192
  new Response(child.stderr).text(),
190
- ]);
193
+ ])
191
194
 
192
195
  return {
193
196
  stdout,
194
197
  stderr,
195
198
  exitCode,
196
- };
197
- };
199
+ }
200
+ }
198
201
 
199
202
  export async function executeForgeFuzz(
200
203
  args: ForgeFuzzArgs,
201
204
  context: ToolContext,
202
- runCommand: RunForgeFuzzCommand = runForgeFuzzCommand
205
+ runCommand: RunForgeFuzzCommand = runForgeFuzzCommand,
203
206
  ): Promise<ForgeFuzzResult> {
204
- const startedAt = Date.now();
205
- const normalized = normalizeArgs(args);
206
- context.metadata({ title: `Run forge fuzz: ${normalized.target}` });
207
+ const startedAt = Date.now()
208
+ const normalized = normalizeArgs(args, context)
209
+ context.metadata({ title: `Run forge fuzz: ${normalized.target}` })
207
210
 
208
211
  const fail = (error: string): ForgeFuzzResult => ({
209
212
  success: false,
@@ -212,85 +215,82 @@ export async function executeForgeFuzz(
212
215
  totalRuns: 0,
213
216
  executionTime: Date.now() - startedAt,
214
217
  error,
215
- });
218
+ })
216
219
 
217
220
  try {
218
221
  const env = {
219
222
  ...Bun.env,
220
223
  FOUNDRY_FUZZ_RUNS: String(normalized.runs),
221
- };
224
+ }
222
225
 
223
226
  const runResult = await runCommand(
224
227
  buildForgeFuzzCommand(normalized),
225
228
  context.abort,
226
229
  normalized.target,
227
- env
228
- );
230
+ env,
231
+ )
229
232
 
230
233
  const lines = `${runResult.stdout}\n${runResult.stderr}`
231
234
  .split(/\r?\n/)
232
235
  .map((line) => line.trim())
233
- .filter((line) => line.length > 0);
236
+ .filter((line) => line.length > 0)
234
237
 
235
- const results: ForgeFuzzResultItem[] = [];
236
- const counterexamples: ForgeFuzzCounterexample[] = [];
237
- let lastTestName: string | undefined;
238
+ const results: ForgeFuzzResultItem[] = []
239
+ const counterexamples: ForgeFuzzCounterexample[] = []
240
+ let lastTestName: string | undefined
238
241
 
239
242
  for (let i = 0; i < lines.length; i += 1) {
240
- const line = lines[i] ?? "";
241
- const parsedResult = parseResultLine(line);
243
+ const line = lines[i] ?? ""
244
+ const parsedResult = parseResultLine(line)
242
245
  if (parsedResult) {
243
- results.push(parsedResult);
244
- lastTestName = parsedResult.testName;
246
+ results.push(parsedResult)
247
+ lastTestName = parsedResult.testName
245
248
  }
246
249
 
247
- const parsedCounterexample = parseCounterexampleLine(line);
250
+ const parsedCounterexample = parseCounterexampleLine(line)
248
251
  if (!parsedCounterexample) {
249
- continue;
252
+ continue
250
253
  }
251
254
 
252
- const fallbackName = parsedCounterexample.testName ?? lastTestName ?? "unknown-test";
253
- const nextLine = lines[i + 1] ?? "";
254
- const reasonMatch = nextLine.match(/^(?:Reason|Error):\s*(.+)$/i);
255
+ const fallbackName = parsedCounterexample.testName ?? lastTestName ?? "unknown-test"
256
+ const nextLine = lines[i + 1] ?? ""
257
+ const reasonMatch = nextLine.match(/^(?:Reason|Error):\s*(.+)$/i)
255
258
  counterexamples.push({
256
259
  testName: fallbackName,
257
260
  inputs: parsedCounterexample.inputs,
258
261
  ...(reasonMatch?.[1] ? { revertReason: reasonMatch[1].trim() } : {}),
259
- });
262
+ })
260
263
  }
261
264
 
262
- const totalRuns = results.reduce((sum, item) => sum + item.runs, 0);
263
- const failedCount = results.filter((item) => item.status === "fail").length;
265
+ const totalRuns = results.reduce((sum, item) => sum + item.runs, 0)
266
+ const failedCount = results.filter((item) => item.status === "fail").length
264
267
  const output: ForgeFuzzResult = {
265
268
  success: runResult.exitCode === 0 && failedCount === 0,
266
269
  results,
267
270
  counterexamples,
268
271
  totalRuns,
269
272
  executionTime: Date.now() - startedAt,
270
- };
273
+ }
271
274
 
272
275
  if (runResult.exitCode !== 0 && failedCount === 0) {
273
- output.error = runResult.stderr.trim() || `forge fuzz exited with code ${runResult.exitCode}`;
276
+ output.error = runResult.stderr.trim() || `forge fuzz exited with code ${runResult.exitCode}`
274
277
  }
275
278
 
276
- return output;
279
+ return output
277
280
  } catch (error) {
278
281
  if (context.abort.aborted || (error instanceof DOMException && error.name === "AbortError")) {
279
- return fail("forge fuzz aborted");
282
+ return fail("forge fuzz aborted")
280
283
  }
281
284
 
282
- const maybeError = error as Error & { code?: string };
285
+ const maybeError = error as Error & { code?: string }
283
286
  if (maybeError.code === "ENOENT") {
284
- return fail("Foundry not found. Install: curl -L https://foundry.paradigm.xyz | bash");
287
+ return fail("Foundry not found. Install: curl -L https://foundry.paradigm.xyz | bash")
285
288
  }
286
- if (
287
- maybeError.code === "ETIMEDOUT" ||
288
- maybeError.message.toLowerCase().includes("timed out")
289
- ) {
290
- return fail("forge fuzz timed out");
289
+ if (maybeError.code === "ETIMEDOUT" || maybeError.message.toLowerCase().includes("timed out")) {
290
+ return fail("forge fuzz timed out")
291
291
  }
292
292
 
293
- return fail(maybeError.message || "forge fuzz failed");
293
+ return fail(maybeError.message || "forge fuzz failed")
294
294
  }
295
295
  }
296
296
 
@@ -305,7 +305,7 @@ export const forgeFuzzTool = tool({
305
305
  fork_url: tool.schema.string().optional(),
306
306
  },
307
307
  async execute(args, context) {
308
- const result = await executeForgeFuzz(args, context);
309
- return JSON.stringify(result);
308
+ const result = await executeForgeFuzz(args, context)
309
+ return JSON.stringify(result)
310
310
  },
311
- });
311
+ })