agent-petri-dish 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Migrates test configs from v1 (sidecar) to v2 (hooks) format.
5
+ # Uses text manipulation rather than YAML.dump to preserve formatting.
6
+
7
+ require "yaml"
8
+
9
+ TESTS_DIR = File.join(__dir__, "..", "tests")
10
+
11
+ Dir.glob("#{TESTS_DIR}/*/config.yml").sort.each do |path|
12
+ test_name = File.basename(File.dirname(path))
13
+ text = File.read(path)
14
+
15
+ # Skip if already migrated (has prompt_mode, no sidecar)
16
+ unless text.include?("sidecar:")
17
+ puts "Skip (no sidecar): #{test_name}"
18
+ next
19
+ end
20
+
21
+ data = YAML.safe_load(text)
22
+ sidecar = data["sidecar"] || {}
23
+ mode = sidecar["mode"] || "accept"
24
+ timeout = sidecar["timeout"] || 300
25
+
26
+ # 1. Remove the sidecar block (and trailing newline)
27
+ text.gsub!(/\nsidecar:\n(?: .+\n)*/, "\n")
28
+
29
+ # 2. Add prompt_mode at the end
30
+ text.rstrip!
31
+ text << "\n\nprompt_mode: #{mode}\n"
32
+
33
+ # 3. Add timeout to runtime if not already there
34
+ unless text.match?(/^ timeout:/)
35
+ # Insert timeout as last line of runtime block
36
+ text.sub!(/^(runtime:\n(?: .+\n)*)/) do
37
+ "#{$1} timeout: #{timeout}\n"
38
+ end
39
+ end
40
+
41
+ # 4. Move permissions array into settings.permissions.allow
42
+ env = data["environment"] || {}
43
+ if env["permissions"].is_a?(Array)
44
+ perms = env["permissions"]
45
+ if perms.empty?
46
+ # Just remove empty permissions
47
+ text.sub!(/^ permissions: \[\]\n/, "")
48
+ else
49
+ # Build the settings.permissions.allow block
50
+ perms_yaml = perms.map { |p| " - #{p}" }.join("\n")
51
+
52
+ # Remove the old permissions block
53
+ text.sub!(/^ permissions:\n(?: - .+\n)*/, "")
54
+
55
+ # Add permissions into settings
56
+ if text.match?(/^ sandbox:/)
57
+ # Insert after settings block content
58
+ text.sub!(/^( settings:\n(?: .+\n(?: .+\n)*)*)/) do
59
+ "#{$1} permissions:\n allow:\n#{perms_yaml}\n"
60
+ end
61
+ elsif text.match?(/^ settings: \{\}/)
62
+ text.sub!(" settings: {}", " settings:\n permissions:\n allow:\n#{perms_yaml}")
63
+ end
64
+ end
65
+ end
66
+
67
+ # 5. Update preamble path
68
+ text.gsub!("lib/preamble-sandbox-lean.md", "lib/preambles/sandbox.md")
69
+ text.gsub!("lib/preamble-sandbox.md", "lib/preambles/sandbox.md")
70
+
71
+ File.write(path, text)
72
+ puts "Migrated: #{test_name}"
73
+ end
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Migrates prompt.md files from v1 (prompt-check protocol) to v2 (hook-driven).
5
+ #
6
+ # Changes:
7
+ # 1. Replace Protocol sections that mention "Prompt check" with simplified version
8
+ # 2. Replace "After All Tests" sections with SIGNAL_FILE instruction
9
+ # 3. Add "After All Tests" + SIGNAL_FILE if missing
10
+
11
+ TESTS_DIR = File.join(__dir__, "..", "tests")
12
+
13
+ SIMPLE_PROTOCOL = <<~PROTOCOL.rstrip
14
+ ## Protocol
15
+
16
+ Follow the test protocol from the preamble. Run each command, report the result, output a RESULT line, move to the next.
17
+ PROTOCOL
18
+
19
+ SIMPLE_AFTER = <<~AFTER.rstrip
20
+ ## After All Tests
21
+
22
+ Write "done" to the SIGNAL_FILE.
23
+ AFTER
24
+
25
+ Dir.glob("#{TESTS_DIR}/*/prompt.md").sort.each do |path|
26
+ test_name = File.basename(File.dirname(path))
27
+ text = File.read(path)
28
+ changed = false
29
+
30
+ # Skip if already has SIGNAL_FILE (already v2)
31
+ if text.include?("SIGNAL_FILE") && !text.include?("Prompt check") && !text.include?("prompt check")
32
+ puts "Skip (already v2): #{test_name}"
33
+ next
34
+ end
35
+
36
+ # 1. Replace Protocol section that contains "Prompt check"
37
+ # Match from "## Protocol" to the next "##" heading or "## Test" heading
38
+ if text =~ /^## Protocol\n/
39
+ # Find the protocol section boundaries
40
+ proto_start = text.index("## Protocol\n")
41
+ # Find the next ## heading after the protocol
42
+ rest_after_proto = text[proto_start + "## Protocol\n".length..]
43
+ next_heading = rest_after_proto.index(/^## /)
44
+
45
+ if next_heading
46
+ old_protocol = text[proto_start, "## Protocol\n".length + next_heading]
47
+ if old_protocol.include?("Prompt check") || old_protocol.include?("prompt check")
48
+ text = text.sub(old_protocol, SIMPLE_PROTOCOL + "\n\n")
49
+ changed = true
50
+ end
51
+ end
52
+ end
53
+
54
+ # 2. Replace "After All Tests" section
55
+ if text =~ /^## After All Tests\n/
56
+ after_start = text.index("## After All Tests\n")
57
+ old_after = text[after_start..]
58
+
59
+ unless old_after.include?("SIGNAL_FILE")
60
+ text = text[0...after_start] + SIMPLE_AFTER + "\n"
61
+ changed = true
62
+ end
63
+ elsif !text.include?("SIGNAL_FILE")
64
+ # 3. Add After All Tests if missing entirely
65
+ text = text.rstrip + "\n\n" + SIMPLE_AFTER + "\n"
66
+ changed = true
67
+ end
68
+
69
+ if changed
70
+ File.write(path, text)
71
+ puts "Migrated: #{test_name}"
72
+ else
73
+ puts "Skip (no changes needed): #{test_name}"
74
+ end
75
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agent-petri-dish
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Josh Nichols
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: petri-dish runs Claude Code sessions inside isolated cenv environments,
13
+ captures hook events, and correlates them into structured results. Your ~/.claude
14
+ is production; the dish is where you do science.
15
+ email:
16
+ - josh.nichols@gmail.com
17
+ executables:
18
+ - petri-dish
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - CONTRIBUTING.md
23
+ - LICENSE
24
+ - README.md
25
+ - bin/petri-dish
26
+ - hooks/event-logger.sh
27
+ - hooks/permission-handler.sh
28
+ - lib/petri-dish.rb
29
+ - lib/petri_dish.rb
30
+ - lib/petri_dish/cli.rb
31
+ - lib/petri_dish/config.rb
32
+ - lib/petri_dish/environment.rb
33
+ - lib/petri_dish/hook_log.rb
34
+ - lib/petri_dish/preambles/escape-hatch.md
35
+ - lib/petri_dish/preambles/guidance.md
36
+ - lib/petri_dish/preambles/permissions.md
37
+ - lib/petri_dish/preambles/sandbox.md
38
+ - lib/petri_dish/results_builder.rb
39
+ - lib/petri_dish/runner.rb
40
+ - lib/petri_dish/transcript.rb
41
+ - lib/petri_dish/version.rb
42
+ - scripts/analyze-hooks.py
43
+ - scripts/hook-block-pattern.sh
44
+ - scripts/hook-logger.sh
45
+ - scripts/inspect-session.sh
46
+ - scripts/migrate-configs.rb
47
+ - scripts/migrate-prompts.rb
48
+ homepage: https://github.com/technicalpickles/petri-dish
49
+ licenses:
50
+ - MIT
51
+ metadata:
52
+ homepage_uri: https://github.com/technicalpickles/petri-dish
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '3.2'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubygems_version: 4.0.6
68
+ specification_version: 4
69
+ summary: Isolated, repeatable experiments against agentic coding tools.
70
+ test_files: []