pwn 0.5.759 → 0.5.761

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.
@@ -258,6 +258,30 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
258
258
  foreign&.join
259
259
  end
260
260
 
261
+ it 'leaves a provider blocked in native IO without waiting for that read to return' do
262
+ started = nil
263
+ allow(PWN::AI::Agent::Loop).to receive(:call_engine) do
264
+ Thread.handle_interrupt(Exception => :never) { sleep 8 }
265
+ end
266
+ keys = "what color is a lemon?\n".chars
267
+ cancelled = false
268
+ launch(keys) do
269
+ if !cancelled && @paint.any? { |row| row.include?('Elapsed') }
270
+ cancelled = true
271
+ # Time cancellation, not typing, cold artwork or mock screen painting.
272
+ started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
273
+ "\u0003"
274
+ elsif @paint.any? { |row| row.include?('Request cancelled at a safe boundary') }
275
+ "\u0004"
276
+ else
277
+ Thread.pass
278
+ nil
279
+ end
280
+ end
281
+ expect(started).not_to be_nil
282
+ expect(Process.clock_gettime(Process::CLOCK_MONOTONIC) - started).to be < 3
283
+ end
284
+
261
285
  it 'forwards tool input, preserves completed evidence before steering, and locks settings while running' do
262
286
  entered = Queue.new
263
287
  calls = 0
@@ -482,11 +506,11 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
482
506
 
483
507
  it 'centers capped retro artwork inside a larger cell-square canvas' do
484
508
  console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
485
- allow(PWN::Banner).to receive(:mini_names).and_return([:pong])
509
+ allow(PWN::Banner).to receive(:mini_names).and_return([:pacman])
486
510
  allow(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0)
487
511
  size = 24
488
512
  canvas = console.banner_frame(size)
489
- art = PWN::Banner.mini_frame(name: :pong, frame: 0, width: size, height: size, branding: false)
513
+ art = PWN::Banner.mini_frame(name: :pacman, frame: 0, width: size, height: size, branding: false)
490
514
  expect(canvas.length).to eq(size)
491
515
  expect(canvas.map(&:length)).to all(eq(size))
492
516
  top = (size - art.length) / 2
@@ -514,6 +538,7 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
514
538
  end
515
539
 
516
540
  it 'carves a themed cell-square banner pane only when all settings still fit, keeping bounds and draft' do
541
+ allow(PWN::Banner).to receive(:mini_names).and_return([:falling_blocks])
517
542
  PWN::Env[:ai] = { active: :ollama, tui: { theme: { category: 'magenta', header: 'white', border: 'blue', title: 'red' } },
518
543
  ollama: { model: 'fixture', system_role_content: 'Complete functional settings.', reasoning_effort: 'high' } }
519
544
  screen = double(erase: nil, refresh: nil, setpos: nil, addstr: nil)
@@ -556,7 +581,32 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
556
581
  expect([console.instance_variable_get(:@editor).text, console.instance_variable_get(:@editor).cursor]).to eq(['retained draft', 4])
557
582
  end
558
583
 
584
+ it 'keeps a full connected maze and one-cell actors in native minimum and typical console layouts' do
585
+ allow(PWN::Banner).to receive(:mini_names).and_return([:pacman])
586
+ PWN::Env[:ai] = { active: :ollama, ollama: { model: 'fixture' } }
587
+ [[100, 26], [120, 36]].each do |columns, rows|
588
+ console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
589
+ console.instance_variable_set(:@width, columns)
590
+ console.instance_variable_set(:@height, rows)
591
+ console.instance_variable_set(:@focus, :animation)
592
+ allow(console).to receive(:put)
593
+ allow(console).to receive(:box)
594
+ allow(console).to receive(:mark_active)
595
+ expect(console.draw_header('ollama', 'fixture')).to eq(10)
596
+ game = console.instance_variable_get(:@banner_game)
597
+ frame = game.step
598
+ maze = frame[:state][:maze]
599
+ expect(maze.length).to eq(8)
600
+ expect(maze.map(&:length)).to all(eq(8))
601
+ expect(maze[2...-2].map { |row| row[2...-2] }.join.count('#')).to be >= 4
602
+ expect(game.cells(frame).flatten.count { |cell| cell[:foreground] == :red }).to eq(1)
603
+ game.key(:right)
604
+ expect(game.step[:state][:pacman]).to eq([2, 6])
605
+ end
606
+ end
607
+
559
608
  it 'grows both game canvas dimensions with the header without recursively shrinking settings' do
609
+ allow(PWN::Banner).to receive(:mini_names).and_return([:falling_blocks])
560
610
  PWN::Env[:ai] = { active: :ollama, ollama: { model: 'fixture', system_role_content: 'operator ' * 100, reasoning_effort: 'high' } }
561
611
  console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
562
612
  console.instance_variable_set(:@width, 120)
@@ -648,9 +698,9 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
648
698
  expect(console.instance_variable_get(:@banner_seed)).not_to eq(seed)
649
699
  end
650
700
 
651
- it 'samples Asteroids or Pong once and caches their faster cadence outside the key path' do
701
+ it 'samples Asteroids or Pac-Man once and caches the game-specific cadence outside the key path' do
652
702
  allow(PWN::Banner).to receive(:mini_cells).and_call_original
653
- %i[pong asteroids].each do |name|
703
+ %i[pacman asteroids].each do |name|
654
704
  pry = Pry.new
655
705
  pry.config.pwn_ai_session_id = 'fast-colored-session'
656
706
  console = PWN::Plugins::REPL::AIConsole::Console.new(pry: pry, input: StringIO.new, curses: nil, getch: nil)
@@ -663,7 +713,11 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
663
713
  console.banner_frame(8, cells: true)
664
714
  seed = console.instance_variable_get(:@banner_seed)
665
715
  now += 1.21
666
- expect(PWN::Banner).to receive(:mini_cells).with(name: name, frame: 24, width: 8, height: 8, seed: seed, branding: false).twice.and_call_original
716
+ if name == :pacman
717
+ expect(console).to receive(:automatic_banner).with(8, now).twice.and_call_original
718
+ else
719
+ expect(PWN::Banner).to receive(:mini_cells).with(name: name, frame: 24, width: 8, height: 8, seed: seed, branding: false).twice.and_call_original
720
+ end
667
721
  2.times { console.banner_frame(8, cells: true) }
668
722
  end
669
723
  end
@@ -1025,6 +1079,213 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
1025
1079
  console.draw
1026
1080
  expect(painted.map { |row| row[2] }.join).to include('SESSION · active')
1027
1081
  expect(screen).to have_received(:setpos).with(a_kind_of(Integer), 2)
1082
+ console.handle("\u0014")
1083
+ painted.clear
1084
+ console.draw
1085
+ expect(painted.map { |row| row[2] }.join).to include(' PLAY ')
1086
+ expect(painted.map { |row| row[2] }.join).not_to include('MISSION CONTROL · active')
1087
+ expect(screen).to have_received(:setpos).with(1, 1)
1088
+ allow(curses).to receive_messages(lines: 24, cols: 80)
1089
+ console.draw
1090
+ expect(console.instance_variable_get(:@focus)).to eq(:mission)
1091
+ end
1092
+
1093
+ it 'cycles mission, session and visible animation with both shortcuts without changing the draft' do
1094
+ console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
1095
+ editor = console.instance_variable_get(:@editor)
1096
+ editor.place('kept draft', 4)
1097
+ console.instance_variable_set(:@header_pane_height, 10)
1098
+ console.banner_frame(8, cells: true)
1099
+ console.handle("\u0014")
1100
+ console.handle("\u0018")
1101
+ expect(console.instance_variable_get(:@focus)).to eq(:animation)
1102
+ console.banner_frame(8, cells: true)
1103
+ game = console.instance_variable_get(:@banner_game)
1104
+ expect(game).to receive(:key).with(:up)
1105
+ expect(game).to receive(:key).with(' ')
1106
+ console.handle(:up)
1107
+ console.handle(' ')
1108
+ expect([editor.text, editor.cursor]).to eq(['kept draft', 4])
1109
+ console.handle("\u0014")
1110
+ expect(console.instance_variable_get(:@focus)).to eq(:mission)
1111
+ expect([editor.text, editor.cursor]).to eq(['kept draft', 4])
1112
+ end
1113
+
1114
+ it 'paints an Asteroids turn on the next draw without waiting for its physics tick' do
1115
+ console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
1116
+ console.banner_frame(8, cells: true)
1117
+ console.instance_variable_set(:@banner_name, :asteroids)
1118
+ console.instance_variable_set(:@banner_frame_seconds, 0.05)
1119
+ console.instance_variable_set(:@focus, :animation)
1120
+ first = console.playable_banner(8, 10.001)
1121
+ console.handle(:left)
1122
+ expect(console.playable_banner(8, 10.002)).not_to eq(first)
1123
+ expect(console.playable_banner(8, 10.003)).to eq(console.playable_banner(8, 10.002))
1124
+ console.instance_variable_set(:@banner_game_frame, { state: { explosion: 12 } })
1125
+ game = console.instance_variable_get(:@banner_game)
1126
+ expect(game).not_to receive(:step)
1127
+ 16.times { console.handle(:up) }
1128
+ console.playable_banner(8, 10.004)
1129
+ end
1130
+
1131
+ it 'waits for Snake input and routes timed turns across the full pane instead of restarting' do
1132
+ console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
1133
+ console.banner_frame(8, cells: true)
1134
+ console.instance_variable_set(:@banner_name, :snake)
1135
+ console.instance_variable_set(:@banner_frame_seconds, 0.1)
1136
+ console.instance_variable_set(:@focus, :animation)
1137
+ first = console.playable_banner(8, 10.0)
1138
+ 40.times { |tick| expect(console.playable_banner(8, 10.05 + (tick * 0.1))).to eq(first) }
1139
+ game = console.instance_variable_get(:@banner_game)
1140
+ frames = []
1141
+ allow(game).to(receive(:step).and_wrap_original { |method| method.call.tap { |frame| frames << frame } })
1142
+ console.handle(:right)
1143
+ console.playable_banner(8, 15.0)
1144
+ head = frames.last[:state][:body].first
1145
+ console.handle(:up)
1146
+ console.playable_banner(8, 15.05)
1147
+ expect(frames.length).to eq(1)
1148
+ console.playable_banner(8, 15.21)
1149
+ expect(frames.last[:state][:body].first).to eq([head[0], head[1] - 1])
1150
+ console.handle(:right)
1151
+ console.playable_banner(8, 15.41)
1152
+ expect(frames.last[:state][:body].first).to eq([head[0] + 1, head[1] - 1])
1153
+ expect(game.instance_variable_get(:@width)).to eq(8)
1154
+ expect(frames.last[:rows].drop(1).map(&:length)).to all(eq(16))
1155
+ expect(console.instance_variable_get(:@editor).text).to eq('')
1156
+ end
1157
+
1158
+ it 'cycles games with Ctrl+G only in animation focus and preserves swarm and modal priorities' do
1159
+ console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
1160
+ console.banner_frame(8, cells: true)
1161
+ console.instance_variable_set(:@banner_name, :falling_blocks)
1162
+ console.instance_variable_set(:@focus, :animation)
1163
+ editor = console.instance_variable_get(:@editor)
1164
+ editor.place('kept draft', 4)
1165
+ %i[snake pacman asteroids galaga frogger falling_blocks].each do |name|
1166
+ console.handle("\u0007")
1167
+ expect(console.instance_variable_get(:@banner_name)).to eq(name)
1168
+ expect(console.instance_variable_get(:@workspace)).to be_nil
1169
+ console.banner_frame(8, cells: true)
1170
+ expect(console.instance_variable_get(:@banner_game).instance_variable_get(:@name)).to eq(name.to_s)
1171
+ expect(console.footer_text).to include('^G=next game')
1172
+ expect(console.footer_text).to include('↑↓←→ start/steer · ● power') if name == :pacman
1173
+ expect(console.footer_text).to include('↑↓←→ move · Space=fire') if name == :galaga
1174
+ expect(console.footer_text).to include('↑↓←→ hop · logs → goals') if name == :frogger
1175
+ expect(console.operation_lines.join).to include('^S swarm')
1176
+ end
1177
+ console.instance_variable_set(:@details, 0)
1178
+ expect(console).to receive(:handle_details).with("\u0007")
1179
+ console.handle("\u0007")
1180
+ console.instance_variable_set(:@details, nil)
1181
+ editor.search_history
1182
+ expect(console).to receive(:handle_search).with("\u0007")
1183
+ console.handle("\u0007")
1184
+ editor.finish_search
1185
+ console.handle("\u0013")
1186
+ expect(console.instance_variable_get(:@workspace)).to be_a(Hash)
1187
+ console.handle("\u0007")
1188
+ expect(console.instance_variable_get(:@workspace)).to be_nil
1189
+ expect(console.instance_variable_get(:@banner_name)).to eq(:falling_blocks)
1190
+ console.handle("\e")
1191
+ console.handle("\u0007")
1192
+ expect(console.instance_variable_get(:@workspace)).to be_a(Hash)
1193
+ expect([editor.text, editor.cursor]).to eq(['kept draft', 4])
1194
+ end
1195
+
1196
+ it 'delivers timed Pac-Man buffered arrows without consuming the mission draft or speeding up on repeats' do
1197
+ console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
1198
+ console.banner_frame(8, cells: true)
1199
+ console.instance_variable_set(:@banner_name, :pacman)
1200
+ console.instance_variable_set(:@banner_seed, 9)
1201
+ console.instance_variable_set(:@banner_frame_seconds, 0.1)
1202
+ console.instance_variable_set(:@focus, :animation)
1203
+ editor = console.instance_variable_get(:@editor)
1204
+ editor.place('kept draft', 4)
1205
+ console.playable_banner(7, 10.01)
1206
+ console.handle(:right)
1207
+ console.playable_banner(7, 10.11)
1208
+ expect(console.instance_variable_get(:@banner_game_frame)[:state][:pacman]).to eq([2, 5])
1209
+ console.handle(:up)
1210
+ 10.times { console.playable_banner(7, 10.12) }
1211
+ expect(console.instance_variable_get(:@banner_game_frame)[:state][:pacman]).to eq([2, 5])
1212
+ [10.21, 10.31, 10.41, 10.51, 10.61, 10.71].each { |time| console.playable_banner(7, time) }
1213
+ expect(console.instance_variable_get(:@banner_game_frame)[:state][:pacman]).to eq([3, 4])
1214
+ console.handle("\e")
1215
+ expect([editor.text, editor.cursor]).to eq(['kept draft', 4])
1216
+ expect(console.instance_variable_get(:@focus)).to eq(:mission)
1217
+ end
1218
+
1219
+ it 'returns to the unchanged automatic animation when focus leaves the game' do
1220
+ console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
1221
+ allow(Process).to receive(:clock_gettime).and_return(10.0)
1222
+ automatic = console.banner_frame(8, cells: true)
1223
+ console.instance_variable_set(:@header_pane_height, 10)
1224
+ 2.times { console.handle("\u0014") }
1225
+ expect(console.banner_frame(8, cells: true)).not_to eq(automatic)
1226
+ console.handle("\u0018")
1227
+ expect(console.banner_frame(8, cells: true)).to eq(automatic)
1228
+ console.instance_variable_set(:@header_pane_height, nil)
1229
+ 2.times { console.handle("\u0014") }
1230
+ expect(console.instance_variable_get(:@focus)).to eq(:mission)
1231
+ end
1232
+
1233
+ it 'streams the new arcade demos on the UI owner without a synchronous full replay build' do
1234
+ %i[pacman galaga frogger].each do |name|
1235
+ console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
1236
+ allow(PWN::Banner).to receive(:mini_names).and_return([name])
1237
+ allow(Process).to receive(:clock_gettime).and_return(10.0)
1238
+ expect(PWN::Banner).not_to receive(:mini_cells)
1239
+ first = console.banner_frame(32, cells: true)
1240
+ demo = console.instance_variable_get(:@banner_demo)
1241
+ expect(demo).to be_a(PWN::Banner::MiniGame)
1242
+ expect(console.instance_variable_get(:@banner_demo_frame)[:state][:started]).to be true
1243
+ expect(first.map(&:length)).to all(eq(32))
1244
+ expect(console.banner_frame(32, cells: true)).to eq(first)
1245
+ allow(Process).to receive(:clock_gettime).and_return(10.2)
1246
+ console.banner_frame(32, cells: true)
1247
+ expect(console.instance_variable_get(:@banner_demo)).to equal(demo)
1248
+ console.instance_variable_set(:@focus, :animation)
1249
+ console.banner_frame(32, cells: true)
1250
+ expect(console.instance_variable_get(:@banner_game)).not_to equal(demo)
1251
+ expect(console.instance_variable_get(:@banner_game_frame)[:state][:started]).to be false
1252
+ end
1253
+ end
1254
+
1255
+ it 'keeps modal, search and cancellation priority above animation controls' do
1256
+ console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
1257
+ console.instance_variable_set(:@focus, :animation)
1258
+ editor = console.instance_variable_get(:@editor)
1259
+ editor.place('draft', 2)
1260
+ game = double(key: nil)
1261
+ console.instance_variable_set(:@banner_game, game)
1262
+ console.instance_variable_set(:@details, 0)
1263
+ expect(console).to receive(:handle_details).with(:up)
1264
+ console.handle(:up)
1265
+ console.instance_variable_set(:@details, nil)
1266
+ console.instance_variable_set(:@model_prompt, {})
1267
+ expect(console).to receive(:handle_model_prompt).with(' ')
1268
+ console.handle(' ')
1269
+ console.instance_variable_set(:@model_prompt, nil)
1270
+ allow(console).to receive(:busy?).and_return(true)
1271
+ expect(console).to receive(:cancel)
1272
+ console.handle("\u0003")
1273
+ console.handle("\u0012")
1274
+ expect(editor.search).not_to be_nil
1275
+ expect(console.instance_variable_get(:@focus)).to eq(:mission)
1276
+ expect(game).not_to have_received(:key)
1277
+ expect([editor.text, editor.cursor]).to eq(['draft', 2])
1278
+ end
1279
+
1280
+ it 'preserves bracketed paste and the mission cursor when leaving animation focus' do
1281
+ console = PWN::Plugins::REPL::AIConsole::Console.new(pry: Pry.new, input: StringIO.new, curses: nil, getch: nil)
1282
+ editor = console.instance_variable_get(:@editor)
1283
+ editor.place('draft', 2)
1284
+ console.instance_variable_set(:@focus, :animation)
1285
+ [:paste_start, ' ', "\n", 'x', :paste_end].each { |key| console.handle(key) }
1286
+ expect(editor.text).to eq("dr \nxaft")
1287
+ expect(editor.cursor).to eq(5)
1288
+ expect(console.instance_variable_get(:@focus)).to eq(:mission)
1028
1289
  end
1029
1290
 
1030
1291
  it 'toggles the pane with Ctrl+T and opens swarm with Ctrl+S' do
@@ -1387,6 +1648,7 @@ describe 'pwn-ai curses launch' do # rubocop:disable Metrics/BlockLength -- publ
1387
1648
 
1388
1649
  ["\e", "\u0003"].each do |cancel_key|
1389
1650
  it "keeps the rendered header and banner unchanged after reasoning cancellation #{cancel_key.inspect}" do
1651
+ allow(PWN::Banner).to receive(:mini_names).and_return([:falling_blocks])
1390
1652
  allow(PWN::Plugins::REPL).to receive(:persist_ai_selection).and_return(false)
1391
1653
  expect(PWN::Plugins::REPL).not_to receive(:pwn_ai_apply_model)
1392
1654
  original = Marshal.dump(PWN::Env[:ai])
@@ -487,9 +487,10 @@
487
487
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.wrap_untrusted_tool Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.wrap_untrusted_tool`: "}]}
488
488
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.write_or_read_evidenced? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.write_or_read_evidenced?`: "}]}
489
489
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop.write_verified? Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop.write_verified?`: "}]}
490
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop::ProviderWindow.model Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop::ProviderWindow.model`: "}]}
491
+ {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop::ProviderWindow.request_model_cancel Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop::ProviderWindow.request_model_cancel`: "}]}
490
492
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop::Steering.new Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop::Steering.new`: "}]}
491
493
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop::Steering.checkpoint Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop::Steering.checkpoint`: "}]}
492
- {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop::Steering.model Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop::Steering.model`: "}]}
493
494
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop::Steering.submit Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop::Steering.submit`: "}]}
494
495
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop::Steering.with_reader Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop::Steering.with_reader`: "}]}
495
496
  {"messages":[{"role":"user","content":"PWN::AI::Agent::Loop::Steering::Restart.new Usage"},{"role":"assistant","content":"`PWN::AI::Agent::Loop::Steering::Restart.new`: "}]}
@@ -1734,17 +1735,36 @@
1734
1735
  {"messages":[{"role":"user","content":"PWN::Banner.mini_asteroids Usage"},{"role":"assistant","content":"`PWN::Banner.mini_asteroids`: "}]}
1735
1736
  {"messages":[{"role":"user","content":"PWN::Banner.mini_asteroids_snapshot Usage"},{"role":"assistant","content":"`PWN::Banner.mini_asteroids_snapshot`: "}]}
1736
1737
  {"messages":[{"role":"user","content":"PWN::Banner.mini_block_board Usage"},{"role":"assistant","content":"`PWN::Banner.mini_block_board`: "}]}
1737
- {"messages":[{"role":"user","content":"PWN::Banner.mini_cells Usage"},{"role":"assistant","content":"`PWN::Banner.mini_cells`: Supported Method Parameters\n\nReturn rows of single-column Unicode block cells, with named terminal colors. Each fresh cell has glyph, foreground and background keys; no ANSI or IO. Square means terminal-cell dimensions, not physical pixels. Caller owns borders. Use mini_frame_seconds for cadence; 1800 frames span 90 or 180 seconds. Colored blocks fill the canvas with half-cell pieces; other games cap at 16. PWN::Banner.mini_cells(\n\nname: 'optional - falling_blocks, snake, pong, or asteroids; defaults to falling_blocks',\nframe: 'optional - integer index, defaults to zero and wraps at 1800',\nwidth: 'optional - columns clamped to 0..64 for blocks, 0..16 otherwise; defaults to 16',\nheight: 'optional - rows clamped to 0..64 for blocks, 0..16 otherwise; defaults to 16',\nseed: 'optional - integer local PRNG seed, defaults to 73; retain per session',\nbranding: 'optional - include the legacy wordmark, defaults to true; false reclaims its row'\n\n)\n"}]}
1738
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_block_plan Usage"},{"role":"assistant","content":"`PWN::Banner.mini_block_plan`: "}]}
1739
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_cells Usage"},{"role":"assistant","content":"`PWN::Banner.mini_cells`: Supported Method Parameters\n\nReturn rows of single-column Unicode block cells, with named terminal colors. Each fresh cell has glyph, foreground and background keys; no ANSI or IO. Square means terminal-cell dimensions, not physical pixels. Caller owns borders. Use mini_frame_seconds for cadence; 1800 frames span 90 or 180 seconds. Blocks, Pac-Man, Galaga and Frogger fill up to 64 cells; other games cap at 16. PWN::Banner.mini_cells(\n\nname: 'optional - falling_blocks, snake, pacman, asteroids, galaga or frogger; legacy pong accepted; defaults to falling_blocks',\nframe: 'optional - integer index, defaults to zero and wraps at 1800',\nwidth: 'optional - columns clamped to 0..64 for blocks/Pac-Man/Galaga/Frogger, 0..16 otherwise; defaults to 16',\nheight: 'optional - rows clamped to 0..64 for blocks/Pac-Man/Galaga/Frogger, 0..16 otherwise; defaults to 16',\nseed: 'optional - integer local PRNG seed, defaults to 73; retain per session',\nbranding: 'optional - include the legacy wordmark, defaults to true; false reclaims its row'\n\n)\n"}]}
1738
1740
  {"messages":[{"role":"user","content":"PWN::Banner.mini_delta Usage"},{"role":"assistant","content":"`PWN::Banner.mini_delta`: "}]}
1741
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_dot Usage"},{"role":"assistant","content":"`PWN::Banner.mini_dot`: "}]}
1739
1742
  {"messages":[{"role":"user","content":"PWN::Banner.mini_emit Usage"},{"role":"assistant","content":"`PWN::Banner.mini_emit`: "}]}
1740
1743
  {"messages":[{"role":"user","content":"PWN::Banner.mini_falling_blocks Usage"},{"role":"assistant","content":"`PWN::Banner.mini_falling_blocks`: "}]}
1741
1744
  {"messages":[{"role":"user","content":"PWN::Banner.mini_fits? Usage"},{"role":"assistant","content":"`PWN::Banner.mini_fits?`: "}]}
1742
- {"messages":[{"role":"user","content":"PWN::Banner.mini_frame Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frame`: Supported Method Parameters\n\nReturn fresh ASCII rows for decorative retro loops, not interactive games. Use equal width and height for a square; 5..16 retains the complete artwork. Smaller panes degrade to a wordmark. Caller owns randomness, clock and IO. PWN::Banner.mini_frame(\n\nname: 'optional - falling_blocks, snake, pong, or asteroids; defaults to falling_blocks',\nframe: 'optional - integer frame index, defaults to zero and wraps at 60',\nwidth: 'optional - output columns clamped to zero through 16, defaults to 16',\nheight: 'optional - output rows clamped to zero through 16, defaults to 16',\nbranding: 'optional - include the legacy wordmark, defaults to true; false reclaims its row'\n\n)\n"}]}
1745
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_frame Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frame`: Supported Method Parameters\n\nReturn fresh ASCII rows for decorative retro loops, not interactive games. Use equal width and height for a square; 5..16 retains the complete artwork. Smaller panes degrade to a wordmark. Caller owns randomness, clock and IO. PWN::Banner.mini_frame(\n\nname: 'optional - falling_blocks, snake, pacman, asteroids, galaga or frogger; legacy pong accepted; defaults to falling_blocks',\nframe: 'optional - integer frame index, defaults to zero and wraps at 60',\nwidth: 'optional - output columns clamped to zero through 16, defaults to 16',\nheight: 'optional - output rows clamped to zero through 16, defaults to 16',\nbranding: 'optional - include the legacy wordmark, defaults to true; false reclaims its row'\n\n)\n"}]}
1743
1746
  {"messages":[{"role":"user","content":"PWN::Banner.mini_frame_seconds Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frame_seconds`: Supported Method Parameters\n\nReturn the presentation interval; simulations preserve their gameplay speed. PWN::Banner.mini_frame_seconds(\n\nname: 'optional - animation name, defaults to falling_blocks'\n\n)\n"}]}
1744
1747
  {"messages":[{"role":"user","content":"PWN::Banner.mini_freeze Usage"},{"role":"assistant","content":"`PWN::Banner.mini_freeze`: "}]}
1748
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_frogger_contact Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frogger_contact`: "}]}
1749
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_frogger_demo Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frogger_demo`: "}]}
1750
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_frogger_move Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frogger_move`: "}]}
1751
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_frogger_round Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frogger_round`: "}]}
1752
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_frogger_snapshot Usage"},{"role":"assistant","content":"`PWN::Banner.mini_frogger_snapshot`: "}]}
1753
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_galaga_move Usage"},{"role":"assistant","content":"`PWN::Banner.mini_galaga_move`: "}]}
1754
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_galaga_round Usage"},{"role":"assistant","content":"`PWN::Banner.mini_galaga_round`: "}]}
1755
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_galaga_snapshot Usage"},{"role":"assistant","content":"`PWN::Banner.mini_galaga_snapshot`: "}]}
1745
1756
  {"messages":[{"role":"user","content":"PWN::Banner.mini_landing Usage"},{"role":"assistant","content":"`PWN::Banner.mini_landing`: "}]}
1746
1757
  {"messages":[{"role":"user","content":"PWN::Banner.mini_names Usage"},{"role":"assistant","content":"`PWN::Banner.mini_names`: Supported Method Parameters\n\nList animation names; callers may sample once and retain the session choice. PWN::Banner.mini_names\n"}]}
1758
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_pacman Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pacman`: "}]}
1759
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_pacman_contact Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pacman_contact`: "}]}
1760
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_pacman_ghosts Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pacman_ghosts`: "}]}
1761
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_pacman_move Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pacman_move`: "}]}
1762
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_pacman_neighbors Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pacman_neighbors`: "}]}
1763
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_pacman_round Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pacman_round`: "}]}
1764
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_pacman_route Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pacman_route`: "}]}
1765
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_pacman_snapshot Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pacman_snapshot`: "}]}
1747
1766
  {"messages":[{"role":"user","content":"PWN::Banner.mini_pixel Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pixel`: "}]}
1767
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_play_piece Usage"},{"role":"assistant","content":"`PWN::Banner.mini_play_piece`: "}]}
1748
1768
  {"messages":[{"role":"user","content":"PWN::Banner.mini_pong Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pong`: "}]}
1749
1769
  {"messages":[{"role":"user","content":"PWN::Banner.mini_pong_snapshot Usage"},{"role":"assistant","content":"`PWN::Banner.mini_pong_snapshot`: "}]}
1750
1770
  {"messages":[{"role":"user","content":"PWN::Banner.mini_quarter_board Usage"},{"role":"assistant","content":"`PWN::Banner.mini_quarter_board`: "}]}
@@ -1753,6 +1773,9 @@
1753
1773
  {"messages":[{"role":"user","content":"PWN::Banner.mini_rotations Usage"},{"role":"assistant","content":"`PWN::Banner.mini_rotations`: "}]}
1754
1774
  {"messages":[{"role":"user","content":"PWN::Banner.mini_simulate_asteroids Usage"},{"role":"assistant","content":"`PWN::Banner.mini_simulate_asteroids`: "}]}
1755
1775
  {"messages":[{"role":"user","content":"PWN::Banner.mini_simulate_falling_blocks Usage"},{"role":"assistant","content":"`PWN::Banner.mini_simulate_falling_blocks`: "}]}
1776
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_simulate_frogger Usage"},{"role":"assistant","content":"`PWN::Banner.mini_simulate_frogger`: "}]}
1777
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_simulate_galaga Usage"},{"role":"assistant","content":"`PWN::Banner.mini_simulate_galaga`: "}]}
1778
+ {"messages":[{"role":"user","content":"PWN::Banner.mini_simulate_pacman Usage"},{"role":"assistant","content":"`PWN::Banner.mini_simulate_pacman`: "}]}
1756
1779
  {"messages":[{"role":"user","content":"PWN::Banner.mini_simulate_pong Usage"},{"role":"assistant","content":"`PWN::Banner.mini_simulate_pong`: "}]}
1757
1780
  {"messages":[{"role":"user","content":"PWN::Banner.mini_simulate_snake Usage"},{"role":"assistant","content":"`PWN::Banner.mini_simulate_snake`: "}]}
1758
1781
  {"messages":[{"role":"user","content":"PWN::Banner.mini_snake Usage"},{"role":"assistant","content":"`PWN::Banner.mini_snake`: "}]}
@@ -1786,6 +1809,13 @@
1786
1809
  {"messages":[{"role":"user","content":"PWN::Banner::Matrix.get Usage"},{"role":"assistant","content":"`PWN::Banner::Matrix.get`: Supported Method Parameters\n\nPWN::Banner::Matrix.get\n"}]}
1787
1810
  {"messages":[{"role":"user","content":"PWN::Banner::Matrix.help Usage"},{"role":"assistant","content":"`PWN::Banner::Matrix.help`: "}]}
1788
1811
  {"messages":[{"role":"user","content":"PWN::Banner::Matrix.you? Usage"},{"role":"assistant","content":"`PWN::Banner::Matrix.you?`: Supported Method Parameters\n\nPWN::Banner::Matrix.has_you\n"}]}
1812
+ {"messages":[{"role":"user","content":"PWN::Banner::MiniGame.new Usage"},{"role":"assistant","content":"`PWN::Banner::MiniGame.new`: "}]}
1813
+ {"messages":[{"role":"user","content":"PWN::Banner::MiniGame.<< Usage"},{"role":"assistant","content":"`PWN::Banner::MiniGame.<<`: "}]}
1814
+ {"messages":[{"role":"user","content":"PWN::Banner::MiniGame.cells Usage"},{"role":"assistant","content":"`PWN::Banner::MiniGame.cells`: "}]}
1815
+ {"messages":[{"role":"user","content":"PWN::Banner::MiniGame.controls Usage"},{"role":"assistant","content":"`PWN::Banner::MiniGame.controls`: "}]}
1816
+ {"messages":[{"role":"user","content":"PWN::Banner::MiniGame.key Usage"},{"role":"assistant","content":"`PWN::Banner::MiniGame.key`: "}]}
1817
+ {"messages":[{"role":"user","content":"PWN::Banner::MiniGame.length Usage"},{"role":"assistant","content":"`PWN::Banner::MiniGame.length`: "}]}
1818
+ {"messages":[{"role":"user","content":"PWN::Banner::MiniGame.step Usage"},{"role":"assistant","content":"`PWN::Banner::MiniGame.step`: "}]}
1789
1819
  {"messages":[{"role":"user","content":"PWN::Banner::Ninja.authors Usage"},{"role":"assistant","content":"`PWN::Banner::Ninja.authors`: Author(s)\n\n0day Inc. <support@0dayinc.com>\n"}]}
1790
1820
  {"messages":[{"role":"user","content":"PWN::Banner::Ninja.get Usage"},{"role":"assistant","content":"`PWN::Banner::Ninja.get`: Supported Method Parameters\n\nPWN::Banner::Ninja.get\n"}]}
1791
1821
  {"messages":[{"role":"user","content":"PWN::Banner::Ninja.help Usage"},{"role":"assistant","content":"`PWN::Banner::Ninja.help`: "}]}
@@ -3307,6 +3337,7 @@
3307
3337
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.new Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.new`: "}]}
3308
3338
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.accept_completion Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.accept_completion`: "}]}
3309
3339
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.add Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.add`: "}]}
3340
+ {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.automatic_banner Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.automatic_banner`: "}]}
3310
3341
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.banner_frame Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.banner_frame`: "}]}
3311
3342
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.begin_request_metrics Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.begin_request_metrics`: "}]}
3312
3343
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.bounded_event Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.bounded_event`: "}]}
@@ -3339,6 +3370,7 @@
3339
3370
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.footer_lines Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.footer_lines`: "}]}
3340
3371
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.footer_text Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.footer_text`: "}]}
3341
3372
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.handle Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.handle`: "}]}
3373
+ {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.handle_animation Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.handle_animation`: "}]}
3342
3374
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.handle_details Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.handle_details`: "}]}
3343
3375
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.handle_model_prompt Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.handle_model_prompt`: "}]}
3344
3376
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.handle_search Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.handle_search`: "}]}
@@ -3354,6 +3386,7 @@
3354
3386
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.open_swarm Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.open_swarm`: "}]}
3355
3387
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.open_system_role Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.open_system_role`: "}]}
3356
3388
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.operation_lines Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.operation_lines`: "}]}
3389
+ {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.playable_banner Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.playable_banner`: "}]}
3357
3390
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.prepare_swarm_mission Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.prepare_swarm_mission`: "}]}
3358
3391
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.put Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.put`: "}]}
3359
3392
  {"messages":[{"role":"user","content":"PWN::Plugins::REPL::AIConsole::Console.put_spans Usage"},{"role":"assistant","content":"`PWN::Plugins::REPL::AIConsole::Console.put_spans`: "}]}
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.759
4
+ version: 0.5.761
5
5
  platform: ruby
6
6
  authors:
7
7
  - 0day Inc.
@@ -505,14 +505,14 @@ dependencies:
505
505
  requirements:
506
506
  - - '='
507
507
  - !ruby/object:Gem::Version
508
- version: 1.6.1
508
+ version: 1.7.0
509
509
  type: :runtime
510
510
  prerelease: false
511
511
  version_requirements: !ruby/object:Gem::Requirement
512
512
  requirements:
513
513
  - - '='
514
514
  - !ruby/object:Gem::Version
515
- version: 1.6.1
515
+ version: 1.7.0
516
516
  - !ruby/object:Gem::Dependency
517
517
  name: meshtastic
518
518
  requirement: !ruby/object:Gem::Requirement