context_spook 1.1.0 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 41d55af33ce0adb0f4950f6a24b43fdc6d5c6d7401e6c6eb59bdce08d149043f
4
- data.tar.gz: fa121dce3b5c17f234a51133e93c947420609849bcc048676e4f0d5f9e718d42
3
+ metadata.gz: a00a3e36535b351b630e539a1dfae938bb2325c2f639e3d0b318f1cac0e8c14f
4
+ data.tar.gz: 3c7976d1672dd67d6b0b07d64350e3baf211d6e8752b83631de9f5a17ae642f3
5
5
  SHA512:
6
- metadata.gz: 812003a9986f5ca5bad980b57fececfc09085de3787dd671197453f977feaa249c67a1af858e93ba2be810ae941b1389dbc28e222fac853550b33b6e3d2076ac
7
- data.tar.gz: 6579a56156f1e6deaf64427d2038de99a2e68749e397fc57860fdf4b6c33359f467d72e7a2cc1a0effd691c7c8411666f23c8b414334d5ab0e55e6540ee39d98
6
+ metadata.gz: 82bf0afffa28eab0b379ec94c9a7a7cd34406ca82ccf4a7e78d26103082b8610ce3461a06a26772381fe59d6cfa401516eed532d33c6742cff980d08dea5508d
7
+ data.tar.gz: f969059b0b7ca9490ceb6560c5735da343c4ee5ee5ff856bae919da5ab66f02886e55a7096ad33b91241a147d78f5b5a82ec652ce25800658d46e4dc6a87bbc2
data/Rakefile CHANGED
@@ -38,6 +38,7 @@ GemHadar do
38
38
  dependency 'mime-types', '~> 3.0'
39
39
  dependency 'yaml', '~> 0.4'
40
40
  dependency 'pathname', '~> 0.4'
41
+ dependency 'ruby-json-toon', '~> 1.0'
41
42
  development_dependency 'all_images', '~> 0.6'
42
43
  development_dependency 'rspec', '~> 3.2'
43
44
  development_dependency 'debug'
data/bin/context_spook CHANGED
@@ -26,6 +26,7 @@ def usage
26
26
  -S suppress output (just show informational output)
27
27
  -i PATTERN Include files matching PATTERN (can be used multiple times)
28
28
  Supports glob patterns like **, *, ?, [abc], {a,b,c}
29
+ -F FORMAT Output context as format JSON (the default) or TOON
29
30
  -h Show this help message
30
31
 
31
32
  Examples:
@@ -54,13 +55,13 @@ def usage
54
55
  exit 0
55
56
  end
56
57
 
57
- opts = go 'o:i:pvSh', defaults: { ?v => true }
58
+ opts = go 'o:i:F:pvSh', defaults: { ?v => true }
58
59
  opts[?h] and usage
59
60
  context = nil
60
61
  output = nil
61
62
 
62
63
  if opts[?i]
63
- context = ContextSpook.generate_context(verbose: opts[?v]) do
64
+ context = ContextSpook.generate_context(verbose: opts[?v], format: opts[?F]) do
64
65
  context do
65
66
  opts[?i].to_a.each do |glob|
66
67
  glob = File.expand_path(glob)
@@ -73,9 +74,10 @@ if opts[?i]
73
74
  end
74
75
  else
75
76
  filename = ARGV.shift or fail 'require context definition file as an argument'
76
- context = ContextSpook.generate_context(filename, verbose: opts[?v])
77
+ context = ContextSpook.generate_context(filename, verbose: opts[?v], format: opts[?F])
77
78
  end
78
79
 
80
+ output = STDOUT
79
81
  if opts[?S]
80
82
  output = NULL
81
83
  else
@@ -84,8 +86,11 @@ else
84
86
  fail "Filename #{output_filename.inspect} already exists!"
85
87
  end
86
88
  output = File.new output_filename, ?w
87
- else
88
- output = STDOUT
89
89
  end
90
90
  end
91
- JSON.dump(context.as_json, output)
91
+ case opts[?F]
92
+ when /\A(JSON|TOON)\z/i, nil
93
+ context.generator.output_context(output:)
94
+ else
95
+ STDERR.puts "Invalid output format #{opts[?F].inspect}"
96
+ end
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: context_spook 1.1.0 ruby lib
2
+ # stub: context_spook 1.3.0 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "context_spook".freeze
6
- s.version = "1.1.0".freeze
6
+ s.version = "1.3.0".freeze
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
@@ -12,19 +12,19 @@ Gem::Specification.new do |s|
12
12
  s.description = "context_spook is a library that collects and organizes project\ninformation to help AI assistants understand codebases better.\n".freeze
13
13
  s.email = "flori@ping.de".freeze
14
14
  s.executables = ["context_spook".freeze]
15
- s.extra_rdoc_files = ["README.md".freeze, "lib/context_spook.rb".freeze, "lib/context_spook/generator.rb".freeze, "lib/context_spook/version.rb".freeze]
16
- s.files = [".contexts/project.rb".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/context_spook".freeze, "context_spook.gemspec".freeze, "hello_world.json".freeze, "hey_world.yaml".freeze, "lib/context_spook.rb".freeze, "lib/context_spook/generator.rb".freeze, "lib/context_spook/version.rb".freeze, "spec/context_spook/generator_spec.rb".freeze, "spec/spec_helper.rb".freeze]
15
+ s.extra_rdoc_files = ["README.md".freeze, "lib/context_spook.rb".freeze, "lib/context_spook/generator.rb".freeze, "lib/context_spook/output_context.rb".freeze, "lib/context_spook/toon.rb".freeze, "lib/context_spook/utils.rb".freeze, "lib/context_spook/verbose_puts.rb".freeze, "lib/context_spook/version.rb".freeze]
16
+ s.files = [".contexts/project.rb".freeze, "Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/context_spook".freeze, "context_spook.gemspec".freeze, "hello_world.json".freeze, "hey_world.yaml".freeze, "lib/context_spook.rb".freeze, "lib/context_spook/generator.rb".freeze, "lib/context_spook/output_context.rb".freeze, "lib/context_spook/toon.rb".freeze, "lib/context_spook/utils.rb".freeze, "lib/context_spook/verbose_puts.rb".freeze, "lib/context_spook/version.rb".freeze, "spec/context_spook/generator_spec.rb".freeze, "spec/context_spook/toon_spec.rb".freeze, "spec/spec_helper.rb".freeze]
17
17
  s.homepage = "https://github.com/flori/context_spook".freeze
18
18
  s.licenses = ["MIT".freeze]
19
19
  s.rdoc_options = ["--title".freeze, "ContextSpook - context_spook collects project context for AI".freeze, "--main".freeze, "README.md".freeze]
20
20
  s.required_ruby_version = Gem::Requirement.new(">= 3.1".freeze)
21
21
  s.rubygems_version = "4.0.3".freeze
22
22
  s.summary = "context_spook collects project context for AI".freeze
23
- s.test_files = ["spec/context_spook/generator_spec.rb".freeze, "spec/spec_helper.rb".freeze]
23
+ s.test_files = ["spec/context_spook/generator_spec.rb".freeze, "spec/context_spook/toon_spec.rb".freeze, "spec/spec_helper.rb".freeze]
24
24
 
25
25
  s.specification_version = 4
26
26
 
27
- s.add_development_dependency(%q<gem_hadar>.freeze, [">= 2.16.2".freeze])
27
+ s.add_development_dependency(%q<gem_hadar>.freeze, [">= 2.17.0".freeze])
28
28
  s.add_development_dependency(%q<all_images>.freeze, ["~> 0.6".freeze])
29
29
  s.add_development_dependency(%q<rspec>.freeze, ["~> 3.2".freeze])
30
30
  s.add_development_dependency(%q<debug>.freeze, [">= 0".freeze])
@@ -36,4 +36,5 @@ Gem::Specification.new do |s|
36
36
  s.add_runtime_dependency(%q<mime-types>.freeze, ["~> 3.0".freeze])
37
37
  s.add_runtime_dependency(%q<yaml>.freeze, ["~> 0.4".freeze])
38
38
  s.add_runtime_dependency(%q<pathname>.freeze, ["~> 0.4".freeze])
39
+ s.add_runtime_dependency(%q<ruby-json-toon>.freeze, ["~> 1.0".freeze])
39
40
  end
@@ -4,36 +4,15 @@ require 'json'
4
4
  require 'mize'
5
5
  require 'mime-types'
6
6
  require 'yaml'
7
+ require 'context_spook/toon'
8
+ require 'context_spook/verbose_puts'
9
+ require 'context_spook/output_context'
7
10
 
8
11
  # The ContextSpook module serves as a namespace container for collecting and
9
12
  # organizing project information for AI assistance.
10
13
  module ContextSpook
11
14
  include DSLKit::Interpreter
12
15
 
13
- # The VerbosePuts module provides a conditional output mechanism for
14
- # displaying status or debug messages.
15
- #
16
- # This module includes a method that outputs messages to standard error only
17
- # when a verbose flag is enabled. It is designed to be included in classes
18
- # that need to conditionally emit verbose logging information during
19
- # processing.
20
- module VerbosePuts
21
- # The verbose_puts method outputs the given arguments to standard error
22
- # only if verbose mode is enabled.
23
- #
24
- # This method serves as a conditional output mechanism, allowing debug or
25
- # status messages to be displayed based on the verbosity setting of the
26
- # object.
27
- #
28
- # @param a [ Array ] the arguments to be printed to standard error
29
- #
30
- # @return [ nil ] always returns nil after attempting to output
31
- def verbose_puts(*a)
32
- @verbose or return
33
- STDERR.puts(a)
34
- end
35
- end
36
-
37
16
  # The generate_context method processes a context definition file or block
38
17
  # and returns the resulting context object.
39
18
  #
@@ -56,14 +35,14 @@ module ContextSpook
56
35
  #
57
36
  # @raise [ ArgumentError ] if neither a filename nor a block is provided
58
37
  # @raise [ ArgumentError ] if both a filename and a block are provided
59
- def self.generate_context(filename = nil, verbose: false, &block)
38
+ def self.generate_context(filename = nil, verbose: false, format: nil, &block)
60
39
  verbose = !!verbose
61
40
  filename.present? ^ block or
62
41
  raise ArgumentError, 'need either a filename or a &block argument'
63
42
  generator = if filename
64
- Generator.send(:new, verbose:).send(:parse, File.read(filename))
43
+ Generator.send(:new, verbose:, format:).send(:parse, File.read(filename))
65
44
  else
66
- Generator.send(:new, verbose:, &block)
45
+ Generator.send(:new, verbose:, format:, &block)
67
46
  end
68
47
  generator.output_context_size
69
48
  generator.context
@@ -75,6 +54,9 @@ module ContextSpook
75
54
  # assistance.
76
55
  class Generator
77
56
  include VerbosePuts
57
+ include OutputContext
58
+
59
+ attr_reader :verbose
78
60
 
79
61
  private_class_method :new
80
62
 
@@ -85,8 +67,9 @@ module ContextSpook
85
67
  # output during processing, defaults to lfalse.
86
68
  # @param block [ Proc ] a block of code to be evaluated within the object's context
87
69
  # If no block is given, the method does nothing.
88
- def initialize(verbose: false, &block)
70
+ def initialize(verbose: false, format: nil, &block)
89
71
  @verbose = !!verbose
72
+ @format = (format || 'JSON').upcase
90
73
  block and instance_eval(&block)
91
74
  end
92
75
 
@@ -98,26 +81,12 @@ module ContextSpook
98
81
  def context(&block)
99
82
  if block
100
83
  @context and raise ArgumentError, "only one context allowed"
101
- @context = Context.new(verbose: @verbose, &block)
84
+ @context = Context.new(generator: self, &block)
102
85
  else
103
86
  @context
104
87
  end
105
88
  end
106
89
 
107
- # The output_context_size method prints the total size of the generated
108
- # context JSON representation.
109
- #
110
- # This method calculates the size of the context object when serialized to
111
- # JSON, formats it using binary units (KiB, MiB, etc.), and outputs the
112
- # result to standard error.
113
- def output_context_size
114
- context_size = @context&.size.to_i
115
- json_content_size = Tins::Unit.format(
116
- context_size, format: '%.2f %U', unit: ?b, prefix: 1024
117
- )
118
- verbose_puts "Built #{json_content_size} of JSON context in total."
119
- end
120
-
121
90
  # The Context class represents and manages project context data, providing
122
91
  # structured storage for file contents, command outputs, variables, and
123
92
  # metadata that can be serialized to JSON for AI assistance.
@@ -126,6 +95,7 @@ module ContextSpook
126
95
  include Tins::Scope
127
96
  include Tins::DSLAccessor
128
97
  include Term::ANSIColor
98
+ include ContextSpook::TOON
129
99
 
130
100
  # The initialize method sets up the object by evaluating the provided block
131
101
  # in the object's context.
@@ -134,11 +104,15 @@ module ContextSpook
134
104
  # during processing, defaults to false.
135
105
  # @param block [ Proc ] a block of code to be evaluated within the object's context
136
106
  # If no block is given, the method does nothing.
137
- def initialize(verbose: false, &block)
138
- @verbose = !!verbose
107
+ def initialize(generator:, &block)
108
+ @generator = generator
139
109
  block and instance_eval(&block)
140
110
  end
141
111
 
112
+ attr_reader :generator
113
+
114
+ delegate :verbose, to: :generator
115
+
142
116
  # The namespace method creates a scoped block with a given name.
143
117
  #
144
118
  # @param name [ Object ] the name to scope the block with
@@ -0,0 +1,36 @@
1
+ # The ContextSpook::OutputContext module provides methods for outputting
2
+ # context data in various formats.
3
+ #
4
+ # This module is included in the ContextSpook::Generator class to enable output
5
+ # functionality for generated context objects.
6
+ module ContextSpook::OutputContext
7
+ # The output_context_size method prints the total size of the generated
8
+ # context JSON representation.
9
+ #
10
+ # This method calculates the size of the context object when serialized to
11
+ # JSON, formats it using binary units (KiB, MiB, etc.), and outputs the
12
+ # result to standard error.
13
+ def output_context_size
14
+ context_size =
15
+ (@format == 'TOON' ? @context&.toon_size : @context&.size).to_i
16
+ context_size = ContextSpook::Utils.format_size(context_size)
17
+ verbose_puts "Built #{context_size} of #@format context in total."
18
+ end
19
+
20
+ # The output_context method writes the generated context to the specified
21
+ # output stream in either TOON or JSON format.
22
+ #
23
+ # This method serializes the context object into either TOON format or JSON
24
+ # format depending on the configured format, and writes the result to the
25
+ # provided output stream, which defaults to STDOUT.
26
+ #
27
+ # @param output [ IO ] the output stream to write the context data to,
28
+ # defaults to STDOUT
29
+ def output_context(output: STDOUT)
30
+ if @format == 'TOON'
31
+ output.puts(@context.to_toon)
32
+ else
33
+ JSON.dump(@context.as_json, output)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,20 @@
1
+ require 'ruby_json_toon'
2
+
3
+ # The ContextSpook::TOON module provides TOON (Token-Oriented Object Notation)
4
+ # serialization functionality for context objects.
5
+ module ContextSpook::TOON
6
+ # Converts the context object to TOON format.
7
+ #
8
+ # @return [String] the TOON-encoded representation of the context
9
+ def to_toon
10
+ RubyJsonToon.encode(as_json)
11
+ end
12
+ memoize method: :to_toon
13
+
14
+ # Calculates the size of the TOON representation.
15
+ #
16
+ # @return [Integer] the size in bytes of the TOON representation
17
+ def toon_size
18
+ to_toon.size
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ # The ContextSpook::Utils module provides utility methods for formatting and
2
+ # processing context data.
3
+ module ContextSpook::Utils
4
+ module_function
5
+
6
+ # The format_size method converts a byte size value into a human-readable
7
+ # string with binary units.
8
+ #
9
+ # This method takes a raw byte count and formats it using the Tins::Unit
10
+ # library to display the size with appropriate binary prefixes (KiB, MiB,
11
+ # etc.) and two decimal places.
12
+ #
13
+ # @param context_size [ Integer ] the size in bytes to be formatted
14
+ #
15
+ # @return [ String ] the formatted size string with binary units
16
+ def format_size(context_size)
17
+ Tins::Unit.format(context_size, format: '%.2f %U', unit: ?b, prefix: 1024)
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ module ContextSpook
2
+ # The VerbosePuts module provides a conditional output mechanism for
3
+ # displaying status or debug messages.
4
+ #
5
+ # This module includes a method that outputs messages to standard error only
6
+ # when a verbose flag is enabled. It is designed to be included in classes
7
+ # that need to conditionally emit verbose logging information during
8
+ # processing.
9
+ module VerbosePuts
10
+ # The verbose_puts method outputs the given arguments to standard error
11
+ # only if verbose mode is enabled.
12
+ #
13
+ # This method serves as a conditional output mechanism, allowing debug or
14
+ # status messages to be displayed based on the verbosity setting of the
15
+ # object.
16
+ #
17
+ # @param a [ Array ] the arguments to be printed to standard error
18
+ #
19
+ # @return [ nil ] always returns nil after attempting to output
20
+ def verbose_puts(*a)
21
+ verbose or return
22
+ STDERR.puts(a)
23
+ end
24
+ end
25
+ end
@@ -1,6 +1,6 @@
1
1
  module ContextSpook
2
2
  # ContextSpook version
3
- VERSION = '1.1.0'
3
+ VERSION = '1.3.0'
4
4
  VERSION_ARRAY = VERSION.split('.').map(&:to_i) # :nodoc:
5
5
  VERSION_MAJOR = VERSION_ARRAY[0] # :nodoc:
6
6
  VERSION_MINOR = VERSION_ARRAY[1] # :nodoc:
data/lib/context_spook.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module ContextSpook
2
2
  end
3
3
  require 'context_spook/version'
4
+ require 'context_spook/utils'
4
5
  require 'context_spook/generator'
@@ -1,56 +1,92 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe ContextSpook::Generator do
4
- let :context do
5
- ContextSpook.generate_context('.contexts/project.rb', verbose: true)
4
+ let :my_context do
5
+ ContextSpook.generate_context('.contexts/project.rb', verbose: false)
6
6
  end
7
7
 
8
- it 'context can be generated from block' do
9
- expect_any_instance_of(described_class).to\
10
- receive(:output_context_size).and_call_original
11
- context = ContextSpook.generate_context do
12
- context do
13
- variable foo: 'bar'
14
- metadata version: '1.0'
8
+ describe 'generating w/o verbose' do
9
+ it 'context can be generated from block' do
10
+ expect_any_instance_of(described_class).to\
11
+ receive(:output_context_size).and_call_original
12
+ my_context = ContextSpook.generate_context do
13
+ context do
14
+ variable foo: 'bar'
15
+ metadata version: '1.0'
16
+ end
15
17
  end
18
+ expect(my_context).to be_a described_class::Context
19
+ expect(my_context.variables[:foo]).to eq 'bar'
20
+ expect(my_context.metadata[:version]).to eq '1.0'
16
21
  end
17
- expect(context).to be_a described_class::Context
18
- expect(context.variables[:foo]).to eq 'bar'
19
- expect(context.metadata[:version]).to eq '1.0'
20
- end
21
22
 
22
- it 'context can be generated from filename' do
23
- expect_any_instance_of(described_class).to\
24
- receive(:output_context_size).and_call_original
25
- expect(context).to be_a described_class::Context
26
- expect(context.metadata[:ruby]).to eq RUBY_DESCRIPTION
27
- end
23
+ it 'context can be generated from filename' do
24
+ expect_any_instance_of(described_class).to\
25
+ receive(:output_context_size).and_call_original
26
+ expect(my_context).to be_a described_class::Context
27
+ expect(my_context.metadata[:ruby]).to eq RUBY_DESCRIPTION
28
+ end
28
29
 
29
- it 'could handle premature output_context_size calls' do
30
- expect_any_instance_of(described_class).to\
31
- receive(:output_context_size).and_call_original
32
- described_class.send(:new).output_context_size
33
- end
30
+ it 'could handle premature output_context_size calls' do
31
+ expect_any_instance_of(described_class).to\
32
+ receive(:output_context_size).and_call_original
33
+ described_class.send(:new).output_context_size
34
+ end
35
+
36
+ it 'cannot do from block and filename' do
37
+ expect {
38
+ ContextSpook.generate_context('.contexts/project.rb') { }
39
+ }.to raise_error(ArgumentError, /need either a filename or a &block/)
40
+ end
34
41
 
35
- it 'cannot do from block and filename' do
36
- expect {
37
- ContextSpook.generate_context('.contexts/project.rb') { }
38
- }.to raise_error(ArgumentError, /need either a filename or a &block/)
42
+ it 'context be transformed to JSON if loaded' do
43
+ context_as_json = my_context.to_json
44
+ expect(my_context.size).to be > 1024
45
+ expect(JSON(context_as_json)).to be_a Hash
46
+ end
47
+
48
+ it 'can output context in JSON' do
49
+ output = StringIO.new
50
+ expect_any_instance_of(described_class).to receive(:output_context).
51
+ with(output:).and_call_original
52
+ my_context.generator.output_context(output:)
53
+ expect(output.string).to include '"ruby":%s' % RUBY_DESCRIPTION.dump
54
+ end
55
+
56
+ it 'can output context in TOON' do
57
+ my_context = ContextSpook.generate_context(
58
+ '.contexts/project.rb', verbose: false, format: 'TOON'
59
+ )
60
+ output = StringIO.new
61
+ expect_any_instance_of(described_class).to receive(:output_context).
62
+ with(output:).and_call_original
63
+ my_context.generator.output_context(output:)
64
+ expect(output.string).to include 'ruby: %s' % RUBY_DESCRIPTION.dump
65
+ end
39
66
  end
40
67
 
41
- it 'context be transformed to JSON if loaded' do
42
- context_as_json = context.to_json
43
- expect(context.size).to be > 1024
44
- expect(JSON(context_as_json)).to be_a Hash
68
+ describe 'generating with verbose' do
69
+ let :my_context do
70
+ ContextSpook.generate_context('.contexts/project.rb', verbose: true)
71
+ end
72
+
73
+ it 'can output context size' do
74
+ expect_any_instance_of(described_class).to receive(:output_context_size).
75
+ and_call_original
76
+ expect_any_instance_of(described_class).to receive(:verbose_puts).
77
+ with(/Built.*of JSON context in total/)
78
+ allow_any_instance_of(described_class::Context).to receive(:verbose_puts)
79
+ my_context
80
+ end
45
81
  end
46
82
 
47
83
  describe 'Context' do
48
84
  it 'can have variables' do
49
- expect(context.variables[:branch]).to be_present
85
+ expect(my_context.variables[:branch]).to be_present
50
86
  end
51
87
 
52
88
  it 'can have files' do
53
- file = context.files['lib/context_spook.rb']
89
+ file = my_context.files['lib/context_spook.rb']
54
90
  expect(file).to be_present
55
91
  expect(file[:content]).to be_present
56
92
  expect(file[:content_types]).to be_present
@@ -61,7 +97,7 @@ describe ContextSpook::Generator do
61
97
  end
62
98
 
63
99
  it 'can have commands' do
64
- command = context.commands['tree']
100
+ command = my_context.commands['tree']
65
101
  expect(command).to be_present
66
102
  expect(command[:working_directory]).to eq Dir.pwd
67
103
  expect(command[:exit_code]).not_to be_nil
@@ -74,38 +110,38 @@ describe ContextSpook::Generator do
74
110
  end
75
111
 
76
112
  it 'can have metada' do
77
- expect(context.metadata[:ruby]).to eq RUBY_DESCRIPTION
113
+ expect(my_context.metadata[:ruby]).to eq RUBY_DESCRIPTION
78
114
  end
79
115
 
80
116
  it 'can have json metadata' do
81
- expect(context.metadata[:hello_world]).to eq("hello" => "world")
117
+ expect(my_context.metadata[:hello_world]).to eq("hello" => "world")
82
118
  end
83
119
 
84
120
  it 'can have yaml metadata' do
85
- expect(context.metadata[:hey_world]).to eq("hey" => "world")
121
+ expect(my_context.metadata[:hey_world]).to eq("hey" => "world")
86
122
  end
87
123
 
88
124
  it 'handles missing json files gracefully' do
89
- expect(context.json('nixda.json')).to be_nil
125
+ expect(my_context.json('nixda.json')).to be_nil
90
126
  end
91
127
 
92
128
  it 'handles invalid json content gracefully' do
93
129
  Tempfile.create('invalid.json') do |f|
94
130
  f.write('{ invalid json }')
95
131
  f.close
96
- expect(context.json(f.path)).to be_nil
132
+ expect(my_context.json(f.path)).to be_nil
97
133
  end
98
134
  end
99
135
 
100
136
  it 'handles missing yaml files gracefully' do
101
- expect(context.yaml('nixda.yaml')).to be_nil
137
+ expect(my_context.yaml('nixda.yaml')).to be_nil
102
138
  end
103
139
 
104
140
  it 'handles invalid yaml content gracefully' do
105
141
  Tempfile.create('invalid.yaml') do |f|
106
142
  f.write('invalid: [yaml')
107
143
  f.close
108
- expect(context.yaml(f.path)).to be_nil
144
+ expect(my_context.yaml(f.path)).to be_nil
109
145
  end
110
146
  end
111
147
  end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe ContextSpook::TOON do
4
+ let(:context) do
5
+ ContextSpook.generate_context do
6
+ context do
7
+ variable foo: 'bar'
8
+ metadata version: '1.0'
9
+ file 'lib/context_spook.rb'
10
+ end
11
+ end
12
+ end
13
+
14
+ describe '#to_toon' do
15
+ it 'converts context to TOON format' do
16
+ toon_output = context.to_toon
17
+ expect(toon_output).to be_a(String)
18
+ expect(toon_output).to_not be_empty
19
+ end
20
+
21
+ it 'produces valid TOON output' do
22
+ toon_output = context.to_toon
23
+ # TOON should be a valid string representation
24
+ expect { RubyJsonToon.decode(toon_output) }.not_to raise_error
25
+ end
26
+ end
27
+
28
+ describe '#toon_size' do
29
+ it 'returns the size of TOON representation in bytes' do
30
+ size = context.toon_size
31
+ expect(size).to be_a(Integer)
32
+ expect(size).to be > 0
33
+ end
34
+
35
+ it 'matches the size of the TOON output' do
36
+ size = context.toon_size
37
+ toon_output = context.to_toon
38
+ expect(size).to eq(toon_output.size)
39
+ end
40
+ end
41
+
42
+ describe 'integration with ContextSpook::Generator::Context' do
43
+ it 'is included in Context class' do
44
+ expect(context).to respond_to(:to_toon)
45
+ expect(context).to respond_to(:toon_size)
46
+ end
47
+
48
+ it 'works with context data' do
49
+ toon_output = context.to_toon
50
+ expect(toon_output).to include('variables:')
51
+ expect(toon_output).to include('metadata:')
52
+ expect(toon_output).to include('files:')
53
+ expect(toon_output).to include('commands:')
54
+ end
55
+ end
56
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: context_spook
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Frank
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 2.16.2
18
+ version: 2.17.0
19
19
  type: :development
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
- version: 2.16.2
25
+ version: 2.17.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: all_images
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -177,6 +177,20 @@ dependencies:
177
177
  - - "~>"
178
178
  - !ruby/object:Gem::Version
179
179
  version: '0.4'
180
+ - !ruby/object:Gem::Dependency
181
+ name: ruby-json-toon
182
+ requirement: !ruby/object:Gem::Requirement
183
+ requirements:
184
+ - - "~>"
185
+ - !ruby/object:Gem::Version
186
+ version: '1.0'
187
+ type: :runtime
188
+ prerelease: false
189
+ version_requirements: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '1.0'
180
194
  description: |
181
195
  context_spook is a library that collects and organizes project
182
196
  information to help AI assistants understand codebases better.
@@ -188,6 +202,10 @@ extra_rdoc_files:
188
202
  - README.md
189
203
  - lib/context_spook.rb
190
204
  - lib/context_spook/generator.rb
205
+ - lib/context_spook/output_context.rb
206
+ - lib/context_spook/toon.rb
207
+ - lib/context_spook/utils.rb
208
+ - lib/context_spook/verbose_puts.rb
191
209
  - lib/context_spook/version.rb
192
210
  files:
193
211
  - ".contexts/project.rb"
@@ -201,8 +219,13 @@ files:
201
219
  - hey_world.yaml
202
220
  - lib/context_spook.rb
203
221
  - lib/context_spook/generator.rb
222
+ - lib/context_spook/output_context.rb
223
+ - lib/context_spook/toon.rb
224
+ - lib/context_spook/utils.rb
225
+ - lib/context_spook/verbose_puts.rb
204
226
  - lib/context_spook/version.rb
205
227
  - spec/context_spook/generator_spec.rb
228
+ - spec/context_spook/toon_spec.rb
206
229
  - spec/spec_helper.rb
207
230
  homepage: https://github.com/flori/context_spook
208
231
  licenses:
@@ -231,4 +254,5 @@ specification_version: 4
231
254
  summary: context_spook collects project context for AI
232
255
  test_files:
233
256
  - spec/context_spook/generator_spec.rb
257
+ - spec/context_spook/toon_spec.rb
234
258
  - spec/spec_helper.rb