solidity-argus 0.1.8 → 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 (178) hide show
  1. package/AGENTS.md +3 -3
  2. package/README.md +229 -13
  3. package/package.json +37 -8
  4. package/skills/INVENTORY.md +88 -57
  5. package/skills/README.md +72 -6
  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/checklists/cyfrin-defi-core/SKILL.md +3 -0
  22. package/skills/manifests/cyfrin.json +16 -0
  23. package/skills/manifests/defifofum.json +25 -0
  24. package/skills/manifests/kadenzipfel.json +48 -0
  25. package/skills/manifests/scvd.json +9 -0
  26. package/skills/manifests/smartbugs.json +9 -0
  27. package/skills/manifests/solodit.json +9 -0
  28. package/skills/manifests/sunweb3sec.json +9 -0
  29. package/skills/manifests/trailofbits.json +9 -0
  30. package/skills/methodology/audit-workflow/SKILL.md +3 -0
  31. package/skills/protocol-patterns/amm-dex/SKILL.md +3 -0
  32. package/skills/references/exploit-reference/SKILL.md +3 -0
  33. package/skills/vulnerability-patterns/access-control/SKILL.md +27 -0
  34. package/skills/vulnerability-patterns/arbitrary-storage-location/SKILL.md +13 -1
  35. package/skills/vulnerability-patterns/assert-violation/SKILL.md +8 -1
  36. package/skills/vulnerability-patterns/asserting-contract-from-code-size/SKILL.md +12 -1
  37. package/skills/vulnerability-patterns/authorization-txorigin/SKILL.md +8 -1
  38. package/skills/vulnerability-patterns/cross-chain-bridge-vulnerabilities/SKILL.md +217 -0
  39. package/skills/vulnerability-patterns/default-visibility/SKILL.md +13 -1
  40. package/skills/vulnerability-patterns/delegatecall-untrusted-callee/SKILL.md +8 -1
  41. package/skills/vulnerability-patterns/dos-gas-limit/SKILL.md +8 -1
  42. package/skills/vulnerability-patterns/dos-revert/SKILL.md +14 -1
  43. package/skills/vulnerability-patterns/erc4626-exchange-rate-manipulation/SKILL.md +64 -0
  44. package/skills/vulnerability-patterns/fee-on-transfer-tokens/SKILL.md +93 -0
  45. package/skills/vulnerability-patterns/flash-loan-attacks/SKILL.md +13 -0
  46. package/skills/vulnerability-patterns/floating-pragma/SKILL.md +8 -1
  47. package/skills/vulnerability-patterns/front-running-attacks/SKILL.md +209 -0
  48. package/skills/vulnerability-patterns/gas-optimization-patterns/SKILL.md +203 -0
  49. package/skills/vulnerability-patterns/governance-attacks/SKILL.md +208 -0
  50. package/skills/vulnerability-patterns/hash-collision/SKILL.md +8 -1
  51. package/skills/vulnerability-patterns/inadherence-to-standards/SKILL.md +12 -1
  52. package/skills/vulnerability-patterns/incorrect-constructor/SKILL.md +8 -1
  53. package/skills/vulnerability-patterns/incorrect-inheritance-order/SKILL.md +8 -1
  54. package/skills/vulnerability-patterns/insufficient-gas-griefing/SKILL.md +12 -1
  55. package/skills/vulnerability-patterns/lack-of-precision/SKILL.md +7 -1
  56. package/skills/vulnerability-patterns/logic-errors/SKILL.md +10 -0
  57. package/skills/vulnerability-patterns/missing-parameter-bounds/SKILL.md +44 -0
  58. package/skills/vulnerability-patterns/missing-protection-signature-replay/SKILL.md +17 -1
  59. package/skills/vulnerability-patterns/msgvalue-loop/SKILL.md +12 -1
  60. package/skills/vulnerability-patterns/off-by-one/SKILL.md +7 -1
  61. package/skills/vulnerability-patterns/oracle-manipulation/SKILL.md +22 -0
  62. package/skills/vulnerability-patterns/outdated-compiler-version/SKILL.md +8 -1
  63. package/skills/vulnerability-patterns/overflow-underflow/SKILL.md +11 -1
  64. package/skills/vulnerability-patterns/proxy-vulnerabilities/SKILL.md +209 -0
  65. package/skills/vulnerability-patterns/reentrancy/SKILL.md +22 -0
  66. package/skills/vulnerability-patterns/shadowing-state-variables/SKILL.md +8 -1
  67. package/skills/vulnerability-patterns/share-accounting-desynchronization/SKILL.md +44 -0
  68. package/skills/vulnerability-patterns/signature-malleability/SKILL.md +11 -1
  69. package/skills/vulnerability-patterns/stateful-parameter-update-drift/SKILL.md +44 -0
  70. package/skills/vulnerability-patterns/unbounded-return-data/SKILL.md +12 -1
  71. package/skills/vulnerability-patterns/unchecked-return-values/SKILL.md +13 -1
  72. package/skills/vulnerability-patterns/unencrypted-private-data-on-chain/SKILL.md +8 -1
  73. package/skills/vulnerability-patterns/unexpected-ecrecover-null-address/SKILL.md +8 -1
  74. package/skills/vulnerability-patterns/uninitialized-storage-pointer/SKILL.md +8 -1
  75. package/skills/vulnerability-patterns/unsafe-erc20-transfers/SKILL.md +132 -0
  76. package/skills/vulnerability-patterns/unsafe-low-level-call/SKILL.md +12 -1
  77. package/skills/vulnerability-patterns/unsecure-signatures/SKILL.md +12 -1
  78. package/skills/vulnerability-patterns/unsupported-opcodes/SKILL.md +11 -1
  79. package/skills/vulnerability-patterns/unused-variables/SKILL.md +8 -1
  80. package/skills/vulnerability-patterns/use-of-deprecated-functions/SKILL.md +8 -1
  81. package/skills/vulnerability-patterns/weak-sources-randomness/SKILL.md +8 -1
  82. package/skills/vulnerability-patterns/weird-tokens/SKILL.md +10 -0
  83. package/skills/vulnerability-patterns/zero-address-misconfiguration/SKILL.md +48 -0
  84. package/src/agents/argus-prompt.ts +27 -10
  85. package/src/agents/pythia-prompt.ts +7 -8
  86. package/src/agents/scribe-prompt.ts +10 -5
  87. package/src/agents/sentinel-prompt.ts +36 -7
  88. package/src/cli/cli-output.ts +16 -0
  89. package/src/cli/cli-program.ts +29 -22
  90. package/src/cli/commands/check-skills.ts +135 -0
  91. package/src/cli/commands/doctor.ts +303 -23
  92. package/src/cli/commands/init.ts +8 -6
  93. package/src/cli/commands/install.ts +10 -8
  94. package/src/cli/commands/lint-skills.ts +118 -0
  95. package/src/cli/index.ts +5 -5
  96. package/src/cli/tui-prompts.ts +4 -2
  97. package/src/cli/types.ts +3 -3
  98. package/src/config/index.ts +1 -1
  99. package/src/config/loader.ts +4 -6
  100. package/src/config/schema.ts +6 -5
  101. package/src/config/types.ts +2 -2
  102. package/src/constants/defaults.ts +2 -0
  103. package/src/create-hooks.ts +225 -29
  104. package/src/create-managers.ts +10 -8
  105. package/src/create-tools.ts +14 -8
  106. package/src/features/background-agent/background-manager.ts +93 -87
  107. package/src/features/background-agent/index.ts +1 -1
  108. package/src/features/context-monitor/context-monitor.ts +3 -3
  109. package/src/features/context-monitor/index.ts +2 -2
  110. package/src/features/error-recovery/session-recovery.ts +2 -4
  111. package/src/features/error-recovery/tool-error-recovery.ts +79 -19
  112. package/src/features/index.ts +5 -5
  113. package/src/features/persistent-state/audit-state-manager.ts +158 -52
  114. package/src/features/persistent-state/global-run-index.ts +38 -0
  115. package/src/features/persistent-state/index.ts +1 -1
  116. package/src/features/persistent-state/run-journal.ts +86 -0
  117. package/src/hooks/agent-tracker.ts +53 -0
  118. package/src/hooks/compaction-hook.ts +46 -37
  119. package/src/hooks/config-handler.ts +31 -11
  120. package/src/hooks/context-budget.ts +42 -0
  121. package/src/hooks/event-hook.ts +48 -23
  122. package/src/hooks/hook-system.ts +4 -4
  123. package/src/hooks/index.ts +5 -5
  124. package/src/hooks/knowledge-sync-hook.ts +19 -21
  125. package/src/hooks/recon-context-builder.ts +66 -0
  126. package/src/hooks/safe-create-hook.ts +9 -11
  127. package/src/hooks/system-prompt-hook.ts +128 -0
  128. package/src/hooks/tool-tracking-hook.ts +162 -29
  129. package/src/hooks/types.ts +2 -1
  130. package/src/index.ts +23 -13
  131. package/src/knowledge/retry.ts +53 -0
  132. package/src/knowledge/scvd-client.ts +103 -83
  133. package/src/knowledge/scvd-errors.ts +89 -0
  134. package/src/knowledge/scvd-index.ts +110 -62
  135. package/src/knowledge/scvd-sync.ts +223 -47
  136. package/src/knowledge/source-manifest.ts +102 -0
  137. package/src/managers/index.ts +1 -1
  138. package/src/managers/types.ts +19 -14
  139. package/src/plugin-interface.ts +19 -8
  140. package/src/shared/binary-utils.ts +44 -34
  141. package/src/shared/deep-merge.ts +55 -36
  142. package/src/shared/file-utils.ts +21 -19
  143. package/src/shared/index.ts +11 -5
  144. package/src/shared/jsonc-parser.ts +123 -28
  145. package/src/shared/logger.ts +91 -17
  146. package/src/shared/project-utils.ts +30 -0
  147. package/src/skills/analysis/cluster.ts +414 -0
  148. package/src/skills/analysis/gates.ts +227 -0
  149. package/src/skills/analysis/index.ts +33 -0
  150. package/src/skills/analysis/normalize.ts +217 -0
  151. package/src/skills/analysis/similarity.ts +224 -0
  152. package/src/skills/argus-skill-resolver.ts +237 -0
  153. package/src/skills/skill-schema.ts +99 -0
  154. package/src/solodit-lifecycle.ts +202 -0
  155. package/src/state/audit-state.ts +10 -8
  156. package/src/state/finding-store.ts +68 -55
  157. package/src/state/types.ts +96 -44
  158. package/src/tools/argus-skill-load-tool.ts +78 -0
  159. package/src/tools/contract-analyzer-tool.ts +60 -77
  160. package/src/tools/forge-coverage-tool.ts +226 -0
  161. package/src/tools/forge-fuzz-tool.ts +127 -127
  162. package/src/tools/forge-test-tool.ts +153 -157
  163. package/src/tools/gas-analysis-tool.ts +264 -0
  164. package/src/tools/pattern-checker-tool.ts +206 -167
  165. package/src/tools/pattern-loader.ts +77 -0
  166. package/src/tools/pattern-schema.ts +51 -0
  167. package/src/tools/proxy-detection-tool.ts +224 -0
  168. package/src/tools/report-generator-tool.ts +333 -142
  169. package/src/tools/slither-tool.ts +300 -210
  170. package/src/tools/solodit-search-tool.ts +255 -80
  171. package/src/tools/sync-knowledge-tool.ts +7 -11
  172. package/src/utils/audit-artifact-detector.ts +118 -0
  173. package/src/utils/dependency-scanner.ts +93 -0
  174. package/src/utils/project-detector.ts +175 -86
  175. package/src/utils/solidity-parser.ts +112 -67
  176. package/src/utils/solodit-health.ts +29 -0
  177. package/src/hooks/event-hook-v2.ts +0 -99
  178. package/src/state/plugin-state.ts +0 -14
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Context budget allocation for Argus agents.
3
+ *
4
+ * Provides per-agent token budgets for system-prompt injection sizing.
5
+ * Under context pressure (>70%), budgets are reduced by 50% to prevent
6
+ * context window overflow during long audits.
7
+ *
8
+ * Decoupled from system-prompt-hook.ts — consumed by the hook when available.
9
+ */
10
+
11
+ const ARGUS_BUDGET = 2000
12
+ const SUBAGENT_BUDGET = 1000
13
+ const PRESSURE_THRESHOLD = 0.7
14
+ const PRESSURE_REDUCTION = 0.5
15
+
16
+ const ARGUS_AGENTS = new Set(["argus"])
17
+ const SUBAGENTS = new Set(["sentinel", "pythia", "scribe"])
18
+
19
+ /**
20
+ * Returns the token budget for a given agent, adjusted for context pressure.
21
+ *
22
+ * @param agent - Agent name (e.g. "argus", "sentinel", "pythia", "scribe")
23
+ * @param contextPressure - Current context usage ratio (0.0–1.0), from ContextMonitor
24
+ * @returns Token budget in tokens. 0 for non-Argus agents.
25
+ */
26
+ export function getTokenBudgetForAgent(agent: string, contextPressure: number = 0): number {
27
+ let budget: number
28
+
29
+ if (ARGUS_AGENTS.has(agent)) {
30
+ budget = ARGUS_BUDGET
31
+ } else if (SUBAGENTS.has(agent)) {
32
+ budget = SUBAGENT_BUDGET
33
+ } else {
34
+ return 0
35
+ }
36
+
37
+ if (contextPressure > PRESSURE_THRESHOLD) {
38
+ budget = Math.floor(budget * PRESSURE_REDUCTION)
39
+ }
40
+
41
+ return budget
42
+ }
@@ -1,32 +1,45 @@
1
- import type { AuditState } from "../state/types"
1
+ import { createLogger } from "../shared/logger"
2
2
  import { createAuditState } from "../state/audit-state"
3
+ import type { AuditState } from "../state/types"
4
+
5
+ export type AuditEventType =
6
+ | "session.created"
7
+ | "session.idle"
8
+ | "session.error"
9
+ | "session.deleted"
10
+ | "audit.phase-changed"
11
+ | "audit.finding-added"
12
+ | "audit.complete"
3
13
 
4
14
  export type EventHookFn = (input: {
5
- event: { type: string; sessionId?: string }
15
+ event: { type: string; sessionId?: string; properties?: Record<string, unknown> }
16
+ }) => Promise<void>
17
+
18
+ export type EventSubHandler = (event: {
19
+ type: string
20
+ sessionId?: string
21
+ auditState: AuditState | null
22
+ setAuditState: (state: AuditState | null) => void
6
23
  }) => Promise<void>
7
24
 
8
- /**
9
- * Creates a session lifecycle event hook that manages audit state.
10
- *
11
- * Returns the hook function plus accessors for reading/writing the
12
- * closure-held audit state. Other hooks (compaction, tool tracking,
13
- * system prompt) share the same state instance via these accessors.
14
- */
15
- export function createEventHook(projectDir?: string): {
25
+ export function createEventHook(
26
+ projectDir?: string,
27
+ subHandlers: EventSubHandler[] = [],
28
+ ): {
16
29
  hook: EventHookFn
17
30
  getAuditState: () => AuditState | null
18
31
  setAuditState: (state: AuditState | null) => void
19
32
  } {
33
+ const logger = createLogger()
20
34
  let currentAuditState: AuditState | null = null
21
35
 
22
36
  const getAuditState = (): AuditState | null => currentAuditState
23
-
24
37
  const setAuditState = (state: AuditState | null): void => {
25
38
  currentAuditState = state
26
39
  }
27
40
 
28
41
  const hook: EventHookFn = async (input): Promise<void> => {
29
- const { type } = input.event
42
+ const { type, sessionId } = input.event
30
43
 
31
44
  switch (type) {
32
45
  case "session.created": {
@@ -37,23 +50,23 @@ export function createEventHook(projectDir?: string): {
37
50
  }
38
51
 
39
52
  case "session.idle": {
40
- if (currentAuditState) {
41
- console.error(
42
- `[argus-state] Session idle — phase: ${currentAuditState.currentPhase}, findings: ${currentAuditState.findings.length}, contracts: ${currentAuditState.contractsReviewed.length}`
43
- )
44
- }
45
- break
46
- }
53
+ if (currentAuditState) {
54
+ logger.debug(
55
+ `Session idle — phase: ${currentAuditState.currentPhase}, findings: ${currentAuditState.findings.length}`,
56
+ )
57
+ }
58
+ break
59
+ }
47
60
 
48
61
  case "session.error": {
49
62
  if (currentAuditState) {
50
- console.error(
51
- `[argus-error] Session error — state snapshot: ${JSON.stringify({
63
+ logger.error(
64
+ `Session error — state snapshot: ${JSON.stringify({
52
65
  sessionId: currentAuditState.sessionId,
53
66
  phase: currentAuditState.currentPhase,
54
67
  findingsCount: currentAuditState.findings.length,
55
68
  contractsReviewed: currentAuditState.contractsReviewed,
56
- })}`
69
+ })}`,
57
70
  )
58
71
  }
59
72
  break
@@ -64,10 +77,22 @@ export function createEventHook(projectDir?: string): {
64
77
  break
65
78
  }
66
79
 
67
- // Unknown events: no-op — never throw
68
80
  default:
69
81
  break
70
82
  }
83
+
84
+ for (const handler of subHandlers) {
85
+ try {
86
+ await handler({
87
+ type,
88
+ sessionId,
89
+ auditState: currentAuditState,
90
+ setAuditState,
91
+ })
92
+ } catch (error) {
93
+ logger.error(`Sub-handler failed for event ${type}:`, error)
94
+ }
95
+ }
71
96
  }
72
97
 
73
98
  return { hook, getAuditState, setAuditState }
@@ -1,9 +1,9 @@
1
- import type { HookName } from "./types";
1
+ import type { HookName } from "./types"
2
2
 
3
3
  export function createHookGuard(disabledHooks: string[]) {
4
- const disabledSet = new Set(disabledHooks);
4
+ const disabledSet = new Set(disabledHooks)
5
5
 
6
6
  return function isHookEnabled(name: HookName): boolean {
7
- return !disabledSet.has(name);
8
- };
7
+ return !disabledSet.has(name)
8
+ }
9
9
  }
@@ -1,5 +1,5 @@
1
- export { createHookGuard } from "./hook-system";
2
- export { safeCreateHook } from "./safe-create-hook";
3
- export { createEventHookV2 } from "./event-hook-v2";
4
- export type { HookName } from "./types";
5
- export type { EventHookV2Fn, AuditEventType, EventSubHandler } from "./event-hook-v2";
1
+ export type { AuditEventType, EventHookFn, EventSubHandler } from "./event-hook"
2
+ export { createEventHook } from "./event-hook"
3
+ export { createHookGuard } from "./hook-system"
4
+ export { safeCreateHook } from "./safe-create-hook"
5
+ export type { HookName } from "./types"
@@ -1,8 +1,9 @@
1
1
  import os from "node:os"
2
2
  import path from "node:path"
3
- import { ScvdClient } from "../knowledge/scvd-client"
4
- import { syncIncremental, type SyncResult } from "../knowledge/scvd-sync"
5
3
  import type { ArgusConfig } from "../config/types"
4
+ import { ScvdClient } from "../knowledge/scvd-client"
5
+ import { type SyncResult, syncIncremental } from "../knowledge/scvd-sync"
6
+ import { createLogger } from "../shared/logger"
6
7
 
7
8
  export type KnowledgeSyncDependencies = {
8
9
  createClient?: (apiUrl: string) => unknown
@@ -18,14 +19,14 @@ function defaultDependencies(): Required<KnowledgeSyncDependencies> {
18
19
  syncIncrementalFn: async (client: unknown, indexPath: string) =>
19
20
  syncIncremental(client as ScvdClient, indexPath),
20
21
  log: (message: string) => {
21
- console.error(message)
22
- },
22
+ createLogger().info(message)
23
+ },
23
24
  }
24
25
  }
25
26
 
26
27
  export function createKnowledgeSyncHook(
27
28
  argusConfig: ArgusConfig,
28
- deps: KnowledgeSyncDependencies = {}
29
+ deps: KnowledgeSyncDependencies = {},
29
30
  ): () => void {
30
31
  const dependencies = { ...defaultDependencies(), ...deps }
31
32
 
@@ -35,23 +36,20 @@ export function createKnowledgeSyncHook(
35
36
  }
36
37
 
37
38
  const apiUrl = argusConfig.knowledge?.scvd?.apiUrl ?? DEFAULT_SCVD_API_URL
38
- const indexPath = path.join(
39
- os.homedir(),
40
- ".cache",
41
- "solidity-argus",
42
- "scvd-index.json"
43
- )
39
+ const indexPath = path.join(os.homedir(), ".cache", "solidity-argus", "scvd-index.json")
44
40
 
45
41
  Promise.resolve().then(async () => {
46
- try {
47
- const client = dependencies.createClient(apiUrl)
48
- const result = await dependencies.syncIncrementalFn(client, indexPath)
49
- if (result.newFindings > 0) {
50
- dependencies.log(
51
- `[argus] SCVD index updated: ${result.newFindings} new findings (total: ${result.totalIndexed})`
52
- )
53
- }
54
- } catch (_e) { /* non-critical: sync errors are logged above */ }
55
- })
42
+ try {
43
+ const client = dependencies.createClient(apiUrl)
44
+ const result = await dependencies.syncIncrementalFn(client, indexPath)
45
+ if (result.newFindings > 0) {
46
+ dependencies.log(
47
+ `[argus] SCVD index updated: ${result.newFindings} new findings (total: ${result.totalIndexed})`,
48
+ )
49
+ }
50
+ } catch (_e) {
51
+ createLogger().debug("Knowledge sync failed during auto-sync")
52
+ }
53
+ })
56
54
  }
57
55
  }
@@ -0,0 +1,66 @@
1
+ import type { AuditArtifact } from "../utils/audit-artifact-detector"
2
+ import type { DependencyRisk } from "../utils/dependency-scanner"
3
+ import type { ProjectConfig } from "../utils/project-detector"
4
+
5
+ export interface ReconContext {
6
+ projectConfig: ProjectConfig | null
7
+ dependencyRisks: DependencyRisk[]
8
+ auditArtifacts: AuditArtifact[]
9
+ }
10
+
11
+ /**
12
+ * Builds an XML-like reconnaissance context block from project data.
13
+ * Returns null if no data is available (all fields empty/null).
14
+ *
15
+ * The block is injected into compaction output so Argus agents retain
16
+ * project intelligence across context window compressions.
17
+ */
18
+ export function buildReconContextBlock(recon: ReconContext): string | null {
19
+ if (
20
+ !recon.projectConfig &&
21
+ recon.dependencyRisks.length === 0 &&
22
+ recon.auditArtifacts.length === 0
23
+ ) {
24
+ return null
25
+ }
26
+
27
+ const lines: string[] = ["<argus-recon>"]
28
+
29
+ if (recon.projectConfig) {
30
+ const frameworks: string[] = []
31
+ if (recon.projectConfig.hasFoundry) frameworks.push("Foundry")
32
+ if (recon.projectConfig.hasHardhat) frameworks.push("Hardhat")
33
+ if (frameworks.length > 0) {
34
+ lines.push(`Framework: ${frameworks.join(", ")}`)
35
+ }
36
+ if (recon.projectConfig.optimizer) {
37
+ lines.push(`Optimizer: runs=${recon.projectConfig.optimizer.runs}`)
38
+ }
39
+ if (recon.projectConfig.evmVersion) {
40
+ lines.push(`EVM Version: ${recon.projectConfig.evmVersion}`)
41
+ }
42
+ if (recon.projectConfig.isUpgradeable) {
43
+ lines.push(`Upgradeable: yes`)
44
+ }
45
+ if (recon.projectConfig.profiles && recon.projectConfig.profiles.length > 0) {
46
+ lines.push(`Profiles: ${recon.projectConfig.profiles.join(", ")}`)
47
+ }
48
+ }
49
+
50
+ if (recon.dependencyRisks.length > 0) {
51
+ lines.push("Dependency Risks:")
52
+ for (const risk of recon.dependencyRisks.slice(0, 5)) {
53
+ lines.push(` - ${risk.package}@${risk.version}: ${risk.risk}`)
54
+ }
55
+ }
56
+
57
+ if (recon.auditArtifacts.length > 0) {
58
+ lines.push("Existing Audit Artifacts:")
59
+ for (const artifact of recon.auditArtifacts.slice(0, 5)) {
60
+ lines.push(` - ${artifact.type}: ${artifact.path}`)
61
+ }
62
+ }
63
+
64
+ lines.push("</argus-recon>")
65
+ return lines.join("\n")
66
+ }
@@ -1,15 +1,13 @@
1
- export function safeCreateHook<T>(
2
- factory: () => T,
3
- hookName: string
4
- ): T | undefined {
1
+ import { createLogger } from "../shared/logger"
2
+
3
+ export function safeCreateHook<T>(factory: () => T, hookName: string): T | undefined {
5
4
  try {
6
- return factory();
5
+ return factory()
7
6
  } catch (error) {
8
- console.error(
9
- `[argus-hook-error] Failed to create hook "${hookName}": ${
10
- error instanceof Error ? error.message : String(error)
11
- }`
12
- );
13
- return undefined;
7
+ const logger = createLogger()
8
+ logger.error(
9
+ `Failed to create hook "${hookName}": ${error instanceof Error ? error.message : String(error)}`,
10
+ )
11
+ return undefined
14
12
  }
15
13
  }
@@ -0,0 +1,128 @@
1
+ import type { AuditState, FindingSeverity } from "../state/types"
2
+
3
+ const DEFAULT_TOKEN_BUDGET = 2000
4
+ const TOKENS_PER_CHAR = 4
5
+
6
+ export interface SystemPromptHookDeps {
7
+ getAuditState: () => AuditState | null
8
+ getAgentForSession: (sessionID: string) => string | undefined
9
+ isArgusAgent: (sessionID: string) => boolean
10
+ getContextPressure?: (systemText: string) => number
11
+ getTokenBudget?: (agent: string, contextPressure: number) => number
12
+ getEnforcerReminder?: (state: AuditState) => string | null
13
+ getReconBlock?: () => string | null
14
+ }
15
+
16
+ const FALLBACK_DIRECTIVES: Record<string, string> = {
17
+ slither:
18
+ "DO NOT re-attempt argus_slither_analyze. Use `argus_analyze_contract` and `argus_check_patterns` instead. Note limitation in report.",
19
+ forge:
20
+ "DO NOT re-attempt argus_forge_test or argus_forge_fuzz. Verify findings via manual code tracing. Note limitation in report.",
21
+ solodit:
22
+ "DO NOT re-attempt argus_solodit_search. Use `argus_check_patterns` with local rules. Note limitation in report.",
23
+ }
24
+
25
+ export function buildFallbackDirectives(unavailableTools: string[]): string[] {
26
+ const directives: string[] = []
27
+ for (const tool of unavailableTools) {
28
+ const directive = FALLBACK_DIRECTIVES[tool]
29
+ if (directive) directives.push(directive)
30
+ }
31
+ return directives
32
+ }
33
+
34
+ export function estimateTokens(text: string): number {
35
+ return Math.ceil(text.length / TOKENS_PER_CHAR)
36
+ }
37
+
38
+ export function buildDynamicContext(
39
+ auditState: AuditState,
40
+ agent: string,
41
+ tokenBudget: number = DEFAULT_TOKEN_BUDGET,
42
+ ): string {
43
+ const severityCounts: Record<FindingSeverity, number> = {
44
+ Critical: 0,
45
+ High: 0,
46
+ Medium: 0,
47
+ Low: 0,
48
+ Informational: 0,
49
+ }
50
+
51
+ for (const finding of auditState.findings) {
52
+ severityCounts[finding.severity]++
53
+ }
54
+
55
+ const tools = auditState.toolsExecuted.map((tool) => tool.tool).join(", ") || "none"
56
+ const unavailable = auditState.unavailableTools ?? []
57
+ const lines: string[] = [
58
+ `<argus-context agent="${agent}">`,
59
+ `Phase: ${auditState.currentPhase}`,
60
+ `Contracts: ${auditState.contractsReviewed.length} reviewed`,
61
+ `Findings: Critical=${severityCounts.Critical} High=${severityCounts.High} Medium=${severityCounts.Medium} Low=${severityCounts.Low} Info=${severityCounts.Informational}`,
62
+ `Tools: ${tools}`,
63
+ ]
64
+
65
+ if (unavailable.length > 0) {
66
+ lines.push(`Unavailable: ${unavailable.join(", ")}`)
67
+ lines.push(...buildFallbackDirectives(unavailable))
68
+ }
69
+
70
+ lines.push("</argus-context>")
71
+
72
+ let summary = lines.join("\n")
73
+
74
+ if (estimateTokens(summary) > tokenBudget) {
75
+ summary = [
76
+ `<argus-context agent="${agent}">`,
77
+ `Phase: ${auditState.currentPhase} | Findings: ${auditState.findings.length} | Contracts: ${auditState.contractsReviewed.length}`,
78
+ "</argus-context>",
79
+ ].join("\n")
80
+ }
81
+
82
+ return summary
83
+ }
84
+
85
+ export function createSystemPromptHook(deps: SystemPromptHookDeps) {
86
+ return async (
87
+ input: { sessionID?: string; model: unknown },
88
+ output: { system: string[] },
89
+ ): Promise<void> => {
90
+ if (!input.sessionID) {
91
+ return
92
+ }
93
+
94
+ if (!deps.isArgusAgent(input.sessionID)) {
95
+ return
96
+ }
97
+
98
+ const auditState = deps.getAuditState()
99
+ if (!auditState) {
100
+ return
101
+ }
102
+
103
+ const agent = deps.getAgentForSession(input.sessionID)
104
+ if (!agent) {
105
+ return
106
+ }
107
+
108
+ const currentSystem = output.system.join("\n")
109
+ const pressure = deps.getContextPressure?.(currentSystem) ?? 0
110
+ const budget = deps.getTokenBudget?.(agent, pressure) ?? DEFAULT_TOKEN_BUDGET
111
+
112
+ output.system.push(buildDynamicContext(auditState, agent, budget))
113
+
114
+ if (deps.getReconBlock) {
115
+ const reconBlock = deps.getReconBlock()
116
+ if (reconBlock && estimateTokens(reconBlock) <= budget) {
117
+ output.system.push(reconBlock)
118
+ }
119
+ }
120
+
121
+ if (agent === "argus" && deps.getEnforcerReminder) {
122
+ const reminder = deps.getEnforcerReminder(auditState)
123
+ if (reminder) {
124
+ output.system.push(reminder)
125
+ }
126
+ }
127
+ }
128
+ }