PentestCode — AI pentesting agent, hard fork of OpenCode (dev branch, MIT). OpenCode — coding agent (183k stars). We stripped code-editing focus and rebuilt it for penetration testing.
Stack: TypeScript, Bun, Effect library, Turbo monorepo. TUI: React/Ink (via @opentui). LLM: ai-sdk (20+ providers). DB: SQLite (drizzle-orm).
opencode-fork/ # Will be renamed to pentestcode
├── packages/
│ ├── core/src/ # Domain logic, tools, sessions
│ │ ├── engagement/ # NEW: pentest state (schema, store, context)
│ │ ├── session/ # Session management (runner, compaction)
│ │ ├── tool/ # Tool registry + built-in tools
│ │ ├── system-context/ # Context sources injected into prompts
│ │ ├── skill/ # Skill discovery + loading
│ │ └── agent.ts # Agent schema, default ID = "pentest"
│ ├── opencode/src/ # Main application package
│ │ ├── agent/agent.ts # Agent definitions (pentest/recon/scanner/...)
│ │ ├── session/prompt/*.txt # System prompts per agent
│ │ ├── session/system.ts # Prompt assembly (uses pentest.txt)
│ │ ├── session/prompt.ts # Session runner loop
│ │ ├── tool/ # Tool implementations (registry.ts is master)
│ │ ├── skill/ # Skill discovery paths
│ │ └── cli/ # CLI commands (yargs)
│ ├── llm/ # LLM client abstraction (ai-sdk)
│ ├── tui/ # Terminal UI (React/Ink)
│ ├── server/ # HTTP server + SSE events
│ ├── schema/ # Shared type definitions
│ ├── protocol/ # HTTP API contracts
│ ├── plugin/ # Plugin system
│ └── client/ # Generated SDK client
├── skills/
│ ├── phases/ # Phase checklists (6 files)
│ ├── services/ # Service knowledge packs (9 files)
│ └── playbooks/ # Methodology playbooks (4 files)
└── ...
| Agent | Type | Description |
|---|---|---|
| pentest | primary (default) | Strategist-coordinator. Plans, spawns subagents, can execute directly. |
| recon | primary | Reconnaissance (passive/active by user choice). |
| scanner | subagent | Port/vuln scanning. Spawned for parallel host scanning. |
| enumerator | subagent | Deep service enumeration (SMB/LDAP/web/etc). |
| exploiter | subagent | Exploitation of specific vulnerabilities. |
| reporter | subagent | Report generation from engagement state. No bash. |
| identity | subagent | AD, LDAP, Kerberos, IAM, NTLM, certificate-based auth attacks. |
| infrastructure | subagent | Network services, SNMP, IPMI, RDP, SSH, FTP, databases, misconfigs. |
| post_exploit | subagent | Lateral movement, privesc, persistence, credential harvesting, pivoting. |
| exploit_dev | subagent | Custom exploits, payload generation, PoC development, bypass techniques. |
| critic | subagent | Finding validator. Checks false positives, validates evidence. Read-only. |
| webapp | subagent | Web application specialist. OWASP Top 10, API security, XSS, SQLi, SSRF. |
| compaction | hidden | Context compression (inherited from OpenCode). |
| title | hidden | Session title generation. |
| summary | hidden | Session summary. |
Agents defined in: packages/opencode/src/agent/agent.ts
Prompts in: packages/opencode/src/session/prompt/*.txt and packages/opencode/src/agent/prompt/*.txt
- Agent loop:
packages/opencode/src/session/prompt.ts→runLoop()(line ~1081) - LLM call:
packages/core/src/session/runner/llm.ts - Tool registry:
packages/opencode/src/tool/registry.ts(master registry) - Tool definitions (core):
packages/core/src/tool/builtins.ts - System prompt assembly:
packages/opencode/src/session/system.ts - Agent definitions:
packages/opencode/src/agent/agent.ts - Engagement schema:
packages/core/src/engagement/schema.ts(Effect Schema, State/Host/Vuln/Cred types) - Engagement store:
packages/core/src/engagement/store.ts(global Ref + JSON persistence) - Engagement context (V2):
packages/core/src/engagement/context.ts(SystemContext source, V2 only) - Pentest tools:
packages/opencode/src/tool/state-query.ts,state-update.ts,nmap-parse.ts,nuclei-parse.ts,gobuster-parse.ts,cme-parse.ts,bloodhound-parse.ts,cred-spray.ts,scope-check.ts,phase-control.ts,report-gen.ts,sqlmap-parse.ts,xss-detect.ts,jwt-analyze.ts,tunnel-manage.ts,attack-path-suggest.ts - App runtime (V1):
packages/opencode/src/effect/app-runtime.ts(LayerNode graph) - Location services (V2):
packages/core/src/location-services.ts(V2 layer graph) - Config:
.pentestcode/pentestcode.jsonc - Skills:
skills/directory, discovered via**/SKILL.mdglob
# Install dependencies
bun install
# Run (dev mode)
bun run dev
# Run TUI directly
bun run --cwd packages/opencode --conditions=browser src/index.ts
# Typecheck
bun turbo typecheck- Removed 12 packages: codemode, desktop, enterprise, storybook, docs, console, slack, stats, web, sdks, infra, artifacts
- Removed translated READMEs, SST config, Nix files
- Updated package.json: name→pentestcode, description→pentesting
- Removed edit/apply-patch/todowrite from core builtins
- Removed GitHub Copilot integration from core
- 178M → 109M
- Agents rewritten: build/plan/general/explore → pentest/recon/scanner/enumerator/exploiter/reporter
- Default agent: "pentest" everywhere (was "build")
- Prompts created: pentest.txt, recon.txt, scanner.txt, enumerator.txt, exploiter.txt, reporter.txt + mode switching prompts
- system.ts: Always uses pentest.txt (bypasses model-specific coding prompts)
- Engagement state module: schema.ts (Effect Schema models), store.ts (file persistence), context.ts (SystemContext source)
- 19 skill files: 6 phases, 9 services, 4 playbooks
- Global engagement store:
~/.pentestcode/engagements/— NOT per-directory (security work isn't tied to a cwd) - EngagementStore.node registered in V1 app-runtime (
app-runtime.ts) and V2 location-services (location-services.ts) - Engagement context injected into V1 system prompt (
prompt.ts) — compact JSON with phase, mode, hosts, vulns - Auto-load: session start loads last engagement from
.lastfile - Skills config:
.pentestcode/pentestcode.jsonchasskills.paths: ["./skills"]. Relative skill paths resolve against the session cwd, each config-dir project root, and the global~/.pentestcode/skillshome — so bundled skills load regardless of cwd (seepackages/opencode/src/skill/index.tsdiscoverSkills)
- state_query — query engagement state (11 query types: summary, hosts, vulns, creds, scope, phase, flags, tasks, host, full, engagements)
- state_update — structured mutations (20+ actions: CRUD for hosts/vulns/credentials/access, phases, modes, scope, flags, notes, attack steps, domain, objectives)
- nmap_parse — parse nmap XML/greppable output via htmlparser2 SAX, auto-updates engagement state
- nuclei_parse — parse Nuclei JSON output, auto-create vulns with severity
- gobuster_parse — parse gobuster/feroxbuster output, classify sensitive files/admin panels/backups
- cme_parse — parse CrackMapExec/NetExec output, auto-update creds/access/hosts
- bloodhound_parse — parse SharpHound JSON, populate AD domain model
- cred_spray — credential reuse planning (plan/suggest spray commands across discovered services)
- scope_check — CIDR containment, wildcard domain matching, excludes priority
- phase_control — status/next/set phase management
- report_gen — markdown/JSON reports with 6 sections (executive_summary, scope, findings, attack_path, credentials, recommendations)
- task_graph — Pentesting Task Tree (PTT) management
- All tools registered in
packages/opencode/src/tool/registry.ts - Agent permissions configured per-agent in
packages/opencode/src/agent/agent.ts
- Deleted stale test files:
code-mode.test.ts,code-mode-integration.test.ts,apply_patch.test.ts,lsp.test.ts - Cleaned
parameters.test.ts— removed apply_patch/lsp/todo imports, schemas, and describe blocks - Deleted orphaned snapshot file
__snapshots__/parameters.test.ts.snap - Verified
@opencode-ai/codemodedependency already removed - Deleted orphaned description
todowrite.txt
- Removed
LspToolfrom registry init and builtin array (keptLSP.nodein deps — EditTool depends on LSP.Service) - Deleted
src/tool/lsp.tsandsrc/tool/lsp.txt(tool file + description) - Cleaned CLI tool rendering (
cmd/run/tool.ts) — removed ApplyPatchTool, LspTool, TodoWriteTool imports, types, render functions, TOOL_RULES entries (~300 lines) - Kept LSP/Format/Worktree service modules intact (deeply integrated, removal breaks types)
- 8 slash commands:
/status,/targets,/vulns,/creds,/scope,/phase,/mode,/report- Template files in
packages/opencode/src/command/template/pentest-*.txt - Registered as built-in commands in
packages/opencode/src/command/index.ts
- Template files in
- Mode switching — mode directives injected into system prompt per engagement mode (auto/free/guided)
- Phase auto-transition hints —
phaseTransitionHint()inprompt.tssuggests next phase based on engagement state - TUI
/statusrenamed to/sysinfoto avoid conflict with pentest/status
-
phase_control.ts— hints to load phase skill after phase change -
nmap-parse.ts— suggests relevant service skills after parsing -
pentest.txt— skill names listed, mapped to phases/services - Subagent prompts — added "Load relevant skills before starting" instructions
-
critic.txt— full rewrite (27→80+ lines) with validation methodology, false positive patterns - All subagents deepened with decision trees, failure handling, context management
-
state-update.txt— added urgency: "IMMEDIATELY after discovering" -
prompt.tsengagement context — added inline reminder -
orchestrator-mode.txt— added state_update mandate
- nuclei_parse — parse Nuclei JSON output, auto-create vulns with severity mapping
- gobuster_parse — parse gobuster/feroxbuster output, classify sensitive files/admin panels/backups
- cme_parse — parse CrackMapExec/NetExec output, auto-update creds/access/hosts
- bloodhound_parse — parse SharpHound JSON, populate AD domain model
- All parsers registered in registry.ts with agent permissions
-
addVulndedup by (title, service_port) — updates existing on match -
addAccessdedup by (access_type, username) — updates existing on match -
addHostmerges services by port (not overwrite) viamergeServices() - Added
deleteHost,updateVuln,deleteVuln,deleteCredentialto store + state_update
-
DomainStatetype in schema.ts (domain_name, forest, trusts, domain_admins, domain_controllers, gpo_names, password_policy) -
domain_infoon Host (domain, is_dc, computer_account, forest) - AD fields on Credential (domain, ticket_type, service_principal, ticket_expiry)
-
setDomain/updateDomainin store.ts and state_update tool
- cred_spray — plan/suggest actions, generates spray commands for discovered services
- Supports NTLM hash spraying, service filtering, existing-access dedup
- Registered with permissions on pentest, identity, infrastructure, post_exploit
-
exploit-dev.txt— realistic capability claims (no ROP chains, honest about LLM limitations) - Host Exhaustion Protocol added to pentest.txt (ACCESS → EXHAUST → PIVOT)
- Anti-patterns explicitly documented (tunnel vision, skipping post-exploit)
- 6 new specialist subagents: identity, infrastructure, post_exploit, exploit_dev, critic, webapp
- Task graph tool for PTT (Pentesting Task Tree)
- Objectives system (add/update/complete objectives)
- Confidence Scoring —
confidence: number(0.0-1.0) on Vuln, Credential, Access. Parsers set base values (0.9-0.95). Displayed in state_query output and compact context. - Structured Evidence —
evidence_items: Array<{tool, command, output, timestamp, confidence}>on Vuln. Parsers populate with tool name and output. Summary inevidencestring field, full data inevidence_items. - Changelog — separate
changelog.json, every store mutation logged vialogChange().state_query changelogretrieves entries. Retention capped at 500 entries (CHANGELOG_MAX_ENTRIES). Loaded/saved alongside engagement state.
- State Diff Injection (#4) —
toDiffContext()in schema.ts computes delta from changelog entries.prompt.tstracks last injection timestamp viamarkInjected()/getLastInjectedTimestamp(). Each LLM turn sees "Changes since last turn:" before the full state dump, showing what's new.state_query diffalso available. - Auto-Critic (#5) —
criticHint()in schema.ts detects unvalidated vulns (status=suspected, confidence<0.8). Injected into prompt as<auto-critic>section with vuln list and instructions to spawn critic subagent. Parser outputs (nmap, nuclei, cme) include[Auto-critic]hints suggesting critic validation. Critic agent stays READ-ONLY, returns verdict → coordinator updates state. - Entity Relationships (#6) —
Relationshipschema with typed edges: EXPLOITED_VIA, CREDENTIAL_FROM, REACHABLE_FROM, TRUSTS, MEMBER_OF, ADMIN_OF, PIVOT_TO, AUTHENTICATES_TO, LATERAL_MOVE, CONTROLS.relationships[]on State. Store methods:addRelationship()(dedup by source+type+target),getRelationships()(filter by entity_id or rel_type),deleteRelationship(). Tools:state_update add_relationship/delete_relationship,state_query relationships. Auto-created by parsers: nmap→REACHABLE_FROM, cme→AUTHENTICATES_TO/ADMIN_OF, bloodhound→MEMBER_OF/ADMIN_OF/TRUSTS. Displayed in compact context. - Phase Quality Gates (#7) —
evaluateQualityGate()in phase-control.ts checks coverage metrics per phase before transition. Missing items block transition; warnings allow with notice.force:trueparameter skips all gates. Gates: recon (hosts+services), enumeration (version coverage), vuln_assess (confirmed vulns, unvalidated check), exploitation (compromised hosts), post_exploit (creds, lateral coverage, objectives).
-
attack_path_suggesttool (renamed frompivot_suggest): complete rewrite from 322→1140 lines - Cost model:
EDGE_BASE_COSTSmap for all 10 relationship types + default fallback (35) for unknown types - Modifiers: credential/vuln confidence, temporal penalty (expired→Infinity), live session bonus (×0.7), OPSEC noise (+0/+10/+20)
- Dijkstra + Yen's K-Shortest Paths (K=3) replaces BFS
- Entity projection: credential→host, user→DC, domain trust→DC-DC edges
- Segment-aware synthetic edges (not O(n²) complete graph anymore)
-
resolveObjectiveTargets(): "domain controller"/CIDR/IP/keyword → host IPs - Inline MinHeap, no external deps
-
AgentContextSummaryschema in schema.ts (id, agent_type, timestamp, findings, failures, next steps) -
agent-contexts.jsonpersistence in store.ts (cap 10 per agent type) -
buildContextSummary()in task.ts: heuristic parser extracts findings/failures/next from subagent output -
formatPriorContext(): XML<prior-agent-context>block injected into fresh subagent prompts - Auto-save on completion (both background and foreground paths)
-
AlertPriorityschema:"normal" | "interrupt"on Alert -
interruptQueueRefin store.ts: accumulates interrupt alerts,drainInterruptAlerts()to consume - Watcher fiber in task.ts: polls every 2s during background subagent, injects into coordinator
- prompt.ts: drains interrupt queue at top of engagement context injection
- state-update.ts: accepts
priorityfield inadd_alert - All 7 subagent prompts updated with interrupt alert instructions
~/.pentestcode/engagements/<name>/
├── state.json # core (compact) — now includes relationships[]
├── changelog.json # deletable, retention 500
├── decisions.json # Wave 3 — deletable, retention 100
├── agent-contexts.json # Wave 3 — deletable, retention 10 per agent type
├── evidence/ # Wave 3 — deletable folder, files per vuln_id
├── wordlists.json # UX — deletable, retention 1000
└── findings.md # UX — deletable, auto-appended markdown
-
prompt.tsnow injects<decision-history>section into engagement context every turn - Shows last 5 decisions with outcomes (successful/failed/pending)
- Failure escalation: 3+ failures triggers warning to avoid repeating and spawn critic
-
decisionSummary()helper in schema.ts for aggregating decision stats
-
EvidenceItemschema extended with:reasoning,source_agent,attempt_number,verification_status -
VerificationStatustype: "unverified"|"verified"|"false_positive" - Nuclei parser populates all new evidence fields (reasoning, source_agent, attempt, verification)
- CME parser populates evidence fields for SMB signing findings
- Report generator renders full evidence chain per finding (tool, agent, attempt#, status, reasoning)
- Compact context shows evidence_count + verified_by agents for each vuln
- sqlmap_parse — parse sqlmap JSON/text output, extract injection points/params/techniques/databases, auto-create vulns
- xss_detect — analyze HTTP responses for reflected/stored XSS, check CSP/X-XSS-Protection headers, classify findings
- jwt_analyze — decode JWT, check alg:none/weak HMAC secrets/JKU injection/expiry/missing claims/admin escalation
- All 3 registered in registry.ts, permissions granted to pentest/webapp/exploiter agents
- Each tool auto-updates engagement state with findings + evidence chain
- tunnel_manage — plan tunnel commands (SSH/chisel/ligolo), register/list/remove live sessions in state
- attack_path_suggest (renamed from pivot_suggest) — cost-based Dijkstra + Yen's K-Shortest path-finding, all 10 relationship types, entity projection, objective targeting
- Both registered in registry.ts, permissions granted to pentest/post_exploit/infrastructure agents
-
bench/verify-claims.ts— validates tool availability (17/17), schema coverage (14/14), decision injection - 5 benchmark challenges: nmap-parse, nuclei-parse, cred-spray-plan, scope-check, cme-parse-ad
-
bench/challenges/directory with JSON challenge definitions -
bench/results/directory for benchmark run outputs - Exit code 0 = all claims verified, exit code 1 = gaps remain
-
cred_spraytool: all spray commands now usenetexecinstead ofcrackmapexec -
cme_parsetool: evidence references updated to "netexec" -
cred-spray.txtandcme-parse.txtdescriptions updated
- Mandatory Parser Workflow — added MANDATORY section to pentest.txt + all 8 subagent prompts binding nmap→nmap_parse, netexec→cme_parse, nuclei→nuclei_parse, gobuster→gobuster_parse, sqlmap→sqlmap_parse, bloodhound→bloodhound_parse, creds→cred_spray. Anti-patterns documented.
- Batch state_update — new
batchaction accepts{operations: [{action,data},...]}array (max 100). Turns 40 sequential LLM steps into 1 for initial engagement setup. Prompt guidance added. - Context Size Reduction —
toCompactContext()now has caps: 10 vulns/host (by severity), 15 services/host, 30 relationships, 10 objectives. OODA fields (alerts/sessions/segments) excluded by default (already intoOODAContext()). Conditional injection: full state on step 1 + every 8th step, summary-only on other steps. ~75% token reduction. - Parallel Dispatch Strengthening — added CORRECT/WRONG examples with multi-tool-use blocks to orchestrator-mode.txt and pentest.txt. Explicit anti-pattern: "dispatch one per turn → WRONG".
-
WordlistUsageschema:(host_ip, port, tool_type, wordlist_path)granularity — tracks what was tried where -
WordlistToolType: dir_fuzz, brute, vhost, subdomain, user_enum, param_fuzz, password_spray -
wordlists.jsonpersistence (deletable, retention 1000) — loaded/saved alongside engagement -
addWordlistUsage()dedup by full tuple,getWordlistUsages()with optional filter -
state_update record_wordlist/state_query wordlists— tools for agents to track usage -
wordlistSummary()helper groups by host:port → tool_type → paths -
<wordlist-usage>context injection in prompt.ts (capped at 50 entries) - "Wordlist Tracking — MANDATORY" sections in pentest.txt, enumerator.txt, webapp.txt, infrastructure.txt
-
appendFinding()in store.ts — write-only append, no Ref, best-effort I/O - Auto-appended on
addVuln(severity icon, title, host, status, evidence chain) - Auto-appended on
addCredential(type, source, valid_for, domain) - Auto-appended on
addAccess(type, user, level, details) - Human-readable markdown with timestamps — reviewable during sessions
- Mentioned in pentest.txt so agent tells user about it
-
PauseBehaviortype: "never" | "always" | "checkpoint" — orthogonal to mode (auto/free/guided) -
pause_on_findingfield on State (optional, default "never") -
setPauseBehavior()in store,state_update set_pauseaction -
/pauseslash command (template + registration in command/index.ts) -
pauseDirectivesin prompt.ts — injected after engagement context when not "never" - Subagents do NOT pause individually — findings flow to coordinator via alerts
- "Output Management — MANDATORY" section in pentest.txt with 15+ tool-specific patterns
- "Context Management — MANDATORY" sections in all subagent prompts (scanner, enumerator, exploiter, infrastructure, webapp, post-exploit)
- "Output Rules" reminders in 4 skill files (enumeration, exploitation, smb, web-server)
- "Available Commands — Mention to Users" section in pentest.txt
- Contextual
<command-hints>injection in prompt.ts based on engagement state - 3 new tips in TUI tips-view.tsx (/creds, /mode, /pause)
~/.pentestcode/engagements/<name>/
├── state.json # core (compact)
├── changelog.json # deletable, retention 500
├── decisions.json # deletable, retention 100
├── agent-contexts.json # deletable, retention 10 per agent type
├── evidence/ # deletable folder, files per vuln_id
├── wordlists.json # NEW — deletable, retention 1000
└── findings.md # NEW — deletable, auto-appended markdown
- Decision Memory (#8) — decisions.json fully implemented (schema, store, CRUD, state_update/state_query). Context injection into prompt added.
- Alert Queue (#9) — fully implemented (schema, store, TTL, max 50, OODA display, state_update/state_query).
- OODA Structured Reasoning (#10) —
toOODAContext()fully implemented (changes, coverage, gaps, alerts, sessions, segments, tasks, objectives). - Attack Path Derivation (#11) —
attack_path_suggesttool: Dijkstra + Yen's K-Shortest (K=3), cost model with 10 relationship types, credential/vuln confidence, temporal validity, OPSEC scoring, entity projection (cred→host, user→DC, domain trust→DC-DC), segment-aware synthetic edges, objective targeting. Renamed frompivot_suggest. - Parallel Subagent Improvements (#12) — parallel dispatch examples + anti-patterns in orchestrator-mode.txt and pentest.txt. Agent context carry implemented:
agent-contexts.jsonpersists per-agent-type summaries (findings, failures, next steps), auto-injected into fresh subagent instances.
-
Scope guard on bash tool— CANCELLED per Zhangir's decision - Tool knowledge in prompts — mandatory parser workflow added to all agent prompts. Parser tools now MUST be used after their corresponding bash commands. Additional tool-specific knowledge can be added as issues surface.
- Inter-agent communication — interrupt alerts (
priority: "interrupt"on alerts). Subagents raise interrupt for critical findings (DC found, admin creds, RCE). Watcher fiber in task.ts polls every 2s, injects into coordinator viainject(). Prompt.ts drains interrupt queue on every turn. All subagent prompts updated with interrupt alert instructions. - Session/shell tracking —
LiveSessionschema +tunnel_managetool +live_sessionsin OODA context. Agents can now register/track/remove tunnels and shells. - Network segmentation model —
NetworkSegmentschema +attack_path_suggesttool. VLANs, reachable networks, pivot hosts tracked in state and used for path suggestions.
-
/pause— set pause behavior on findings (never/always/checkpoint) -
/playbook— load and follow a playbook interactively -
/export— export engagement state to external formats
- Rebrand TUI (banner, logo, colors)
- Add engagement status bar (phase, hosts, vulns, creds counts)
- Add vulnerability/host/credential table rendering in TUI
- Rename config dir from
.opencode/to.pentestcode/ - Rename
@opencode-ai/*package scopes to@pentestcode/*(or keep as fork)
- Remove or stub LSP service module (
packages/opencode/src/lsp/) — EditTool depends on LSP.Service - Remove or stub Format integration (
packages/opencode/src/format/) - Remove or repurpose git-specific logic (
packages/opencode/src/git.ts) - Remove worktree support (
packages/opencode/src/worktree/) — needs httpapi + test cleanup - Clean up
packages/opencode/src/session/reminders.ts— may still reference coding concepts
- Cross-compile binaries (
bun run build --skip-embed-web-ui) — linux/darwin × x64/arm64 + baseline + musl (no Windows targets) - Smoke test binary on current platform (
bun run build --single --skip-embed-web-ui) - Set up GitHub Release workflow (
OPENCODE_RELEASE=1 GH_REPO=s0ld13rr/pentestcode bun run build) - Verify install.sh works against published release
- End-to-end test on CTF target with new build
- Verify engagement state persistence across sessions
- Test multi-agent coordination (coordinator spawns 3+ subagents in parallel)
- Test parser tools with real tool output (nmap, nuclei, netexec, gobuster, bloodhound, sqlmap)
- Hard fork, no upstream tracking — deep domain changes make merging impractical
- Multi-agent strategist-operator split — pentest agent coordinates, subagents execute (4.3x improvement per HPTSA research)
- Pentesting Task Tree (PTT) — hierarchical attack tree with difficulty scoring, strategic abandonment, credential propagation
- Selective context injection — full state every 8 turns, summary+diff on other turns; OODA and compact contexts deduplicated
- 4-layer prompt system — identity (always) → engagement state (dynamic) → phase skill (per-phase) → service knowledge (on-demand)
- File-based engagement store — single JSON at
~/.pentestcode/engagements/<name>/state.json, not relational; simpler and portable - Global storage — engagements at
~/.pentestcode/engagements/, not per-directory. Security work isn't tied to cwd. Supports parallel activities (bounty + CTF + work pentest). - Universal tools — tools work for pentesting, bug bounty, vuln research, CTF, infra security. Not narrowly scoped.
- Skills as SKILL.md files — no code changes needed, just add markdown files
- edit tool kept — useful for modifying exploit scripts, payloads, configs
- recon agent has full tool access — passive/active controlled by prompt and user choice, not permissions
- Multi-session on same engagement — two terminals can load the same engagement. Each has its own Ref. Last-write-wins on disk.
state_update reload_engagementto pick up changes from other session.
- V1 (
packages/opencode/) — active for interactive TUI sessions. Usesprompt.ts→runLoop(). Does NOT useSystemContextRegistry. - V2 (
packages/core/) — durable runner path. UsesSystemContextRegistry,LocationServiceMap, etc. - EngagementStore.node is
makeGlobalNode(no deps) — works in both V1 and V2 graphs. - EngagementContext.node is
makeLocationNode(depends on SystemContextRegistry) — V2 only. V1 injects engagement context directly inprompt.ts.
makeGlobalNode: no scope dependencies, goes inAppLayerand any graphmakeLocationNode: depends on Location.Service (scoped), only works in location-scoped graphs- Rule: a global node CANNOT depend on a location-scoped node. Reverse is fine.
LayerNode.make(used in V1app-runtime.ts): can go in either graph
Schema.Literal("a")— single literal (1 arg only)Schema.Literals(["a", "b", "c"])— literal union (takes array)Schema.optional(Schema.X)— optional field (noSchema.optionalWith, no defaults in schema)Schema.Record(Schema.String, ValueSchema)— positional args, NOTSchema.Record({ key, value })- No
Schema.withDefaultin this version. Handle defaults in application code (store.create, tool constructors).
export const MyTool = Tool.define("tool_id", Effect.gen(function* () {
const store = yield* SomeService
return {
description: DESCRIPTION_FROM_TXT,
parameters: Schema.Struct({ ... }),
execute: (params, ctx) => Effect.gen(function* () {
// ...
return { title: "...", metadata: {}, output: "..." }
}).pipe(Effect.orDie),
}
}))- All agents in same session share the same EngagementStore.Service Ref (cooperative fibers, no race conditions in single-thread Bun).
- Subagent spawned via
tasktool gets fresh prompt context but shares same Ref. - State updates by subagent are immediately visible to parent agent.