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.
- package/AGENTS.md +3 -3
- package/README.md +93 -37
- package/package.json +34 -7
- package/skills/INVENTORY.md +88 -57
- package/skills/README.md +26 -23
- package/skills/case-studies/beanstalk-governance/SKILL.md +52 -0
- package/skills/case-studies/bzx-flash-loan/SKILL.md +53 -0
- package/skills/case-studies/cream-finance/SKILL.md +52 -0
- package/skills/case-studies/curve-reentrancy/SKILL.md +52 -0
- package/skills/case-studies/dao-hack/SKILL.md +51 -0
- package/skills/case-studies/euler-finance/SKILL.md +52 -0
- package/skills/case-studies/harvest-finance/SKILL.md +52 -0
- package/skills/case-studies/level-finance/SKILL.md +51 -0
- package/skills/case-studies/mango-markets/SKILL.md +53 -0
- package/skills/case-studies/nomad-bridge/SKILL.md +51 -0
- package/skills/case-studies/parity-multisig/SKILL.md +55 -0
- package/skills/case-studies/poly-network/SKILL.md +51 -0
- package/skills/case-studies/rari-fuse/SKILL.md +51 -0
- package/skills/case-studies/ronin-bridge/SKILL.md +52 -0
- package/skills/case-studies/wormhole-bridge/SKILL.md +51 -0
- package/skills/manifests/smartbugs.json +1 -3
- package/skills/manifests/sunweb3sec.json +1 -3
- package/skills/vulnerability-patterns/access-control/SKILL.md +14 -0
- package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +13 -1
- package/skills/vulnerability-patterns/assert-violation/SKILL.md +8 -1
- package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +12 -1
- package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +2 -1
- package/skills/vulnerability-patterns/cross-chain-bridge-vulnerabilities/SKILL.md +217 -0
- package/skills/vulnerability-patterns/default-visibility/SKILL.md +13 -1
- package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +2 -1
- package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +8 -1
- package/skills/vulnerability-patterns/dos-revert/SKILL.md +1 -0
- package/skills/vulnerability-patterns/erc4626-exchange-rate-manipulation/SKILL.md +64 -0
- package/skills/vulnerability-patterns/fee-on-transfer-tokens/SKILL.md +93 -0
- package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +1 -0
- package/skills/vulnerability-patterns/floating-pragma/SKILL.md +8 -1
- package/skills/vulnerability-patterns/front-running-attacks/SKILL.md +209 -0
- package/skills/vulnerability-patterns/gas-optimization-patterns/SKILL.md +203 -0
- package/skills/vulnerability-patterns/governance-attacks/SKILL.md +208 -0
- package/skills/vulnerability-patterns/hash-collision/SKILL.md +8 -1
- package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +12 -1
- package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +8 -1
- package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +8 -1
- package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +12 -1
- package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +7 -1
- package/skills/vulnerability-patterns/logic-errors/SKILL.md +10 -0
- package/skills/vulnerability-patterns/missing-parameter-bounds/SKILL.md +44 -0
- package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +17 -1
- package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +12 -1
- package/skills/vulnerability-patterns/off-by-one/SKILL.md +7 -1
- package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +9 -0
- package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +8 -1
- package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +1 -0
- package/skills/vulnerability-patterns/proxy-vulnerabilities/SKILL.md +209 -0
- package/skills/vulnerability-patterns/reentrancy/SKILL.md +9 -0
- package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +8 -1
- package/skills/vulnerability-patterns/share-accounting-desynchronization/SKILL.md +44 -0
- package/skills/vulnerability-patterns/signature-malleability/SKILL.md +2 -1
- package/skills/vulnerability-patterns/stateful-parameter-update-drift/SKILL.md +44 -0
- package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +12 -1
- package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +2 -1
- package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +8 -1
- package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +8 -1
- package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +8 -1
- package/skills/vulnerability-patterns/unsafe-erc20-transfers/SKILL.md +132 -0
- package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +12 -1
- package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +12 -1
- package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +11 -1
- package/skills/vulnerability-patterns/unused-variables/SKILL.md +8 -1
- package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +8 -1
- package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +8 -1
- package/skills/vulnerability-patterns/weird-tokens/SKILL.md +10 -0
- package/skills/vulnerability-patterns/zero-address-misconfiguration/SKILL.md +48 -0
- package/src/agents/argus-prompt.ts +34 -7
- package/src/agents/pythia-prompt.ts +13 -4
- package/src/agents/scribe-prompt.ts +20 -2
- package/src/agents/sentinel-prompt.ts +45 -5
- package/src/cli/cli-program.ts +29 -26
- package/src/cli/commands/check-skills.ts +135 -0
- package/src/cli/commands/doctor.ts +48 -26
- package/src/cli/commands/init.ts +5 -3
- package/src/cli/commands/install.ts +7 -5
- package/src/cli/commands/lint-skills.ts +16 -12
- package/src/cli/index.ts +5 -5
- package/src/cli/types.ts +3 -3
- package/src/config/index.ts +1 -1
- package/src/config/loader.ts +4 -6
- package/src/config/schema.ts +6 -5
- package/src/config/types.ts +2 -2
- package/src/constants/defaults.ts +2 -0
- package/src/create-hooks.ts +145 -34
- package/src/create-managers.ts +10 -8
- package/src/create-tools.ts +13 -9
- package/src/features/background-agent/background-manager.ts +93 -87
- package/src/features/background-agent/index.ts +1 -1
- package/src/features/context-monitor/context-monitor.ts +3 -3
- package/src/features/context-monitor/index.ts +2 -2
- package/src/features/error-recovery/session-recovery.ts +2 -4
- package/src/features/error-recovery/tool-error-recovery.ts +12 -7
- package/src/features/index.ts +5 -5
- package/src/features/persistent-state/audit-state-manager.ts +143 -60
- package/src/features/persistent-state/global-run-index.ts +38 -0
- package/src/features/persistent-state/index.ts +1 -1
- package/src/features/persistent-state/run-journal.ts +86 -0
- package/src/hooks/config-handler.ts +28 -11
- package/src/hooks/context-budget.ts +2 -5
- package/src/hooks/event-hook.ts +47 -23
- package/src/hooks/hook-system.ts +4 -4
- package/src/hooks/index.ts +5 -5
- package/src/hooks/knowledge-sync-hook.ts +18 -21
- package/src/hooks/recon-context-builder.ts +2 -2
- package/src/hooks/safe-create-hook.ts +6 -7
- package/src/hooks/system-prompt-hook.ts +18 -1
- package/src/hooks/tool-tracking-hook.ts +110 -51
- package/src/hooks/types.ts +2 -1
- package/src/index.ts +24 -37
- package/src/knowledge/retry.ts +22 -22
- package/src/knowledge/scvd-client.ts +88 -95
- package/src/knowledge/scvd-errors.ts +35 -35
- package/src/knowledge/scvd-index.ts +78 -80
- package/src/knowledge/scvd-sync.ts +106 -101
- package/src/managers/index.ts +1 -1
- package/src/managers/types.ts +19 -14
- package/src/plugin-interface.ts +7 -9
- package/src/shared/binary-utils.ts +44 -35
- package/src/shared/deep-merge.ts +55 -36
- package/src/shared/file-utils.ts +21 -19
- package/src/shared/index.ts +11 -5
- package/src/shared/jsonc-parser.ts +123 -28
- package/src/shared/logger.ts +16 -3
- package/src/shared/project-utils.ts +30 -0
- package/src/skills/analysis/cluster.ts +414 -0
- package/src/skills/analysis/gates.ts +227 -0
- package/src/skills/analysis/index.ts +33 -0
- package/src/skills/analysis/normalize.ts +217 -0
- package/src/skills/analysis/similarity.ts +224 -0
- package/src/skills/argus-skill-resolver.ts +17 -6
- package/src/skills/skill-schema.ts +11 -10
- package/src/solodit-lifecycle.ts +203 -0
- package/src/state/audit-state.ts +8 -8
- package/src/state/finding-store.ts +68 -55
- package/src/state/types.ts +88 -67
- package/src/tools/argus-skill-load-tool.ts +12 -7
- package/src/tools/contract-analyzer-tool.ts +142 -77
- package/src/tools/forge-coverage-tool.ts +226 -0
- package/src/tools/forge-fuzz-tool.ts +127 -127
- package/src/tools/forge-test-tool.ts +201 -158
- package/src/tools/gas-analysis-tool.ts +264 -0
- package/src/tools/pattern-checker-tool.ts +203 -191
- package/src/tools/pattern-loader.ts +5 -111
- package/src/tools/pattern-schema.ts +3 -0
- package/src/tools/proxy-detection-tool.ts +224 -0
- package/src/tools/report-generator-tool.ts +305 -206
- package/src/tools/slither-tool.ts +266 -218
- package/src/tools/solodit-search-tool.ts +235 -119
- package/src/tools/sync-knowledge-tool.ts +7 -11
- package/src/utils/audit-artifact-detector.ts +28 -29
- package/src/utils/dependency-scanner.ts +37 -37
- package/src/utils/project-detector.ts +111 -124
- package/src/utils/solidity-parser.ts +175 -75
- package/skills/patterns/access-control.yaml +0 -31
- package/skills/patterns/erc4626.yaml +0 -29
- package/skills/patterns/flash-loan.yaml +0 -20
- package/skills/patterns/oracle.yaml +0 -30
- package/skills/patterns/proxy.yaml +0 -30
- package/skills/patterns/reentrancy.yaml +0 -30
- package/skills/patterns/signature.yaml +0 -31
- package/src/hooks/event-hook-v2.ts +0 -99
- 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
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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)
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
257
|
+
cliOutput.log(
|
|
258
|
+
`${GREEN}✓${RESET} Skills: required audit skills resolvable (${requiredSkills.join(", ")})`,
|
|
259
|
+
)
|
|
245
260
|
} else {
|
|
246
|
-
cliOutput.log(
|
|
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(
|
|
274
|
+
cliOutput.log(
|
|
275
|
+
`${GREEN}✓${RESET} Skills: required audit skills resolvable (${requiredSkills.join(", ")})`,
|
|
276
|
+
)
|
|
258
277
|
} else {
|
|
259
|
-
cliOutput.log(
|
|
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", {
|
|
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
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -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(
|
|
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(
|
|
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
|
|
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(
|
|
26
|
+
async execute(_args: string[]): Promise<number> {
|
|
27
27
|
const configPath = findOpencodeConfig()
|
|
28
28
|
|
|
29
29
|
if (!configPath) {
|
|
30
|
-
cliOutput.error(
|
|
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 (
|
|
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 {
|
|
2
|
-
import {
|
|
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
|
|
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() &&
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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
package/src/config/index.ts
CHANGED
package/src/config/loader.ts
CHANGED
|
@@ -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
|
}
|
package/src/config/schema.ts
CHANGED
|
@@ -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
|
|
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:
|
|
75
|
+
solodit: SoloditConfigSchema.default({
|
|
75
76
|
enabled: true,
|
|
76
77
|
port: 3000,
|
|
77
78
|
}),
|
package/src/config/types.ts
CHANGED