pentesting 0.3.2 → 0.4.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.
@@ -0,0 +1,24 @@
1
+ import {
2
+ checkForUpdate,
3
+ checkForUpdateAsync,
4
+ compareSemver,
5
+ doUpdate,
6
+ fetchLatestVersion,
7
+ formatUpdateNotification,
8
+ readVersionCache,
9
+ semverTuple,
10
+ writeVersionCache
11
+ } from "./chunk-LZGHM27D.js";
12
+ import "./chunk-IU6YJKJT.js";
13
+ import "./chunk-3RG5ZIWI.js";
14
+ export {
15
+ checkForUpdate,
16
+ checkForUpdateAsync,
17
+ compareSemver,
18
+ doUpdate,
19
+ fetchLatestVersion,
20
+ formatUpdateNotification,
21
+ readVersionCache,
22
+ semverTuple,
23
+ writeVersionCache
24
+ };
@@ -0,0 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ export {
9
+ __require
10
+ };
@@ -0,0 +1,182 @@
1
+ // src/config/agent-constants.ts
2
+ var AGENT_STATUS = {
3
+ IDLE: "idle",
4
+ RUNNING: "running",
5
+ PAUSED: "paused",
6
+ STUCK: "stuck",
7
+ WAITING_INPUT: "waiting_input",
8
+ COMPLETED: "completed"
9
+ };
10
+ var PHASE_ID = {
11
+ RECON: "recon",
12
+ SCAN: "scan",
13
+ ENUM: "enum",
14
+ VULN: "vuln",
15
+ EXPLOIT: "exploit",
16
+ PRIVESC: "privesc",
17
+ PIVOT: "pivot",
18
+ PERSIST: "persist",
19
+ EXFIL: "exfil",
20
+ REPORT: "report"
21
+ };
22
+ var PHASE_STATUS = {
23
+ PENDING: "pending",
24
+ IN_PROGRESS: "in_progress",
25
+ COMPLETED: "completed",
26
+ FAILED: "failed",
27
+ SKIPPED: "skipped"
28
+ };
29
+ var THOUGHT_TYPE = {
30
+ OBSERVATION: "observation",
31
+ HYPOTHESIS: "hypothesis",
32
+ PLAN: "plan",
33
+ ACTION: "action",
34
+ RESULT: "result",
35
+ REFLECTION: "reflection",
36
+ STUCK: "stuck",
37
+ BREAKTHROUGH: "breakthrough"
38
+ };
39
+ var AGENT_EVENT = {
40
+ // Lifecycle
41
+ PLUGINS_LOADED: "plugins_loaded",
42
+ HOOKS_LOADED: "hooks_loaded",
43
+ COMMANDS_LOADED: "commands_loaded",
44
+ MCP_SERVER_ADDED: "mcp_server_added",
45
+ // Execution
46
+ ITERATION: "iteration",
47
+ THOUGHT: "thought",
48
+ RESPONSE: "response",
49
+ TOOL_CALL: "tool_call",
50
+ TOOL_RESULT: "tool_result",
51
+ COMMAND_EXECUTE: "command_execute",
52
+ APPROVAL_NEEDED: "approval_needed",
53
+ TOKEN_USAGE: "token_usage",
54
+ // State changes
55
+ TARGET_SET: "target_set",
56
+ PHASE_CHANGE: "phase_change",
57
+ AGENT_SWITCH: "agent_switch",
58
+ PAUSED: "paused",
59
+ RESUMED: "resumed",
60
+ RESET: "reset",
61
+ // Discoveries
62
+ FINDING: "finding",
63
+ CREDENTIAL: "credential",
64
+ COMPROMISED: "compromised",
65
+ // Completion
66
+ COMPLETE: "complete",
67
+ REPORT: "report",
68
+ ERROR: "error",
69
+ HINT_RECEIVED: "hint_received",
70
+ CONTEXT_COMPACTED: "context_compacted"
71
+ };
72
+ var CLI_COMMAND = {
73
+ HELP: "help",
74
+ TARGET: "target",
75
+ START: "start",
76
+ STOP: "stop",
77
+ FINDINGS: "findings",
78
+ CLEAR: "clear",
79
+ EXIT: "exit"
80
+ };
81
+ var MESSAGE_TYPE = {
82
+ USER: "user",
83
+ ASSISTANT: "assistant",
84
+ TOOL: "tool",
85
+ THINKING: "thinking",
86
+ ERROR: "error",
87
+ SYSTEM: "system",
88
+ RESULT: "result"
89
+ };
90
+ var TOOL_NAME = {
91
+ // System
92
+ BASH: "bash",
93
+ READ_FILE: "read_file",
94
+ WRITE_FILE: "write_file",
95
+ LIST_DIRECTORY: "list_directory",
96
+ // Network
97
+ NMAP_SCAN: "nmap_scan",
98
+ TCPDUMP_CAPTURE: "tcpdump_capture",
99
+ // Web
100
+ WEB_REQUEST: "web_request",
101
+ DIRECTORY_BRUTEFORCE: "directory_bruteforce",
102
+ SQL_INJECTION: "sql_injection",
103
+ BROWSER_AUTOMATION: "browser_automation",
104
+ // Exploit
105
+ SEARCHSPLOIT: "searchsploit",
106
+ METASPLOIT: "metasploit",
107
+ GENERATE_PAYLOAD: "generate_payload",
108
+ // Credential
109
+ BRUTEFORCE_LOGIN: "bruteforce_login",
110
+ CRACK_HASH: "crack_hash",
111
+ DUMP_CREDENTIALS: "dump_credentials",
112
+ // Privilege Escalation
113
+ CHECK_SUDO: "check_sudo",
114
+ FIND_SUID: "find_suid",
115
+ RUN_PRIVESC_ENUM: "run_privesc_enum",
116
+ // Post-Exploitation
117
+ SETUP_TUNNEL: "setup_tunnel",
118
+ LATERAL_MOVEMENT: "lateral_movement",
119
+ // Reporting
120
+ REPORT_FINDING: "report_finding",
121
+ TAKE_SCREENSHOT: "take_screenshot"
122
+ };
123
+ var SENSITIVE_TOOLS = [
124
+ TOOL_NAME.WRITE_FILE,
125
+ TOOL_NAME.BRUTEFORCE_LOGIN,
126
+ TOOL_NAME.METASPLOIT,
127
+ TOOL_NAME.SQL_INJECTION,
128
+ TOOL_NAME.DUMP_CREDENTIALS,
129
+ TOOL_NAME.GENERATE_PAYLOAD,
130
+ TOOL_NAME.LATERAL_MOVEMENT
131
+ ];
132
+
133
+ // src/config/constants.ts
134
+ var APP_NAME = "pentesting";
135
+ var APP_VERSION = "0.4.0";
136
+ var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
137
+ var LLM_API_KEY = process.env.PENTEST_API_KEY || process.env.ANTHROPIC_API_KEY || "";
138
+ var LLM_BASE_URL = process.env.PENTEST_BASE_URL || void 0;
139
+ var LLM_MODEL = process.env.PENTEST_MODEL || "claude-sonnet-4-20250514";
140
+ var LLM_MAX_TOKENS = parseInt(process.env.PENTEST_MAX_TOKENS || "16384", 10);
141
+ var AGENT_CONFIG = {
142
+ maxIterations: 200,
143
+ maxToolCallsPerIteration: 10,
144
+ autoApprove: false,
145
+ sensitiveTools: SENSITIVE_TOOLS,
146
+ defaultTimeout: 6e4,
147
+ longRunningTimeout: 6e5,
148
+ stuckThreshold: 5,
149
+ stuckTimeThreshold: 3e5,
150
+ maxPhaseAttempts: 20
151
+ };
152
+ var PENTEST_PHASES = [
153
+ { id: PHASE_ID.RECON, name: "Reconnaissance", description: "Information gathering" },
154
+ { id: PHASE_ID.SCAN, name: "Scanning", description: "Port and service scanning" },
155
+ { id: PHASE_ID.ENUM, name: "Enumeration", description: "Deep service enumeration" },
156
+ { id: PHASE_ID.VULN, name: "Vulnerability Analysis", description: "Vulnerability identification" },
157
+ { id: PHASE_ID.EXPLOIT, name: "Exploitation", description: "Gaining access" },
158
+ { id: PHASE_ID.PRIVESC, name: "Privilege Escalation", description: "Elevating privileges" },
159
+ { id: PHASE_ID.PIVOT, name: "Pivoting", description: "Lateral movement" },
160
+ { id: PHASE_ID.PERSIST, name: "Persistence", description: "Maintaining access" },
161
+ { id: PHASE_ID.EXFIL, name: "Data Exfiltration", description: "Data extraction" },
162
+ { id: PHASE_ID.REPORT, name: "Reporting", description: "Documentation" }
163
+ ];
164
+
165
+ export {
166
+ AGENT_STATUS,
167
+ PHASE_ID,
168
+ PHASE_STATUS,
169
+ THOUGHT_TYPE,
170
+ AGENT_EVENT,
171
+ CLI_COMMAND,
172
+ MESSAGE_TYPE,
173
+ TOOL_NAME,
174
+ APP_NAME,
175
+ APP_VERSION,
176
+ APP_DESCRIPTION,
177
+ LLM_API_KEY,
178
+ LLM_BASE_URL,
179
+ LLM_MODEL,
180
+ LLM_MAX_TOKENS,
181
+ AGENT_CONFIG
182
+ };
@@ -0,0 +1,134 @@
1
+ import {
2
+ APP_NAME,
3
+ APP_VERSION
4
+ } from "./chunk-IU6YJKJT.js";
5
+
6
+ // src/core/update/auto-update.ts
7
+ import { execSync } from "child_process";
8
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
9
+ import { join } from "path";
10
+ import { homedir } from "os";
11
+ var UPDATE_CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
12
+ var VERSION_CACHE_FILE = join(homedir(), ".pentest", "latest_version.json");
13
+ function semverTuple(version) {
14
+ const match = version.match(/^(\d+)\.(\d+)\.(\d+)/);
15
+ if (!match) return [0, 0, 0];
16
+ return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])];
17
+ }
18
+ function compareSemver(a, b) {
19
+ const [a1, a2, a3] = semverTuple(a);
20
+ const [b1, b2, b3] = semverTuple(b);
21
+ if (a1 !== b1) return a1 < b1 ? -1 : 1;
22
+ if (a2 !== b2) return a2 < b2 ? -1 : 1;
23
+ if (a3 !== b3) return a3 < b3 ? -1 : 1;
24
+ return 0;
25
+ }
26
+ function readVersionCache() {
27
+ try {
28
+ if (!existsSync(VERSION_CACHE_FILE)) return null;
29
+ const data = JSON.parse(readFileSync(VERSION_CACHE_FILE, "utf-8"));
30
+ return data;
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+ function writeVersionCache(info) {
36
+ try {
37
+ const dir = join(homedir(), ".pentest");
38
+ if (!existsSync(dir)) {
39
+ mkdirSync(dir, { recursive: true });
40
+ }
41
+ writeFileSync(VERSION_CACHE_FILE, JSON.stringify(info, null, 2));
42
+ } catch {
43
+ }
44
+ }
45
+ function fetchLatestVersion(packageName = APP_NAME) {
46
+ try {
47
+ const output = execSync(`npm view ${packageName} version`, {
48
+ encoding: "utf-8",
49
+ timeout: 1e4,
50
+ stdio: ["pipe", "pipe", "pipe"]
51
+ });
52
+ return output.trim();
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+ function checkForUpdate(forceCheck = false) {
58
+ const currentVersion = APP_VERSION;
59
+ const cached = readVersionCache();
60
+ const now = Date.now();
61
+ if (!forceCheck && cached && now - cached.checkedAt < UPDATE_CHECK_INTERVAL) {
62
+ const hasUpdate2 = compareSemver(cached.version, currentVersion) > 0;
63
+ return {
64
+ hasUpdate: hasUpdate2,
65
+ currentVersion,
66
+ latestVersion: cached.version
67
+ };
68
+ }
69
+ const latestVersion = fetchLatestVersion();
70
+ if (!latestVersion) {
71
+ return {
72
+ hasUpdate: false,
73
+ currentVersion,
74
+ latestVersion: null,
75
+ error: "Failed to fetch latest version"
76
+ };
77
+ }
78
+ writeVersionCache({
79
+ version: latestVersion,
80
+ checkedAt: now
81
+ });
82
+ const hasUpdate = compareSemver(latestVersion, currentVersion) > 0;
83
+ return {
84
+ hasUpdate,
85
+ currentVersion,
86
+ latestVersion
87
+ };
88
+ }
89
+ async function checkForUpdateAsync() {
90
+ return new Promise((resolve) => {
91
+ setImmediate(() => {
92
+ resolve(checkForUpdate());
93
+ });
94
+ });
95
+ }
96
+ function formatUpdateNotification(result) {
97
+ if (!result.hasUpdate || !result.latestVersion) return null;
98
+ return `
99
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
100
+ \u2502 \u{1F195} A new version of ${APP_NAME} is available! \u2502
101
+ \u2502 \u2502
102
+ \u2502 Current: ${result.currentVersion.padEnd(10)} Latest: ${result.latestVersion.padEnd(10)} \u2502
103
+ \u2502 \u2502
104
+ \u2502 Run: npm update -g ${APP_NAME} \u2502
105
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
106
+ `.trim();
107
+ }
108
+ function doUpdate() {
109
+ try {
110
+ execSync(`npm update -g ${APP_NAME}`, {
111
+ encoding: "utf-8",
112
+ timeout: 12e4,
113
+ stdio: "inherit"
114
+ });
115
+ return { success: true, message: `Updated ${APP_NAME} successfully!` };
116
+ } catch (e) {
117
+ return {
118
+ success: false,
119
+ message: `Failed to update: ${e instanceof Error ? e.message : String(e)}`
120
+ };
121
+ }
122
+ }
123
+
124
+ export {
125
+ semverTuple,
126
+ compareSemver,
127
+ readVersionCache,
128
+ writeVersionCache,
129
+ fetchLatestVersion,
130
+ checkForUpdate,
131
+ checkForUpdateAsync,
132
+ formatUpdateNotification,
133
+ doUpdate
134
+ };
package/dist/index.js CHANGED
@@ -1,10 +1,24 @@
1
1
  #!/usr/bin/env node
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
2
+ import {
3
+ AGENT_CONFIG,
4
+ AGENT_EVENT,
5
+ AGENT_STATUS,
6
+ APP_DESCRIPTION,
7
+ APP_VERSION,
8
+ CLI_COMMAND,
9
+ LLM_API_KEY,
10
+ LLM_BASE_URL,
11
+ LLM_MAX_TOKENS,
12
+ LLM_MODEL,
13
+ MESSAGE_TYPE,
14
+ PHASE_ID,
15
+ PHASE_STATUS,
16
+ THOUGHT_TYPE,
17
+ TOOL_NAME
18
+ } from "./chunk-IU6YJKJT.js";
19
+ import {
20
+ __require
21
+ } from "./chunk-3RG5ZIWI.js";
8
22
 
9
23
  // src/index.tsx
10
24
  import { render } from "ink";
@@ -206,138 +220,6 @@ Analyze your situation honestly:
206
220
 
207
221
  Based on this reflection, propose 3 completely different approaches to try next.`;
208
222
 
209
- // src/config/agent-constants.ts
210
- var AGENT_STATUS = {
211
- IDLE: "idle",
212
- RUNNING: "running",
213
- PAUSED: "paused",
214
- STUCK: "stuck",
215
- WAITING_INPUT: "waiting_input",
216
- COMPLETED: "completed"
217
- };
218
- var PHASE_ID = {
219
- RECON: "recon",
220
- SCAN: "scan",
221
- ENUM: "enum",
222
- VULN: "vuln",
223
- EXPLOIT: "exploit",
224
- PRIVESC: "privesc",
225
- PIVOT: "pivot",
226
- PERSIST: "persist",
227
- EXFIL: "exfil",
228
- REPORT: "report"
229
- };
230
- var PHASE_STATUS = {
231
- PENDING: "pending",
232
- IN_PROGRESS: "in_progress",
233
- COMPLETED: "completed",
234
- FAILED: "failed",
235
- SKIPPED: "skipped"
236
- };
237
- var THOUGHT_TYPE = {
238
- OBSERVATION: "observation",
239
- HYPOTHESIS: "hypothesis",
240
- PLAN: "plan",
241
- ACTION: "action",
242
- RESULT: "result",
243
- REFLECTION: "reflection",
244
- STUCK: "stuck",
245
- BREAKTHROUGH: "breakthrough"
246
- };
247
- var AGENT_EVENT = {
248
- // Lifecycle
249
- PLUGINS_LOADED: "plugins_loaded",
250
- HOOKS_LOADED: "hooks_loaded",
251
- COMMANDS_LOADED: "commands_loaded",
252
- MCP_SERVER_ADDED: "mcp_server_added",
253
- // Execution
254
- ITERATION: "iteration",
255
- THOUGHT: "thought",
256
- RESPONSE: "response",
257
- TOOL_CALL: "tool_call",
258
- TOOL_RESULT: "tool_result",
259
- COMMAND_EXECUTE: "command_execute",
260
- APPROVAL_NEEDED: "approval_needed",
261
- TOKEN_USAGE: "token_usage",
262
- // State changes
263
- TARGET_SET: "target_set",
264
- PHASE_CHANGE: "phase_change",
265
- AGENT_SWITCH: "agent_switch",
266
- PAUSED: "paused",
267
- RESUMED: "resumed",
268
- RESET: "reset",
269
- // Discoveries
270
- FINDING: "finding",
271
- CREDENTIAL: "credential",
272
- COMPROMISED: "compromised",
273
- // Completion
274
- COMPLETE: "complete",
275
- REPORT: "report",
276
- ERROR: "error",
277
- HINT_RECEIVED: "hint_received",
278
- CONTEXT_COMPACTED: "context_compacted"
279
- };
280
- var CLI_COMMAND = {
281
- HELP: "help",
282
- TARGET: "target",
283
- START: "start",
284
- STOP: "stop",
285
- FINDINGS: "findings",
286
- CLEAR: "clear",
287
- EXIT: "exit"
288
- };
289
- var MESSAGE_TYPE = {
290
- USER: "user",
291
- ASSISTANT: "assistant",
292
- TOOL: "tool",
293
- THINKING: "thinking",
294
- ERROR: "error",
295
- SYSTEM: "system",
296
- RESULT: "result"
297
- };
298
- var TOOL_NAME = {
299
- // System
300
- BASH: "bash",
301
- READ_FILE: "read_file",
302
- WRITE_FILE: "write_file",
303
- LIST_DIRECTORY: "list_directory",
304
- // Network
305
- NMAP_SCAN: "nmap_scan",
306
- TCPDUMP_CAPTURE: "tcpdump_capture",
307
- // Web
308
- WEB_REQUEST: "web_request",
309
- DIRECTORY_BRUTEFORCE: "directory_bruteforce",
310
- SQL_INJECTION: "sql_injection",
311
- BROWSER_AUTOMATION: "browser_automation",
312
- // Exploit
313
- SEARCHSPLOIT: "searchsploit",
314
- METASPLOIT: "metasploit",
315
- GENERATE_PAYLOAD: "generate_payload",
316
- // Credential
317
- BRUTEFORCE_LOGIN: "bruteforce_login",
318
- CRACK_HASH: "crack_hash",
319
- DUMP_CREDENTIALS: "dump_credentials",
320
- // Privilege Escalation
321
- CHECK_SUDO: "check_sudo",
322
- FIND_SUID: "find_suid",
323
- RUN_PRIVESC_ENUM: "run_privesc_enum",
324
- // Post-Exploitation
325
- SETUP_TUNNEL: "setup_tunnel",
326
- LATERAL_MOVEMENT: "lateral_movement",
327
- // Reporting
328
- REPORT_FINDING: "report_finding",
329
- TAKE_SCREENSHOT: "take_screenshot"
330
- };
331
- var SENSITIVE_TOOLS = [
332
- TOOL_NAME.WRITE_FILE,
333
- TOOL_NAME.BRUTEFORCE_LOGIN,
334
- TOOL_NAME.METASPLOIT,
335
- TOOL_NAME.SQL_INJECTION,
336
- TOOL_NAME.DUMP_CREDENTIALS,
337
- TOOL_NAME.GENERATE_PAYLOAD,
338
- TOOL_NAME.LATERAL_MOVEMENT
339
- ];
340
-
341
223
  // src/core/tools/tool-definitions.ts
342
224
  var SYSTEM_TOOLS = [
343
225
  {
@@ -1381,37 +1263,6 @@ const { chromium } = require('playwright');
1381
1263
  return executeBash(`script -q /dev/null -c "cat" | tee "${filename || "terminal.txt"}"`);
1382
1264
  }
1383
1265
 
1384
- // src/config/constants.ts
1385
- var APP_VERSION = "0.3.2";
1386
- var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
1387
- var LLM_API_KEY = process.env.PENTEST_API_KEY || process.env.ANTHROPIC_API_KEY || "";
1388
- var LLM_BASE_URL = process.env.PENTEST_BASE_URL || void 0;
1389
- var LLM_MODEL = process.env.PENTEST_MODEL || "claude-sonnet-4-20250514";
1390
- var LLM_MAX_TOKENS = parseInt(process.env.PENTEST_MAX_TOKENS || "16384", 10);
1391
- var AGENT_CONFIG = {
1392
- maxIterations: 200,
1393
- maxToolCallsPerIteration: 10,
1394
- autoApprove: false,
1395
- sensitiveTools: SENSITIVE_TOOLS,
1396
- defaultTimeout: 6e4,
1397
- longRunningTimeout: 6e5,
1398
- stuckThreshold: 5,
1399
- stuckTimeThreshold: 3e5,
1400
- maxPhaseAttempts: 20
1401
- };
1402
- var PENTEST_PHASES = [
1403
- { id: PHASE_ID.RECON, name: "Reconnaissance", description: "Information gathering" },
1404
- { id: PHASE_ID.SCAN, name: "Scanning", description: "Port and service scanning" },
1405
- { id: PHASE_ID.ENUM, name: "Enumeration", description: "Deep service enumeration" },
1406
- { id: PHASE_ID.VULN, name: "Vulnerability Analysis", description: "Vulnerability identification" },
1407
- { id: PHASE_ID.EXPLOIT, name: "Exploitation", description: "Gaining access" },
1408
- { id: PHASE_ID.PRIVESC, name: "Privilege Escalation", description: "Elevating privileges" },
1409
- { id: PHASE_ID.PIVOT, name: "Pivoting", description: "Lateral movement" },
1410
- { id: PHASE_ID.PERSIST, name: "Persistence", description: "Maintaining access" },
1411
- { id: PHASE_ID.EXFIL, name: "Data Exfiltration", description: "Data extraction" },
1412
- { id: PHASE_ID.REPORT, name: "Reporting", description: "Documentation" }
1413
- ];
1414
-
1415
1266
  // src/core/hooks/hook-executor.ts
1416
1267
  import { spawn as spawn2 } from "child_process";
1417
1268
  import * as fs2 from "fs/promises";
@@ -5033,6 +4884,21 @@ var App = ({ autoApprove = false, target }) => {
5033
4884
  setCheckpointCount(contextManagerRef.current?.getCheckpoints().length || 0);
5034
4885
  }
5035
4886
  });
4887
+ import("./auto-update-H72IBVEQ.js").then(({ checkForUpdateAsync, formatUpdateNotification }) => {
4888
+ checkForUpdateAsync().then((result) => {
4889
+ if (result.hasUpdate) {
4890
+ const notification = formatUpdateNotification(result);
4891
+ if (notification) {
4892
+ setMessages((prev) => [...prev, {
4893
+ id: "update-notification",
4894
+ type: MESSAGE_TYPE.SYSTEM,
4895
+ content: notification,
4896
+ timestamp: /* @__PURE__ */ new Date()
4897
+ }]);
4898
+ }
4899
+ }
4900
+ });
4901
+ });
5036
4902
  }, []);
5037
4903
  const startTimeRef = useRef(0);
5038
4904
  const timerRef = useRef(null);
@@ -5196,6 +5062,12 @@ var App = ({ autoApprove = false, target }) => {
5196
5062
  /compact Compact context
5197
5063
  /sessions List saved sessions
5198
5064
  /resume [id] Resume session
5065
+ /replay Show session recordings
5066
+
5067
+ \u2500\u2500 Skills & Extras \u2500\u2500
5068
+ /skills List available skills
5069
+ /update Check for updates
5070
+ /update now Install update
5199
5071
 
5200
5072
  \u2500\u2500 Findings \u2500\u2500
5201
5073
  /findings Show findings
@@ -5423,6 +5295,63 @@ var App = ({ autoApprove = false, target }) => {
5423
5295
  Tokens: ${tokenUsage.total.toLocaleString()}
5424
5296
  Checkpoints: ${checkpointCount}`);
5425
5297
  return;
5298
+ case "skills":
5299
+ try {
5300
+ const { getSkillManager } = await import("./skill-2AON6M2V.js");
5301
+ const skillMgr = getSkillManager();
5302
+ const skills = skillMgr.list();
5303
+ if (skills.length === 0) {
5304
+ addMessage(MESSAGE_TYPE.SYSTEM, "No skills found. Add SKILL.md files to ~/.pentest/skills/");
5305
+ } else {
5306
+ addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4DA} ${skills.length} Skills:`);
5307
+ skills.forEach((s) => {
5308
+ addMessage(MESSAGE_TYPE.SYSTEM, ` ${s.type === "flow" ? "\u{1F504}" : "\u{1F4DD}"} ${s.name}: ${s.description}`);
5309
+ });
5310
+ }
5311
+ } catch (e) {
5312
+ addMessage(MESSAGE_TYPE.ERROR, `Skill error: ${e instanceof Error ? e.message : String(e)}`);
5313
+ }
5314
+ return;
5315
+ case "replay":
5316
+ try {
5317
+ const { findRecentWireFiles, getReplaySummary, parseWireFile } = await import("./replay-6WU2ANWJ.js");
5318
+ const wireFiles = findRecentWireFiles(sessionDirRef.current);
5319
+ if (wireFiles.length === 0) {
5320
+ addMessage(MESSAGE_TYPE.SYSTEM, "No session recordings found");
5321
+ } else {
5322
+ addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4FC} ${wireFiles.length} Session Recordings:`);
5323
+ for (const file of wireFiles.slice(0, 5)) {
5324
+ const events = parseWireFile(file);
5325
+ const summary = getReplaySummary(events);
5326
+ const name = file.split("/").pop()?.replace(".jsonl", "") || "unknown";
5327
+ addMessage(MESSAGE_TYPE.SYSTEM, ` ${name}: ${summary.toolCalls} tools, ${summary.findings} findings, ${summary.duration.toFixed(1)}s`);
5328
+ }
5329
+ }
5330
+ } catch (e) {
5331
+ addMessage(MESSAGE_TYPE.ERROR, `Replay error: ${e instanceof Error ? e.message : String(e)}`);
5332
+ }
5333
+ return;
5334
+ case "update":
5335
+ try {
5336
+ const { checkForUpdate, formatUpdateNotification, doUpdate } = await import("./update-DNXSBIOM.js");
5337
+ const result = checkForUpdate(true);
5338
+ if (result.hasUpdate) {
5339
+ const notification = formatUpdateNotification(result);
5340
+ if (notification) addMessage(MESSAGE_TYPE.SYSTEM, notification);
5341
+ if (args[0] === "now") {
5342
+ addMessage(MESSAGE_TYPE.SYSTEM, "\u{1F504} Updating...");
5343
+ const updateResult = doUpdate();
5344
+ addMessage(updateResult.success ? MESSAGE_TYPE.SYSTEM : MESSAGE_TYPE.ERROR, updateResult.message);
5345
+ } else {
5346
+ addMessage(MESSAGE_TYPE.SYSTEM, "Run /update now to update");
5347
+ }
5348
+ } else {
5349
+ addMessage(MESSAGE_TYPE.SYSTEM, `\u2713 You have the latest version (${result.currentVersion})`);
5350
+ }
5351
+ } catch (e) {
5352
+ addMessage(MESSAGE_TYPE.ERROR, `Update error: ${e instanceof Error ? e.message : String(e)}`);
5353
+ }
5354
+ return;
5426
5355
  case "think":
5427
5356
  addMessage(MESSAGE_TYPE.SYSTEM, "\u{1F9E0} Thinking mode: Extended reasoning enabled");
5428
5357
  return;
@@ -0,0 +1,130 @@
1
+ import {
2
+ __require
3
+ } from "./chunk-3RG5ZIWI.js";
4
+
5
+ // src/core/replay/session-replay.ts
6
+ import { existsSync, readFileSync } from "fs";
7
+ function parseWireFile(filePath) {
8
+ if (!existsSync(filePath)) return [];
9
+ const events = [];
10
+ const content = readFileSync(filePath, "utf-8");
11
+ for (const line of content.split("\n")) {
12
+ if (!line.trim()) continue;
13
+ try {
14
+ const record = JSON.parse(line);
15
+ events.push({
16
+ type: record.message.type,
17
+ timestamp: record.message.timestamp,
18
+ data: record.message.data
19
+ });
20
+ } catch {
21
+ }
22
+ }
23
+ return events;
24
+ }
25
+ function getReplaySummary(events) {
26
+ if (events.length === 0) {
27
+ return { events, duration: 0, toolCalls: 0, findings: 0 };
28
+ }
29
+ const firstTs = events[0].timestamp;
30
+ const lastTs = events[events.length - 1].timestamp;
31
+ const duration = (lastTs - firstTs) / 1e3;
32
+ let toolCalls = 0;
33
+ let findings = 0;
34
+ for (const event of events) {
35
+ if (event.type === "tool_call") toolCalls++;
36
+ if (event.type === "status_update") {
37
+ const data = event.data;
38
+ if (data.event === "finding") findings++;
39
+ }
40
+ }
41
+ return { events, duration, toolCalls, findings };
42
+ }
43
+ function formatReplayEvent(event) {
44
+ const time = new Date(event.timestamp).toLocaleTimeString();
45
+ switch (event.type) {
46
+ case "turn_begin":
47
+ return `[${time}] \u{1F504} Turn started`;
48
+ case "step_begin": {
49
+ const data = event.data;
50
+ return `[${time}] \u{1F4CD} Step ${data.stepIndex + 1}`;
51
+ }
52
+ case "content_part": {
53
+ const data = event.data;
54
+ const preview = data.content.slice(0, 50).replace(/\n/g, " ");
55
+ const icon = data.isThinking ? "\u{1F4AD}" : "\u{1F4AC}";
56
+ return `[${time}] ${icon} ${preview}...`;
57
+ }
58
+ case "tool_call": {
59
+ const data = event.data;
60
+ return `[${time}] \u25B6 Tool: ${data.toolName}`;
61
+ }
62
+ case "tool_result": {
63
+ const data = event.data;
64
+ const icon = data.isError ? "\u2717" : "\u2713";
65
+ return `[${time}] ${icon} Tool result`;
66
+ }
67
+ case "status_update": {
68
+ const data = event.data;
69
+ if (data.event === "finding") {
70
+ return `[${time}] \u{1F3AF} Finding: ${data.title}`;
71
+ }
72
+ if (data.event === "phase_change") {
73
+ return `[${time}] \u{1F4CD} Phase: ${data.phase}`;
74
+ }
75
+ return `[${time}] \u{1F4CA} ${data.event}`;
76
+ }
77
+ case "turn_end":
78
+ return `[${time}] \u2713 Turn complete`;
79
+ default:
80
+ return `[${time}] ${event.type}`;
81
+ }
82
+ }
83
+ async function replaySession(wirePath, options = {}) {
84
+ const events = parseWireFile(wirePath);
85
+ const summary = getReplaySummary(events);
86
+ if (events.length === 0) return summary;
87
+ const speed = options.speed ?? 0;
88
+ let lastTs = events[0].timestamp;
89
+ for (const event of events) {
90
+ const formatted = formatReplayEvent(event);
91
+ if (options.onEvent) {
92
+ options.onEvent(event, formatted);
93
+ }
94
+ if (speed > 0) {
95
+ const delay = (event.timestamp - lastTs) / speed;
96
+ if (delay > 0 && delay < 5e3) {
97
+ await new Promise((r) => setTimeout(r, delay));
98
+ }
99
+ lastTs = event.timestamp;
100
+ }
101
+ }
102
+ return summary;
103
+ }
104
+ function findRecentWireFiles(sessionDir, limit = 10) {
105
+ const { readdirSync, statSync } = __require("fs");
106
+ const { join } = __require("path");
107
+ if (!existsSync(sessionDir)) return [];
108
+ const files = [];
109
+ try {
110
+ const entries = readdirSync(sessionDir, { withFileTypes: true });
111
+ for (const entry of entries) {
112
+ if (entry.isFile() && entry.name.endsWith(".jsonl")) {
113
+ const filePath = join(sessionDir, entry.name);
114
+ const stat = statSync(filePath);
115
+ files.push({ path: filePath, mtime: stat.mtimeMs });
116
+ }
117
+ }
118
+ } catch {
119
+ return [];
120
+ }
121
+ files.sort((a, b) => b.mtime - a.mtime);
122
+ return files.slice(0, limit).map((f) => f.path);
123
+ }
124
+ export {
125
+ findRecentWireFiles,
126
+ formatReplayEvent,
127
+ getReplaySummary,
128
+ parseWireFile,
129
+ replaySession
130
+ };
@@ -0,0 +1,416 @@
1
+ import "./chunk-3RG5ZIWI.js";
2
+
3
+ // src/core/skill/flow.ts
4
+ var FlowError = class extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "FlowError";
8
+ }
9
+ };
10
+ var FlowParseError = class extends FlowError {
11
+ constructor(message) {
12
+ super(message);
13
+ this.name = "FlowParseError";
14
+ }
15
+ };
16
+ var FlowValidationError = class extends FlowError {
17
+ constructor(message) {
18
+ super(message);
19
+ this.name = "FlowValidationError";
20
+ }
21
+ };
22
+ function parseChoice(text) {
23
+ const match = text.match(/<choice>([^<]*)<\/choice>/);
24
+ return match ? match[1].trim() : null;
25
+ }
26
+ function validateFlow(nodes, outgoing) {
27
+ const beginNodes = Array.from(nodes.values()).filter((n) => n.kind === "begin");
28
+ const endNodes = Array.from(nodes.values()).filter((n) => n.kind === "end");
29
+ if (beginNodes.length !== 1) {
30
+ throw new FlowValidationError(`Expected exactly one BEGIN node, found ${beginNodes.length}`);
31
+ }
32
+ if (endNodes.length !== 1) {
33
+ throw new FlowValidationError(`Expected exactly one END node, found ${endNodes.length}`);
34
+ }
35
+ const beginId = beginNodes[0].id;
36
+ const endId = endNodes[0].id;
37
+ const reachable = /* @__PURE__ */ new Set();
38
+ const queue = [beginId];
39
+ while (queue.length > 0) {
40
+ const nodeId = queue.pop();
41
+ if (reachable.has(nodeId)) continue;
42
+ reachable.add(nodeId);
43
+ const edges = outgoing.get(nodeId) || [];
44
+ for (const edge of edges) {
45
+ if (!reachable.has(edge.dst)) {
46
+ queue.push(edge.dst);
47
+ }
48
+ }
49
+ }
50
+ if (!reachable.has(endId)) {
51
+ throw new FlowValidationError("END node is not reachable from BEGIN");
52
+ }
53
+ for (const node of nodes.values()) {
54
+ if (!reachable.has(node.id)) continue;
55
+ const edges = outgoing.get(node.id) || [];
56
+ if (edges.length <= 1) continue;
57
+ const labels = [];
58
+ for (const edge of edges) {
59
+ if (!edge.label?.trim()) {
60
+ throw new FlowValidationError(`Node "${node.id}" has an unlabeled edge`);
61
+ }
62
+ labels.push(edge.label);
63
+ }
64
+ if (new Set(labels).size !== labels.length) {
65
+ throw new FlowValidationError(`Node "${node.id}" has duplicate edge labels`);
66
+ }
67
+ }
68
+ return { beginId, endId };
69
+ }
70
+ function parseMermaidFlowchart(code) {
71
+ const nodes = /* @__PURE__ */ new Map();
72
+ const outgoing = /* @__PURE__ */ new Map();
73
+ const lines = code.split("\n").map((l) => l.trim()).filter((l) => l);
74
+ for (const line of lines) {
75
+ if (line.match(/^flowchart|^graph/i)) continue;
76
+ const nodeMatch = line.match(/^([A-Za-z0-9_]+)(\[([^\]]+)\]|\{([^\}]+)\}|\(\(([^\)]+)\)\)|\(\[([^\]]+)\]\))?$/);
77
+ if (nodeMatch && !line.includes("-->")) {
78
+ const id = nodeMatch[1];
79
+ const label = nodeMatch[3] || nodeMatch[4] || nodeMatch[5] || nodeMatch[6] || id;
80
+ let kind = "task";
81
+ if (nodeMatch[4]) kind = "decision";
82
+ else if (nodeMatch[5] || label.toLowerCase().includes("start") || label.toLowerCase().includes("begin")) kind = "begin";
83
+ else if (nodeMatch[6] || label.toLowerCase().includes("end")) kind = "end";
84
+ nodes.set(id, { id, label, kind });
85
+ continue;
86
+ }
87
+ const edgeMatch = line.match(/^([A-Za-z0-9_]+)\s*-->\s*(?:\|([^\|]+)\|\s*)?([A-Za-z0-9_]+)/);
88
+ if (edgeMatch) {
89
+ const src = edgeMatch[1];
90
+ const label = edgeMatch[2] || null;
91
+ const dst = edgeMatch[3];
92
+ if (!nodes.has(src)) nodes.set(src, { id: src, label: src, kind: "task" });
93
+ if (!nodes.has(dst)) nodes.set(dst, { id: dst, label: dst, kind: "task" });
94
+ const edges = outgoing.get(src) || [];
95
+ edges.push({ src, dst, label });
96
+ outgoing.set(src, edges);
97
+ }
98
+ }
99
+ const { beginId, endId } = validateFlow(nodes, outgoing);
100
+ return { nodes, outgoing, beginId, endId };
101
+ }
102
+ function parseD2Flowchart(code) {
103
+ const nodes = /* @__PURE__ */ new Map();
104
+ const outgoing = /* @__PURE__ */ new Map();
105
+ const lines = code.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#"));
106
+ for (const line of lines) {
107
+ const edgeMatch = line.match(/^([A-Za-z0-9_]+)\s*->\s*([A-Za-z0-9_]+)(?:\s*:\s*(.+))?$/);
108
+ if (edgeMatch) {
109
+ const src = edgeMatch[1];
110
+ const dst = edgeMatch[2];
111
+ const label = edgeMatch[3] || null;
112
+ if (!nodes.has(src)) {
113
+ const kind = src.toLowerCase().includes("start") ? "begin" : "task";
114
+ nodes.set(src, { id: src, label: src, kind });
115
+ }
116
+ if (!nodes.has(dst)) {
117
+ const kind = dst.toLowerCase().includes("end") ? "end" : "task";
118
+ nodes.set(dst, { id: dst, label: dst, kind });
119
+ }
120
+ const edges = outgoing.get(src) || [];
121
+ edges.push({ src, dst, label });
122
+ outgoing.set(src, edges);
123
+ continue;
124
+ }
125
+ const nodeMatch = line.match(/^([A-Za-z0-9_]+)\s*:\s*"?([^"{}]+)"?\s*(?:\{([^}]+)\})?$/);
126
+ if (nodeMatch) {
127
+ const id = nodeMatch[1];
128
+ const label = nodeMatch[2].trim();
129
+ const attrs = nodeMatch[3] || "";
130
+ let kind = "task";
131
+ if (attrs.includes("diamond")) kind = "decision";
132
+ else if (attrs.includes("oval") && label.toLowerCase().includes("start")) kind = "begin";
133
+ else if (attrs.includes("oval") && label.toLowerCase().includes("end")) kind = "end";
134
+ nodes.set(id, { id, label, kind });
135
+ }
136
+ }
137
+ const { beginId, endId } = validateFlow(nodes, outgoing);
138
+ return { nodes, outgoing, beginId, endId };
139
+ }
140
+ var FlowExecutor = class {
141
+ flow;
142
+ currentNodeId;
143
+ moveCount = 0;
144
+ maxMoves;
145
+ constructor(flow, maxMoves = 1e3) {
146
+ this.flow = flow;
147
+ this.currentNodeId = flow.beginId;
148
+ this.maxMoves = maxMoves;
149
+ }
150
+ get currentNode() {
151
+ return this.flow.nodes.get(this.currentNodeId);
152
+ }
153
+ get isComplete() {
154
+ return this.currentNodeId === this.flow.endId;
155
+ }
156
+ get availableChoices() {
157
+ const edges = this.flow.outgoing.get(this.currentNodeId) || [];
158
+ return edges.filter((e) => e.label).map((e) => e.label);
159
+ }
160
+ /**
161
+ * Move to next node
162
+ */
163
+ move(choice) {
164
+ if (this.isComplete) {
165
+ throw new FlowError("Flow is already complete");
166
+ }
167
+ if (this.moveCount >= this.maxMoves) {
168
+ throw new FlowError(`Max moves (${this.maxMoves}) exceeded`);
169
+ }
170
+ const edges = this.flow.outgoing.get(this.currentNodeId) || [];
171
+ if (edges.length === 0) {
172
+ throw new FlowError(`No outgoing edges from node "${this.currentNodeId}"`);
173
+ }
174
+ let nextEdge;
175
+ if (edges.length === 1) {
176
+ nextEdge = edges[0];
177
+ } else if (choice) {
178
+ nextEdge = edges.find((e) => e.label?.toLowerCase() === choice.toLowerCase());
179
+ if (!nextEdge) {
180
+ throw new FlowError(`Invalid choice "${choice}". Available: ${this.availableChoices.join(", ")}`);
181
+ }
182
+ } else {
183
+ throw new FlowError(`Choice required. Available: ${this.availableChoices.join(", ")}`);
184
+ }
185
+ this.currentNodeId = nextEdge.dst;
186
+ this.moveCount++;
187
+ return this.currentNode;
188
+ }
189
+ /**
190
+ * Reset flow
191
+ */
192
+ reset() {
193
+ this.currentNodeId = this.flow.beginId;
194
+ this.moveCount = 0;
195
+ }
196
+ };
197
+
198
+ // src/core/skill/skill.ts
199
+ import { existsSync, readdirSync, readFileSync } from "fs";
200
+ import { join } from "path";
201
+ import { homedir } from "os";
202
+ function getBuiltinSkillsDir() {
203
+ return join(__dirname, "..", "..", "skills");
204
+ }
205
+ function getUserSkillsDirCandidates() {
206
+ return [
207
+ join(homedir(), ".config", "agents", "skills"),
208
+ join(homedir(), ".agents", "skills"),
209
+ join(homedir(), ".pentest", "skills"),
210
+ join(homedir(), ".claude", "skills")
211
+ ];
212
+ }
213
+ function getProjectSkillsDirCandidates(workDir) {
214
+ return [
215
+ join(workDir, ".agents", "skills"),
216
+ join(workDir, ".pentest", "skills"),
217
+ join(workDir, ".claude", "skills")
218
+ ];
219
+ }
220
+ function findFirstExistingDir(candidates) {
221
+ for (const candidate of candidates) {
222
+ if (existsSync(candidate)) {
223
+ return candidate;
224
+ }
225
+ }
226
+ return null;
227
+ }
228
+ function parseFrontmatter(content) {
229
+ const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
230
+ if (!match) return {};
231
+ const yaml = match[1];
232
+ const result = {};
233
+ for (const line of yaml.split("\n")) {
234
+ const colonIndex = line.indexOf(":");
235
+ if (colonIndex > 0) {
236
+ const key = line.slice(0, colonIndex).trim();
237
+ const value = line.slice(colonIndex + 1).trim().replace(/^["']|["']$/g, "");
238
+ result[key] = value;
239
+ }
240
+ }
241
+ return result;
242
+ }
243
+ function extractCodeBlocks(content) {
244
+ const blocks = [];
245
+ const regex = /```(\w+)?\n([\s\S]*?)```/g;
246
+ let match;
247
+ while ((match = regex.exec(content)) !== null) {
248
+ blocks.push({
249
+ lang: (match[1] || "").toLowerCase(),
250
+ code: match[2].trim()
251
+ });
252
+ }
253
+ return blocks;
254
+ }
255
+ function parseSkillText(content, dirPath) {
256
+ const frontmatter = parseFrontmatter(content);
257
+ const name = frontmatter.name || dirPath.split("/").pop() || "unnamed";
258
+ const description = frontmatter.description || "No description provided.";
259
+ const skillType = frontmatter.type || "standard";
260
+ let flow;
261
+ if (skillType === "flow") {
262
+ try {
263
+ const codeBlocks = extractCodeBlocks(content);
264
+ for (const block of codeBlocks) {
265
+ if (block.lang === "mermaid") {
266
+ flow = parseMermaidFlowchart(block.code);
267
+ break;
268
+ } else if (block.lang === "d2") {
269
+ flow = parseD2Flowchart(block.code);
270
+ break;
271
+ }
272
+ }
273
+ if (!flow) {
274
+ throw new FlowError("Flow skills require a mermaid or d2 code block");
275
+ }
276
+ } catch (e) {
277
+ console.error(`Failed to parse flow skill ${name}:`, e);
278
+ return {
279
+ name,
280
+ description,
281
+ type: "standard",
282
+ dir: dirPath,
283
+ content
284
+ };
285
+ }
286
+ }
287
+ return {
288
+ name,
289
+ description,
290
+ type: skillType,
291
+ dir: dirPath,
292
+ flow,
293
+ content
294
+ };
295
+ }
296
+ function discoverSkills(skillsDir) {
297
+ if (!existsSync(skillsDir)) return [];
298
+ const skills = [];
299
+ try {
300
+ const entries = readdirSync(skillsDir, { withFileTypes: true });
301
+ for (const entry of entries) {
302
+ if (!entry.isDirectory()) continue;
303
+ const skillDir = join(skillsDir, entry.name);
304
+ const skillMd = join(skillDir, "SKILL.md");
305
+ if (!existsSync(skillMd)) continue;
306
+ try {
307
+ const content = readFileSync(skillMd, "utf-8");
308
+ const skill = parseSkillText(content, skillDir);
309
+ skills.push(skill);
310
+ } catch {
311
+ }
312
+ }
313
+ } catch {
314
+ }
315
+ return skills.sort((a, b) => a.name.localeCompare(b.name));
316
+ }
317
+ function discoverSkillsFromRoots(skillsDirs) {
318
+ const skillsByName = /* @__PURE__ */ new Map();
319
+ for (const dir of skillsDirs) {
320
+ for (const skill of discoverSkills(dir)) {
321
+ skillsByName.set(skill.name.toLowerCase(), skill);
322
+ }
323
+ }
324
+ return Array.from(skillsByName.values()).sort((a, b) => a.name.localeCompare(b.name));
325
+ }
326
+ function resolveSkillRoots(workDir) {
327
+ const roots = [];
328
+ const builtinDir = getBuiltinSkillsDir();
329
+ if (existsSync(builtinDir)) {
330
+ roots.push(builtinDir);
331
+ }
332
+ const userDir = findFirstExistingDir(getUserSkillsDirCandidates());
333
+ if (userDir) {
334
+ roots.push(userDir);
335
+ }
336
+ const projectDir = findFirstExistingDir(getProjectSkillsDirCandidates(workDir));
337
+ if (projectDir) {
338
+ roots.push(projectDir);
339
+ }
340
+ return roots;
341
+ }
342
+ var SkillManager = class {
343
+ skills = /* @__PURE__ */ new Map();
344
+ workDir;
345
+ constructor(workDir) {
346
+ this.workDir = workDir || process.cwd();
347
+ }
348
+ /**
349
+ * Load all skills
350
+ */
351
+ load() {
352
+ const roots = resolveSkillRoots(this.workDir);
353
+ const skills = discoverSkillsFromRoots(roots);
354
+ this.skills.clear();
355
+ for (const skill of skills) {
356
+ this.skills.set(skill.name.toLowerCase(), skill);
357
+ }
358
+ }
359
+ /**
360
+ * Find skill by name
361
+ */
362
+ find(name) {
363
+ return this.skills.get(name.toLowerCase());
364
+ }
365
+ /**
366
+ * List all skills
367
+ */
368
+ list() {
369
+ return Array.from(this.skills.values());
370
+ }
371
+ /**
372
+ * Get skill content
373
+ */
374
+ getContent(name) {
375
+ const skill = this.find(name);
376
+ return skill?.content || null;
377
+ }
378
+ /**
379
+ * Format skills for system prompt
380
+ */
381
+ formatForPrompt() {
382
+ const skills = this.list();
383
+ if (skills.length === 0) return "No skills found.";
384
+ return skills.map(
385
+ (s) => `- ${s.name}
386
+ - Type: ${s.type}
387
+ - Description: ${s.description}`
388
+ ).join("\n");
389
+ }
390
+ };
391
+ var skillManager = null;
392
+ function getSkillManager(workDir) {
393
+ if (!skillManager) {
394
+ skillManager = new SkillManager(workDir);
395
+ skillManager.load();
396
+ }
397
+ return skillManager;
398
+ }
399
+ export {
400
+ FlowError,
401
+ FlowExecutor,
402
+ FlowParseError,
403
+ FlowValidationError,
404
+ SkillManager,
405
+ discoverSkills,
406
+ discoverSkillsFromRoots,
407
+ extractCodeBlocks,
408
+ getSkillManager,
409
+ parseChoice,
410
+ parseD2Flowchart,
411
+ parseFrontmatter,
412
+ parseMermaidFlowchart,
413
+ parseSkillText,
414
+ resolveSkillRoots,
415
+ validateFlow
416
+ };
@@ -0,0 +1,24 @@
1
+ import {
2
+ checkForUpdate,
3
+ checkForUpdateAsync,
4
+ compareSemver,
5
+ doUpdate,
6
+ fetchLatestVersion,
7
+ formatUpdateNotification,
8
+ readVersionCache,
9
+ semverTuple,
10
+ writeVersionCache
11
+ } from "./chunk-LZGHM27D.js";
12
+ import "./chunk-IU6YJKJT.js";
13
+ import "./chunk-3RG5ZIWI.js";
14
+ export {
15
+ checkForUpdate,
16
+ checkForUpdateAsync,
17
+ compareSemver,
18
+ doUpdate,
19
+ fetchLatestVersion,
20
+ formatUpdateNotification,
21
+ readVersionCache,
22
+ semverTuple,
23
+ writeVersionCache
24
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pentesting",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Autonomous Penetration Testing AI Agent",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",