pentesting 0.3.2 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auto-update-ZU3T3VSG.js +24 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-JUHBSTKO.js +182 -0
- package/dist/chunk-TABUHG2A.js +134 -0
- package/dist/index.js +152 -172
- package/dist/replay-6WU2ANWJ.js +130 -0
- package/dist/skill-2AON6M2V.js +416 -0
- package/dist/update-4H3DBFBE.js +24 -0
- package/package.json +1 -1
|
@@ -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-TABUHG2A.js";
|
|
12
|
+
import "./chunk-JUHBSTKO.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.1";
|
|
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-JUHBSTKO.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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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-JUHBSTKO.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";
|
|
@@ -3304,6 +3155,44 @@ var AutonomousHackingAgent = class extends EventEmitter4 {
|
|
|
3304
3155
|
current: this.state.currentPhase
|
|
3305
3156
|
};
|
|
3306
3157
|
}
|
|
3158
|
+
// ===== Image Analysis with Vision =====
|
|
3159
|
+
async analyzeImage(base64, mimeType, prompt) {
|
|
3160
|
+
this.think(THOUGHT_TYPE.OBSERVATION, `Analyzing image: ${prompt}`);
|
|
3161
|
+
try {
|
|
3162
|
+
const response = await this.client.messages.create({
|
|
3163
|
+
model: LLM_MODEL,
|
|
3164
|
+
max_tokens: 4096,
|
|
3165
|
+
messages: [{
|
|
3166
|
+
role: "user",
|
|
3167
|
+
content: [
|
|
3168
|
+
{
|
|
3169
|
+
type: "image",
|
|
3170
|
+
source: {
|
|
3171
|
+
type: "base64",
|
|
3172
|
+
media_type: mimeType,
|
|
3173
|
+
data: base64
|
|
3174
|
+
}
|
|
3175
|
+
},
|
|
3176
|
+
{
|
|
3177
|
+
type: "text",
|
|
3178
|
+
text: `${AUTONOMOUS_HACKING_PROMPT}
|
|
3179
|
+
|
|
3180
|
+
Analyze this image from a security perspective:
|
|
3181
|
+
${prompt}`
|
|
3182
|
+
}
|
|
3183
|
+
]
|
|
3184
|
+
}]
|
|
3185
|
+
});
|
|
3186
|
+
const textContent = response.content.find((c) => c.type === "text");
|
|
3187
|
+
const result = textContent && "text" in textContent ? textContent.text : "No analysis available";
|
|
3188
|
+
this.think(THOUGHT_TYPE.REFLECTION, `Image analysis complete`);
|
|
3189
|
+
return result;
|
|
3190
|
+
} catch (error) {
|
|
3191
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3192
|
+
this.think(THOUGHT_TYPE.STUCK, `Image analysis failed: ${errorMsg}`);
|
|
3193
|
+
throw error;
|
|
3194
|
+
}
|
|
3195
|
+
}
|
|
3307
3196
|
// ===== Thought Process Recording =====
|
|
3308
3197
|
think(type, content) {
|
|
3309
3198
|
const thought = {
|
|
@@ -4758,7 +4647,7 @@ function readClipboardImage() {
|
|
|
4758
4647
|
if (result === "success" && existsSync3(tmpPath)) {
|
|
4759
4648
|
const imageBuffer = readFileSync3(tmpPath);
|
|
4760
4649
|
const base64 = imageBuffer.toString("base64");
|
|
4761
|
-
return { path: tmpPath, base64 };
|
|
4650
|
+
return { path: tmpPath, base64, mimeType: "image/png" };
|
|
4762
4651
|
}
|
|
4763
4652
|
} else if (os === "linux") {
|
|
4764
4653
|
try {
|
|
@@ -4771,7 +4660,7 @@ function readClipboardImage() {
|
|
|
4771
4660
|
if (stats.size > 0) {
|
|
4772
4661
|
const imageBuffer = readFileSync3(tmpPath);
|
|
4773
4662
|
const base64 = imageBuffer.toString("base64");
|
|
4774
|
-
return { path: tmpPath, base64 };
|
|
4663
|
+
return { path: tmpPath, base64, mimeType: "image/png" };
|
|
4775
4664
|
}
|
|
4776
4665
|
}
|
|
4777
4666
|
} catch {
|
|
@@ -4791,7 +4680,7 @@ function readClipboardImage() {
|
|
|
4791
4680
|
if (result === "success" && existsSync3(tmpPath)) {
|
|
4792
4681
|
const imageBuffer = readFileSync3(tmpPath);
|
|
4793
4682
|
const base64 = imageBuffer.toString("base64");
|
|
4794
|
-
return { path: tmpPath, base64 };
|
|
4683
|
+
return { path: tmpPath, base64, mimeType: "image/png" };
|
|
4795
4684
|
}
|
|
4796
4685
|
}
|
|
4797
4686
|
} catch (error) {
|
|
@@ -5033,6 +4922,21 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
5033
4922
|
setCheckpointCount(contextManagerRef.current?.getCheckpoints().length || 0);
|
|
5034
4923
|
}
|
|
5035
4924
|
});
|
|
4925
|
+
import("./auto-update-ZU3T3VSG.js").then(({ checkForUpdateAsync, formatUpdateNotification }) => {
|
|
4926
|
+
checkForUpdateAsync().then((result) => {
|
|
4927
|
+
if (result.hasUpdate) {
|
|
4928
|
+
const notification = formatUpdateNotification(result);
|
|
4929
|
+
if (notification) {
|
|
4930
|
+
setMessages((prev) => [...prev, {
|
|
4931
|
+
id: "update-notification",
|
|
4932
|
+
type: MESSAGE_TYPE.SYSTEM,
|
|
4933
|
+
content: notification,
|
|
4934
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
4935
|
+
}]);
|
|
4936
|
+
}
|
|
4937
|
+
}
|
|
4938
|
+
});
|
|
4939
|
+
});
|
|
5036
4940
|
}, []);
|
|
5037
4941
|
const startTimeRef = useRef(0);
|
|
5038
4942
|
const timerRef = useRef(null);
|
|
@@ -5196,6 +5100,12 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
5196
5100
|
/compact Compact context
|
|
5197
5101
|
/sessions List saved sessions
|
|
5198
5102
|
/resume [id] Resume session
|
|
5103
|
+
/replay Show session recordings
|
|
5104
|
+
|
|
5105
|
+
\u2500\u2500 Skills & Extras \u2500\u2500
|
|
5106
|
+
/skills List available skills
|
|
5107
|
+
/update Check for updates
|
|
5108
|
+
/update now Install update
|
|
5199
5109
|
|
|
5200
5110
|
\u2500\u2500 Findings \u2500\u2500
|
|
5201
5111
|
/findings Show findings
|
|
@@ -5365,6 +5275,19 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
5365
5275
|
if (img) {
|
|
5366
5276
|
addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4F7} Image from clipboard: ${img.path}`);
|
|
5367
5277
|
addMessage(MESSAGE_TYPE.SYSTEM, ` Size: ${Math.round(img.base64.length / 1024)}KB base64`);
|
|
5278
|
+
const prompt = args.join(" ") || "Analyze this image for security-relevant information";
|
|
5279
|
+
addMessage(MESSAGE_TYPE.SYSTEM, ` Sending to agent: "${prompt}"`);
|
|
5280
|
+
setIsProcessing(true);
|
|
5281
|
+
startTimer();
|
|
5282
|
+
try {
|
|
5283
|
+
const response = await agent.analyzeImage(img.base64, img.mimeType, prompt);
|
|
5284
|
+
const duration = stopTimer();
|
|
5285
|
+
addMessage(MESSAGE_TYPE.ASSISTANT, response, duration);
|
|
5286
|
+
} catch (e) {
|
|
5287
|
+
stopTimer();
|
|
5288
|
+
addMessage(MESSAGE_TYPE.ERROR, `Image analysis failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
5289
|
+
}
|
|
5290
|
+
setIsProcessing(false);
|
|
5368
5291
|
}
|
|
5369
5292
|
} else {
|
|
5370
5293
|
const text = readClipboardText();
|
|
@@ -5423,6 +5346,63 @@ var App = ({ autoApprove = false, target }) => {
|
|
|
5423
5346
|
Tokens: ${tokenUsage.total.toLocaleString()}
|
|
5424
5347
|
Checkpoints: ${checkpointCount}`);
|
|
5425
5348
|
return;
|
|
5349
|
+
case "skills":
|
|
5350
|
+
try {
|
|
5351
|
+
const { getSkillManager } = await import("./skill-2AON6M2V.js");
|
|
5352
|
+
const skillMgr = getSkillManager();
|
|
5353
|
+
const skills = skillMgr.list();
|
|
5354
|
+
if (skills.length === 0) {
|
|
5355
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "No skills found. Add SKILL.md files to ~/.pentest/skills/");
|
|
5356
|
+
} else {
|
|
5357
|
+
addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4DA} ${skills.length} Skills:`);
|
|
5358
|
+
skills.forEach((s) => {
|
|
5359
|
+
addMessage(MESSAGE_TYPE.SYSTEM, ` ${s.type === "flow" ? "\u{1F504}" : "\u{1F4DD}"} ${s.name}: ${s.description}`);
|
|
5360
|
+
});
|
|
5361
|
+
}
|
|
5362
|
+
} catch (e) {
|
|
5363
|
+
addMessage(MESSAGE_TYPE.ERROR, `Skill error: ${e instanceof Error ? e.message : String(e)}`);
|
|
5364
|
+
}
|
|
5365
|
+
return;
|
|
5366
|
+
case "replay":
|
|
5367
|
+
try {
|
|
5368
|
+
const { findRecentWireFiles, getReplaySummary, parseWireFile } = await import("./replay-6WU2ANWJ.js");
|
|
5369
|
+
const wireFiles = findRecentWireFiles(sessionDirRef.current);
|
|
5370
|
+
if (wireFiles.length === 0) {
|
|
5371
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "No session recordings found");
|
|
5372
|
+
} else {
|
|
5373
|
+
addMessage(MESSAGE_TYPE.SYSTEM, `\u{1F4FC} ${wireFiles.length} Session Recordings:`);
|
|
5374
|
+
for (const file of wireFiles.slice(0, 5)) {
|
|
5375
|
+
const events = parseWireFile(file);
|
|
5376
|
+
const summary = getReplaySummary(events);
|
|
5377
|
+
const name = file.split("/").pop()?.replace(".jsonl", "") || "unknown";
|
|
5378
|
+
addMessage(MESSAGE_TYPE.SYSTEM, ` ${name}: ${summary.toolCalls} tools, ${summary.findings} findings, ${summary.duration.toFixed(1)}s`);
|
|
5379
|
+
}
|
|
5380
|
+
}
|
|
5381
|
+
} catch (e) {
|
|
5382
|
+
addMessage(MESSAGE_TYPE.ERROR, `Replay error: ${e instanceof Error ? e.message : String(e)}`);
|
|
5383
|
+
}
|
|
5384
|
+
return;
|
|
5385
|
+
case "update":
|
|
5386
|
+
try {
|
|
5387
|
+
const { checkForUpdate, formatUpdateNotification, doUpdate } = await import("./update-4H3DBFBE.js");
|
|
5388
|
+
const result = checkForUpdate(true);
|
|
5389
|
+
if (result.hasUpdate) {
|
|
5390
|
+
const notification = formatUpdateNotification(result);
|
|
5391
|
+
if (notification) addMessage(MESSAGE_TYPE.SYSTEM, notification);
|
|
5392
|
+
if (args[0] === "now") {
|
|
5393
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "\u{1F504} Updating...");
|
|
5394
|
+
const updateResult = doUpdate();
|
|
5395
|
+
addMessage(updateResult.success ? MESSAGE_TYPE.SYSTEM : MESSAGE_TYPE.ERROR, updateResult.message);
|
|
5396
|
+
} else {
|
|
5397
|
+
addMessage(MESSAGE_TYPE.SYSTEM, "Run /update now to update");
|
|
5398
|
+
}
|
|
5399
|
+
} else {
|
|
5400
|
+
addMessage(MESSAGE_TYPE.SYSTEM, `\u2713 You have the latest version (${result.currentVersion})`);
|
|
5401
|
+
}
|
|
5402
|
+
} catch (e) {
|
|
5403
|
+
addMessage(MESSAGE_TYPE.ERROR, `Update error: ${e instanceof Error ? e.message : String(e)}`);
|
|
5404
|
+
}
|
|
5405
|
+
return;
|
|
5426
5406
|
case "think":
|
|
5427
5407
|
addMessage(MESSAGE_TYPE.SYSTEM, "\u{1F9E0} Thinking mode: Extended reasoning enabled");
|
|
5428
5408
|
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-TABUHG2A.js";
|
|
12
|
+
import "./chunk-JUHBSTKO.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
|
+
};
|