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,20 +1,22 @@
1
- import { execSync } from "node:child_process"
2
1
  import { existsSync, readdirSync, readFileSync } from "node:fs"
3
2
  import { basename, dirname, extname, join } from "node:path"
4
- import type { CliCommand } from "../types"
5
- import type { ArgusConfig } from "../../config/types"
6
3
  import { loadArgusConfig } from "../../config/loader"
4
+ import type { ArgusConfig } from "../../config/types"
5
+ import { createLogger } from "../../shared/logger"
7
6
  import {
8
7
  getRequiredAuditSkills,
9
8
  normalizeSkillName,
9
+ type ResolvedSkill,
10
10
  resolveArgusSkills,
11
11
  resolveSkillRoots,
12
- type ResolvedSkill,
13
12
  } from "../../skills/argus-skill-resolver"
14
13
  import { parseFrontmatter, validateSkillFrontmatter } from "../../skills/skill-schema"
15
14
  import { detectViaIr } from "../../tools/slither-tool"
16
15
  import { checkSoloditHealth } from "../../utils/solodit-health"
17
16
  import { cliOutput } from "../cli-output"
17
+ import type { CliCommand } from "../types"
18
+
19
+ const logger = createLogger()
18
20
 
19
21
  const GREEN = "\x1b[32m"
20
22
  const RED = "\x1b[31m"
@@ -23,13 +25,15 @@ const RESET = "\x1b[0m"
23
25
 
24
26
  function checkBinary(name: string): { found: boolean; version: string | null } {
25
27
  try {
26
- const version = execSync(`${name} --version`, {
28
+ const result = Bun.spawnSync([name, "--version"], {
29
+ stdout: "pipe",
30
+ stderr: "pipe",
27
31
  timeout: 5000,
28
- stdio: ["pipe", "pipe", "pipe"],
29
32
  })
30
- .toString()
31
- .trim()
32
- .split("\n")[0] ?? null
33
+ if (result.exitCode !== 0) {
34
+ return { found: false, version: null }
35
+ }
36
+ const version = new TextDecoder().decode(result.stdout).trim().split("\n")[0] ?? null
33
37
  return { found: true, version }
34
38
  } catch {
35
39
  return { found: false, version: null }
@@ -70,7 +74,8 @@ export function findDuplicateSkills(
70
74
  const nameToSources = new Map<string, Set<string>>()
71
75
  for (const { name, source } of entries) {
72
76
  if (!nameToSources.has(name)) nameToSources.set(name, new Set())
73
- nameToSources.get(name)!.add(source)
77
+ const sources = nameToSources.get(name)
78
+ if (sources) sources.add(source)
74
79
  }
75
80
  return Array.from(nameToSources)
76
81
  .filter(([, sources]) => sources.size > 1)
@@ -112,9 +117,7 @@ export function buildSkillHealthReport(
112
117
  }
113
118
 
114
119
  const duplicates = duplicateEntries ? findDuplicateSkills(duplicateEntries) : []
115
- const missingCategories = REQUIRED_CATEGORIES.filter(
116
- (cat) => (categoryBreakdown[cat] ?? 0) === 0,
117
- )
120
+ const missingCategories = REQUIRED_CATEGORIES.filter((cat) => (categoryBreakdown[cat] ?? 0) === 0)
118
121
 
119
122
  return {
120
123
  categoryBreakdown,
@@ -146,6 +149,7 @@ function scanMarkdownFiles(dir: string, maxDepth = 8): string[] {
146
149
  }
147
150
  }
148
151
  } catch {
152
+ logger.debug("Failed to read directory during skill scan")
149
153
  }
150
154
  }
151
155
  return files
@@ -175,6 +179,7 @@ function collectAllSkillNames(
175
179
  const name = normalizeSkillName(rawName)
176
180
  if (name) entries.push({ name, source: root.source })
177
181
  } catch {
182
+ logger.debug("Failed to parse skill file frontmatter")
178
183
  }
179
184
  }
180
185
  }
@@ -184,7 +189,7 @@ function collectAllSkillNames(
184
189
  export const doctorCommand: CliCommand = {
185
190
  name: "doctor",
186
191
  description: "Check tool dependencies and configuration",
187
- async execute(args: string[]): Promise<number> {
192
+ async execute(_args: string[]): Promise<number> {
188
193
  const cwd = process.cwd()
189
194
  let hasFailure = false
190
195
 
@@ -202,7 +207,9 @@ export const doctorCommand: CliCommand = {
202
207
  if (forge.found) {
203
208
  cliOutput.log(`${GREEN}✓${RESET} Forge: installed (${forge.version})`)
204
209
  } else {
205
- cliOutput.log(`${RED}✗${RESET} Forge: not found — curl -L https://foundry.paradigm.xyz | bash`)
210
+ cliOutput.log(
211
+ `${RED}✗${RESET} Forge: not found — curl -L https://foundry.paradigm.xyz | bash`,
212
+ )
206
213
  hasFailure = true
207
214
  }
208
215
 
@@ -210,7 +217,9 @@ export const doctorCommand: CliCommand = {
210
217
  if (solcSelect.found) {
211
218
  cliOutput.log(`${GREEN}✓${RESET} solc-select: installed (${solcSelect.version})`)
212
219
  } else {
213
- cliOutput.log(`${YELLOW}⚠${RESET} solc-select: not found — pipx install solc-select (needed for via_ir flatten fallback)`)
220
+ cliOutput.log(
221
+ `${YELLOW}⚠${RESET} solc-select: not found — pipx install solc-select (needed for via_ir flatten fallback)`,
222
+ )
214
223
  }
215
224
 
216
225
  const projectType = checkSolidityProject(cwd)
@@ -221,9 +230,13 @@ export const doctorCommand: CliCommand = {
221
230
  }
222
231
 
223
232
  if (projectType === "foundry" && detectViaIr(cwd)) {
224
- cliOutput.log(`${YELLOW}⚠${RESET} via_ir: enabled in foundry.toml — Slither will use flatten fallback`)
233
+ cliOutput.log(
234
+ `${YELLOW}⚠${RESET} via_ir: enabled in foundry.toml — Slither will use flatten fallback`,
235
+ )
225
236
  if (!forge.found) {
226
- cliOutput.log(`${RED}✗${RESET} forge is required for via_ir flatten fallback but is missing`)
237
+ cliOutput.log(
238
+ `${RED}✗${RESET} forge is required for via_ir flatten fallback but is missing`,
239
+ )
227
240
  hasFailure = true
228
241
  }
229
242
  if (!solcSelect.found) {
@@ -241,9 +254,13 @@ export const doctorCommand: CliCommand = {
241
254
  const missingSkills = requiredSkills.filter((skillName) => !resolvedSkills.has(skillName))
242
255
 
243
256
  if (missingSkills.length === 0) {
244
- cliOutput.log(`${GREEN}✓${RESET} Skills: required audit skills resolvable (${requiredSkills.join(", ")})`)
257
+ cliOutput.log(
258
+ `${GREEN}✓${RESET} Skills: required audit skills resolvable (${requiredSkills.join(", ")})`,
259
+ )
245
260
  } else {
246
- cliOutput.log(`${RED}✗${RESET} Skills: missing required skills (${missingSkills.join(", ")})`)
261
+ cliOutput.log(
262
+ `${RED}✗${RESET} Skills: missing required skills (${missingSkills.join(", ")})`,
263
+ )
247
264
  hasFailure = true
248
265
  }
249
266
  } catch {
@@ -254,15 +271,21 @@ export const doctorCommand: CliCommand = {
254
271
  const missingSkills = requiredSkills.filter((skillName) => !resolvedSkills.has(skillName))
255
272
 
256
273
  if (missingSkills.length === 0) {
257
- cliOutput.log(`${GREEN}✓${RESET} Skills: required audit skills resolvable (${requiredSkills.join(", ")})`)
274
+ cliOutput.log(
275
+ `${GREEN}✓${RESET} Skills: required audit skills resolvable (${requiredSkills.join(", ")})`,
276
+ )
258
277
  } else {
259
- cliOutput.log(`${RED}✗${RESET} Skills: missing required skills (${missingSkills.join(", ")})`)
278
+ cliOutput.log(
279
+ `${RED}✗${RESET} Skills: missing required skills (${missingSkills.join(", ")})`,
280
+ )
260
281
  hasFailure = true
261
282
  }
262
283
  }
263
284
 
264
285
  try {
265
- const response = await fetch("https://api.scvd.dev/stats", { signal: AbortSignal.timeout(5000) })
286
+ const response = await fetch("https://api.scvd.dev/stats", {
287
+ signal: AbortSignal.timeout(5000),
288
+ })
266
289
  if (response.ok) {
267
290
  cliOutput.log(`${GREEN}✓${RESET} SCVD API: reachable`)
268
291
  } else {
@@ -296,9 +319,7 @@ export const doctorCommand: CliCommand = {
296
319
  const allEntries = collectAllSkillNames(cwd, config)
297
320
  const report = buildSkillHealthReport(healthSkills, allEntries)
298
321
 
299
- const catParts = ALL_CATEGORIES.map(
300
- (cat) => `${cat}: ${report.categoryBreakdown[cat] ?? 0}`,
301
- )
322
+ const catParts = ALL_CATEGORIES.map((cat) => `${cat}: ${report.categoryBreakdown[cat] ?? 0}`)
302
323
  cliOutput.log(`${GREEN}✓${RESET} Categories: ${catParts.join(", ")}`)
303
324
 
304
325
  const tierParts = Object.entries(report.trustTierBreakdown).map(
@@ -334,6 +355,7 @@ export const doctorCommand: CliCommand = {
334
355
  }
335
356
  } catch {
336
357
  cliOutput.log(`${RED}✗${RESET} Could not analyze skill health`)
358
+ hasFailure = true
337
359
  }
338
360
 
339
361
  return hasFailure ? 1 : 0
@@ -1,7 +1,7 @@
1
1
  import { existsSync, mkdirSync, writeFileSync } from "node:fs"
2
2
  import { join } from "node:path"
3
- import type { CliCommand } from "../types"
4
3
  import { cliOutput } from "../cli-output"
4
+ import type { CliCommand } from "../types"
5
5
 
6
6
  const GREEN = "\x1b[32m"
7
7
  const YELLOW = "\x1b[33m"
@@ -18,13 +18,15 @@ const DEFAULT_CONFIG = {
18
18
  export const initCommand: CliCommand = {
19
19
  name: "init",
20
20
  description: "Initialize Argus configuration for this project",
21
- async execute(args: string[]): Promise<number> {
21
+ async execute(_args: string[]): Promise<number> {
22
22
  const cwd = process.cwd()
23
23
  const configDir = join(cwd, ".opencode")
24
24
  const configPath = join(configDir, "solidity-argus.json")
25
25
 
26
26
  if (existsSync(configPath)) {
27
- cliOutput.error(`${YELLOW}⚠${RESET} Config already exists: ${configPath} — remove it first if you want to reinitialize.`)
27
+ cliOutput.error(
28
+ `${YELLOW}⚠${RESET} Config already exists: ${configPath} — remove it first if you want to reinitialize.`,
29
+ )
28
30
  return 1
29
31
  }
30
32
 
@@ -1,8 +1,8 @@
1
1
  import { existsSync, readFileSync, writeFileSync } from "node:fs"
2
- import { join } from "node:path"
3
2
  import { homedir } from "node:os"
4
- import type { CliCommand } from "../types"
3
+ import { join } from "node:path"
5
4
  import { cliOutput } from "../cli-output"
5
+ import type { CliCommand } from "../types"
6
6
 
7
7
  const GREEN = "\x1b[32m"
8
8
  const YELLOW = "\x1b[33m"
@@ -23,11 +23,13 @@ export function findOpencodeConfig(homeOverride?: string): string | null {
23
23
  export const installCommand: CliCommand = {
24
24
  name: "install",
25
25
  description: "Register solidity-argus in your OpenCode config",
26
- async execute(args: string[]): Promise<number> {
26
+ async execute(_args: string[]): Promise<number> {
27
27
  const configPath = findOpencodeConfig()
28
28
 
29
29
  if (!configPath) {
30
- cliOutput.error(`${YELLOW}⚠${RESET} opencode.json not found — create one first, or run: opencode init`)
30
+ cliOutput.error(
31
+ `${YELLOW}⚠${RESET} opencode.json not found — create one first, or run: opencode init`,
32
+ )
31
33
  return 1
32
34
  }
33
35
 
@@ -47,7 +49,7 @@ export const installCommand: CliCommand = {
47
49
 
48
50
  cliOutput.log(`${GREEN}✓${RESET} Added solidity-argus to ${configPath}`)
49
51
  return 0
50
- } catch (error) {
52
+ } catch (_error) {
51
53
  cliOutput.error(`${YELLOW}⚠${RESET} Failed to update ${configPath}`)
52
54
  return 1
53
55
  }
@@ -1,16 +1,20 @@
1
- import { readFileSync, readdirSync } from "node:fs"
2
- import { extname, join } from "node:path"
1
+ import { readdirSync, readFileSync } from "node:fs"
2
+ import { join } from "node:path"
3
+ import { createLogger } from "../../shared/logger"
3
4
  import type { CliCommand } from "../types"
5
+
6
+ const logger = createLogger()
7
+
8
+ import { loadArgusConfig } from "../../config/loader"
4
9
  import { resolveSkillRoots } from "../../skills/argus-skill-resolver"
5
10
  import { parseFrontmatter, validateSkillFrontmatter } from "../../skills/skill-schema"
6
- import { loadArgusConfig } from "../../config/loader"
7
11
  import { cliOutput } from "../cli-output"
8
12
 
9
13
  const GREEN = "\x1b[32m"
10
14
  const RED = "\x1b[31m"
11
15
  const RESET = "\x1b[0m"
12
16
 
13
- function findMarkdownFiles(dir: string, maxDepth = 8): string[] {
17
+ function findSkillFiles(dir: string, maxDepth = 8): string[] {
14
18
  const files: string[] = []
15
19
  const stack: Array<{ path: string; depth: number }> = [{ path: dir, depth: 0 }]
16
20
 
@@ -24,13 +28,11 @@ function findMarkdownFiles(dir: string, maxDepth = 8): string[] {
24
28
  const fullPath = join(current.path, entry.name)
25
29
  if (entry.isDirectory()) {
26
30
  stack.push({ path: fullPath, depth: current.depth + 1 })
27
- } else if (entry.isFile() && extname(entry.name).toLowerCase() === ".md") {
31
+ } else if (entry.isFile() && entry.name.toUpperCase() === "SKILL.MD") {
28
32
  files.push(fullPath)
29
33
  }
30
34
  }
31
- } catch {
32
- continue
33
- }
35
+ } catch {}
34
36
  }
35
37
 
36
38
  return files
@@ -77,26 +79,28 @@ export const lintSkillsCommand: CliCommand = {
77
79
  try {
78
80
  config = loadArgusConfig(cwd)
79
81
  } catch {
80
- // fallback to undefined, resolveSkillRoots handles this
82
+ logger.debug("Config load failed, using defaults")
81
83
  }
82
84
 
83
85
  const roots = resolveSkillRoots(cwd, config)
84
86
  const skillFiles: Array<{ path: string; content: string }> = []
85
87
 
86
88
  for (const root of roots) {
87
- const files = findMarkdownFiles(root.path)
89
+ const files = findSkillFiles(root.path)
88
90
  for (const file of files) {
89
91
  try {
90
92
  skillFiles.push({ path: file, content: readFileSync(file, "utf8") })
91
93
  } catch {
92
- // continue on read errors
94
+ logger.debug("Skipping unreadable skill file")
93
95
  }
94
96
  }
95
97
  }
96
98
 
97
99
  const result = lintSkillFiles(skillFiles)
98
100
 
99
- cliOutput.log(`Skill Lint: ${result.valid} valid, ${result.invalid} invalid, ${result.skipped} skipped (no frontmatter)`)
101
+ cliOutput.log(
102
+ `Skill Lint: ${result.valid} valid, ${result.invalid} invalid, ${result.skipped} skipped (no frontmatter)`,
103
+ )
100
104
 
101
105
  if (result.errors.length > 0) {
102
106
  for (const { file, errors } of result.errors) {
package/src/cli/index.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env bun
2
- import { createCliProgram } from "./cli-program";
2
+ import { createCliProgram } from "./cli-program"
3
3
 
4
- const program = createCliProgram();
5
- const args = Bun.argv.slice(2);
6
- const exitCode = await program.dispatch(args);
4
+ const program = createCliProgram()
5
+ const args = Bun.argv.slice(2)
6
+ const exitCode = await program.dispatch(args)
7
7
 
8
8
  // Set exit code without process.exit() so stdout flushes before termination
9
- process.exitCode = exitCode;
9
+ process.exitCode = exitCode
package/src/cli/types.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Defines the contract for all CLI subcommands
4
4
  */
5
5
  export interface CliCommand {
6
- name: string;
7
- description: string;
8
- execute: (args: string[]) => Promise<number>;
6
+ name: string
7
+ description: string
8
+ execute: (args: string[]) => Promise<number>
9
9
  }
@@ -1,3 +1,3 @@
1
+ export { loadArgusConfig } from "./loader"
1
2
  export { ArgusConfigSchema } from "./schema"
2
3
  export type { ArgusConfig } from "./types"
3
- export { loadArgusConfig } from "./loader"
@@ -1,10 +1,10 @@
1
1
  import { homedir } from "node:os"
2
2
  import { join } from "node:path"
3
- import { ArgusConfigSchema } from "./schema"
4
- import type { ArgusConfig } from "./types"
5
- import { detectConfigFile, readJsoncFile } from "../shared/file-utils"
6
3
  import { deepMerge } from "../shared/deep-merge"
4
+ import { detectConfigFile, readJsoncFile } from "../shared/file-utils"
7
5
  import { createLogger } from "../shared/logger"
6
+ import { ArgusConfigSchema } from "./schema"
7
+ import type { ArgusConfig } from "./types"
8
8
 
9
9
  export function _mergeConfigs(
10
10
  userRaw: Record<string, unknown> | null,
@@ -28,9 +28,7 @@ export function loadArgusConfig(projectDir: string): ArgusConfig {
28
28
  const userRaw = userConfigInfo.path ? readJsoncFile(userConfigInfo.path) : null
29
29
 
30
30
  const projectConfigInfo = detectConfigFile(projectDir)
31
- const projectRaw = projectConfigInfo.path
32
- ? readJsoncFile(projectConfigInfo.path)
33
- : null
31
+ const projectRaw = projectConfigInfo.path ? readJsoncFile(projectConfigInfo.path) : null
34
32
 
35
33
  return _mergeConfigs(userRaw, projectRaw)
36
34
  }
@@ -2,6 +2,7 @@ import { z } from "zod"
2
2
 
3
3
  const AgentConfigSchema = z.object({
4
4
  model: z.string().optional(),
5
+ steps: z.number().positive().optional(),
5
6
  permission: z.record(z.string(), z.any()).optional(),
6
7
  tools: z.record(z.string(), z.boolean()).optional(),
7
8
  })
@@ -28,13 +29,12 @@ const KnowledgeConfigSchema = z.object({
28
29
 
29
30
  const ReportingConfigSchema = z.object({
30
31
  format: z.enum(["markdown"]).default("markdown"),
31
- severityThreshold: z
32
- .enum(["critical", "high", "medium", "low", "informational"])
33
- .default("low"),
32
+ severityThreshold: z.enum(["critical", "high", "medium", "low", "informational"]).default("low"),
34
33
  gasAnalysis: z.boolean().default(false),
34
+ output_dir: z.string().default(".opencode/reports/"),
35
35
  })
36
36
 
37
- const SolditConfigSchema = z.object({
37
+ const SoloditConfigSchema = z.object({
38
38
  enabled: z.boolean().default(true),
39
39
  port: z.number().default(3000),
40
40
  })
@@ -70,8 +70,9 @@ export const ArgusConfigSchema = z.object({
70
70
  format: "markdown",
71
71
  severityThreshold: "low",
72
72
  gasAnalysis: false,
73
+ output_dir: ".opencode/reports/",
73
74
  }),
74
- solodit: SolditConfigSchema.default({
75
+ solodit: SoloditConfigSchema.default({
75
76
  enabled: true,
76
77
  port: 3000,
77
78
  }),
@@ -1,4 +1,4 @@
1
- import { z } from "zod"
2
- import { ArgusConfigSchema } from "./schema"
1
+ import type { z } from "zod"
2
+ import type { ArgusConfigSchema } from "./schema"
3
3
 
4
4
  export type ArgusConfig = z.infer<typeof ArgusConfigSchema>
@@ -4,3 +4,5 @@ export const DEFAULT_MODELS = {
4
4
  pythia: "anthropic/claude-sonnet-4-6",
5
5
  scribe: "anthropic/claude-sonnet-4-6",
6
6
  } as const
7
+
8
+ export const DEFAULT_STEPS = 50 as const