@trenchwork/erosolar 1.1.37 → 1.1.38

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 (75) hide show
  1. package/README.md +219 -170
  2. package/SECURITY.md +28 -6
  3. package/agents/engagement-delivery.rules.json +7 -0
  4. package/agents/variant-research.rules.json +7 -0
  5. package/dist/bin/erosolar.js +40 -0
  6. package/dist/bin/erosolar.js.map +1 -1
  7. package/dist/capabilities/_opsContext.d.ts +65 -1
  8. package/dist/capabilities/_opsContext.d.ts.map +1 -1
  9. package/dist/capabilities/_opsContext.js +43 -0
  10. package/dist/capabilities/_opsContext.js.map +1 -1
  11. package/dist/capabilities/heliaCapability.d.ts +15 -0
  12. package/dist/capabilities/heliaCapability.d.ts.map +1 -0
  13. package/dist/capabilities/heliaCapability.js +34 -0
  14. package/dist/capabilities/heliaCapability.js.map +1 -0
  15. package/dist/capabilities/index.d.ts +1 -0
  16. package/dist/capabilities/index.d.ts.map +1 -1
  17. package/dist/capabilities/index.js +1 -0
  18. package/dist/capabilities/index.js.map +1 -1
  19. package/dist/capabilities/llmRedteamCapability.d.ts.map +1 -1
  20. package/dist/capabilities/llmRedteamCapability.js +47 -0
  21. package/dist/capabilities/llmRedteamCapability.js.map +1 -1
  22. package/dist/core/artifactLog.d.ts +82 -0
  23. package/dist/core/artifactLog.d.ts.map +1 -0
  24. package/dist/core/artifactLog.js +224 -0
  25. package/dist/core/artifactLog.js.map +1 -0
  26. package/dist/core/artifactStore.d.ts +17 -0
  27. package/dist/core/artifactStore.d.ts.map +1 -1
  28. package/dist/core/artifactStore.js +22 -0
  29. package/dist/core/artifactStore.js.map +1 -1
  30. package/dist/core/cliTriggerHandlers.d.ts +15 -0
  31. package/dist/core/cliTriggerHandlers.d.ts.map +1 -0
  32. package/dist/core/cliTriggerHandlers.js +60 -0
  33. package/dist/core/cliTriggerHandlers.js.map +1 -0
  34. package/dist/core/portalPresence.d.ts +74 -0
  35. package/dist/core/portalPresence.d.ts.map +1 -0
  36. package/dist/core/portalPresence.js +187 -0
  37. package/dist/core/portalPresence.js.map +1 -0
  38. package/dist/core/updateChecker.d.ts.map +1 -1
  39. package/dist/core/updateChecker.js +8 -6
  40. package/dist/core/updateChecker.js.map +1 -1
  41. package/dist/headless/interactiveShell.js +23 -0
  42. package/dist/headless/interactiveShell.js.map +1 -1
  43. package/dist/plugins/tools/mcp/mcpClient.js +1 -1
  44. package/dist/plugins/tools/mcp/mcpClient.js.map +1 -1
  45. package/dist/runtime/node.d.ts.map +1 -1
  46. package/dist/runtime/node.js +2 -2
  47. package/dist/runtime/node.js.map +1 -1
  48. package/dist/runtime/phaseEmitter.d.ts +6 -0
  49. package/dist/runtime/phaseEmitter.d.ts.map +1 -1
  50. package/dist/runtime/phaseEmitter.js +0 -0
  51. package/dist/runtime/phaseEmitter.js.map +1 -1
  52. package/dist/tools/engagementTools.d.ts.map +1 -1
  53. package/dist/tools/engagementTools.js +73 -1
  54. package/dist/tools/engagementTools.js.map +1 -1
  55. package/dist/tools/heliaControl.d.ts +17 -0
  56. package/dist/tools/heliaControl.d.ts.map +1 -1
  57. package/dist/tools/heliaControl.js +108 -2
  58. package/dist/tools/heliaControl.js.map +1 -1
  59. package/dist/tools/heliaTools.d.ts +22 -0
  60. package/dist/tools/heliaTools.d.ts.map +1 -0
  61. package/dist/tools/heliaTools.js +125 -0
  62. package/dist/tools/heliaTools.js.map +1 -0
  63. package/dist/tools/scheduleTools.d.ts.map +1 -1
  64. package/dist/tools/scheduleTools.js +5 -2
  65. package/dist/tools/scheduleTools.js.map +1 -1
  66. package/dist/tools/webTools.js +1 -1
  67. package/dist/tools/webTools.js.map +1 -1
  68. package/dist/utils/heliaClient.d.ts +23 -0
  69. package/dist/utils/heliaClient.d.ts.map +1 -0
  70. package/dist/utils/heliaClient.js +79 -0
  71. package/dist/utils/heliaClient.js.map +1 -0
  72. package/dist/utils/lambdaClient.d.ts.map +1 -1
  73. package/dist/utils/lambdaClient.js +9 -0
  74. package/dist/utils/lambdaClient.js.map +1 -1
  75. package/package.json +2 -2
package/README.md CHANGED
@@ -3,17 +3,19 @@
3
3
  [![npm version](https://img.shields.io/npm/v/@trenchwork/erosolar)](https://www.npmjs.com/package/@trenchwork/erosolar)
4
4
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
5
 
6
- A multi-provider terminal agent for coding work and authorized offensive
7
- security research. Default backend is DeepSeek-V4-Pro (via a shared
8
- key gated by Erosolar Auth, no key bundled in the package), with
9
- Anthropic / OpenAI / Google / xAI / Qwen / Ollama as opt-in providers.
10
-
11
- This README documents the **current shipped state** of the CLI: the
12
- agent loop, capability surface, profiles, rulebook system, MCP
13
- integration, artifact store, and the auth-gated key fetch. Sections
14
- toward the end cover architecture and contribution. Examples,
15
- "try asking…" hints, and onboarding fluff are deliberately absent
16
- [CLAUDE.md](CLAUDE.md) is strict about that.
6
+ A multi-provider terminal agent for coding work and authorized
7
+ offensive-security research. Default backend is **DeepSeek-V4-Pro**
8
+ via a shared key gated by Erosolar Auth (no key bundled in the npm
9
+ tarball); Anthropic / OpenAI / Google / xAI / Qwen / Ollama are opt-in
10
+ providers with bring-your-own keys.
11
+
12
+ This README documents the **current shipped state** of the CLI — the
13
+ auth-gated boot, profiles, capability surface, MCP integration,
14
+ artifact store, and operational knobs. Examples, "try asking…" hints,
15
+ and onboarding fluff are deliberately absent ([CLAUDE.md](CLAUDE.md)
16
+ is strict about that). Deep technical reference lives in
17
+ [`docs/ENGINEERING.md`](docs/ENGINEERING.md); security model and
18
+ threat boundaries live in [`SECURITY.md`](SECURITY.md).
17
19
 
18
20
  ---
19
21
 
@@ -23,12 +25,12 @@ toward the end cover architecture and contribution. Examples,
23
25
  npm install -g @trenchwork/erosolar
24
26
  ```
25
27
 
26
- Two CLI binaries land on `$PATH`, both pointing to the same entry:
28
+ Two binaries land on `$PATH` and point at the same entry:
27
29
 
28
30
  - `erosolar` — preferred
29
31
  - `trenchwork` — alternate name
30
32
 
31
- Node ≥ 20 required (`"engines": { "node": ">=20.0.0" }`).
33
+ Node ≥ 20 required.
32
34
 
33
35
  ### First-run boot
34
36
 
@@ -38,64 +40,47 @@ erosolar
38
40
 
39
41
  On first launch the CLI:
40
42
 
41
- 1. Pops a browser tab to `https://trenchwork.org/auth?port=…`. You sign in
42
- with **Google SSO** (Erosolar Auth Firebase Auth under the hood,
43
- project `erosolar-1b0db`). SSO is enforced both at the web auth
44
- page and at the Firestore-rule layer
43
+ 1. Opens a browser tab to `https://ero.solar/auth?port=…`. Sign in with
44
+ **Google SSO** (Firebase Auth, project `erosolar-1b0db`). SSO is
45
+ enforced both at the auth page and at the Firestore rule layer
45
46
  (`request.auth.token.firebase.sign_in_provider == 'google.com'`).
46
47
  2. Stores a Firebase ID token + refresh token at `~/.erosolar/auth.json`
47
- (`0o600`). The refresh token is long-lived; the ID token is auto-
48
- renewed before expiry.
48
+ (`0o600`). The refresh token is long-lived; the ID token rotates
49
+ automatically before expiry.
49
50
  3. **Approval gate.** The CLI checks `approved_users/{your-email}` in
50
- Firestore. The package ships offsec capabilities (Kali tools /
51
- AFL++ / gdb / pwntools / Ghidra MCP), so only allowlisted emails
52
- can launch. If your email isn't approved, the CLI writes a
53
- pending request to `approval_requests/{uid}` for the operator to
54
- review and exits with a clear message — re-run after the request
55
- is approved.
51
+ Firestore. The package ships offsec capabilities (Kali tools, AFL++,
52
+ gdb, pwntools, Ghidra), so only allowlisted emails can launch. If
53
+ your email isn't approved, the CLI writes a pending request to
54
+ `approval_requests/{uid}` and exits with a clear message — re-run
55
+ once the operator approves.
56
56
  4. Fetches the shared DeepSeek API key from Firestore at
57
- `shared_secrets/deepseek` and writes it into `process.env.DEEPSEEK_API_KEY`
58
- for the duration of the process. **Nothing lands on disk.** Reads
59
- are gated by Firestore rules to authenticated *and approved*
60
- users only; writes are admin-only.
61
- 5. Starts the interactive Ink-based shell.
57
+ `shared_secrets/deepseek` and writes it into the process env for the
58
+ duration of the run. **Nothing lands on disk.** Reads are gated to
59
+ authenticated + approved users; writes are admin-only.
60
+ 5. Starts the Ink-based interactive shell.
62
61
 
63
62
  ### Requesting approval
64
63
 
65
- Sign in once via Google SSO. The CLI will detect that your email
66
- isn't on the allowlist and file a pending request at
67
- `approval_requests/{uid}`. The operator reviews the queue and
68
- adds your email via:
64
+ Sign in once via Google SSO. The CLI files a pending request at
65
+ `approval_requests/{uid}`. The operator reviews and approves with:
69
66
 
70
67
  ```sh
71
68
  node scripts/approve-user.mjs <your-email>
72
69
  ```
73
70
 
74
- You can re-run `erosolar` once the operator confirms approval.
75
-
76
- ### Approval gate — admin operations
77
-
78
- Adding a user (uses the Firebase CLI's stored OAuth token; bypasses
79
- Firestore rules via owner privileges):
80
-
81
- ```sh
82
- node scripts/approve-user.mjs alice@example.com
83
- ```
84
-
85
- Inspecting pending requests via Firebase Console:
86
- [Firestore → `approval_requests`](https://console.firebase.google.com/project/erosolar-1b0db/firestore/data/~2Fapproval_requests)
71
+ Re-run `erosolar` after approval is confirmed.
87
72
 
88
73
  ### Bring-your-own key
89
74
 
90
- If you'd rather use your own DeepSeek key (or any other provider key),
91
- the Firestore fetch is skipped. Resolution order is:
75
+ If `DEEPSEEK_API_KEY` is set in env, the Firestore fetch is skipped.
76
+ Resolution order:
92
77
 
93
78
  1. `process.env.DEEPSEEK_API_KEY` (env override always wins)
94
- 2. `~/.erosolar/secrets.json` (set in-CLI via `/key sk-…` or `/secrets`)
79
+ 2. `~/.erosolar/secrets.json` (set in-CLI via `/key sk-…` or `/secrets set`)
95
80
  3. Firestore `shared_secrets/deepseek` (the post-login fallback)
96
81
 
97
- Same chain applies to `TAVILY_API_KEY` and any other provider key the
98
- shared-secrets module knows about.
82
+ The same chain applies to `TAVILY_API_KEY` and any provider key the
83
+ shared-secrets module recognises.
99
84
 
100
85
  ### Modes
101
86
 
@@ -103,7 +88,7 @@ shared-secrets module knows about.
103
88
  erosolar # interactive
104
89
  erosolar -q "explain X" # one-shot, prints answer, exits
105
90
  git diff | erosolar # pipe mode — stdin becomes the prompt
106
- erosolar --profile variant-research "<goal>" # use a non-default profile
91
+ erosolar --profile variant-research "<goal>"
107
92
  erosolar --self-test # provider/auth/capability smoke test
108
93
  ```
109
94
 
@@ -111,45 +96,62 @@ erosolar --self-test # provider/auth/capability smoke test
111
96
 
112
97
  ## Profiles
113
98
 
114
- A **profile** binds a system prompt template, default model/provider,
115
- and a [rulebook](#rulebooks) (a phase-structured guidance document the
116
- LLM reads from its system prompt).
99
+ A **profile** binds a system prompt template, default model + provider,
100
+ and a [rulebook](#rulebooks) (a phase-structured guidance document
101
+ inlined into the system prompt). Switch via `--profile <name>` or the
102
+ `EROSOLAR_PROFILE` environment variable. Three profiles ship:
117
103
 
118
104
  ### `erosolar-code` (default)
119
105
 
120
- General-purpose terminal agent for coding work. Default model
121
- `deepseek-v4-pro`, default provider `deepseek`. The full default tool
122
- inventory is mounted: filesystem (Read/Write/Edit/MultiEdit), Bash,
123
- Glob/Grep/Search, Git (status/log/diff/show/blame), TodoWrite, Memory
124
- (save/load/list/delete), Skills, NotebookEdit, WebSearch/WebExtract,
125
- HITL, plus any MCP servers you declare in `mcp.json`. Rulebook at
106
+ General-purpose terminal agent for coding, builds, tests, sysadmin,
107
+ package management. Default model `deepseek-v4-pro`, default provider
108
+ `deepseek`. Coding tool inventory only the offsec capabilities are
109
+ withheld so the model's tool list isn't padded with sqlmap / ROP-search
110
+ in normal sessions. Rulebook:
126
111
  [`agents/erosolar-code.rules.json`](agents/erosolar-code.rules.json).
127
112
 
128
113
  ### `variant-research`
129
114
 
130
115
  Authorized offsec / vulnerability-research surface. Walks the standard
131
116
  n-day-to-0-day pivot: recon → vulnerable+patched binary acquisition →
132
- patch diff (Ghidra MCP) → variant search → fuzz campaign (AFL++) →
133
- crash triage (gdb/pwndbg) → PoC development (pwntools) → coordinated
134
- disclosure. Rulebook at
117
+ patch diff (Ghidra) → variant search → fuzz campaign (AFL++) → crash
118
+ triage (gdb/pwndbg) → PoC development (pwntools) → coordinated
119
+ disclosure. Rulebook:
135
120
  [`agents/variant-research.rules.json`](agents/variant-research.rules.json).
136
121
 
137
122
  The disclosure terminal is **pinned to coordinated channels**
138
123
  (HackerOne / Bugcrowd / vendor PSIRT / CERT-CC / internal write-up /
139
124
  90-day published advisory). The rulebook contains an explicit
140
- `vr.r.no_brokerage` rule: it is not a vector for selling unreported
125
+ `vr.r.no_brokerage` rule it is not a vector for selling unreported
141
126
  exploits.
142
127
 
143
- Operator authorizes targets the CLI does not second-guess argv or
144
- target identifiers. The companion research workspace lives at
145
- [`Aroxora/patchpivot`](https://github.com/Aroxora/patchpivot) (private):
146
- a target portfolio, per-investigation findings dirs, and a disclosure
147
- log all driven by this profile.
128
+ The companion research workspace is [`Aroxora/patchpivot`](https://github.com/Aroxora/patchpivot)
129
+ (private): target portfolio, per-investigation findings dirs, and a
130
+ disclosure log driven by this profile.
148
131
 
149
132
  ```sh
150
133
  erosolar --profile variant-research "investigate the patch at <commit-url>"
151
134
  ```
152
135
 
136
+ ### `engagement-delivery`
137
+
138
+ Sibling of `variant-research` running the same eight-phase VR workflow,
139
+ but the terminal phase delivers to an **authorized engagement
140
+ recipient** — a U.S. government contract / task order, a U.S. defense
141
+ prime under contract, or a published bug-bounty program. The rulebook
142
+ adds an intake phase that requires an active engagement identifier in
143
+ the artifact store before any other phase advances; missing engagement
144
+ id → halt + prompt operator. Rulebook:
145
+ [`agents/engagement-delivery.rules.json`](agents/engagement-delivery.rules.json).
146
+
147
+ ```sh
148
+ erosolar --profile engagement-delivery "<task>"
149
+ ```
150
+
151
+ Both `variant-research` and `engagement-delivery` get the offsec
152
+ capability surface. `erosolar-code` does not. Gating lives in
153
+ `src/runtime/profileGates.ts`.
154
+
153
155
  ---
154
156
 
155
157
  ## Capability surface
@@ -162,82 +164,92 @@ Capabilities are typed wrappers around external tools, registered in
162
164
  | Family | Tools |
163
165
  |---|---|
164
166
  | Filesystem | `Read`, `Write`, `Edit`, `MultiEdit`, `Glob`, `Grep`, `Search` |
165
- | Process | `Bash` (with bracketed-paste-safe stdin and 1MB stdout cap) |
166
- | Git | `git_status`, `git_log`, `git_diff`, `git_show`, `git_blame`, `git_revert` |
167
+ | Process | `Bash` (bracketed-paste-safe stdin, 1MB stdout cap) |
168
+ | Git | `git_status`, `git_log`, `git_diff`, `git_show`, `git_blame`, `git_revert`, full enhanced-git surface |
167
169
  | Memory | `memory_save`, `memory_load`, `memory_list`, `memory_delete` |
168
- | Planning | `TodoWrite` (state + plan-formatter integration) |
169
- | Skills | `Skill`, `list_skills` (loads `.erosolar/skills/<name>/SKILL.md`) |
170
+ | Planning | `TodoWrite` (state + plan-formatter integration), `PlanMode` |
171
+ | Skills | `Skill`, `list_skills` (loads `~/.erosolar/skills/<name>/SKILL.md`) |
170
172
  | Web | `WebSearch`, `WebExtract` (Tavily under the hood) |
171
173
  | Sub-agent | `spawn_agent`, `agent_status`, `agent_output`, `agent_stop` |
172
174
  | Notebook | `NotebookEdit` |
173
175
  | HITL | `hitl_decision`, `hitl_approve`, `hitl_choose` |
176
+ | Worktree | `worktree_create`, `worktree_remove`, `worktree_list` |
177
+ | Monitor | `monitor_start`, `monitor_stop` (long-running command observers) |
178
+ | Schedule | `schedule_*`, `trigger_*` (in-session reminders / cron-style triggers) |
174
179
 
175
- ### Offensive (variant-research / when wired)
180
+ ### Offensive (variant-research / engagement-delivery only)
176
181
 
177
182
  | Capability | Module | Tools |
178
183
  |---|---|---|
179
184
  | Kali (network/web) | `kaliCapability.ts` | `kali_sqlmap`, `kali_gobuster`, `kali_ffuf`, `kali_feroxbuster`, `kali_nikto`, `kali_wpscan`, `kali_hydra`, `kali_john`, `kali_hashcat`, `kali_masscan`, `kali_amass`, `kali_subfinder` |
180
185
  | Static binary analysis | `binaryAnalysisCapability.ts` | `bin_file`, `bin_strings`, `bin_objdump`, `bin_readelf`, `bin_nm`, `bin_checksec`, `bin_ropgadget`, `bin_radare2_cmd` |
186
+ | Headless Ghidra | `ghidraHeadlessCapability.ts` | `ghidra_analyze`, `ghidra_decompile`, `ghidra_diff` |
181
187
  | AFL++ fuzzing | `aflppCapability.ts` | `afl_compile_harness`, `afl_fuzz_start` (detached), `afl_fuzz_status` (auto-registers crashes in artifact store), `afl_fuzz_stop`, `afl_showmap`, `afl_cmin`, `afl_tmin` |
182
188
  | Crash triage | `gdbCapability.ts` | `gdb_run_with_input`, `gdb_inspect_at`, `gdb_disassemble` (pwndbg/GEF inherited from `~/.gdbinit`) |
183
- | Exploit development | `pwntoolsCapability.ts` | `pwn_eval`, `pwn_rop_search`, `pwn_packed` |
189
+ | Exploit dev | `pwntoolsCapability.ts` | `pwn_eval`, `pwn_rop_search`, `pwn_packed` |
184
190
 
185
191
  The offsec capabilities are guardrail-free per
186
- [trenchwork.org/about](https://trenchwork.org/about) — the operator authorizes
192
+ [ero.solar/about](https://ero.solar/about) — the operator authorizes
187
193
  their own engagements; the CLI does not validate target/argv. Install
188
- the underlying binaries via `kali-linux-everything` + `ghidra` on Kali
189
- Linux, or pull the Kali rolling repo on Debian/Ubuntu.
194
+ the underlying binaries via `kali-linux-everything` + `ghidra` on
195
+ Kali, or the Kali rolling repo on Debian/Ubuntu.
190
196
 
191
197
  ---
192
198
 
193
199
  ## MCP servers
194
200
 
195
201
  The CLI auto-loads MCP servers declared in `mcp.json` (or
196
- `mcp.json.example` as a template). Tools from each MCP server appear in
197
- the flat tool list with the prefix `mcp__<server>__<tool>`.
202
+ `mcp.json.example` as a template). MCP tools appear in the flat tool
203
+ list with the prefix `mcp__<server>__<tool>`.
198
204
 
199
205
  ```json
200
206
  {
201
207
  "mcpServers": {
202
- "ghidra": {
203
- "command": "python3",
204
- "args": ["-m", "ghidra_mcp"],
205
- "env": { "GHIDRA_INSTALL_DIR": "/usr/share/ghidra" }
206
- },
208
+ "ghidra": { "command": "python3", "args": ["-m", "ghidra_mcp"], "env": { "GHIDRA_INSTALL_DIR": "/usr/share/ghidra" } },
207
209
  "mcp_kali_server": { "command": "mcp-kali-server", "args": [] },
208
- "metasploitmcp": { "command": "metasploitmcp", "args": [] },
209
- "tavily": { "command": "tavily-mcp", "args": [] }
210
+ "metasploitmcp": { "command": "metasploitmcp", "args": [] },
211
+ "tavily": { "command": "tavily-mcp", "args": [] }
210
212
  }
211
213
  }
212
214
  ```
213
215
 
214
- The `mcp-kali-server` and `metasploitmcp` packages ship in
215
- `kali-linux-everything`. `ghidra-mcp` is a separate `pip install`.
216
- `tavily-mcp` is on npm. None are required; they're additive.
216
+ `mcp-kali-server` and `metasploitmcp` ship in `kali-linux-everything`.
217
+ `ghidra-mcp` is a separate `pip install`. `tavily-mcp` is on npm.
218
+ None are required; they're additive.
217
219
 
218
220
  ---
219
221
 
220
222
  ## Slash commands
221
223
 
222
- In-shell commands handled before the agent loop sees them:
224
+ Handled before the agent loop sees the input. The set below reflects
225
+ the actual command table (`src/shell/commandRegistry.ts` plus the
226
+ inline matchers in `src/headless/interactiveShell.ts`):
223
227
 
224
- | Command | Effect |
225
- |---|---|
226
- | `/help` | Lists available commands |
227
- | `/key <sk-…>` | Saves DeepSeek key to `~/.erosolar/secrets.json` |
228
- | `/secrets` | Inspect / set / unset every known provider key |
229
- | `/profile <name>` | Switch profile mid-session |
230
- | `/model <id>` | Override default model for this session |
231
- | `/auto on\|off\|verify` | Auto-continue mode (loops until task-complete detector says done) |
232
- | `/hitl on\|off` | Toggle human-in-the-loop confirmations |
233
- | `/debug on\|off\|status` | Toggle agent-internal debug logging |
234
- | `/bash <cmd>` | Run a one-shot shell command without going through the agent |
235
- | `/diff` | Show git diff for the current run |
236
- | `/revert` | Roll back files modified during the current turn |
237
- | `/compact` | Force a context compaction pass |
238
- | `/status` | Token usage, model, profile, auth state |
239
- | `/mcp` | List active MCP servers and their tools |
240
- | `/quit`, `/exit` | Leave the shell |
228
+ | Command | Aliases | Effect |
229
+ |---|---|---|
230
+ | `/help` | `/h`, `/?` | Inline help panel |
231
+ | `/model [name]` | `/m` | No arg → picker menu; arg → silent switch (`provider`, `provider model`, `provider/model`, or `model`) |
232
+ | `/providers` | | List configured providers |
233
+ | `/key <sk-…>` | | Save `DEEPSEEK_API_KEY` to `~/.erosolar/secrets.json` |
234
+ | `/secrets [set [name]]` | `/s` | Inspect / set / unset every known provider key |
235
+ | `/auto` | `/continue`, `/loop`, `/dual` | Cycle auto-continue mode (off on dual → off) |
236
+ | `/bash <cmd>` | `/sh` | One-shot shell command, bypasses the agent |
237
+ | `/clear` | `/c` | Clear screen, redraw welcome |
238
+ | `/context` | | Refresh workspace context |
239
+ | `/sessions` | | Session management menu |
240
+ | `/revert [confirm]` | | Preview / roll back files modified during the current run |
241
+ | `/tools` | | List active tools |
242
+ | `/mcp` | | MCP server + tool status |
243
+ | `/learn` | | Learning-system status |
244
+ | `/debug [on\|off\|status]` | | Toggle agent-internal debug logging |
245
+ | `/stats` | `/status` | Session token + cost stats |
246
+ | `/keys` | `/shortcuts`, `/kb` | Keyboard shortcuts panel |
247
+ | `/exit` | `/quit`, `/q` | Leave the shell |
248
+
249
+ Profile is selected at boot only — there is no in-session `/profile`
250
+ switch; restart with `--profile <name>` or set `EROSOLAR_PROFILE`.
251
+ HITL is configured per-tool through the capability's `autoPause`
252
+ option (Option+V toggles the equivalent).
241
253
 
242
254
  ---
243
255
 
@@ -264,13 +276,13 @@ In-shell commands handled before the agent loop sees them:
264
276
  | `EROSOLAR_HOME` | Override `~/.erosolar` storage root |
265
277
  | `EROSOLAR_PROFILE` | Default profile (alternative to `--profile`) |
266
278
  | `EROSOLAR_INK_DEBUG=1` | Verbose Ink prompt + state logging on stderr |
267
- | `<PROFILE>_MODEL` | Per-profile default model override (e.g. `EROSOLAR_CODE_MODEL=claude-opus-4-5-20251101`) |
279
+ | `<PROFILE>_MODEL` | Per-profile default model override (e.g. `EROSOLAR_CODE_MODEL=claude-opus-4-7`) |
268
280
  | `<PROFILE>_PROVIDER` | Per-profile default provider override |
269
281
  | `<PROFILE>_SYSTEM_PROMPT` | Per-profile system prompt override |
270
282
  | `NO_COLOR`, `FORCE_COLOR` | Standard color toggles |
271
- | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `XAI_API_KEY`, `DASHSCOPE_API_KEY`, `OLLAMA_BASE_URL` | Other provider credentials when you select those models |
283
+ | `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `XAI_API_KEY`, `DASHSCOPE_API_KEY`, `OLLAMA_BASE_URL` | Other provider credentials when those models are selected |
272
284
 
273
- ### `.erosolar/settings.json` (hooks + permissions)
285
+ ### `~/.erosolar/settings.json` (hooks + permissions)
274
286
 
275
287
  The harness reads `~/.erosolar/settings.json` (user) and
276
288
  `<workspace>/.erosolar/settings.json` (project) for hooks, allow/deny
@@ -281,19 +293,24 @@ contract.
281
293
 
282
294
  ## Variant research workflow
283
295
 
284
- The `variant-research` profile drives an 8-phase rulebook the LLM
285
- reads from its system prompt:
296
+ The `variant-research` and `engagement-delivery` rulebooks drive the
297
+ same eight-phase pivot the LLM reads from its system prompt:
286
298
 
287
299
  | Phase | Intent | Tools |
288
300
  |---|---|---|
289
301
  | `recon` | Identify target patch / CVE / advisory; record bug-class hypothesis | Tavily / WebSearch |
290
302
  | `acquire` | Build vulnerable + patched binaries; persist to artifact store | Bash, Git, `bin_*` |
291
- | `bindiff` | Diff binaries and pull decompiled C of changed functions | Ghidra MCP |
292
- | `variant` | Hunt the same buggy pattern in older versions / forks / siblings | Ghidra MCP, Grep |
303
+ | `bindiff` | Diff binaries; pull decompiled C of changed functions | Ghidra MCP / `ghidra_*` |
304
+ | `variant` | Hunt the same buggy pattern in older versions / forks / siblings | Ghidra, Grep |
293
305
  | `fuzz` | Build harness + run AFL++ campaign as detached job | `afl_*` |
294
- | `triage` | Classify each crash; correlate to Ghidra decomp; identify primitive | `gdb_*`, Ghidra MCP |
295
- | `poc` | Develop minimal reliable PoC that reproduces the primitive | `pwn_*` |
296
- | `disclose` | Author write-up; submit through coordinated channel | — (the rulebook lists allowed channels) |
306
+ | `triage` | Classify each crash; correlate to decomp; identify primitive | `gdb_*`, Ghidra |
307
+ | `poc` | Develop minimal reliable PoC reproducing the primitive | `pwn_*` |
308
+ | `disclose` / `deliver` | Author write-up; ship through coordinated / contracted channel | — (rulebook lists allowed channels) |
309
+
310
+ `engagement-delivery` adds a leading `intake` phase that gates the
311
+ workflow on a registered engagement identifier (USG contract / task
312
+ order, defense-prime engagement reference, or published bug-bounty
313
+ program scope).
297
314
 
298
315
  The artifact store is the load-bearing piece: every multi-megabyte
299
316
  output (binaries, decompilations, crash corpora) is registered with a
@@ -301,9 +318,6 @@ sha256 id and tagged. The LLM passes ids around in conversation
301
318
  instead of pasting blobs into context — without this, a long workflow
302
319
  gets summarized away mid-investigation.
303
320
 
304
- The companion `patchpivot` repo (separate, private) holds target
305
- portfolios and per-investigation workspaces.
306
-
307
321
  ---
308
322
 
309
323
  ## Architecture
@@ -324,28 +338,33 @@ No DAG planner, no ReAct scratchpad. Multi-step planning is implicit
324
338
  in the LLM's tool calls, guided by the rulebook injected into the
325
339
  system prompt. Sub-agents (`spawn_agent` / `agent_status` /
326
340
  `agent_output` / `agent_stop`) are the long-task supervisor — anything
327
- that runs for hours (fuzz campaigns, large recompiles) goes detached
328
- and the parent polls.
341
+ running for hours (fuzz campaigns, large recompiles) goes detached and
342
+ the parent polls.
329
343
 
330
344
  ### Capability registry
331
345
 
332
346
  Each capability module implements `CapabilityModule` and contributes a
333
347
  flat `ToolSuite` to the runtime. Registration happens in
334
- `src/capabilities/index.ts`. Tools declare JSON Schema parameters,
335
- inputs are validated and coerced via
336
- `src/core/schemaValidator.ts` before the handler runs. MCP tools are
337
- first-class — same `ToolDefinition` shape, prefixed `mcp__`.
348
+ `src/capabilities/index.ts`. Tools declare JSON Schema parameters;
349
+ inputs are validated and coerced via `src/core/schemaValidator.ts`
350
+ before the handler runs. MCP tools are first-class — same
351
+ `ToolDefinition` shape, `mcp__<server>__` prefix.
352
+
353
+ Profile-based gating (`src/runtime/profileGates.ts`) decides which
354
+ capabilities are mounted. Cowork / productivity capabilities were
355
+ extracted to `~/GitHub/erosolar-cowork` in 2026-05 and are not
356
+ present in this repo (enforced by `test/capability-separation.test.ts`).
338
357
 
339
- ### Rulebook
358
+ ### Rulebooks
340
359
 
341
360
  A rulebook (`src/contracts/schemas/agent-rules.schema.json`) is a
342
361
  phase-structured JSON document with `globalPrinciples`, `phases`, and
343
- per-step `entryCriteria` / `exitCriteria` / `rules`. The CLI renders
344
- it to markdown and inlines it into the profile's system prompt at
345
- boot. It is **declarative guidance**, not an enforced state machine —
346
- the LLM is expected to follow it; nothing in the runtime blocks a
347
- phase from being skipped. The rule severities (`critical` /
348
- `required` / `recommended`) influence how strongly the LLM treats them.
362
+ per-step `entryCriteria` / `exitCriteria` / `rules`. The CLI renders it
363
+ to markdown and inlines it into the profile's system prompt at boot.
364
+ It is **declarative guidance**, not an enforced state machine — the
365
+ LLM is expected to follow it; nothing in the runtime blocks a phase
366
+ from being skipped. Rule severities (`critical` / `required` /
367
+ `recommended`) influence how strongly the LLM treats them.
349
368
 
350
369
  ### Artifact store
351
370
 
@@ -361,17 +380,17 @@ compaction.
361
380
 
362
381
  `src/core/sharedSecrets.ts` — REST fetch of `shared_secrets/<name>`
363
382
  from Firestore using the user's Firebase ID token. Auto-refreshes the
364
- token if expired. Writes to `process.env`, no on-disk cache (a
365
- rotated key shouldn't get stuck stale on a user's machine). Triggered
366
- once at boot from `src/bin/deepseek.ts` after `requireAuth()`.
383
+ token if expired. Writes to `process.env`, no on-disk cache (a rotated
384
+ key shouldn't get stuck stale on a user's machine). Triggered once at
385
+ boot from `src/bin/deepseek.ts` after `requireAuth()`.
367
386
 
368
387
  ### Context manager
369
388
 
370
389
  `src/core/contextManager.ts` — token-budget aware sliding window with
371
390
  LLM-driven summarization. Defaults: 130k max, 100k target compaction
372
391
  trigger, last 10 exchanges always preserved, file-read truncation
373
- keeps head + tail of large files. Uses `EROSOLAR_CODE_MODEL` (or the
374
- profile's default) for the summarization pass.
392
+ keeps head + tail of large files. Uses the profile's default model for
393
+ the summarization pass.
375
394
 
376
395
  ### HITL
377
396
 
@@ -383,6 +402,31 @@ the LLM (it decides when to ask), not the runtime — there's no
383
402
 
384
403
  ---
385
404
 
405
+ ## Companion surfaces
406
+
407
+ This monorepo contains the CLI plus three companion surfaces that
408
+ share Erosolar Auth, balance, and the model brain:
409
+
410
+ - **Helia** ([`Erosolar_Browser/`](Erosolar_Browser)) — Electron +
411
+ Chromium browser modeled after ChatGPT Atlas, with a side-panel
412
+ Erosolar agent that has full page context and CDP-driven action
413
+ automation. Cross-platform: macOS (dmg/zip, x64+arm64), Windows
414
+ (NSIS + portable), Linux (AppImage + .deb). Distributed at
415
+ [ero.solar/helia](https://ero.solar/helia) with an S3-manifest
416
+ auto-updater. Current Helia version: `0.1.21`.
417
+ - **Site** ([`site/`](site)) — Firebase-hosted marketing/portal
418
+ (`/about`, `/portal`, `/auth`, `/docs`, `/defense`, `/process`,
419
+ `/audit`, `/admin`, `/npm`, `/helia`).
420
+ - **AWS Lambda backend** ([`aws/lambda/`](aws/lambda)) — single Lambda
421
+ fronted by API Gateway, Stripe checkout, Firestore for state,
422
+ service-account JSON pulled from AWS Secrets Manager. Replaces the
423
+ legacy Cloud Functions deployment; portal + CLI both call into it.
424
+
425
+ Cross-surface PRs are atomic by design. See CLAUDE.md "Monorepo for
426
+ one product, separate repos for different products" for the rule.
427
+
428
+ ---
429
+
386
430
  ## Building from source
387
431
 
388
432
  ```sh
@@ -394,38 +438,40 @@ npm test # full jest suite
394
438
  node dist/bin/erosolar.js --self-test
395
439
  ```
396
440
 
397
- The `pretest` hook runs the build, and `prepublishOnly` rebuilds
398
- before any npm publish so the published tarball is always rebuilt
399
- from source.
441
+ `pretest` runs the build, and `prepublishOnly` rebuilds before any npm
442
+ publish so the published tarball is always rebuilt from source.
400
443
 
401
444
  ### Tests
402
445
 
403
446
  `test/**/*.test.ts` — driven by jest with the config in
404
- `jest.config.cjs`. Test discipline (per `CLAUDE.md`): real behavior
447
+ `jest.config.cjs`. Test discipline (per CLAUDE.md): real behavior
405
448
  end-to-end, no mocks for things the test claims to verify. Tests for
406
449
  provider calls require credentials; those are gated by env-var
407
- presence and skipped (with a clear reason) when absent. Skipped !=
408
- passing.
450
+ presence and skipped (with a clear reason) when absent.
451
+ **Skipped ≠ passing.**
452
+
453
+ A pre-push git hook (`scripts/git-hooks/pre-push`) runs `npm test`
454
+ before every push. Install once per checkout:
455
+ `git config core.hooksPath scripts/git-hooks`. Bypass in an emergency
456
+ with `git push --no-verify`.
409
457
 
410
458
  ### Releasing
411
459
 
412
460
  `npm run release` → `scripts/create-release.sh patch` (interactive —
413
- checks clean git, runs tests, bumps version, builds, publishes,
414
- tags). For non-interactive publishes the workflow is
415
- `npm version patch && npm publish --access public`.
461
+ checks clean git, runs tests, bumps version, builds, publishes, tags).
462
+ Non-interactive publishes: `npm version patch && npm publish --access public`.
416
463
 
417
464
  ---
418
465
 
419
466
  ## Versioning + deprecation policy
420
467
 
421
468
  Semver. Patches add features and fix bugs without API breakage; minor
422
- bumps signal new public surfaces; major bumps signal breaking
423
- changes. Dev releases use the `next` dist-tag.
469
+ bumps signal new public surfaces; major bumps signal breaking changes.
424
470
 
425
471
  Versions `1.1.16` → `1.1.19` are **deprecated** — they bundled an
426
- embedded DeepSeek API key that's now revoked. Any installation on
427
- that range will print an `npm deprecate` warning. Upgrade to ≥
428
- `1.1.20`.
472
+ embedded DeepSeek API key that has been revoked. Installations on that
473
+ range print an `npm deprecate` warning. Upgrade to ≥ `1.1.20`. The
474
+ shared-key fetch via Firebase Auth replaces the embedded key.
429
475
 
430
476
  ---
431
477
 
@@ -436,19 +482,22 @@ that range will print an `npm deprecate` warning. Upgrade to ≥
436
482
  > reference (SSO, allowlist, revocation, profile gating, audit log,
437
483
  > threat model, residual risk).
438
484
 
439
-
440
485
  - The CLI is dual-use offensive-security tooling. U.S. classification
441
- is EAR-controlled (CCL [ECCN 4D004](https://www.federalregister.gov/documents/2021/10/21/2021-22774/information-security-controls-cybersecurity-items)),
442
- not USML/ITAR. Domestic development and use is unrestricted; export
443
- controls apply to international transfer.
444
- - The `kaliCapability.ts` and offsec capabilities are guardrail-free.
445
- Operator authorization is assumed both for legal authorization
446
- (you have permission to test the target) and ethical authorization
447
- (the engagement scope covers what you're about to run).
448
- - Disclosure is pinned. The `variant-research` rulebook's disclose
486
+ is EAR-controlled (CCL [ECCN 4D004](https://www.federalregister.gov/documents/2021/10/21/2021-22774/information-security-controls-cybersecurity-items)
487
+ + 4E001 for development technology). Domestic development and use is
488
+ unrestricted; export controls apply to international transfer. Good-faith
489
+ security research is affirmatively protected by Van Buren (2021), the
490
+ DOJ May-2022 CFAA Charging Policy, and the LOC § 1201 Triennial
491
+ Exemption (2024).
492
+ - `kaliCapability.ts` and the offsec capability surface are
493
+ guardrail-free. Operator authorization is assumed both legal
494
+ authorization (you have permission to test the target) and ethical
495
+ authorization (the engagement scope covers what you're about to run).
496
+ - Disclosure is pinned. The `variant-research` rulebook's `disclose`
449
497
  phase has explicit `vr.r.no_brokerage` and `vr.r.respect_embargo`
450
498
  rules. PoCs go to vendor / HackerOne / Bugcrowd / CERT-CC, not to
451
- brokers.
499
+ brokers. `engagement-delivery` ships only to authorized contracted
500
+ recipients.
452
501
  - Secrets handling: the npm package ships zero embedded API keys.
453
502
  Keys come from env, user-set local file, or Firestore via Firebase
454
503
  Auth (admin-managed). Error messages are sanitized through
@@ -458,8 +507,8 @@ that range will print an `npm deprecate` warning. Upgrade to ≥
458
507
  permissions, in an `0o700` directory. Atomic writes prevent
459
508
  half-written JSON from breaking subsequent loads.
460
509
 
461
- See [`/about`](https://trenchwork.org/about) for the full disclosure
462
- including BIS / DDTC links and the relevant rulemaking.
510
+ See [`/about`](https://ero.solar/about) for the full disclosure
511
+ including BIS links and the relevant rulemaking.
463
512
 
464
513
  ---
465
514
 
@@ -468,8 +517,8 @@ including BIS / DDTC links and the relevant rulemaking.
468
517
  - npm: https://www.npmjs.com/package/@trenchwork/erosolar
469
518
  - Source: https://github.com/Aroxora/deepseek-coder-cli
470
519
  - Companion research workspace: `Aroxora/patchpivot` (private)
471
- - Helia (macOS browser companion): https://trenchwork.org/helia
472
- - Erosolar Auth: https://trenchwork.org/auth
473
- - Project context: https://trenchwork.org/about
520
+ - Helia (browser companion, mac/win/linux): https://ero.solar/helia
521
+ - Erosolar Auth: https://ero.solar/auth
522
+ - Project context: https://ero.solar/about
474
523
 
475
524
  License: MIT (see [LICENSE](LICENSE)).