pentesting 0.43.0 → 0.44.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.
package/dist/main.js CHANGED
@@ -10,7 +10,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
10
10
  import { render } from "ink";
11
11
  import { Command } from "commander";
12
12
  import chalk from "chalk";
13
- import gradient from "gradient-string";
14
13
 
15
14
  // src/platform/tui/app.tsx
16
15
  import { useState as useState4, useCallback as useCallback4, useEffect as useEffect4, useRef as useRef4 } from "react";
@@ -307,14 +306,12 @@ var ORPHAN_PROCESS_NAMES = [
307
306
  "dnsspoof",
308
307
  "tcpdump",
309
308
  "tshark",
310
- "socat",
311
- "nc",
312
- "python"
309
+ "socat"
313
310
  ];
314
311
 
315
312
  // src/shared/constants/agent.ts
316
313
  var APP_NAME = "Pentest AI";
317
- var APP_VERSION = "0.43.0";
314
+ var APP_VERSION = "0.44.0";
318
315
  var APP_DESCRIPTION = "Autonomous Penetration Testing AI Agent";
319
316
  var LLM_ROLES = {
320
317
  SYSTEM: "system",
@@ -342,6 +339,146 @@ var SENSITIVE_INPUT_TYPES = [
342
339
  INPUT_TYPES.CREDENTIAL
343
340
  ];
344
341
 
342
+ // src/shared/utils/time-strategy.ts
343
+ var PHASE_RATIOS = {
344
+ /** Sprint ends at 25% of total time */
345
+ SPRINT_END: 0.25,
346
+ /** Exploit phase ends at 50% of total time */
347
+ EXPLOIT_END: 0.5,
348
+ /** Creative phase ends at 75% of total time */
349
+ CREATIVE_END: 0.75
350
+ // Harvest: 75%-100%
351
+ };
352
+ var DEFAULT_DURATION_SEC = 86400;
353
+ var MIN_DURATION_SEC = 300;
354
+ var MAX_DURATION_SEC = 604800;
355
+ var MS_PER_SEC = 1e3;
356
+ function getSessionDurationMs() {
357
+ const envValue = process.env.PENTEST_DURATION;
358
+ if (envValue) {
359
+ const parsed = parseInt(envValue, 10);
360
+ if (!isNaN(parsed) && parsed > 0) {
361
+ const clamped = Math.max(MIN_DURATION_SEC, Math.min(MAX_DURATION_SEC, parsed));
362
+ return clamped * MS_PER_SEC;
363
+ }
364
+ }
365
+ return DEFAULT_DURATION_SEC * MS_PER_SEC;
366
+ }
367
+ function computeDeadline(startedAt) {
368
+ return startedAt + getSessionDurationMs();
369
+ }
370
+ function formatDuration(totalSec) {
371
+ if (totalSec <= 0) return "0s";
372
+ const h = Math.floor(totalSec / 3600);
373
+ const m = Math.floor(totalSec % 3600 / 60);
374
+ const s = Math.floor(totalSec % 60);
375
+ if (h > 0 && m > 0) return `${h}h${m}m`;
376
+ if (h > 0) return `${h}h`;
377
+ if (m > 0 && s > 0) return `${m}m${s}s`;
378
+ if (m > 0) return `${m}m`;
379
+ return `${s}s`;
380
+ }
381
+ function getTimeAdaptiveStrategy(elapsedMs, deadlineMs) {
382
+ let totalMs;
383
+ let remainingMs;
384
+ if (deadlineMs > 0) {
385
+ remainingMs = Math.max(0, deadlineMs - Date.now());
386
+ totalMs = elapsedMs + remainingMs;
387
+ } else {
388
+ totalMs = getSessionDurationMs();
389
+ remainingMs = Math.max(0, totalMs - elapsedMs);
390
+ }
391
+ if (totalMs <= 0) totalMs = 1;
392
+ const progress = Math.min(1, elapsedMs / totalMs);
393
+ const remainingSec = Math.floor(remainingMs / MS_PER_SEC);
394
+ const totalDurationSec = Math.floor(totalMs / MS_PER_SEC);
395
+ const elapsedStr = formatDuration(Math.floor(elapsedMs / MS_PER_SEC));
396
+ const remainStr = formatDuration(remainingSec);
397
+ const totalStr = formatDuration(totalDurationSec);
398
+ const pctStr = `${Math.floor(progress * 100)}%`;
399
+ if (progress >= PHASE_RATIOS.CREATIVE_END) {
400
+ return {
401
+ phase: "harvest",
402
+ label: "HARVEST",
403
+ urgency: "critical",
404
+ progress,
405
+ totalDurationSec,
406
+ remainingSec,
407
+ directive: [
408
+ `\u{1F3C1} HARVEST MODE [${pctStr} elapsed, ${remainStr} of ${totalStr} remaining]:`,
409
+ "- STOP exploring new attack vectors. Exploit what you HAVE.",
410
+ "- Submit ALL discovered flags immediately",
411
+ "- Check obvious flag locations: /root/flag.txt, user.txt, env vars, DB tables",
412
+ "- Re-check previously accessed systems for missed flags/proof",
413
+ "- Collect and record ALL evidence and proof files",
414
+ "- Write final report with all findings",
415
+ "- Credential spray ALL discovered creds on ALL remaining services",
416
+ remainingSec <= 300 ? "- \u26A0\uFE0F FINAL 5 MINUTES \u2014 submit everything NOW, stop all scans" : "- Focus on highest-confidence paths only"
417
+ ].join("\n")
418
+ };
419
+ }
420
+ if (progress >= PHASE_RATIOS.EXPLOIT_END) {
421
+ return {
422
+ phase: "creative",
423
+ label: "CREATIVE",
424
+ urgency: "high",
425
+ progress,
426
+ totalDurationSec,
427
+ remainingSec,
428
+ directive: [
429
+ `\u{1F52C} CREATIVE MODE [${pctStr} elapsed, ${remainStr} of ${totalStr} remaining]:`,
430
+ "- Try advanced techniques: chained exploits, custom tools, race conditions",
431
+ "- Binary analysis for SUID/custom binaries, kernel exploits for privesc",
432
+ "- Protocol-level attacks: SMB relay, Kerberoasting, ADCS abuse",
433
+ '- Search for latest bypasses: web_search("{defense} bypass {year}")',
434
+ "- If stuck >5min on any vector, SWITCH immediately",
435
+ "- Start preparing evidence collection for final phase",
436
+ "- If all targets are compromised \u2192 skip to harvest immediately"
437
+ ].join("\n")
438
+ };
439
+ }
440
+ if (progress >= PHASE_RATIOS.SPRINT_END) {
441
+ return {
442
+ phase: "exploit",
443
+ label: "EXPLOIT",
444
+ urgency: "medium",
445
+ progress,
446
+ totalDurationSec,
447
+ remainingSec,
448
+ directive: [
449
+ `\u{1F3AF} EXPLOIT MODE [${pctStr} elapsed, ${remainStr} of ${totalStr} remaining]:`,
450
+ "- Concentrate on discovered vectors with highest success probability",
451
+ "- Run parallel exploitation attempts on multiple targets",
452
+ "- Search for CVE PoCs for every identified service+version",
453
+ "- Chain: credentials \u2192 spray everywhere, LFI \u2192 config \u2192 DB creds",
454
+ "- Keep background scans running for new targets",
455
+ "- Time-box each vector: 10min max then switch",
456
+ "- If all vectors exhausted early \u2192 move to creative techniques immediately"
457
+ ].join("\n")
458
+ };
459
+ }
460
+ return {
461
+ phase: "sprint",
462
+ label: "SPRINT",
463
+ urgency: "low",
464
+ progress,
465
+ totalDurationSec,
466
+ remainingSec,
467
+ directive: [
468
+ `\u26A1 SPRINT MODE [${pctStr} elapsed, ${remainStr} of ${totalStr} remaining]:`,
469
+ "- RustScan first for fast port discovery \u2192 then nmap -Pn -sV -sC on found ports",
470
+ "- ALWAYS use nmap -Pn (no exceptions \u2014 firewalls block ICMP)",
471
+ "- Run ALL scans in parallel (rustscan, nmap UDP, web fuzzing, CVE search)",
472
+ "- Try default/weak credentials on every discovered service IMMEDIATELY",
473
+ "- Check for exposed files (.env, .git, backup.sql, phpinfo)",
474
+ "- Anonymous access: FTP, SMB null session, Redis no auth, MongoDB",
475
+ "- OSINT: web_search for target intel, shodan, github repos",
476
+ "- Goal: Maximum attack surface discovery + instant wins",
477
+ "- \u26A1 If recon completes early \u2192 ATTACK IMMEDIATELY, do not wait for this phase to end"
478
+ ].join("\n")
479
+ };
480
+ }
481
+
345
482
  // src/shared/utils/id.ts
346
483
  var ID_DEFAULT_RADIX = 36;
347
484
  var ID_DEFAULT_LENGTH = 6;
@@ -2027,7 +2164,7 @@ async function cleanupAllProcesses() {
2027
2164
  backgroundProcesses.clear();
2028
2165
  for (const name of ORPHAN_PROCESS_NAMES) {
2029
2166
  try {
2030
- execSync2(`pkill -f "${name}" 2>/dev/null || true`, { stdio: "ignore", timeout: SYSTEM_LIMITS.PROCESS_OP_TIMEOUT_MS });
2167
+ execSync2(`pkill -x "${name}" 2>/dev/null || true`, { stdio: "ignore", timeout: SYSTEM_LIMITS.PROCESS_OP_TIMEOUT_MS });
2031
2168
  } catch {
2032
2169
  }
2033
2170
  }
@@ -2652,6 +2789,22 @@ var AttackGraph = class {
2652
2789
  if (nodeCount >= GRAPH_LIMITS.ASCII_MAX_NODES) break;
2653
2790
  const sIcon = statusIcons[node.status] || "?";
2654
2791
  const fail = node.status === NODE_STATUS.FAILED && node.failReason ? ` \u2014 ${node.failReason}` : "";
2792
+ let detail = "";
2793
+ if (type === NODE_TYPE.CREDENTIAL) {
2794
+ const source = node.data.source ? ` (from: ${node.data.source})` : "";
2795
+ detail = source;
2796
+ } else if (type === NODE_TYPE.VULNERABILITY) {
2797
+ const sev = node.data.severity ? ` [${String(node.data.severity).toUpperCase()}]` : "";
2798
+ const exploit = node.data.hasExploit ? " [EXPLOIT]" : "";
2799
+ const target = node.data.target ? ` \u2192 ${node.data.target}` : "";
2800
+ detail = `${sev}${exploit}${target}`;
2801
+ } else if (type === NODE_TYPE.ACCESS) {
2802
+ const via = node.data.via ? ` via ${node.data.via}` : "";
2803
+ detail = via;
2804
+ } else if (type === NODE_TYPE.SERVICE) {
2805
+ const ver = node.data.version ? ` (${node.data.version})` : "";
2806
+ detail = ver;
2807
+ }
2655
2808
  const outEdges = this.edges.filter((e) => e.from === node.id);
2656
2809
  const edgeStr = outEdges.length > 0 ? ` \u2192 ${outEdges.map((e) => {
2657
2810
  const target = this.nodes.get(e.to);
@@ -2659,10 +2812,19 @@ var AttackGraph = class {
2659
2812
  const eStatus = e.status === EDGE_STATUS.FAILED ? " \u2717" : e.status === EDGE_STATUS.SUCCEEDED ? " \u2713" : "";
2660
2813
  return `${eName}${eStatus}`;
2661
2814
  }).join(", ")}` : "";
2662
- lines.push(`\u2502 ${sIcon} ${node.label}${fail}${edgeStr}`);
2815
+ lines.push(`\u2502 ${sIcon} ${node.label}${detail}${fail}${edgeStr}`);
2663
2816
  nodeCount++;
2664
2817
  }
2665
2818
  }
2819
+ const succeededNodes = Array.from(this.nodes.values()).filter((n) => n.status === NODE_STATUS.SUCCEEDED);
2820
+ if (succeededNodes.length > 0) {
2821
+ lines.push(`\u2502`);
2822
+ lines.push(`\u251C\u2500\u2500\u2500 Exploited \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\u2524`);
2823
+ for (const n of succeededNodes) {
2824
+ const via = n.data.via ? ` via ${n.data.via}` : "";
2825
+ lines.push(`\u2502 \u25CF ${n.label}${via}`);
2826
+ }
2827
+ }
2666
2828
  const stats = this.getStats();
2667
2829
  lines.push(`\u2502`);
2668
2830
  lines.push(`\u251C\u2500\u2500\u2500 Summary \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\u2524`);
@@ -3183,6 +3345,7 @@ var SharedState = class {
3183
3345
  // ─── Improvement #7: Dynamic Technique Library ────────────────
3184
3346
  dynamicTechniques = new DynamicTechniqueLibrary();
3185
3347
  constructor() {
3348
+ const now = Date.now();
3186
3349
  this.data = {
3187
3350
  engagement: null,
3188
3351
  targets: /* @__PURE__ */ new Map(),
@@ -3196,8 +3359,10 @@ var SharedState = class {
3196
3359
  ctfMode: true,
3197
3360
  // CTF mode ON by default
3198
3361
  flags: [],
3199
- startedAt: Date.now(),
3200
- deadlineAt: 0,
3362
+ startedAt: now,
3363
+ // Auto-configure from PENTEST_DURATION env (seconds).
3364
+ // Defaults to 24h if not set. Can be overridden via setDeadline().
3365
+ deadlineAt: computeDeadline(now),
3201
3366
  challengeAnalysis: null
3202
3367
  };
3203
3368
  }
@@ -3206,6 +3371,7 @@ var SharedState = class {
3206
3371
  * Used by /clear command to start a fresh session without recreating the agent.
3207
3372
  */
3208
3373
  reset() {
3374
+ const now = Date.now();
3209
3375
  this.data = {
3210
3376
  engagement: null,
3211
3377
  targets: /* @__PURE__ */ new Map(),
@@ -3218,8 +3384,8 @@ var SharedState = class {
3218
3384
  missionChecklist: [],
3219
3385
  ctfMode: true,
3220
3386
  flags: [],
3221
- startedAt: Date.now(),
3222
- deadlineAt: 0,
3387
+ startedAt: now,
3388
+ deadlineAt: computeDeadline(now),
3223
3389
  challengeAnalysis: null
3224
3390
  };
3225
3391
  this.attackGraph.reset();
@@ -3404,24 +3570,28 @@ var SharedState = class {
3404
3570
  if (this.data.deadlineAt === 0) return 0;
3405
3571
  return Math.max(0, this.data.deadlineAt - Date.now());
3406
3572
  }
3407
- /** Get time status string for prompt injection */
3573
+ /** Get time status string for prompt injection (ratio-based, aligned with PHASE_RATIOS) */
3408
3574
  getTimeStatus() {
3409
3575
  const elapsed = this.getElapsedMs();
3410
3576
  const mins = Math.floor(elapsed / 6e4);
3411
3577
  let status = `\u23F1 Elapsed: ${mins}min`;
3412
3578
  if (this.data.deadlineAt > 0) {
3413
3579
  const remainingMs = this.getRemainingMs();
3580
+ const totalMs = elapsed + remainingMs;
3581
+ const progress = totalMs > 0 ? elapsed / totalMs : 1;
3414
3582
  const remainingMins = Math.floor(remainingMs / 6e4);
3415
- if (remainingMins <= 0) {
3583
+ if (remainingMs <= 0) {
3416
3584
  status += " | \u26A0\uFE0F TIME IS UP \u2014 submit any flags you have NOW";
3417
- } else if (remainingMins <= 15) {
3585
+ } else if (progress >= 0.95) {
3418
3586
  status += ` | \u{1F534} ${remainingMins}min LEFT \u2014 FINAL PUSH: submit flags, check obvious locations, env vars, DBs`;
3419
- } else if (remainingMins <= 30) {
3420
- status += ` | \u{1F7E1} ${remainingMins}min LEFT \u2014 wrap up current vector, ensure all findings are captured`;
3421
- } else if (remainingMins <= 60) {
3422
- status += ` | \u{1F7E0} ${remainingMins}min LEFT \u2014 focus on highest-probability vectors only`;
3587
+ } else if (progress >= 0.75) {
3588
+ status += ` | \u{1F7E1} ${remainingMins}min LEFT \u2014 HARVEST: wrap up, collect evidence, submit flags`;
3589
+ } else if (progress >= 0.5) {
3590
+ status += ` | \u{1F7E0} ${remainingMins}min LEFT \u2014 CREATIVE: advanced techniques, switch if stuck`;
3591
+ } else if (progress >= 0.25) {
3592
+ status += ` | \u{1F535} ${remainingMins}min LEFT \u2014 EXPLOIT: focus on discovered vectors`;
3423
3593
  } else {
3424
- status += ` | \u{1F7E2} ${remainingMins}min remaining`;
3594
+ status += ` | \u{1F7E2} ${remainingMins}min remaining \u2014 SPRINT: recon + quick wins`;
3425
3595
  }
3426
3596
  }
3427
3597
  return status;
@@ -4912,26 +5082,31 @@ Detail: ${detail}
4912
5082
  },
4913
5083
  {
4914
5084
  name: TOOL_NAMES.ADD_FINDING,
4915
- description: "Add a security finding. Always include attackPattern for MITRE ATT&CK mapping.",
5085
+ description: `Add a security finding with full details.
5086
+ ALWAYS provide: description (HOW you exploited it, step-by-step), evidence (actual command output proving success), and attackPattern (MITRE ATT&CK tactic).
5087
+ Findings without evidence are marked as UNVERIFIED and have low credibility.`,
4916
5088
  parameters: {
4917
- title: { type: "string", description: "Finding title" },
5089
+ title: { type: "string", description: 'Concise finding title (e.g., "Path Traversal via /download endpoint")' },
4918
5090
  severity: { type: "string", description: "Severity: critical, high, medium, low, info" },
4919
- affected: { type: "array", items: { type: "string" }, description: "Affected host:port" },
5091
+ affected: { type: "array", items: { type: "string" }, description: 'Affected host:port or URLs (e.g., ["ctf.example.com:443/download"])' },
5092
+ description: { type: "string", description: "Detailed description: what the vulnerability is, how you exploited it step-by-step, what access it gives, and the impact. Include credentials found, methods used, and exploitation chain." },
5093
+ evidence: { type: "array", items: { type: "string" }, description: 'Actual command outputs proving the finding (e.g., ["curl output showing /etc/passwd", "uid=0(root) gid=0(root)"]). Copy real output here.' },
4920
5094
  attackPattern: { type: "string", description: "MITRE ATT&CK tactic: initial_access, execution, persistence, privilege_escalation, defense_evasion, credential_access, discovery, lateral_movement, collection, exfiltration, command_and_control, impact" }
4921
5095
  },
4922
- required: ["title", "severity"],
5096
+ required: ["title", "severity", "description", "evidence"],
4923
5097
  execute: async (p) => {
4924
5098
  const evidence = p.evidence || [];
4925
5099
  const title = p.title;
4926
5100
  const severity = p.severity;
4927
5101
  const affected = p.affected || [];
5102
+ const description = p.description || "";
4928
5103
  const validation = validateFinding(evidence, severity);
4929
5104
  state.addFinding({
4930
5105
  id: generateId(AGENT_LIMITS.ID_RADIX, AGENT_LIMITS.ID_LENGTH),
4931
5106
  title,
4932
5107
  severity,
4933
5108
  affected,
4934
- description: p.description || "",
5109
+ description,
4935
5110
  evidence,
4936
5111
  isVerified: validation.isVerified,
4937
5112
  remediation: "",
@@ -8686,6 +8861,11 @@ var NETWORK_ERROR_CODES = {
8686
8861
  ENOTFOUND: "ENOTFOUND",
8687
8862
  CONNECT_TIMEOUT: "UND_ERR_CONNECT_TIMEOUT"
8688
8863
  };
8864
+ var TRANSIENT_NETWORK_ERRORS = [
8865
+ NETWORK_ERROR_CODES.ECONNRESET,
8866
+ NETWORK_ERROR_CODES.ETIMEDOUT,
8867
+ NETWORK_ERROR_CODES.ENOTFOUND
8868
+ ];
8689
8869
  var LLMError = class extends Error {
8690
8870
  /** Structured error information */
8691
8871
  errorInfo;
@@ -8696,8 +8876,11 @@ var LLMError = class extends Error {
8696
8876
  }
8697
8877
  };
8698
8878
  function classifyError(error) {
8879
+ if (error === null || error === void 0) {
8880
+ return { type: LLM_ERROR_TYPES.UNKNOWN, message: String(error), isRetryable: false, suggestedAction: "Analyze error" };
8881
+ }
8699
8882
  const e = error;
8700
- const statusCode = e.status || e.statusCode;
8883
+ const statusCode = e?.status || e?.statusCode;
8701
8884
  const errorMessage = e?.error?.error?.message || e?.error?.message || e?.message || String(error);
8702
8885
  if (statusCode === HTTP_STATUS.RATE_LIMIT || errorMessage.toLowerCase().includes("rate limit")) {
8703
8886
  return { type: LLM_ERROR_TYPES.RATE_LIMIT, message: errorMessage, statusCode, isRetryable: true, suggestedAction: "Wait and retry" };
@@ -8708,7 +8891,7 @@ function classifyError(error) {
8708
8891
  if (statusCode === HTTP_STATUS.BAD_REQUEST) {
8709
8892
  return { type: LLM_ERROR_TYPES.INVALID_REQUEST, message: errorMessage, statusCode, isRetryable: false, suggestedAction: "Modify request" };
8710
8893
  }
8711
- if (e.code === NETWORK_ERROR_CODES.ECONNRESET || e.code === NETWORK_ERROR_CODES.ETIMEDOUT || e.code === NETWORK_ERROR_CODES.ENOTFOUND) {
8894
+ if (e.code && TRANSIENT_NETWORK_ERRORS.includes(e.code)) {
8712
8895
  return { type: LLM_ERROR_TYPES.NETWORK_ERROR, message: errorMessage, isRetryable: true, suggestedAction: "Check network" };
8713
8896
  }
8714
8897
  if (errorMessage.toLowerCase().includes("timeout") || e.code === NETWORK_ERROR_CODES.CONNECT_TIMEOUT) {
@@ -10317,100 +10500,6 @@ var INITIAL_TASKS = {
10317
10500
  RECON: "Initial reconnaissance and target discovery"
10318
10501
  };
10319
10502
 
10320
- // src/shared/utils/time-strategy.ts
10321
- var TIME_CONSTANTS = {
10322
- /** Milliseconds per minute */
10323
- MS_PER_MINUTE: 6e4,
10324
- /** Minutes threshold for sprint-to-exploit switch (no deadline mode) */
10325
- SPRINT_MINUTES: 10,
10326
- /** Deadline percentage thresholds for phase transitions */
10327
- PHASE_SPRINT: 0.25,
10328
- PHASE_EXPLOIT: 0.5,
10329
- PHASE_CREATIVE: 0.75
10330
- };
10331
- function getTimeAdaptiveStrategy(elapsedMs, deadlineMs) {
10332
- if (deadlineMs === 0) {
10333
- const mins = Math.floor(elapsedMs / TIME_CONSTANTS.MS_PER_MINUTE);
10334
- if (mins < TIME_CONSTANTS.SPRINT_MINUTES) {
10335
- return {
10336
- phase: "sprint",
10337
- label: "SPRINT",
10338
- urgency: "low",
10339
- directive: `\u26A1 SPRINT MODE (${mins}min elapsed): Parallel reconnaissance. Full port scan + service enumeration + default credentials. Attack immediately on any finding.`
10340
- };
10341
- }
10342
- return {
10343
- phase: "exploit",
10344
- label: "EXPLOIT",
10345
- urgency: "low",
10346
- directive: `\u{1F3AF} EXPLOIT MODE (${mins}min elapsed): Focus on discovered vectors. Chain findings. Build custom tools if needed.`
10347
- };
10348
- }
10349
- const totalMs = deadlineMs - (deadlineMs > Date.now() ? Date.now() - elapsedMs : 0);
10350
- const remainingMs = Math.max(0, deadlineMs - Date.now());
10351
- const pct = totalMs > 0 ? 1 - remainingMs / totalMs : 1;
10352
- const remainingMins = Math.floor(remainingMs / TIME_CONSTANTS.MS_PER_MINUTE);
10353
- if (pct < TIME_CONSTANTS.PHASE_SPRINT) {
10354
- return {
10355
- phase: "sprint",
10356
- label: "SPRINT",
10357
- urgency: "low",
10358
- directive: [
10359
- `\u26A1 SPRINT MODE (${remainingMins}min remaining):`,
10360
- "- Run ALL scans in parallel (nmap TCP/UDP, web fuzzing, CVE search)",
10361
- "- Try default/weak credentials on every discovered service IMMEDIATELY",
10362
- "- Check for exposed files (.env, .git, backup.sql, phpinfo)",
10363
- "- Anonymous access: FTP, SMB null session, Redis no auth, MongoDB",
10364
- "- Goal: Maximum attack surface discovery + instant wins"
10365
- ].join("\n")
10366
- };
10367
- }
10368
- if (pct < TIME_CONSTANTS.PHASE_EXPLOIT) {
10369
- return {
10370
- phase: "exploit",
10371
- label: "EXPLOIT",
10372
- urgency: "medium",
10373
- directive: [
10374
- `\u{1F3AF} EXPLOIT MODE (${remainingMins}min remaining):`,
10375
- "- Concentrate on discovered vectors with highest success probability",
10376
- "- Run parallel exploitation attempts on multiple targets",
10377
- "- Search for CVE PoCs for every identified service+version",
10378
- "- Chain: credentials \u2192 spray everywhere, LFI \u2192 config \u2192 DB creds",
10379
- "- Keep background scans running for new targets"
10380
- ].join("\n")
10381
- };
10382
- }
10383
- if (pct < TIME_CONSTANTS.PHASE_CREATIVE) {
10384
- return {
10385
- phase: "creative",
10386
- label: "CREATIVE",
10387
- urgency: "high",
10388
- directive: [
10389
- `\u{1F52C} CREATIVE MODE (${remainingMins}min remaining):`,
10390
- "- Try advanced techniques: chained exploits, custom tools, race conditions",
10391
- "- Binary analysis for SUID/custom binaries, kernel exploits for privesc",
10392
- "- Protocol-level attacks: SMB relay, Kerberoasting, ADCS abuse",
10393
- '- Search for latest bypasses: web_search("{defense} bypass 2025")',
10394
- "- If stuck >5min on any vector, SWITCH immediately"
10395
- ].join("\n")
10396
- };
10397
- }
10398
- return {
10399
- phase: "harvest",
10400
- label: "HARVEST",
10401
- urgency: "critical",
10402
- directive: [
10403
- `\u{1F3C1} HARVEST MODE (${remainingMins}min remaining \u2014 FINAL PUSH):`,
10404
- "- STOP exploring new vectors. Focus on what you HAVE.",
10405
- "- Submit ALL discovered flags immediately",
10406
- "- Check obvious flag locations: /root/flag.txt, env vars, DB tables",
10407
- "- Re-check previously accessed systems for missed flags",
10408
- "- Collect and record ALL evidence and proof files",
10409
- "- Write final report with all findings"
10410
- ].join("\n")
10411
- };
10412
- }
10413
-
10414
10503
  // src/shared/constants/service-ports.ts
10415
10504
  var SERVICE_PORTS = {
10416
10505
  SSH: 22,
@@ -10960,12 +11049,13 @@ var STRATEGIST_LIMITS = {
10960
11049
  * Full state can be 20K+; Strategist needs summary, not everything. */
10961
11050
  MAX_INPUT_CHARS: 15e3,
10962
11051
  /** Maximum characters for the Strategist's response.
10963
- * WHY: Directives should be terse and actionable (~500-800 tokens).
10964
- * Longer = more cost, less focus. */
10965
- MAX_OUTPUT_CHARS: 3e3,
11052
+ * WHY: Directives should be terse and actionable (~800-1500 tokens).
11053
+ * Enhanced format includes SITUATION, priorities, EXHAUSTED, and SEARCH ORDERS. */
11054
+ MAX_OUTPUT_CHARS: 5e3,
10966
11055
  /** Maximum lines in the directive output.
10967
- * WHY: Forces concise, prioritized directives. */
10968
- MAX_DIRECTIVE_LINES: 40
11056
+ * WHY: Forces concise, prioritized directives while allowing
11057
+ * structured format (priorities + exhausted + search orders). */
11058
+ MAX_DIRECTIVE_LINES: 60
10969
11059
  };
10970
11060
 
10971
11061
  // src/agents/strategist.ts
@@ -11134,11 +11224,14 @@ NOTE: This directive is from ${age}min ago (Strategist call failed this turn). V
11134
11224
  this.totalCalls = 0;
11135
11225
  }
11136
11226
  };
11137
- var FALLBACK_SYSTEM_PROMPT = `You are a penetration testing strategist.
11138
- Analyze the situation and write a precise tactical directive for the attack agent.
11139
- Be specific: name exact tools, commands, and parameters.
11140
- Maximum 30 lines. Prioritize by probability of success.
11141
- NEVER suggest approaches listed in the failed attempts section.`;
11227
+ var FALLBACK_SYSTEM_PROMPT = `You are an elite autonomous penetration testing STRATEGIST \u2014 a red team tactical commander.
11228
+ Analyze the engagement state and issue precise attack orders for the execution agent.
11229
+ Format: SITUATION line, numbered PRIORITY items with ACTION/SEARCH/SUCCESS/FALLBACK/CHAIN fields, EXHAUSTED list, and SEARCH ORDERS.
11230
+ Be surgically specific: name exact tools, commands, parameters, and wordlists. The agent copy-pastes your commands.
11231
+ Include mandatory web_search directives for every unknown service/version.
11232
+ Detect stalls (repeated failures, no progress) and force completely different attack vectors.
11233
+ Chain every finding: "If X works \u2192 immediately do Y \u2192 which enables Z."
11234
+ Maximum 50 lines. Zero preamble. Direct imperatives only. Never repeat failed approaches.`;
11142
11235
 
11143
11236
  // src/agents/main-agent.ts
11144
11237
  var MainAgent = class extends CoreAgent {
@@ -11319,7 +11412,7 @@ var AgentFactory = class {
11319
11412
  };
11320
11413
 
11321
11414
  // src/platform/tui/utils/format.ts
11322
- var formatDuration = (ms) => {
11415
+ var formatDuration2 = (ms) => {
11323
11416
  const totalSec = ms / 1e3;
11324
11417
  if (totalSec < 60) return `${totalSec.toFixed(1)}s`;
11325
11418
  const minutes = Math.floor(totalSec / 60);
@@ -11333,7 +11426,7 @@ var formatTokens = (count) => {
11333
11426
  };
11334
11427
  var formatMeta = (ms, tokens) => {
11335
11428
  const parts = [];
11336
- if (ms > 0) parts.push(formatDuration(ms));
11429
+ if (ms > 0) parts.push(formatDuration2(ms));
11337
11430
  if (tokens > 0) parts.push(`\u2191 ${formatTokens(tokens)} tokens`);
11338
11431
  return parts.length > 0 ? `(${parts.join(" \xB7 ")})` : "";
11339
11432
  };
@@ -11367,135 +11460,59 @@ var formatInlineStatus = () => {
11367
11460
  import { useState, useRef, useCallback } from "react";
11368
11461
 
11369
11462
  // src/shared/constants/theme.ts
11463
+ var COLORS = {
11464
+ primary: "#e11d48",
11465
+ // Rose 600 - red team primary
11466
+ accent: "#fb923c",
11467
+ // Orange 400 - fire accent
11468
+ secondary: "#94a3b8",
11469
+ // Slate - secondary/muted
11470
+ white: "#f8fafc",
11471
+ // Main text
11472
+ red: "#ff4500",
11473
+ // OrangeRed - Error/Critical (distinct from rose primary)
11474
+ yellow: "#facc15"
11475
+ // Yellow 400 - Warning/Pending (pure yellow)
11476
+ };
11370
11477
  var THEME = {
11371
- // Backgrounds (deep dark with blue tint)
11478
+ ...COLORS,
11372
11479
  bg: {
11373
11480
  primary: "#050505",
11374
- // Deepest black
11375
- secondary: "#0a0c10",
11376
- // Dark void
11377
- tertiary: "#0f172a",
11378
- // Slate dark
11379
- elevated: "#1e293b",
11380
- // Bright slate
11381
11481
  input: "#020617"
11382
- // Midnight
11383
11482
  },
11384
- // Text colors
11385
11483
  text: {
11386
- primary: "#f8fafc",
11387
- // Near white
11388
- secondary: "#cbd5e1",
11389
- // Brighter slate
11390
- muted: "#94a3b8",
11391
- // Lighter blue-gray
11392
- accent: "#1d4ed8",
11393
- // Blue 700 - deep blue
11394
- highlight: "#ffffff"
11395
- // Pure white
11484
+ primary: COLORS.white,
11485
+ secondary: COLORS.secondary,
11486
+ muted: "#64748b",
11487
+ accent: COLORS.accent
11488
+ // Using Emerald for "accented" text
11396
11489
  },
11397
- // Status colors (deep blue-focused)
11398
11490
  status: {
11399
- success: "#10b981",
11400
- // Emerald
11401
- warning: "#f59e0b",
11402
- // Amber
11403
- error: "#ef4444",
11404
- // Red
11405
- info: "#1d4ed8",
11406
- // Blue 700
11407
- running: "#1e40af",
11408
- // Blue 800 (AI activity)
11409
- pending: "#64748b"
11410
- // Slate
11411
- },
11412
- // Severity colors
11413
- semantic: {
11414
- critical: "#7f1d1d",
11415
- // Dark red
11416
- high: "#ef4444",
11417
- // Red
11418
- medium: "#f97316",
11419
- // Orange
11420
- low: "#eab308",
11421
- // Yellow
11422
- info: "#1d4ed8"
11423
- // Blue 700
11491
+ success: COLORS.accent,
11492
+ // Success feels good in emerald
11493
+ warning: COLORS.yellow,
11494
+ error: COLORS.red,
11495
+ running: COLORS.primary
11496
+ // System operations in blue
11424
11497
  },
11425
- // Border colors
11426
11498
  border: {
11427
11499
  default: "#1e293b",
11428
- focus: "#3b82f6",
11429
- // Blue 500 (UI decorative)
11430
- error: "#ef4444",
11431
- success: "#22c55e"
11500
+ focus: COLORS.primary,
11501
+ error: COLORS.red
11432
11502
  },
11433
- // Phase colors (deep blue-focused)
11434
- phase: {
11435
- recon: "#94a3b8",
11436
- enum: "#1d4ed8",
11437
- // Blue 700 (phase indicator)
11438
- vuln: "#f59e0b",
11439
- exploit: "#ef4444",
11440
- privesc: "#8b5cf6",
11441
- persist: "#22c55e",
11442
- report: "#64748b"
11443
- },
11444
- // Accent colors (NO cyan/teal - pure blue palette)
11445
- accent: {
11446
- pink: "#f472b6",
11447
- rose: "#fb7185",
11448
- fuchsia: "#e879f9",
11449
- purple: "#a78bfa",
11450
- violet: "#8b5cf6",
11451
- indigo: "#818cf8",
11452
- blue: "#1d4ed8",
11453
- // Blue 700 - primary accent
11454
- emerald: "#34d399",
11455
- green: "#4ade80",
11456
- lime: "#a3e635",
11457
- yellow: "#facc15",
11458
- amber: "#fbbf24",
11459
- orange: "#fb923c",
11460
- red: "#f87171"
11461
- },
11462
- // Gradients (deep blue-focused)
11463
11503
  gradient: {
11464
- cyber: [
11465
- "#3b82f6",
11466
- // Blue 500
11467
- "#3584f4",
11468
- "#2f86f2",
11469
- "#2988f0",
11470
- "#238aee",
11471
- "#1d8cec",
11472
- // Mid blue
11473
- "#1d7ad8",
11474
- "#1d78c6",
11475
- "#1d76b4",
11476
- "#1d74a2",
11477
- "#1e40af"
11478
- // Blue 800
11479
- ],
11480
- danger: ["#ef4444", "#7f1d1d"],
11481
- success: ["#22c55e", "#14532d"],
11482
- gold: ["#f59e0b", "#78350f"],
11483
- royal: ["#818cf8", "#312e81"]
11504
+ cyber: [COLORS.primary, "#f43f5e", "#fb923c"]
11505
+ // Red team fire gradient: rose → pink → amber
11484
11506
  },
11485
- // Spinner color (deep blue — UI feedback)
11486
- spinner: "#3b82f6",
11487
- // Blue 500
11488
- // Identity color (branded accent - deep blue)
11489
- identity: "#1d4ed8"
11490
- // Blue 700
11507
+ spinner: COLORS.primary
11491
11508
  };
11492
11509
  var ASCII_BANNER = `
11493
- ____ __ __ _
11494
- / __ \\___ ____ / /____ _______/ /_(_)___ ____ _
11495
- / /_/ / _ \\/ __ \\/ __/ _ \\/ ___/ __/ / / __ \\/ __ \`/
11496
- / ____/ __/ / / / /_/ __(__ ) /_/ / / / / / /_/ /
11497
- /_/ \\___/_/ /_/\\__/\\___/____/\\__/_/_/_/ /_/\\__, /
11498
- /____/
11510
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557
11511
+ \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D
11512
+ \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2557
11513
+ \u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551
11514
+ \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
11515
+ \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D
11499
11516
  `;
11500
11517
  var ICONS = {
11501
11518
  // Status
@@ -11509,7 +11526,7 @@ var ICONS = {
11509
11526
  target: "\u25C8",
11510
11527
  // Diamond for target
11511
11528
  scan: "\u25CE",
11512
- exploit: "\u26A1",
11529
+ exploit: "\u2607",
11513
11530
  shell: "\u276F",
11514
11531
  // Progress
11515
11532
  pending: "\u25CB",
@@ -11521,9 +11538,9 @@ var ICONS = {
11521
11538
  medium: "\u25C7",
11522
11539
  low: "\u25E6",
11523
11540
  // Security
11524
- lock: "\u{1F512}",
11525
- unlock: "\u{1F513}",
11526
- key: "\u{1F511}",
11541
+ lock: "\u22A1",
11542
+ unlock: "\u2B1A",
11543
+ key: "\u26B7",
11527
11544
  flag: "\u2691",
11528
11545
  // Simple flag
11529
11546
  pwned: "\u25C8"
@@ -11580,7 +11597,7 @@ var MESSAGE_STYLES = {
11580
11597
  error: THEME.status.error,
11581
11598
  tool: THEME.status.running,
11582
11599
  result: THEME.text.muted,
11583
- status: THEME.accent.blue
11600
+ status: THEME.primary
11584
11601
  },
11585
11602
  prefixes: {
11586
11603
  user: "\u276F",
@@ -11641,21 +11658,9 @@ var useAgentState = () => {
11641
11658
  const [isProcessing, setIsProcessing] = useState(false);
11642
11659
  const [currentStatus, setCurrentStatus] = useState("");
11643
11660
  const [elapsedTime, setElapsedTime] = useState(0);
11644
- const [retryState, setRetryState] = useState({
11645
- isRetrying: false,
11646
- attempt: 0,
11647
- maxRetries: 0,
11648
- delayMs: 0,
11649
- error: "",
11650
- countdown: 0
11651
- });
11661
+ const [retryState, setRetryState] = useState({ status: "idle" });
11652
11662
  const [currentTokens, setCurrentTokens] = useState(0);
11653
- const [inputRequest, setInputRequest] = useState({
11654
- isActive: false,
11655
- prompt: "",
11656
- isPassword: false,
11657
- resolve: null
11658
- });
11663
+ const [inputRequest, setInputRequest] = useState({ status: "inactive" });
11659
11664
  const [stats, setStats] = useState({ phase: DEFAULTS.INIT_PHASE, targets: 0, findings: 0, todo: 0 });
11660
11665
  const lastResponseMetaRef = useRef(null);
11661
11666
  const startTimeRef = useRef(0);
@@ -11796,7 +11801,7 @@ var useAgentEvents = (agent, eventsRef, state) => {
11796
11801
  const isPassword = /password|passphrase/i.test(p);
11797
11802
  const inputType = /sudo/i.test(p) ? "sudo_password" : isPassword ? "password" : "text";
11798
11803
  setInputRequest({
11799
- isActive: true,
11804
+ status: "active",
11800
11805
  prompt: p.trim(),
11801
11806
  isPassword,
11802
11807
  inputType,
@@ -11810,7 +11815,7 @@ var useAgentEvents = (agent, eventsRef, state) => {
11810
11815
  const isPassword = hiddenTypes.includes(request.type);
11811
11816
  const displayPrompt = buildCredentialPrompt(request);
11812
11817
  setInputRequest({
11813
- isActive: true,
11818
+ status: "active",
11814
11819
  prompt: displayPrompt,
11815
11820
  isPassword,
11816
11821
  inputType: request.type,
@@ -11891,7 +11896,7 @@ function handleRetry(e, addMessage, setRetryState, retryCountdownRef, retryCount
11891
11896
  const retryNum = retryCountRef.current;
11892
11897
  addMessage("system", `\u27F3 Retry #${retryNum} \xB7 ${e.data.error} \xB7 waiting ${delaySec}s...`);
11893
11898
  setRetryState({
11894
- isRetrying: true,
11899
+ status: "retrying",
11895
11900
  attempt: retryNum,
11896
11901
  maxRetries: e.data.maxRetries,
11897
11902
  delayMs: e.data.delayMs,
@@ -11903,10 +11908,10 @@ function handleRetry(e, addMessage, setRetryState, retryCountdownRef, retryCount
11903
11908
  retryCountdownRef.current = setInterval(() => {
11904
11909
  remaining -= 1;
11905
11910
  if (remaining <= 0) {
11906
- setRetryState((prev) => ({ ...prev, isRetrying: false, countdown: 0 }));
11911
+ setRetryState({ status: "idle" });
11907
11912
  if (retryCountdownRef.current) clearInterval(retryCountdownRef.current);
11908
11913
  } else {
11909
- setRetryState((prev) => ({ ...prev, countdown: remaining }));
11914
+ setRetryState((prev) => prev.status === "retrying" ? { ...prev, countdown: remaining } : prev);
11910
11915
  }
11911
11916
  }, 1e3);
11912
11917
  }
@@ -12001,9 +12006,9 @@ var useAgent = (shouldAutoApprove, target) => {
12001
12006
  inputRequestRef.current = inputRequest;
12002
12007
  const cancelInputRequest = useCallback2(() => {
12003
12008
  const ir = inputRequestRef.current;
12004
- if (ir.isActive && ir.resolve) {
12009
+ if (ir.status === "active") {
12005
12010
  ir.resolve(null);
12006
- setInputRequest({ isActive: false, prompt: "", isPassword: false, resolve: null });
12011
+ setInputRequest({ status: "inactive" });
12007
12012
  addMessage("system", "Input cancelled");
12008
12013
  }
12009
12014
  }, [setInputRequest, addMessage]);
@@ -12045,7 +12050,7 @@ import { Box as Box2, Text as Text2, Static } from "ink";
12045
12050
  // src/platform/tui/components/inline-status.tsx
12046
12051
  import { Box, Text } from "ink";
12047
12052
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12048
- function formatDuration2(ms) {
12053
+ function formatDuration3(ms) {
12049
12054
  const seconds = Math.floor(ms / 1e3);
12050
12055
  if (seconds < 60) return `${seconds}s`;
12051
12056
  const minutes = Math.floor(seconds / 60);
@@ -12057,13 +12062,13 @@ function formatDuration2(ms) {
12057
12062
  }
12058
12063
  function getRoleColor(role) {
12059
12064
  const roleColors = {
12060
- listener: THEME.accent.blue,
12061
- active_shell: THEME.accent.green,
12062
- server: THEME.accent.blue,
12063
- sniffer: THEME.accent.amber,
12064
- spoofer: THEME.accent.orange,
12065
- proxy: THEME.accent.purple,
12066
- callback: THEME.accent.pink,
12065
+ listener: THEME.primary,
12066
+ active_shell: THEME.primary,
12067
+ server: THEME.secondary,
12068
+ sniffer: THEME.yellow,
12069
+ spoofer: THEME.yellow,
12070
+ proxy: THEME.primary,
12071
+ callback: THEME.primary,
12067
12072
  background: THEME.text.muted
12068
12073
  };
12069
12074
  return roleColors[role] || THEME.text.secondary;
@@ -12087,7 +12092,7 @@ function StatusIndicator({ running, exitCode }) {
12087
12092
  ] });
12088
12093
  }
12089
12094
  function ProcessRow({ proc, compact }) {
12090
- const duration = formatDuration2(proc.durationMs);
12095
+ const duration = formatDuration3(proc.durationMs);
12091
12096
  const port = proc.listeningPort ? `:${proc.listeningPort}` : "";
12092
12097
  const purpose = proc.purpose || proc.description || "";
12093
12098
  const truncatedPurpose = compact && purpose.length > TUI_DISPLAY_LIMITS.purposeMaxLength ? purpose.slice(0, TUI_DISPLAY_LIMITS.purposeTruncated) + "..." : purpose;
@@ -12260,7 +12265,7 @@ var StatusDisplay = memo3(({
12260
12265
  };
12261
12266
  const meta = formatMeta(elapsedTime * 1e3, currentTokens);
12262
12267
  return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 0, children: [
12263
- retryState.isRetrying && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12268
+ retryState.status === "retrying" && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12264
12269
  /* @__PURE__ */ jsx4(Text4, { color: THEME.status.warning, children: /* @__PURE__ */ jsx4(MusicSpinner, { color: THEME.status.warning }) }),
12265
12270
  /* @__PURE__ */ jsxs3(Text4, { color: THEME.status.warning, children: [
12266
12271
  " \u27F3 Retry #",
@@ -12270,13 +12275,13 @@ var StatusDisplay = memo3(({
12270
12275
  " \xB7 ",
12271
12276
  truncateError(retryState.error)
12272
12277
  ] }),
12273
- /* @__PURE__ */ jsxs3(Text4, { color: THEME.accent.blue, bold: true, children: [
12278
+ /* @__PURE__ */ jsxs3(Text4, { color: THEME.primary, bold: true, children: [
12274
12279
  " \xB7 ",
12275
12280
  retryState.countdown,
12276
12281
  "s"
12277
12282
  ] })
12278
12283
  ] }),
12279
- isProcessing && !retryState.isRetrying && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12284
+ isProcessing && retryState.status !== "retrying" && /* @__PURE__ */ jsxs3(Box3, { marginBottom: 1, children: [
12280
12285
  /* @__PURE__ */ jsx4(Text4, { color: THEME.spinner, children: /* @__PURE__ */ jsx4(MusicSpinner, { color: THEME.spinner }) }),
12281
12286
  /* @__PURE__ */ jsxs3(Text4, { color: THEME.text.muted, children: [
12282
12287
  " ",
@@ -12325,11 +12330,13 @@ var ChatInput = memo4(({
12325
12330
  const onChangeRef = useRef3(onChange);
12326
12331
  onChangeRef.current = onChange;
12327
12332
  useInput(useCallback3((_input, key) => {
12328
- if (inputRequestRef.current.isActive) return;
12333
+ if (inputRequestRef.current.status === "active") return;
12329
12334
  if (key.tab && isSlashModeRef.current && !hasArgsRef.current && suggestionsRef.current.length > 0) {
12330
12335
  const best = suggestionsRef.current[0];
12331
12336
  const argsHint = best.args ? " " : "";
12332
- onChangeRef.current(`/${best.name}${argsHint}`);
12337
+ const completed = `/${best.name}${argsHint}`;
12338
+ onChangeRef.current("");
12339
+ setTimeout(() => onChangeRef.current(completed), 0);
12333
12340
  }
12334
12341
  }, []));
12335
12342
  return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
@@ -12343,7 +12350,7 @@ var ChatInput = memo4(({
12343
12350
  marginBottom: 0,
12344
12351
  children: suggestions.map((cmd, i) => {
12345
12352
  const isFirst = i === 0;
12346
- const nameColor = isFirst ? THEME.text.accent : THEME.text.secondary;
12353
+ const nameColor = isFirst ? THEME.primary : THEME.text.secondary;
12347
12354
  const aliasText = cmd.alias ? ` /${cmd.alias}` : "";
12348
12355
  const argsText = cmd.args ? ` ${cmd.args}` : "";
12349
12356
  return /* @__PURE__ */ jsxs4(Box4, { children: [
@@ -12357,7 +12364,7 @@ var ChatInput = memo4(({
12357
12364
  " \u2014 ",
12358
12365
  cmd.description
12359
12366
  ] }),
12360
- isFirst && /* @__PURE__ */ jsx5(Text5, { color: THEME.accent.blue, children: " [Tab]" })
12367
+ isFirst && /* @__PURE__ */ jsx5(Text5, { color: THEME.primary, children: " [Tab]" })
12361
12368
  ] }, cmd.name);
12362
12369
  })
12363
12370
  }
@@ -12366,9 +12373,9 @@ var ChatInput = memo4(({
12366
12373
  Box4,
12367
12374
  {
12368
12375
  borderStyle: "single",
12369
- borderColor: inputRequest.isActive ? THEME.status.warning : THEME.border.default,
12376
+ borderColor: inputRequest.status === "active" ? THEME.status.warning : THEME.border.default,
12370
12377
  paddingX: 1,
12371
- children: inputRequest.isActive ? /* @__PURE__ */ jsxs4(Box4, { children: [
12378
+ children: inputRequest.status === "active" ? /* @__PURE__ */ jsxs4(Box4, { children: [
12372
12379
  /* @__PURE__ */ jsx5(Text5, { color: THEME.status.warning, children: "[auth]" }),
12373
12380
  /* @__PURE__ */ jsxs4(Text5, { color: THEME.text.muted, children: [
12374
12381
  " ",
@@ -12427,7 +12434,7 @@ var Footer = memo5(({ phase, targets, findings, todo, elapsedTime, isProcessing
12427
12434
  /* @__PURE__ */ jsxs5(Box5, { gap: 2, children: [
12428
12435
  /* @__PURE__ */ jsxs5(Text6, { color: THEME.text.muted, children: [
12429
12436
  "Phase: ",
12430
- /* @__PURE__ */ jsx6(Text6, { color: THEME.accent.blue, children: phase })
12437
+ /* @__PURE__ */ jsx6(Text6, { color: THEME.primary, children: phase })
12431
12438
  ] }),
12432
12439
  /* @__PURE__ */ jsxs5(Text6, { color: THEME.text.muted, children: [
12433
12440
  "Targets: ",
@@ -12485,7 +12492,7 @@ var App = ({ autoApprove = false, target }) => {
12485
12492
  inputRequestRef.current = inputRequest;
12486
12493
  const handleExit = useCallback4(() => {
12487
12494
  const ir = inputRequestRef.current;
12488
- if (ir.isActive && ir.resolve) ir.resolve(null);
12495
+ if (ir.status === "active") ir.resolve(null);
12489
12496
  cleanupAllProcesses().catch(() => {
12490
12497
  });
12491
12498
  exit();
@@ -12549,8 +12556,29 @@ var App = ({ autoApprove = false, target }) => {
12549
12556
  addMessage("system", "No findings.");
12550
12557
  break;
12551
12558
  }
12552
- addMessage("system", `--- ${findings.length} Findings ---`);
12553
- findings.forEach((f) => addMessage("system", `[${f.severity}] ${f.title}${f.attackPattern ? ` (ATT&CK: ${f.attackPattern})` : ""}`));
12559
+ const findingLines = [`\u2500\u2500\u2500 ${findings.length} Findings \u2500\u2500\u2500`, ""];
12560
+ findings.forEach((f, i) => {
12561
+ const verified = f.isVerified ? `[${ICONS.success}] Verified` : `[${ICONS.warning}] Unverified`;
12562
+ const atk = f.attackPattern ? ` | ATT&CK: ${f.attackPattern}` : "";
12563
+ findingLines.push(`[${i + 1}] [${f.severity.toUpperCase()}] ${f.title}`);
12564
+ findingLines.push(` ${verified}${atk}`);
12565
+ if (f.affected.length > 0) {
12566
+ findingLines.push(` Affected: ${f.affected.join(", ")}`);
12567
+ }
12568
+ if (f.description) {
12569
+ findingLines.push(` ${f.description}`);
12570
+ }
12571
+ if (f.evidence.length > 0) {
12572
+ findingLines.push(` Evidence:`);
12573
+ f.evidence.slice(0, 3).forEach((e) => {
12574
+ const preview = e.length > 120 ? e.slice(0, 120) + "..." : e;
12575
+ findingLines.push(` \u25B8 ${preview}`);
12576
+ });
12577
+ if (f.evidence.length > 3) findingLines.push(` ... +${f.evidence.length - 3} more`);
12578
+ }
12579
+ findingLines.push("");
12580
+ });
12581
+ addMessage("system", findingLines.join("\n"));
12554
12582
  break;
12555
12583
  case UI_COMMANDS.ASSETS:
12556
12584
  case UI_COMMANDS.ASSETS_SHORT:
@@ -12614,17 +12642,17 @@ ${procData.stdout || "(no output)"}
12614
12642
  }, [addMessage, executeTask, handleCommand]);
12615
12643
  const handleSecretSubmit = useCallback4((value) => {
12616
12644
  const ir = inputRequestRef.current;
12617
- if (!ir.isActive || !ir.resolve) return;
12645
+ if (ir.status !== "active") return;
12618
12646
  const displayText = ir.isPassword ? "\u2022".repeat(value.length) : value;
12619
12647
  const promptLabel = ir.prompt || "Input";
12620
12648
  addMessage("system", `\u21B3 ${promptLabel} ${displayText}`);
12621
12649
  ir.resolve(value);
12622
- setInputRequest({ isActive: false, prompt: "", isPassword: false, resolve: null });
12650
+ setInputRequest({ status: "inactive" });
12623
12651
  setSecretInput("");
12624
12652
  }, [addMessage, setInputRequest]);
12625
12653
  useInput2(useCallback4((ch, key) => {
12626
12654
  if (key.escape) {
12627
- if (inputRequestRef.current.isActive) cancelInputRequest();
12655
+ if (inputRequestRef.current.status === "active") cancelInputRequest();
12628
12656
  else if (isProcessingRef.current) abort();
12629
12657
  }
12630
12658
  if (key.ctrl && ch === "c") handleExit();
@@ -12711,9 +12739,9 @@ program.command("interactive", { isDefault: true }).alias("i").description("Star
12711
12739
  const opts = program.opts();
12712
12740
  const skipPermissions = opts.dangerouslySkipPermissions || false;
12713
12741
  console.clear();
12714
- console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
12742
+ console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
12715
12743
  console.log(
12716
- " " + chalk.hex(THEME.text.secondary)(`v${APP_VERSION}`) + chalk.hex(THEME.text.muted)(" \u2502 ") + chalk.hex(THEME.accent.blue)("Type /help for commands") + "\n"
12744
+ " " + chalk.hex(THEME.text.secondary)(`v${APP_VERSION}`) + chalk.hex(THEME.text.muted)(" \u2502 ") + chalk.hex(THEME.primary)("Type /help for commands") + "\n"
12717
12745
  );
12718
12746
  if (skipPermissions) {
12719
12747
  console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions"));
@@ -12733,11 +12761,11 @@ program.command("interactive", { isDefault: true }).alias("i").description("Star
12733
12761
  program.command("run <objective>").alias("r").description("Run a single objective and exit").option("-o, --output <file>", "Output file for results").option("--max-steps <n>", "Maximum number of steps", String(CLI_DEFAULT.MAX_STEPS)).action(async (objective, options) => {
12734
12762
  const opts = program.opts();
12735
12763
  const skipPermissions = opts.dangerouslySkipPermissions || false;
12736
- console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
12764
+ console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
12737
12765
  if (skipPermissions) {
12738
12766
  console.log(chalk.hex(THEME.status.error)("[!] WARNING: Running with --dangerously-skip-permissions\n"));
12739
12767
  }
12740
- console.log(chalk.hex(THEME.accent.blue)(`[target] Objective: ${objective}
12768
+ console.log(chalk.hex(THEME.primary)(`[target] Objective: ${objective}
12741
12769
  `));
12742
12770
  const agent = AgentFactory.createMainAgent(skipPermissions);
12743
12771
  if (skipPermissions) {
@@ -12759,7 +12787,7 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
12759
12787
  if (options.output) {
12760
12788
  const fs = await import("fs/promises");
12761
12789
  await fs.writeFile(options.output, JSON.stringify({ result: result2 }, null, 2));
12762
- console.log(chalk.hex(THEME.accent.blue)(`
12790
+ console.log(chalk.hex(THEME.primary)(`
12763
12791
  [+] Report saved to: ${options.output}`));
12764
12792
  }
12765
12793
  await shutdown(0);
@@ -12773,8 +12801,8 @@ program.command("run <objective>").alias("r").description("Run a single objectiv
12773
12801
  program.command("scan <target>").description("Quick scan a target").option("-s, --scan-type <type>", `Scan type (${CLI_SCAN_TYPES.join("|")})`, CLI_DEFAULT.SCAN_TYPE).option("-p, --ports <ports>", "Specific ports to scan").action(async (target, options) => {
12774
12802
  const opts = program.opts();
12775
12803
  const skipPermissions = opts.dangerouslySkipPermissions || false;
12776
- console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
12777
- console.log(chalk.hex(THEME.accent.blue)(`
12804
+ console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
12805
+ console.log(chalk.hex(THEME.primary)(`
12778
12806
  [scan] Target: ${target} (${options.scanType})
12779
12807
  `));
12780
12808
  const agent = AgentFactory.createMainAgent(skipPermissions);
@@ -12796,11 +12824,11 @@ program.command("scan <target>").description("Quick scan a target").option("-s,
12796
12824
  }
12797
12825
  });
12798
12826
  program.command("help-extended").description("Show extended help with examples").action(() => {
12799
- console.log(gradient([...THEME.gradient.cyber]).multiline(ASCII_BANNER));
12827
+ console.log(chalk.hex(THEME.primary)(ASCII_BANNER));
12800
12828
  console.log(`
12801
- ${chalk.hex(THEME.accent.blue)(APP_NAME + " - Autonomous Penetration Testing AI")}
12829
+ ${chalk.hex(THEME.primary)(APP_NAME + " - Autonomous Penetration Testing AI")}
12802
12830
 
12803
- ${chalk.hex(THEME.status.warning)("Usage:")}
12831
+ ${chalk.hex(THEME.status.warning)("Usage:")}
12804
12832
 
12805
12833
  ${chalk.hex(THEME.status.success)("$ pentesting")} Start interactive mode
12806
12834
  ${chalk.hex(THEME.status.success)("$ pentesting -t 192.168.1.1")} Start with target
@@ -12808,24 +12836,24 @@ ${chalk.hex(THEME.status.warning)("Usage:")}
12808
12836
 
12809
12837
  ${chalk.hex(THEME.status.warning)("Commands:")}
12810
12838
 
12811
- ${chalk.hex(THEME.accent.blue)("pentesting")} Interactive TUI mode
12812
- ${chalk.hex(THEME.accent.blue)("pentesting run <objective>")} Run single objective
12813
- ${chalk.hex(THEME.accent.blue)("pentesting scan <target>")} Quick scan target
12839
+ ${chalk.hex(THEME.primary)("pentesting")} Interactive TUI mode
12840
+ ${chalk.hex(THEME.primary)("pentesting run <objective>")} Run single objective
12841
+ ${chalk.hex(THEME.primary)("pentesting scan <target>")} Quick scan target
12814
12842
 
12815
12843
  ${chalk.hex(THEME.status.warning)("Options:")}
12816
12844
 
12817
- ${chalk.hex(THEME.accent.blue)("--dangerously-skip-permissions")} Skip all permission prompts
12818
- ${chalk.hex(THEME.accent.blue)("-t, --target <ip>")} Set target
12819
- ${chalk.hex(THEME.accent.blue)("-o, --output <file>")} Save results to file
12845
+ ${chalk.hex(THEME.primary)("--dangerously-skip-permissions")} Skip all permission prompts
12846
+ ${chalk.hex(THEME.primary)("-t, --target <ip>")} Set target
12847
+ ${chalk.hex(THEME.primary)("-o, --output <file>")} Save results to file
12820
12848
 
12821
12849
  ${chalk.hex(THEME.status.warning)("Interactive Commands:")}
12822
12850
 
12823
- ${chalk.hex(THEME.accent.blue)("/target <ip>")} Set target
12824
- ${chalk.hex(THEME.accent.blue)("/start")} Start autonomous mode
12825
- ${chalk.hex(THEME.accent.blue)("/config")} Manage configuration
12826
- ${chalk.hex(THEME.accent.blue)("/hint <text>")} Provide hint
12827
- ${chalk.hex(THEME.accent.blue)("/findings")} Show findings
12828
- ${chalk.hex(THEME.accent.blue)("/reset")} Reset session
12851
+ ${chalk.hex(THEME.primary)("/target <ip>")} Set target
12852
+ ${chalk.hex(THEME.primary)("/start")} Start autonomous mode
12853
+ ${chalk.hex(THEME.primary)("/config")} Manage configuration
12854
+ ${chalk.hex(THEME.primary)("/hint <text>")} Provide hint
12855
+ ${chalk.hex(THEME.primary)("/findings")} Show findings
12856
+ ${chalk.hex(THEME.primary)("/reset")} Reset session
12829
12857
 
12830
12858
  ${chalk.hex(THEME.status.warning)("Examples:")}
12831
12859
 
@@ -12840,9 +12868,9 @@ ${chalk.hex(THEME.status.warning)("Examples:")}
12840
12868
 
12841
12869
  ${chalk.hex(THEME.status.warning)("Environment:")}
12842
12870
 
12843
- ${chalk.hex(THEME.accent.blue)("PENTEST_API_KEY")} Required - LLM API key
12844
- ${chalk.hex(THEME.accent.blue)("PENTEST_BASE_URL")} Optional - AI API base URL
12845
- ${chalk.hex(THEME.accent.blue)("PENTEST_MODEL")} Optional - Model override
12871
+ ${chalk.hex(THEME.primary)("PENTEST_API_KEY")} Required - LLM API key
12872
+ ${chalk.hex(THEME.primary)("PENTEST_BASE_URL")} Optional - AI API base URL
12873
+ ${chalk.hex(THEME.primary)("PENTEST_MODEL")} Optional - Model override
12846
12874
  `);
12847
12875
  });
12848
12876
  program.parse();