Module: PWN::Plugins::REPL::AI

Defined in:
lib/pwn/plugins/repl/ai.rb

Overview

pwn-ai REPL mode.

Constant Summary collapse

PWN_AI_SLASH_COMMANDS =
%w[
  /back /cron /debug /delegate /help /learning /memory /mcp /model /sessions /skills /trace
].freeze
PWN_AI_SLASH_SUBCOMMANDS =
{
  '/cron' => %w[list create run remove],
  '/debug' => [],
  '/trace' => [],
  '/delegate' => [],
  '/help' => [],
  '/memory' => %w[list recall remember forget clear],
  '/mcp' => %w[list backends use current connect disconnect ping tools call status help],
  '/model' => %w[list],
  '/sessions' => %w[list resume delete stats],
  '/skills' => %w[list recall],
  '/learning' => %w[list requeue]
}.freeze

Class Method Summary collapse

Class Method Details

.add_commandsObject



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/pwn/plugins/repl/ai.rb', line 14

def self.add_commands
  Pry::Commands.create_command 'pwn-ai' do
    description 'Initiate pwn.ai autonomous agent TUI (instruct tasks using PWN modules + CLI tools; memory/sessions/agents/cron/skills-aware from PWN::Config/PWN::Memory etc).'

    def process
      pi = pry_instance
      pi.config.pwn_ai = true
      pi.config.pwn_ai_agent = true
      pi.config.color = false if pi.config.pwn_ai

      # Switch to custom multi-line input for pwn-ai (SHIFT+ENTER newline, ENTER submit)
      pi.config.input = PWNMultiLineInput.new(pi)
      PWN::Plugins::REPL.install_pwn_ai_completer!(pry: pi)

      # Load and make aware of skills folder (scaled in PWN::Config per user pwn_env_path parent)
      skills_path = begin
        PWN::Config.pwn_skills_path
      rescue StandardError
        "#{Dir.home}/.pwn/skills"
      end
      PWN::ModuleSkills.install(pwn_skills_path: skills_path) if defined?(PWN::ModuleSkills) && PWN::ModuleSkills.respond_to?(:install)
      PWN::Config.load_skills(pwn_skills_path: skills_path)
      skills_count = (PWN.const_defined?(:Skills) ? PWN::Skills.keys.length : 0)

      # pwn-ai activation: initialise memory/sessions/cron stores
      PWN::Config.load_memory
      mem_count = (PWN.const_defined?(:Memory) ? PWN::Memory.load.keys.length : 0)
      sess = begin
        PWN::Plugins::REPL.pwn_ai_activation_session(pry: pi)
      rescue StandardError
        nil
      end
      pi.config.pwn_ai_session_id = sess[:id] if sess
      cron_count = (PWN.const_defined?(:Cron) ? PWN::Cron.list.keys.length : 0)

      puts '[*] pwn-ai agent TUI activated (PWN REPL driver w/ memory, sessions, delegation, cron).'
      puts "[*] Memory facts: #{mem_count} | Session: #{pi.config.pwn_ai_session_id} | Cron jobs: #{cron_count} | Skills: #{skills_count}"
      puts '[*] Instruct the AI agent to carry out a task, e.g.:'
      puts "    'Use NmapIt to port scan target.com then use TransparentBrowser to spider and SAST::TestCaseEngine to analyze code if cloned. Generate report with PWN::Reports.'"
      puts "    'Execute CLI nmap -sV target.com and summarize findings using PWN modules.'"
      puts "[*] Skills loaded from #{skills_path} (#{skills_count} available) + memory/sessions/cron to expand autonomous capabilities."
      puts "[*] Type 'back' or CTRL+D to exit pwn-ai mode."
      puts '[*] MULTILINE in pwn-ai: SHIFT+ENTER (or ALT+ENTER, or trailing `\\`) inserts a newline; ENTER submits to the AI.'
      puts '[*] TAB menus: leading `/` = commands (/cron /skills /sessions …); `/` later = host paths; otherwise Ruby completion (same as the pwn REPL).'
      puts "[*] tmux + terminator users: Ensure ~/.tmux.conf has 'set -s extended-keys on' and 'set -g xterm-keys on', then restart tmux. Use TERM=xterm-256color."
      tag = pi.config.pwn_ai_session_id.to_s.empty? ? '<SESSION_ID>' : pi.config.pwn_ai_session_id

      dbg_lvl = ''
      dbg_lvl = 'trace' if pi.config.pwn_ai_trace
      dbg_lvl = 'debug' if pi.config.pwn_ai_debug && !pi.config.pwn_ai_trace

      puts "\n\n\npwn-ai #{dbg_lvl} ON → ~/.pwn/logs/pwn-ai-DEBUG-#{tag}-R<REQUEST_NUMBER>.log" unless dbg_lvl.empty?
    end
  end

  Pry::Commands.create_command 'ai.profile' do
    description 'Select a session routing profile: ai.profile NAME (no args lists names).'

    def process
      PWN::Plugins::REPL.pwn_ai_profile_command(pry: pry_instance, args: args, output: output)
    end
  end

  Pry::Commands.create_command 'ai.memory' do
    description 'View/edit pinned engagement memory: ai.memory [view|edit TEXT|clear].'

    def process
      PWN::Plugins::REPL.pwn_ai_memory_command(pry: pry_instance, args: args, output: output)
    end
  end

  Pry::Commands.create_command 'pwn-ai-memory' do
    description 'Manage pwn-ai persistent memory.'

    def process
      cmd = args[0]
      case cmd
      when 'list', 'recall', nil
        q = args[1]
        res = PWN::Memory.recall(query: q)
        puts res.inspect
      when 'remember'
        key = args[1]
        val = args[2..].join(' ')
        PWN::Memory.remember(key: key, value: val)
        puts "Remembered #{key}"
      when 'forget'
        PWN::Memory.forget(key: args[1])
        puts "Forgot #{args[1]}"
      when 'clear'
        PWN::Memory.clear(force: true)
        puts 'Memory cleared'
      else
        puts PWN::Memory.help
      end
    end
  end

  Pry::Commands.create_command 'pwn-ai-sessions' do
    description 'List/resume/delete pwn-ai sessions.'

    def process
      cmd = args[0]
      case cmd
      when 'list', nil
        puts PWN::Sessions.list.inspect
      when 'resume'
        sid = args[1]
        hist = PWN::Sessions.to_response_history(session_id: sid)
        puts "Loaded session #{sid} with #{hist[:choices].size} entries (set manually into response_history if needed)"
      when 'delete'
        PWN::Sessions.delete(session_id: args[1], force: true)
        puts "Deleted #{args[1]}"
      when 'stats'
        puts PWN::Sessions.stats
      else
        puts PWN::Sessions.help
      end
    end
  end

  Pry::Commands.create_command 'pwn-ai-cron' do
    description 'Manage scheduled pwn-ai / cron jobs.'

    def process
      cmd = args[0]
      case cmd
      when 'list', nil
        puts PWN::Cron.list.inspect
      when 'create'
        # simplistic: pwn-ai-cron create '0 * * * *' 'prompt here'
        sched = args[1]
        pr = args[2..].join(' ')
        job = PWN::Cron.create(schedule: sched, prompt: pr)
        puts "Created #{job}"
      when 'run'
        res = PWN::Cron.run(id: args[1])
        puts res
      when 'remove'
        PWN::Cron.remove(id: args[1])
        puts 'Removed'
      else
        puts PWN::Cron.help
      end
    end
  end

  Pry::Commands.create_command 'pwn-ai-delegate' do
    description 'Delegate sub-task to a PWN::AI::Agent or simple sub-chat.'

    def process
      goal = args.join(' ')
      puts "[*] Delegating: #{goal}"
      # Simple delegation: use a specialized agent if matches, else another chat turn
      if goal =~ /sast|code|scan/i
        res = PWN::AI::Agent::SAST.analyze(request: goal)
      elsif goal =~ /vuln|report/i
        res = PWN::AI::Agent::VulnGen.analyze(request: goal)
      else
        # fallback sub call to active engine (no full loop here)
        engine = PWN::Env[:ai][:active].to_s.downcase.to_sym
        case engine
        when :anthropic then res = PWN::AI::Anthropic.chat(request: goal)
        when :gemini then res = PWN::AI::Gemini.chat(request: goal)
        when :grok then res = PWN::AI::Grok.chat(request: goal)
        else res = PWN::AI::Ollama.chat(request: goal)
        end
      end
      puts res
    end
  end
  Pry::Commands.create_command 'toggle-debug' do
    description 'Stream pwn-ai stage log to the TUI and ~/.pwn/logs/pwn-ai-DEBUG-<SESSION_ID>-RN.log'

    def process
      pi = pry_instance
      if pi.config.pwn_ai_debug
        path = PWN::Plugins::Log.stop_debug
        pi.config.pwn_ai_debug = false
        pi.config.pwn_ai_trace = false
        if path
          output.puts "pwn-ai debug OFF (was #{path})"
        else
          output.puts 'pwn-ai debug OFF'
        end
      else
        sid = pi.config.pwn_ai_session_id
        PWN::Plugins::Log.start_debug(tee: output, session_id: sid)
        pi.config.pwn_ai_debug = true
        output.puts 'pwn-ai debug ON.'
      end
    end
  end

  Pry::Commands.create_command 'toggle-trace' do
    description 'toggle-debug plus TracePoint; ENTER after each Loop step. Stored on Pry.config like toggle-debug, not Env.'

    def process
      pi = pry_instance
      if pi.config.pwn_ai_trace
        PWN::Plugins::Log.stop_debug
        pi.config.pwn_ai_debug = false
        pi.config.pwn_ai_trace = false
        output.puts 'pwn-ai trace OFF (debug OFF)'
      else
        sid = pi.config.pwn_ai_session_id
        PWN::Plugins::Log.start_debug(tee: output, session_id: sid, trace: true)
        pi.config.pwn_ai_debug = true
        pi.config.pwn_ai_trace = true
        output.puts 'pwn-ai trace ON (debug ON, TracePoint, ENTER each loop step)'
      end
    end
  end

  Pry::Commands.create_command 'toggle-pwn-ai-speaks' do
    description 'Use speech capabilities within pwn.ai to speak answers.'

    def process
      pi = pry_instance
      pi.config.pwn_ai_speak ? pi.config.pwn_ai_speak = false : pi.config.pwn_ai_speak = true
    end
  end
end

.authorsObject

Author(s)

0day Inc. [email protected]



912
913
914
915
916
# File 'lib/pwn/plugins/repl/ai.rb', line 912

public_class_method def self.authors
  "AUTHOR(S):
    0day Inc. <[email protected]>
  "
end

.helpObject

Display usage for this module.



920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
# File 'lib/pwn/plugins/repl/ai.rb', line 920

public_class_method def self.help
  puts "USAGE:
    # Register pwn-ai Pry commands and debug toggles.
    #{self}.add_commands

    # Classify pwn-ai TAB completion as command, path, or Ruby.
    #{self}.pwn_ai_complete_kind(
      line: 'optional - full line buffer used to classify completion'
    )

    # TAB hits for pwn-ai slash commands, host paths, or Ruby.
    #{self}.pwn_ai_complete(
      target: 'required - token Reline is completing',
      line: 'optional - full line buffer',
      pry: 'optional - Pry instance for Ruby completion'
    )

    # TAB hits for leading-slash pwn-ai commands.
    #{self}.pwn_ai_complete_command(
      line: 'optional - full line buffer',
      target: 'required - token Reline is completing'
    )

    # TAB hits for host paths when slash is not first.
    #{self}.pwn_ai_complete_path(
      target: 'required - path prefix to complete',
      line: 'optional - full line buffer'
    )

    # TAB hits for Ruby constants and methods in pwn-ai.
    #{self}.pwn_ai_complete_ruby(
      target: 'optional - token Reline is completing',
      pry: 'optional - Pry instance (defaults to Thread.current[:pwn_ai_completer_pry])'
    )

    # Install Reline dropdown for pwn-ai (commands / paths / Ruby).
    #{self}.install_pwn_ai_completer!(
      pry: 'optional - Pry instance stored for pwn-ai TAB completion'
    )

    # Restore Pry Ruby completion after leaving pwn-ai.
    #{self}.restore_pwn_ai_completer!

    # Consume a prepared CLI session or create a new interactive session.
    #{self}.pwn_ai_activation_session(pry: 'required - Pry instance')

    # Validate/select a named model profile without changing provider defaults.
    #{self}.pwn_ai_profile_command(
      pry: 'required - Pry instance', args: ['profile-name'],
      env: 'optional - configuration hash', output: 'optional - output IO'
    )

    # View/edit the current session pinned engagement notes.
    #{self}.pwn_ai_memory_command(
      pry: 'required - Pry instance', args: ['edit', 'evidence notes'],
      root: 'optional - artifacts root', output: 'optional - output IO'
    )

    # Run a leading-slash pwn-ai command locally. Returns true when handled.
    #{self}.pwn_ai_dispatch_slash!(
      request: 'optional - full line such as /skills list',
      pry: 'optional - Pry instance used by /back'
    )

    # List configured pwn-ai engine names.
    #{self}.pwn_ai_engines

    # Resolve the provider class for an engine name.
    #{self}.pwn_ai_provider_class(
      engine: 'optional - engine name such as ollama or openai'
    )

    # Normalize provider catalog hashes into model id strings.
    #{self}.pwn_ai_model_ids(
      models: 'optional - catalog payload from a provider list call'
    )

    # List LLM ids for an engine including Codex slug catalogs.
    #{self}.pwn_ai_list_llms(
      engine: 'required - engine name such as openai or ollama'
    )

    # Return the currently selected model string for an engine.
    #{self}.pwn_ai_engine_model(
      engine: 'required - engine name such as ollama'
    )

    # Apply /model arguments to the active engine.
    #{self}.pwn_ai_run_model(
      args: 'optional - Array of slash tokens after /model'
    )

    # Persist engine and model into the encrypted pwn.yaml vault.
    #{self}.persist_ai_selection(
      engine: 'required - engine name to store as ai.active',
      model: 'required - model id to store for that engine'
    )

    # Run /cron locally without Loop.run.
    #{self}.pwn_ai_run_cron(
      args: 'optional - Array of slash tokens after /cron'
    )

    # Run /sessions locally without Loop.run.
    #{self}.pwn_ai_run_sessions(
      args: 'optional - Array of slash tokens after /sessions'
    )

    # Run /memory locally without Loop.run.
    #{self}.pwn_ai_run_memory(
      args: 'optional - Array of slash tokens after /memory'
    )

    # List or requeue conflicted learning outcomes.
    #{self}.pwn_ai_run_learning(
      args: 'optional - list [--conflicted] or requeue'
    )

    # Run /skills locally without Loop.run.
    #{self}.pwn_ai_run_skills(
      args: 'optional - Array of slash tokens after /skills'
    )

    # Run /mcp locally without Loop.run.
    #{self}.pwn_ai_run_mcp(
      args: 'optional - slash tokens after /mcp such as list tools'
    )

    # Print the AUTHOR(S) string for this module.
    #{self}.authors
  "
  constants.sort
end