pwn 0.5.702 → 0.5.704

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +7 -0
  3. data/build_gem.sh +1 -1
  4. data/documentation/General-PWN-Usage.md +2 -1
  5. data/documentation/Installation.md +5 -3
  6. data/documentation/pwn-REPL.md +2 -1
  7. data/etc/default_skills/pwn/ai/agent/learning/SKILL.md +4 -0
  8. data/etc/default_skills/pwn/ai/agent/loop/SKILL.md +4 -0
  9. data/etc/default_skills/pwn/ai/agent/mistakes/SKILL.md +4 -0
  10. data/etc/default_skills/pwn/ai/agent/open_goal/SKILL.md +3 -0
  11. data/etc/default_skills/pwn/ai/agent/policy/SKILL.md +11 -0
  12. data/etc/default_skills/pwn/ai/agent/prompt_cache/SKILL.md +1 -0
  13. data/etc/default_skills/pwn/ai/agent/reward/SKILL.md +1 -0
  14. data/etc/default_skills/pwn/ai/agent/task_summarizer/SKILL.md +12 -0
  15. data/etc/default_skills/pwn/ai/agent/tool_guard/SKILL.md +9 -0
  16. data/etc/default_skills/pwn/ai/agent/turn_finalizer/SKILL.md +5 -0
  17. data/etc/default_skills/pwn/ai/http_retry/SKILL.md +1 -0
  18. data/etc/default_skills/pwn/banner/matrix/SKILL.md +1 -0
  19. data/etc/default_skills/pwn/cron/SKILL.md +7 -0
  20. data/etc/default_skills/pwn/ffi/SKILL.md +1 -0
  21. data/etc/default_skills/pwn/ffi/adalm_pluto/SKILL.md +2 -0
  22. data/etc/default_skills/pwn/ffi/fftw/SKILL.md +10 -0
  23. data/etc/default_skills/pwn/ffi/hack_rf/SKILL.md +22 -0
  24. data/etc/default_skills/pwn/ffi/liquid/SKILL.md +33 -0
  25. data/etc/default_skills/pwn/ffi/rtl_sdr/SKILL.md +18 -0
  26. data/etc/default_skills/pwn/ffi/soapy_sdr/SKILL.md +24 -0
  27. data/etc/default_skills/pwn/ffi/stdio/SKILL.md +4 -0
  28. data/etc/default_skills/pwn/ffi/volk/SKILL.md +17 -0
  29. data/etc/default_skills/pwn/memory/SKILL.md +4 -0
  30. data/etc/default_skills/pwn/memory_index/SKILL.md +1 -0
  31. data/etc/default_skills/pwn/migrate/SKILL.md +1 -0
  32. data/etc/default_skills/pwn/module_skills/SKILL.md +2 -0
  33. data/etc/default_skills/pwn/plugins/log/SKILL.md +16 -0
  34. data/etc/default_skills/pwn/plugins/pony/SKILL.md +2 -0
  35. data/etc/default_skills/pwn/plugins/repl/SKILL.md +4 -0
  36. data/etc/default_skills/pwn/plugins/tty_spinner/SKILL.md +1 -0
  37. data/etc/default_skills/pwn/plugins/vault/SKILL.md +1 -0
  38. data/etc/default_skills/pwn/sdr/decoder/adsb/SKILL.md +1 -0
  39. data/etc/default_skills/pwn/sdr/decoder/base/SKILL.md +1 -0
  40. data/etc/default_skills/pwn/sdr/decoder/dsp/SKILL.md +3 -0
  41. data/etc/default_skills/pwn/sdr/decoder/flex/SKILL.md +1 -0
  42. data/etc/default_skills/pwn/sessions/SKILL.md +1 -0
  43. data/etc/default_skills/pwn/version/SKILL.md +1 -1
  44. data/lib/pwn/ai/agent/loop.rb +31 -10
  45. data/lib/pwn/ai/agent/tool_guard.rb +24 -13
  46. data/lib/pwn/config.rb +90 -91
  47. data/lib/pwn/module_skills.rb +71 -1
  48. data/lib/pwn/plugins/github.rb +1 -1
  49. data/lib/pwn/plugins/log.rb +71 -8
  50. data/lib/pwn/plugins/repl.rb +35 -3
  51. data/lib/pwn/plugins/tty_spinner.rb +1 -1
  52. data/lib/pwn/plugins/zaproxy.rb +1 -1
  53. data/lib/pwn/version.rb +1 -1
  54. data/spec/integration/config_spec.rb +29 -1
  55. data/spec/lib/pwn/ai/agent/loop_spec.rb +12 -0
  56. data/spec/lib/pwn/ai/agent/tools/ruby_eval_spec.rb +13 -0
  57. data/spec/lib/pwn/module_skills_spec.rb +12 -0
  58. data/spec/lib/pwn/plugins/log_spec.rb +88 -9
  59. data/spec/lib/pwn/plugins/repl_pwn_vault_spec.rb +37 -1
  60. data/spec/lib/pwn/plugins/repl_spec.rb +16 -1
  61. data/third_party/pwn_rdoc.jsonl +15 -2
  62. metadata +1 -1
@@ -384,7 +384,10 @@ module PWN
384
384
  name = "\001\e[1m\002\001\e[33m\002#{pi.config.prompt_name}\001\e[0m\002"
385
385
  dchars = "\001\e[32m\002>>>\001\e[33m\002"
386
386
  dchars = "\001\e[33m\002***\001\e[33m\002" if mode == :splat
387
- if pi.config.pwn_ai_debug
387
+ if pi.config.pwn_ai_trace
388
+ dchars = "\001\e[31m\002(TRACE) >>>\001\e[33m\002"
389
+ dchars = "\001\e[31m\002(TRACE) ***\001\e[33m\002" if mode == :splat
390
+ elsif pi.config.pwn_ai_debug
388
391
  dchars = "\001\e[32m\002(DEBUG) >>>\001\e[33m\002"
389
392
  dchars = "\001\e[33m\002(DEBUG) ***\001\e[33m\002" if mode == :splat
390
393
  end
@@ -465,6 +468,7 @@ module PWN
465
468
  rescue StandardError
466
469
  "#{Dir.home}/.pwn/skills"
467
470
  end
471
+ PWN::ModuleSkills.install(pwn_skills_path: skills_path) if defined?(PWN::ModuleSkills) && PWN::ModuleSkills.respond_to?(:install)
468
472
  PWN::Config.load_skills(pwn_skills_path: skills_path)
469
473
  skills_count = (PWN.const_defined?(:Skills) ? PWN::Skills.keys.length : 0)
470
474
 
@@ -1047,13 +1051,14 @@ module PWN
1047
1051
  end
1048
1052
 
1049
1053
  Pry::Commands.create_command 'toggle-debug' do
1050
- description 'Stream pwn-ai stage log to the TUI and /tmp/pwn-ai-DEBUG-<SESSION_ID>-RN.log (trace: Env ai.agent.debug_trace)'
1054
+ description 'Stream pwn-ai stage log to the TUI and /tmp/pwn-ai-DEBUG-<SESSION_ID>-RN.log'
1051
1055
 
1052
1056
  def process
1053
1057
  pi = pry_instance
1054
1058
  if pi.config.pwn_ai_debug
1055
1059
  path = PWN::Plugins::Log.stop_debug
1056
1060
  pi.config.pwn_ai_debug = false
1061
+ pi.config.pwn_ai_trace = false
1057
1062
  if path
1058
1063
  output.puts "pwn-ai debug OFF (was #{path})"
1059
1064
  else
@@ -1068,6 +1073,26 @@ module PWN
1068
1073
  end
1069
1074
  end
1070
1075
 
1076
+ Pry::Commands.create_command 'toggle-trace' do
1077
+ description 'toggle-debug plus TracePoint; ENTER after each Loop step. Stored on Pry.config like toggle-debug, not Env.'
1078
+
1079
+ def process
1080
+ pi = pry_instance
1081
+ if pi.config.pwn_ai_trace
1082
+ PWN::Plugins::Log.stop_debug
1083
+ pi.config.pwn_ai_debug = false
1084
+ pi.config.pwn_ai_trace = false
1085
+ output.puts 'pwn-ai trace OFF (debug OFF)'
1086
+ else
1087
+ sid = pi.config.pwn_ai_session_id
1088
+ PWN::Plugins::Log.start_debug(tee: output, session_id: sid, trace: true)
1089
+ pi.config.pwn_ai_debug = true
1090
+ pi.config.pwn_ai_trace = true
1091
+ output.puts 'pwn-ai trace ON (debug ON, TracePoint, ENTER each loop step)'
1092
+ end
1093
+ end
1094
+ end
1095
+
1071
1096
  Pry::Commands.create_command 'toggle-pwn-ai-speaks' do
1072
1097
  description 'Use speech capabilities within pwn.ai to speak answers.'
1073
1098
 
@@ -1558,12 +1583,13 @@ module PWN
1558
1583
  end
1559
1584
 
1560
1585
  PWN_AI_SLASH_COMMANDS = %w[
1561
- /back /cron /debug /delegate /help /memory /model /sessions /skills
1586
+ /back /cron /debug /delegate /help /memory /model /sessions /skills /trace
1562
1587
  ].freeze
1563
1588
 
1564
1589
  PWN_AI_SLASH_SUBCOMMANDS = {
1565
1590
  '/cron' => %w[list create run remove],
1566
1591
  '/debug' => [],
1592
+ '/trace' => [],
1567
1593
  '/delegate' => [],
1568
1594
  '/help' => [],
1569
1595
  '/memory' => %w[list recall remember forget clear],
@@ -1740,6 +1766,12 @@ module PWN
1740
1766
  else
1741
1767
  puts '[*] toggle-debug'
1742
1768
  end
1769
+ when '/trace'
1770
+ if pi.respond_to?(:eval)
1771
+ pi.eval('toggle-trace')
1772
+ else
1773
+ puts '[*] toggle-trace'
1774
+ end
1743
1775
  when '/cron'
1744
1776
  pwn_ai_run_cron(args: args)
1745
1777
  when '/sessions'
@@ -136,7 +136,7 @@ module PWN
136
136
  return unless spin.respond_to?(:join)
137
137
 
138
138
  spin.join(JOIN_SECS)
139
- rescue TTY::Spinner::NotSpinningError, StandardError
139
+ rescue StandardError
140
140
  nil
141
141
  end
142
142
 
@@ -143,7 +143,7 @@ module PWN
143
143
  end
144
144
 
145
145
  zap_obj
146
- rescue Selenium::WebDriver::Error::SessionNotCreatedError, StandardError, SystemExit, Interrupt => e
146
+ rescue StandardError, SystemExit, Interrupt => e
147
147
  stop(zap_obj: zap_obj) unless zap_obj.nil?
148
148
  raise e
149
149
  end
data/lib/pwn/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PWN
4
- VERSION = '0.5.702'
4
+ VERSION = '0.5.704'
5
5
  end
@@ -88,7 +88,7 @@ RSpec.describe 'PWN::Config / etc/*.EXAMPLE hygiene', :aggregate_failures do
88
88
  att&ck
89
89
  ]
90
90
  seeded = PWN::Config.install_default_skills(pwn_skills_path: dir)
91
- expect(seeded.map { |r| r[:name] }).to eq names
91
+ expect(seeded.map { |r| r[:name] }).to match_array(names)
92
92
  names.each do |name|
93
93
  path = File.join(dir, name, 'SKILL.md')
94
94
  expect(File.file?(path)).to eq(true), path
@@ -129,4 +129,32 @@ RSpec.describe 'PWN::Config / etc/*.EXAMPLE hygiene', :aggregate_failures do
129
129
  expect(File.file?(gw_ref)).to eq(true), gw_ref
130
130
  end
131
131
  end
132
+
133
+ it 'installs every SKILL.md under default_skills recursively and load_skills indexes nested dirs' do
134
+ Dir.mktmpdir('pwn_nested_skills') do |dir|
135
+ src = File.join(dir, 'src')
136
+ dest = File.join(dir, 'skills')
137
+ nested = File.join(src, 'research', 'nested-probe')
138
+ FileUtils.mkdir_p(File.join(nested, 'references', 'deep'))
139
+ File.write(
140
+ File.join(nested, 'SKILL.md'),
141
+ "---\nname: nested-probe\ndescription: nested probe desc\n---\n# Nested\n"
142
+ )
143
+ File.write(File.join(nested, 'references', 'deep', 'note.md'), "# note\n")
144
+ FileUtils.mkdir_p(File.join(nested, 'scripts'))
145
+ File.write(File.join(nested, 'scripts', 'skip.rb'), "# helper\n")
146
+ seeded = PWN::Config.install_default_skills(pwn_skills_path: dest, source: src)
147
+ expect(seeded.map { |r| r[:name] }).to include('research/nested-probe')
148
+ expect(File.file?(File.join(dest, 'research', 'nested-probe', 'SKILL.md'))).to eq(true)
149
+ expect(File.file?(File.join(dest, 'research', 'nested-probe', 'references', 'deep', 'note.md'))).to eq(true)
150
+ expect(File).not_to exist(File.join(dest, 'research', 'nested-probe', 'scripts', 'skip.rb'))
151
+ prev = defined?(PWN::Skills) ? PWN::Skills : nil
152
+ skills = PWN::Config.load_skills(pwn_skills_path: dest)
153
+ expect(skills.keys.map(&:to_s)).to include('research/nested-probe')
154
+ expect(skills[:'research/nested-probe'][:description]).to eq('nested probe desc')
155
+ ensure
156
+ PWN.send(:remove_const, :Skills) if PWN.const_defined?(:Skills)
157
+ PWN.const_set(:Skills, prev.freeze) if prev
158
+ end
159
+ end
132
160
  end
@@ -1011,6 +1011,16 @@ describe PWN::AI::Agent::Loop do # rubocop:disable Metrics/BlockLength
1011
1011
  request: 'remember what I said about nmap and implement the scanner fix'
1012
1012
  )
1013
1013
  ).to eq(:act)
1014
+ expect(
1015
+ described_class.request_intent(
1016
+ request: 'are all the skills in ~/.pwn/skills loaded into memory when `pwn-ai` is executed?'
1017
+ )
1018
+ ).not_to eq(:recall)
1019
+ expect(
1020
+ described_class.request_intent(
1021
+ request: 'what does the `pwn/www/youtube` allow me to do?'
1022
+ )
1023
+ ).not_to eq(:recall)
1014
1024
  end
1015
1025
 
1016
1026
  it 'classifies pure greetings and light weather smalltalk as :greeting' do
@@ -1349,6 +1359,8 @@ describe PWN::AI::Agent::Loop do # rubocop:disable Metrics/BlockLength
1349
1359
  expect(src).to match(/debug_msgs_line|msgs=.*\[/)
1350
1360
  expect(src).to match(/PWN::Plugins::Log\.progress/)
1351
1361
  expect(src).to match(/quiet_debug_tui!/)
1362
+ expect(src).to match(/wait_trace_step!/)
1363
+ expect(src).not_to match(/ai.*agent.*debug_trace/)
1352
1364
  expect(described_class).to respond_to(:debug_on?)
1353
1365
  end
1354
1366
  end # rubocop:enable Metrics/BlockLength
@@ -48,6 +48,19 @@ describe 'PWN::AI::Agent::Tools ruby_eval' do
48
48
  expect { HTTP::CookieJar }.not_to raise_error
49
49
  end
50
50
 
51
+ it 'restores Digest if the payload assigns Digest so later payload_sig still hashes' do
52
+ entry = PWN::AI::Agent::Registry.lookup(name: 'pwn_eval')
53
+ result = entry.handler.call(code: 'Digest = "(self.we"')
54
+ expect(result[:error]).to be_nil
55
+ expect(Digest).to be_a(Module)
56
+ expect(Digest::SHA256.hexdigest('x')).to be_a(String)
57
+ a = PWN::AI::Agent::Loop.send(:payload_sig, name: 'pwn_eval', args: '{"code":"1"}')
58
+ b = PWN::AI::Agent::Loop.send(:payload_sig, name: 'pwn_eval', args: '{"code":"2"}')
59
+ expect(a).not_to eq(b)
60
+ expect(a).not_to match(/nosig-/)
61
+ expect(b).not_to match(/nosig-/)
62
+ end
63
+
51
64
  it 'enforces a timeout on pwn_eval and reports timeout after Ns' do
52
65
  tmp = Dir.mktmpdir
53
66
  stub_const('PWN::AI::Agent::Mistakes::MISTAKES_FILE', File.join(tmp, 'mistakes.json'))
@@ -53,6 +53,18 @@ describe PWN::ModuleSkills do
53
53
  expect(File.read(path)).to include(rec[:const])
54
54
  expect(File.read(path)).not_to match(/hermes/i)
55
55
  end
56
+ drift = described_class.drift
57
+ expect(drift[:missing]).to eq([])
58
+ expect(drift[:stale]).to eq([])
59
+ expect(drift[:orphans]).to eq([])
60
+ end
61
+
62
+ it 'exposes drift so rake/spec can refuse a stale gem skill tree' do
63
+ expect(described_class).to respond_to(:drift)
64
+ rake = File.read(File.expand_path('../../../Rakefile', __dir__))
65
+ expect(rake).to match(/module_skills/)
66
+ expect(rake).to match(/ModuleSkills\.refresh!/)
67
+ expect(rake).to match(/Task\[:spec\]\.enhance|task spec: :module_skills/)
56
68
  end
57
69
 
58
70
  it 'install overwrites existing generated module skills' do
@@ -247,15 +247,17 @@ describe PWN::Plugins::Log do
247
247
  expect(body).not_to match(/PWN::AI::Agent::OpenGoal\.current/)
248
248
  end
249
249
 
250
- it 'traces PWN module calls into the same debug log when trace: true' do
250
+ it 'traces Loop/Dispatch calls and not the rest of PWN::AI when trace: true' do
251
251
  path = PWN::Plugins::Log.start_debug(
252
252
  tee: StringIO.new,
253
253
  path: "/tmp/pwn-ai-DEBUG-#{Process.pid}-#{Time.now.to_i}.log",
254
254
  trace: true
255
255
  )
256
+ PWN::AI::Agent::Dispatch.effect(name: 'skills_recall', args: {})
256
257
  PWN::AI::Agent::OpenGoal.current if defined?(PWN::AI::Agent::OpenGoal)
257
258
  body = File.read(path)
258
- expect(body).to match(/PWN::AI::Agent::OpenGoal/)
259
+ expect(body).to match(/PWN::AI::Agent::Dispatch/)
260
+ expect(body).not_to match(/PWN::AI::Agent::OpenGoal/)
259
261
  end
260
262
 
261
263
  it 'does not raise when a traced module #name is a Symbol' do
@@ -268,13 +270,13 @@ describe PWN::Plugins::Log do
268
270
  :ok
269
271
  end
270
272
  end
271
- stub_const('PWN::AI::Agent::SymNameProbe', probe)
273
+ stub_const('PWN::AI::Agent::Loop::SymNameProbe', probe)
272
274
  PWN::Plugins::Log.start_debug(
273
275
  tee: StringIO.new,
274
276
  path: "/tmp/pwn-ai-DEBUG-#{Process.pid}-sym.log",
275
277
  trace: true
276
278
  )
277
- expect { PWN::AI::Agent::SymNameProbe.ping }.not_to raise_error
279
+ expect { PWN::AI::Agent::Loop::SymNameProbe.ping }.not_to raise_error
278
280
  end
279
281
 
280
282
  it 'logs the complete traced call including parameters' do
@@ -283,19 +285,19 @@ describe PWN::Plugins::Log do
283
285
  opts[:msg]
284
286
  end
285
287
  end
286
- stub_const('PWN::AI::Agent::ArgProbe', probe)
288
+ stub_const('PWN::AI::Agent::Dispatch::ArgProbe', probe)
287
289
  path = PWN::Plugins::Log.start_debug(
288
290
  tee: StringIO.new,
289
291
  path: "/tmp/pwn-ai-DEBUG-#{Process.pid}-args.log",
290
292
  trace: true
291
293
  )
292
- PWN::AI::Agent::ArgProbe.echo(
294
+ PWN::AI::Agent::Dispatch::ArgProbe.echo(
293
295
  msg: 'hello-browser',
294
296
  url: 'https://example.test/open',
295
297
  password: 's3cret-token'
296
298
  )
297
299
  body = File.read(path)
298
- expect(body).to include('PWN::AI::Agent::ArgProbe.echo')
300
+ expect(body).to include('PWN::AI::Agent::Dispatch::ArgProbe.echo')
299
301
  expect(body).to include('hello-browser')
300
302
  expect(body).to include('https://example.test/open')
301
303
  expect(body).not_to include('s3cret-token')
@@ -308,13 +310,13 @@ describe PWN::Plugins::Log do
308
310
  opts[:msg]
309
311
  end
310
312
  end
311
- stub_const('PWN::AI::Agent::ArgProbe', probe)
313
+ stub_const('PWN::AI::Agent::Dispatch::ArgProbe', probe)
312
314
  path = PWN::Plugins::Log.start_debug(
313
315
  tee: StringIO.new,
314
316
  path: "/tmp/pwn-ai-DEBUG-#{Process.pid}-redact.log",
315
317
  trace: true
316
318
  )
317
- PWN::AI::Agent::ArgProbe.echo(
319
+ PWN::AI::Agent::Dispatch::ArgProbe.echo(
318
320
  header: 'Bearer eyJhbGciOiJIUzI1NiJ9.aaa.bbb',
319
321
  blob: "-----BEGIN PRIVATE KEY-----\nMIIB\n-----END PRIVATE KEY-----",
320
322
  note: 'xai-abcdefghijklmnopqrstuvwxyz0123456789ABCD'
@@ -332,3 +334,80 @@ describe PWN::Plugins::Log do
332
334
  end
333
335
  end
334
336
  end
337
+
338
+ describe 'PWN::Plugins::Log toggle-trace step' do
339
+ after do
340
+ PWN::Plugins::Log.stop_debug if PWN::Plugins::Log.respond_to?(:stop_debug)
341
+ end
342
+
343
+ it 'pairs TracePoint with ENTER-step when start_debug(trace: true)' do
344
+ io = StringIO.new("\n")
345
+ path = PWN::Plugins::Log.start_debug(
346
+ tee: StringIO.new,
347
+ path: "/tmp/pwn-ai-DEBUG-#{Process.pid}-step.log",
348
+ trace: true,
349
+ step_io: io
350
+ )
351
+ expect(PWN::Plugins::Log.debug_enabled?).to eq(true)
352
+ expect(PWN::Plugins::Log.trace_enabled?).to eq(true)
353
+ expect(PWN::Plugins::Log).to respond_to(:wait_trace_step!)
354
+ PWN::Plugins::Log.wait_trace_step!(label: 'engine')
355
+ expect(io.pos).to be > 0
356
+ body = File.read(path)
357
+ expect(body).to match(/trace step/)
358
+ expect(body).to match(/Press ENTER to Continue\.\.\./i)
359
+ end
360
+
361
+ it 'prints "Press ENTER to Continue..." in red on its own TUI line' do
362
+ io = StringIO.new("\n")
363
+ tee = StringIO.new
364
+ PWN::Plugins::Log.start_debug(
365
+ tee: tee,
366
+ path: "/tmp/pwn-ai-DEBUG-#{Process.pid}-enter-color.log",
367
+ trace: true,
368
+ step_io: io
369
+ )
370
+ PWN::Plugins::Log.wait_trace_step!(label: 'engine')
371
+ expect(tee.string.lines.map(&:chomp)).to include("\e[31mPress ENTER to Continue...\e[0m")
372
+ end
373
+
374
+ it 'redacts real_config_value? opaque tokens' do
375
+ probe = Module.new do
376
+ def self.real_config_value?(opts = {})
377
+ opts[:value]
378
+ end
379
+ end
380
+ stub_const('PWN::AI::Agent::Dispatch::CfgProbe', probe)
381
+ path = PWN::Plugins::Log.start_debug(
382
+ tee: StringIO.new,
383
+ path: "/tmp/pwn-ai-DEBUG-#{Process.pid}-oauth.log",
384
+ trace: true
385
+ )
386
+ token = 'bnQe3hs8ZSP2KkHj3LSnwzIeDvDv0fmbTQEzsUx4XXW5g22D4Uu6hva_MYossX-GedInWt_dwTgGzpMxoKQECQ'
387
+ PWN::AI::Agent::Dispatch::CfgProbe.real_config_value?(value: token)
388
+ body = File.read(path)
389
+ expect(body).not_to include(token)
390
+ expect(body).to match(/\[REDACTED\]/)
391
+ end
392
+
393
+ it 'does not wait for ENTER when trace is off or nested' do
394
+ io = StringIO.new("\n")
395
+ PWN::Plugins::Log.start_debug(
396
+ tee: StringIO.new,
397
+ path: "/tmp/pwn-ai-DEBUG-#{Process.pid}-nostep.log",
398
+ step_io: io
399
+ )
400
+ expect(PWN::Plugins::Log.trace_enabled?).to eq(false)
401
+ PWN::Plugins::Log.wait_trace_step!(label: 'engine')
402
+ expect(io.pos).to eq(0)
403
+ PWN::Plugins::Log.stop_debug
404
+ PWN::Plugins::Log.start_debug(
405
+ tee: StringIO.new,
406
+ path: "/tmp/pwn-ai-DEBUG-#{Process.pid}-nested.log",
407
+ trace: true,
408
+ step_io: io
409
+ )
410
+ PWN::Plugins::Log.wait_trace_step!(label: 'engine', nested: true)
411
+ expect(io.pos).to eq(0)
412
+ end
413
+ end
@@ -137,7 +137,8 @@ describe 'toggle-debug' do
137
137
  let(:repl_src) { File.read(PWN::Plugins::REPL.method(:add_commands).source_location.first) }
138
138
  let(:block) do
139
139
  start = repl_src.index("Pry::Commands.create_command 'toggle-debug'")
140
- stop = repl_src.index("Pry::Commands.create_command 'toggle-pwn-ai-speaks'")
140
+ stop = repl_src.index("Pry::Commands.create_command 'toggle-trace'")
141
+ stop ||= repl_src.index("Pry::Commands.create_command 'toggle-pwn-ai-speaks'")
141
142
  repl_src[start...stop]
142
143
  end
143
144
 
@@ -147,6 +148,41 @@ describe 'toggle-debug' do
147
148
  expect(block).to include('pwn-ai-DEBUG')
148
149
  expect(block).to include('-R')
149
150
  end
151
+
152
+ it 'does not persist toggle-debug in PWN::Env' do
153
+ expect(block).not_to include('PWN::Env')
154
+ expect(block).to include('pwn_ai_debug')
155
+ end
156
+ end
157
+
158
+ describe 'toggle-trace' do
159
+ let(:repl_src) { File.read(PWN::Plugins::REPL.method(:add_commands).source_location.first) }
160
+ let(:block) do
161
+ start = repl_src.index("Pry::Commands.create_command 'toggle-trace'")
162
+ stop = repl_src.index("Pry::Commands.create_command 'toggle-pwn-ai-speaks'")
163
+ repl_src[start...stop]
164
+ end
165
+
166
+ it 'exists as a Pry command next to toggle-debug' do
167
+ expect(repl_src).to include("Pry::Commands.create_command 'toggle-trace'")
168
+ expect(block).to include('pwn_ai_trace')
169
+ expect(block).to include('pwn_ai_debug')
170
+ end
171
+
172
+ it 'turns debug and TracePoint on together and off together' do
173
+ expect(block).to include('start_debug')
174
+ expect(block).to include('trace: true')
175
+ expect(block).to include('stop_debug')
176
+ expect(block).to match(/pwn_ai_debug = true/)
177
+ expect(block).to match(/pwn_ai_trace = true/)
178
+ expect(block).to match(/pwn_ai_debug = false/)
179
+ expect(block).to match(/pwn_ai_trace = false/)
180
+ end
181
+
182
+ it 'does not store toggle-trace in PWN::Env' do
183
+ expect(block).not_to include('PWN::Env')
184
+ expect(block).not_to include('debug_trace')
185
+ end
150
186
  end
151
187
 
152
188
  describe 'pwn-ai debug final answer placement' do
@@ -20,6 +20,15 @@ describe PWN::Plugins::REPL do
20
20
  expect(src).to include('current_context_length')
21
21
  end
22
22
 
23
+ it 'paints (TRACE) in red on the PS1 when toggle-trace is on, not green (DEBUG)' do
24
+ src = File.read(described_class.method(:refresh_ps1_proc).source_location.first)
25
+ expect(src).to include('pwn_ai_trace')
26
+ expect(src).to include('(TRACE)')
27
+ expect(src).to match(/\\e\[31m.*\(TRACE\)/)
28
+ expect(src).to match(/pwn_ai_trace.*\(TRACE\)/m)
29
+ expect(src).to match(/pwn_ai_debug.*\(DEBUG\)/m)
30
+ end
31
+
23
32
  it 'formats compact token counts for the PS1 budget' do
24
33
  expect(described_class.compact_context_tokens(tokens: 0)).to eq('0')
25
34
  expect(described_class.compact_context_tokens(tokens: 26_000)).to eq('26K')
@@ -35,6 +44,12 @@ describe PWN::Plugins::REPL do
35
44
  expect(reader).to match(/ready_tty!/)
36
45
  end
37
46
 
47
+ it 'reinstalls generated module skills into ~/.pwn/skills before load_skills on pwn-ai start' do
48
+ src = File.read(described_class.method(:add_commands).source_location.first)
49
+ expect(src).to match(/ModuleSkills\.install/)
50
+ expect(src).to match(/ModuleSkills\.install.*load_skills|install_default_skills.*load_skills/m)
51
+ end
52
+
38
53
  it 'ready_tty! halts leftover spinner workers so PS1 can redraw without Enter' do
39
54
  src = File.read(described_class.method(:ready_tty!).source_location.first)
40
55
  expect(src).to match(/halt_all!/)
@@ -67,7 +82,7 @@ describe PWN::Plugins::REPL do
67
82
  hits = described_class.pwn_ai_complete(target: '/sk', line: '/sk')
68
83
  expect(hits).to include('/skills')
69
84
  hits = described_class.pwn_ai_complete(target: '/', line: '/')
70
- %w[/cron /skills /sessions /memory /debug /back /help /model].each do |cmd|
85
+ %w[/cron /skills /sessions /memory /debug /trace /back /help /model].each do |cmd|
71
86
  expect(hits).to include(cmd)
72
87
  end
73
88
  hits = described_class.pwn_ai_complete(target: 'li', line: '/cron li')
@@ -391,6 +391,7 @@
391
391
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.task_summary_record! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.task_summary_record!`: "}]}
392
392
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.timeout_task Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.timeout_task`: "}]}
393
393
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.tool_effects Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.tool_effects`: "}]}
394
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.wait_trace_step! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.wait_trace_step!`: "}]}
394
395
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.world_knowledge? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.world_knowledge?`: "}]}
395
396
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.write_verified? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.write_verified?`: "}]}
396
397
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Metrics.advantage Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Metrics.advantage`: Supported Method Parameters\n\na = PWN::AI::Agent::Metrics.advantage(name: ‘shell’)\n\nC1 — tool.success_rate − global_rate over the rolling window.\n"}]}
@@ -729,6 +730,7 @@
729
730
  {"messages":[{"role":"user","content":"PWN::AI::Agent::ToolGuard.payload_spent Usage"},{"role":"assistant","content":"`PWN::AI::Agent::ToolGuard.payload_spent`: "}]}
730
731
  {"messages":[{"role":"user","content":"PWN::AI::Agent::ToolGuard.placeholder? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::ToolGuard.placeholder?`: "}]}
731
732
  {"messages":[{"role":"user","content":"PWN::AI::Agent::ToolGuard.present? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::ToolGuard.present?`: "}]}
733
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::ToolGuard.protect_core_constants! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::ToolGuard.protect_core_constants!`: "}]}
732
734
  {"messages":[{"role":"user","content":"PWN::AI::Agent::ToolGuard.protect_http! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::ToolGuard.protect_http!`: "}]}
733
735
  {"messages":[{"role":"user","content":"PWN::AI::Agent::ToolGuard.reset_timeout_budget Usage"},{"role":"assistant","content":"`PWN::AI::Agent::ToolGuard.reset_timeout_budget`: "}]}
734
736
  {"messages":[{"role":"user","content":"PWN::AI::Agent::ToolGuard.reset_timeout_budget! Usage"},{"role":"assistant","content":"`PWN::AI::Agent::ToolGuard.reset_timeout_budget!`: "}]}
@@ -1394,19 +1396,22 @@
1394
1396
  {"messages":[{"role":"user","content":"PWN::Bounty::LifecycleAuthzReplay.write_runbook Usage"},{"role":"assistant","content":"`PWN::Bounty::LifecycleAuthzReplay.write_runbook`: "}]}
1395
1397
  {"messages":[{"role":"user","content":"PWN::Bounty::LifecycleAuthzReplay.write_yaml Usage"},{"role":"assistant","content":"`PWN::Bounty::LifecycleAuthzReplay.write_yaml`: "}]}
1396
1398
  {"messages":[{"role":"user","content":"PWN::Config.authors Usage"},{"role":"assistant","content":"`PWN::Config.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
1399
+ {"messages":[{"role":"user","content":"PWN::Config.copy_missing_references Usage"},{"role":"assistant","content":"`PWN::Config.copy_missing_references`: "}]}
1397
1400
  {"messages":[{"role":"user","content":"PWN::Config.deep_fill! Usage"},{"role":"assistant","content":"`PWN::Config.deep_fill!`: "}]}
1398
1401
  {"messages":[{"role":"user","content":"PWN::Config.default_env Usage"},{"role":"assistant","content":"`PWN::Config.default_env`: Supported Method Parameters\n\nenv = PWN::Config.default_env(\n\npwn_env_path: 'optional - Path to pwn.yaml file. Defaults to ~/.pwn/pwn.yaml'\n\n)\n"}]}
1399
1402
  {"messages":[{"role":"user","content":"PWN::Config.default_skill_names Usage"},{"role":"assistant","content":"`PWN::Config.default_skill_names`: "}]}
1400
- {"messages":[{"role":"user","content":"PWN::Config.default_skill_src_dir Usage"},{"role":"assistant","content":"`PWN::Config.default_skill_src_dir`: "}]}
1401
1403
  {"messages":[{"role":"user","content":"PWN::Config.default_skills_dir Usage"},{"role":"assistant","content":"`PWN::Config.default_skills_dir`: "}]}
1404
+ {"messages":[{"role":"user","content":"PWN::Config.each_skill_md Usage"},{"role":"assistant","content":"`PWN::Config.each_skill_md`: "}]}
1402
1405
  {"messages":[{"role":"user","content":"PWN::Config.env_template Usage"},{"role":"assistant","content":"`PWN::Config.env_template`: Supported Method Parameters\n\ntmpl = PWN::Config.env_template\n\nThe canonical current-release ~/.pwn/pwn.yaml shape as a pure Hash (no I/O, no vault write, no puts). Single source of truth used by:\n\n* PWN::Config.default_env — seed a fresh ~/.pwn/pwn.yaml\n* PWN::Migrate.vault_drift — diff a user vault against this release\n* PWN::Migrate.backfill_vault — deep-merge missing keys UNDER the\n user values on `pwn setup --migrate --fix`\n"}]}
1403
1406
  {"messages":[{"role":"user","content":"PWN::Config.help Usage"},{"role":"assistant","content":"`PWN::Config.help`: "}]}
1407
+ {"messages":[{"role":"user","content":"PWN::Config.ingest_skill_md Usage"},{"role":"assistant","content":"`PWN::Config.ingest_skill_md`: "}]}
1404
1408
  {"messages":[{"role":"user","content":"PWN::Config.init_driver_options Usage"},{"role":"assistant","content":"`PWN::Config.init_driver_options`: Supported Method Parameters\n\nenv = PWN::Config.init_driver_options\n"}]}
1405
1409
  {"messages":[{"role":"user","content":"PWN::Config.install_default_skills Usage"},{"role":"assistant","content":"`PWN::Config.install_default_skills`: "}]}
1406
1410
  {"messages":[{"role":"user","content":"PWN::Config.load_memory Usage"},{"role":"assistant","content":"`PWN::Config.load_memory`: Supported Method Parameters\n\nPWN::Config.load_memory\n"}]}
1407
- {"messages":[{"role":"user","content":"PWN::Config.load_skills Usage"},{"role":"assistant","content":"`PWN::Config.load_skills`: Supported Method Parameters\n\nskills = PWN::Config.load_skills(\n\npwn_skills_path: 'optional - Path to skills folder. Defaults to ~/.pwn/skills'\n\n)\n\nLoads skills into the PWN::Skills constant. Two on-disk shapes are accepted so upgrades are seamless:\n\nagentskills.io → <root>/<name>/SKILL.md (preferred; written by write_skill)\nlegacy flat → <root>/<name>.{md,txt,rb,skill,yml,yaml}\n\nEach entry: { type:, format:, path:, dir:, content:, description:,\n\nreferences:, frontmatter:, loaded:?, error:? }\n"}]}
1411
+ {"messages":[{"role":"user","content":"PWN::Config.load_skills Usage"},{"role":"assistant","content":"`PWN::Config.load_skills`: Supported Method Parameters\n\nskills = PWN::Config.load_skills(\n\npwn_skills_path: 'optional - Path to skills folder. Defaults to ~/.pwn/skills'\n\n)\n\nLoads skills into the PWN::Skills constant. Two on-disk shapes are accepted so upgrades are seamless:\n\nagentskills.io → <root>/**/SKILL.md (recursive; written by write_skill)\nmodule skills → <root>/pwn/**/SKILL.md\nlegacy flat → <root>/<name>.{md,txt,rb,skill,yml,yaml}\n\nEach entry: { type:, format:, path:, dir:, content:, description:,\n\nreferences:, frontmatter:, loaded:?, error:? }\n"}]}
1408
1412
  {"messages":[{"role":"user","content":"PWN::Config.merge_ai_defaults! Usage"},{"role":"assistant","content":"`PWN::Config.merge_ai_defaults!`: "}]}
1409
1413
  {"messages":[{"role":"user","content":"PWN::Config.migrate_legacy_skills Usage"},{"role":"assistant","content":"`PWN::Config.migrate_legacy_skills`: Supported Method Parameters\n\nreport = PWN::Config.migrate_legacy_skills(\n\npwn_skills_path: 'optional - override skills root',\ndelete_legacy: 'optional - remove flat file after migration (default true)'\n\n)\n\nOne-shot converter: every flat ~/.pwn/skills/*.md (etc.) becomes a spec-conformant <name>/SKILL.md with backfilled frontmatter. Idempotent.\n"}]}
1414
+ {"messages":[{"role":"user","content":"PWN::Config.module_skill_rel? Usage"},{"role":"assistant","content":"`PWN::Config.module_skill_rel?`: "}]}
1410
1415
  {"messages":[{"role":"user","content":"PWN::Config.parse_skill_frontmatter Usage"},{"role":"assistant","content":"`PWN::Config.parse_skill_frontmatter`: Supported Method Parameters\n\nfm = PWN::Config.parse_skill_frontmatter(content: ‘…’)\n\n→ { frontmatter: Hash(String keys), body: String } Missing / malformed frontmatter returns { frontmatter: {}, body: content }.\n"}]}
1411
1416
  {"messages":[{"role":"user","content":"PWN::Config.parse_skill_references Usage"},{"role":"assistant","content":"`PWN::Config.parse_skill_references`: Supported Method Parameters\n\nrefs = PWN::Config.parse_skill_references(content: ‘…’)\n\nExtracts an Array of reference strings (URLs, CWE/CVE/ATT&CK ids, etc.) from a skill body. Supports three sources, merged & uniq’d:\n\n1) frontmatter `references:` (legacy pwn)\n2) frontmatter `metadata: { references: [...] }` (spec-conformant slot)\n3) markdown `## References` bullet section\n"}]}
1412
1417
  {"messages":[{"role":"user","content":"PWN::Config.pwn_cron_path Usage"},{"role":"assistant","content":"`PWN::Config.pwn_cron_path`: Supported Method Parameters\n\npath = PWN::Config.pwn_cron_path\n"}]}
@@ -1667,12 +1672,15 @@
1667
1672
  {"messages":[{"role":"user","content":"PWN::Migrate.verify_one Usage"},{"role":"assistant","content":"`PWN::Migrate.verify_one`: "}]}
1668
1673
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.authors Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.authors`: "}]}
1669
1674
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.camel Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.camel`: "}]}
1675
+ {"messages":[{"role":"user","content":"PWN::ModuleSkills.drift Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.drift`: "}]}
1676
+ {"messages":[{"role":"user","content":"PWN::ModuleSkills.enrich_live_methods! Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.enrich_live_methods!`: "}]}
1670
1677
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.enumerate Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.enumerate`: "}]}
1671
1678
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.example_call Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.example_call`: "}]}
1672
1679
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.extract_references Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.extract_references`: "}]}
1673
1680
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.fallback_record Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.fallback_record`: "}]}
1674
1681
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.help Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.help`: "}]}
1675
1682
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.install Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.install`: "}]}
1683
+ {"messages":[{"role":"user","content":"PWN::ModuleSkills.live_class_methods Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.live_class_methods`: "}]}
1676
1684
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.parse_file Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.parse_file`: "}]}
1677
1685
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.preserved_skill? Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.preserved_skill?`: "}]}
1678
1686
  {"messages":[{"role":"user","content":"PWN::ModuleSkills.prune_orphans Usage"},{"role":"assistant","content":"`PWN::ModuleSkills.prune_orphans`: "}]}
@@ -2041,6 +2049,7 @@
2041
2049
  {"messages":[{"role":"user","content":"PWN::Plugins::JiraDataCenter.rest_call Usage"},{"role":"assistant","content":"`PWN::Plugins::JiraDataCenter.rest_call`: Supported Method Parameters\n\nrest_call(\n\ntoken: 'required - personal access token',\nhttp_method: 'optional HTTP method (defaults to GET)',\nrest_call: 'required rest call to make per the schema',\nparams: 'optional params passed in the URI or HTTP Headers',\nhttp_body: 'optional HTTP body sent in HTTP methods that support it e.g. POST'\n\n)\n"}]}
2042
2050
  {"messages":[{"role":"user","content":"PWN::Plugins::JiraDataCenter.update_issue Usage"},{"role":"assistant","content":"`PWN::Plugins::JiraDataCenter.update_issue`: Supported Method Parameters\n\nissue_resp = PWN::Plugins::JiraDataCenter.update_issue(\n\nfields: 'required - fields to update in the issue (e.g. summary, description, labels, components, custom fields, etc.)',\nattachments: 'optional - array of attachment paths to upload to the issue (e.g. [\"/tmp/file1.txt\", \"/tmp/file2.txt\"])',\n\n)\n"}]}
2043
2051
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.append Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.append`: Supported Method Parameters\n\nPWN::Log.create( )\n"}]}
2052
+ {"messages":[{"role":"user","content":"PWN::Plugins::Log.apply_trace! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.apply_trace!`: "}]}
2044
2053
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.authors Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
2045
2054
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.capture_stderr! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.capture_stderr!`: "}]}
2046
2055
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.close_debug_file! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.close_debug_file!`: "}]}
@@ -2060,8 +2069,10 @@
2060
2069
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.next_request_log! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.next_request_log!`: "}]}
2061
2070
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.note_exception! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.note_exception!`: "}]}
2062
2071
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.note_interrupt! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.note_interrupt!`: "}]}
2072
+ {"messages":[{"role":"user","content":"PWN::Plugins::Log.opaque_secret? Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.opaque_secret?`: "}]}
2063
2073
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.open_debug_file! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.open_debug_file!`: "}]}
2064
2074
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.progress Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.progress`: "}]}
2075
+ {"messages":[{"role":"user","content":"PWN::Plugins::Log.prompt_trace_enter! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.prompt_trace_enter!`: "}]}
2065
2076
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.quiet_tui! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.quiet_tui!`: "}]}
2066
2077
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.raw_stderr Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.raw_stderr`: "}]}
2067
2078
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.remove_stderr_tee! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.remove_stderr_tee!`: "}]}
@@ -2074,8 +2085,10 @@
2074
2085
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.stop_debug Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.stop_debug`: "}]}
2075
2086
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.stop_trace! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.stop_trace!`: "}]}
2076
2087
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.trace_arguments Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.trace_arguments`: "}]}
2088
+ {"messages":[{"role":"user","content":"PWN::Plugins::Log.trace_enabled? Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.trace_enabled?`: "}]}
2077
2089
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.trace_module_name Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.trace_module_name`: "}]}
2078
2090
  {"messages":[{"role":"user","content":"PWN::Plugins::Log.traceable? Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.traceable?`: "}]}
2091
+ {"messages":[{"role":"user","content":"PWN::Plugins::Log.wait_trace_step! Usage"},{"role":"assistant","content":"`PWN::Plugins::Log.wait_trace_step!`: "}]}
2079
2092
  {"messages":[{"role":"user","content":"PWN::Plugins::Log::DebugStderrTee.new Usage"},{"role":"assistant","content":"`PWN::Plugins::Log::DebugStderrTee.new`: "}]}
2080
2093
  {"messages":[{"role":"user","content":"PWN::Plugins::Log::DebugStderrTee.<< Usage"},{"role":"assistant","content":"`PWN::Plugins::Log::DebugStderrTee.<<`: "}]}
2081
2094
  {"messages":[{"role":"user","content":"PWN::Plugins::Log::DebugStderrTee.close Usage"},{"role":"assistant","content":"`PWN::Plugins::Log::DebugStderrTee.close`: "}]}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.702
4
+ version: 0.5.704
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.