context_spook 1.2.0 → 1.4.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 +4 -4
- data/bin/context_spook +5 -11
- data/context_spook.gemspec +6 -6
- data/lib/context_spook/generator.rb +36 -43
- data/lib/context_spook/output_context.rb +36 -0
- data/lib/context_spook/toon/example.md +69 -0
- data/lib/context_spook/toon.rb +21 -0
- data/lib/context_spook/utils.rb +2 -0
- data/lib/context_spook/verbose_puts.rb +25 -0
- data/lib/context_spook/version.rb +1 -1
- data/spec/context_spook/generator_spec.rb +78 -42
- data/spec/context_spook/toon_spec.rb +56 -0
- metadata +10 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 48da6b99283d3e0b02730a5ef6c02d07726dca15c7d29d6e40b73cc5d1f11e37
|
|
4
|
+
data.tar.gz: 3c8f65d9f003f1c7053157beab50d95ff692852bca4c5efef83241c01ff2740c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3f0c9d83fc057968a95f74dff08f035908ea9e362e608007046919acc7e9e5d0e2d6cc7a4e9526d24ba8d50cc5ca8c450b8537c4b4d4dead1910b429806a5978
|
|
7
|
+
data.tar.gz: a6bea380021e97a6095c66418628e67cfa6afdcc1f85585a71d622d17355e5640f8cb64403c8eb196183753f260be40a20fc1c32dd7d39db1d24cae0f6518cbc
|
data/bin/context_spook
CHANGED
|
@@ -61,7 +61,7 @@ context = nil
|
|
|
61
61
|
output = nil
|
|
62
62
|
|
|
63
63
|
if opts[?i]
|
|
64
|
-
context = ContextSpook.generate_context(verbose: opts[?v]) do
|
|
64
|
+
context = ContextSpook.generate_context(verbose: opts[?v], format: opts[?F]) do
|
|
65
65
|
context do
|
|
66
66
|
opts[?i].to_a.each do |glob|
|
|
67
67
|
glob = File.expand_path(glob)
|
|
@@ -74,9 +74,10 @@ if opts[?i]
|
|
|
74
74
|
end
|
|
75
75
|
else
|
|
76
76
|
filename = ARGV.shift or fail 'require context definition file as an argument'
|
|
77
|
-
context = ContextSpook.generate_context(filename, verbose: opts[?v])
|
|
77
|
+
context = ContextSpook.generate_context(filename, verbose: opts[?v], format: opts[?F])
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
output = STDOUT
|
|
80
81
|
if opts[?S]
|
|
81
82
|
output = NULL
|
|
82
83
|
else
|
|
@@ -85,18 +86,11 @@ else
|
|
|
85
86
|
fail "Filename #{output_filename.inspect} already exists!"
|
|
86
87
|
end
|
|
87
88
|
output = File.new output_filename, ?w
|
|
88
|
-
else
|
|
89
|
-
output = STDOUT
|
|
90
89
|
end
|
|
91
90
|
end
|
|
92
91
|
case opts[?F]
|
|
93
|
-
when /\
|
|
94
|
-
|
|
95
|
-
STDERR.puts "Built #{ContextSpook::Utils.format_size(context.toon_size)} of TOON context in total."
|
|
96
|
-
end
|
|
97
|
-
output.puts context.to_toon
|
|
98
|
-
when /\AJSON\z/i, nil
|
|
99
|
-
JSON.dump(context.as_json, output)
|
|
92
|
+
when /\A(JSON|TOON)\z/i, nil
|
|
93
|
+
context.generator.output_context(output:)
|
|
100
94
|
else
|
|
101
95
|
STDERR.puts "Invalid output format #{opts[?F].inspect}"
|
|
102
96
|
end
|
data/context_spook.gemspec
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: context_spook 1.
|
|
2
|
+
# stub: context_spook 1.4.0 ruby lib
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
s.name = "context_spook".freeze
|
|
6
|
-
s.version = "1.
|
|
6
|
+
s.version = "1.4.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/toon.rb".freeze, "lib/context_spook/utils.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/toon.rb".freeze, "lib/context_spook/utils.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/toon/example.md".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.
|
|
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])
|
|
@@ -5,36 +5,14 @@ require 'mize'
|
|
|
5
5
|
require 'mime-types'
|
|
6
6
|
require 'yaml'
|
|
7
7
|
require 'context_spook/toon'
|
|
8
|
+
require 'context_spook/verbose_puts'
|
|
9
|
+
require 'context_spook/output_context'
|
|
8
10
|
|
|
9
11
|
# The ContextSpook module serves as a namespace container for collecting and
|
|
10
12
|
# organizing project information for AI assistance.
|
|
11
13
|
module ContextSpook
|
|
12
14
|
include DSLKit::Interpreter
|
|
13
15
|
|
|
14
|
-
# The VerbosePuts module provides a conditional output mechanism for
|
|
15
|
-
# displaying status or debug messages.
|
|
16
|
-
#
|
|
17
|
-
# This module includes a method that outputs messages to standard error only
|
|
18
|
-
# when a verbose flag is enabled. It is designed to be included in classes
|
|
19
|
-
# that need to conditionally emit verbose logging information during
|
|
20
|
-
# processing.
|
|
21
|
-
module VerbosePuts
|
|
22
|
-
# The verbose_puts method outputs the given arguments to standard error
|
|
23
|
-
# only if verbose mode is enabled.
|
|
24
|
-
#
|
|
25
|
-
# This method serves as a conditional output mechanism, allowing debug or
|
|
26
|
-
# status messages to be displayed based on the verbosity setting of the
|
|
27
|
-
# object.
|
|
28
|
-
#
|
|
29
|
-
# @param a [ Array ] the arguments to be printed to standard error
|
|
30
|
-
#
|
|
31
|
-
# @return [ nil ] always returns nil after attempting to output
|
|
32
|
-
def verbose_puts(*a)
|
|
33
|
-
@verbose or return
|
|
34
|
-
STDERR.puts(a)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
16
|
# The generate_context method processes a context definition file or block
|
|
39
17
|
# and returns the resulting context object.
|
|
40
18
|
#
|
|
@@ -57,14 +35,14 @@ module ContextSpook
|
|
|
57
35
|
#
|
|
58
36
|
# @raise [ ArgumentError ] if neither a filename nor a block is provided
|
|
59
37
|
# @raise [ ArgumentError ] if both a filename and a block are provided
|
|
60
|
-
def self.generate_context(filename = nil, verbose: false, &block)
|
|
38
|
+
def self.generate_context(filename = nil, verbose: false, format: nil, &block)
|
|
61
39
|
verbose = !!verbose
|
|
62
40
|
filename.present? ^ block or
|
|
63
41
|
raise ArgumentError, 'need either a filename or a &block argument'
|
|
64
42
|
generator = if filename
|
|
65
|
-
Generator.send(:new, verbose:).send(:parse, File.read(filename))
|
|
43
|
+
Generator.send(:new, verbose:, format:).send(:parse, File.read(filename))
|
|
66
44
|
else
|
|
67
|
-
Generator.send(:new, verbose:, &block)
|
|
45
|
+
Generator.send(:new, verbose:, format:, &block)
|
|
68
46
|
end
|
|
69
47
|
generator.output_context_size
|
|
70
48
|
generator.context
|
|
@@ -76,6 +54,19 @@ module ContextSpook
|
|
|
76
54
|
# assistance.
|
|
77
55
|
class Generator
|
|
78
56
|
include VerbosePuts
|
|
57
|
+
include OutputContext
|
|
58
|
+
|
|
59
|
+
# The verbose method returns the verbose flag indicating whether verbose
|
|
60
|
+
# output is enabled.
|
|
61
|
+
#
|
|
62
|
+
# @return [ TrueClass, FalseClass ] true if verbose output is enabled,
|
|
63
|
+
# false otherwise
|
|
64
|
+
attr_reader :verbose
|
|
65
|
+
|
|
66
|
+
# The format method returns the format identifier for the context output.
|
|
67
|
+
#
|
|
68
|
+
# @return [ String ] the format identifier, either 'JSON' or 'TOON'
|
|
69
|
+
attr_reader :format
|
|
79
70
|
|
|
80
71
|
private_class_method :new
|
|
81
72
|
|
|
@@ -86,8 +77,9 @@ module ContextSpook
|
|
|
86
77
|
# output during processing, defaults to lfalse.
|
|
87
78
|
# @param block [ Proc ] a block of code to be evaluated within the object's context
|
|
88
79
|
# If no block is given, the method does nothing.
|
|
89
|
-
def initialize(verbose: false, &block)
|
|
80
|
+
def initialize(verbose: false, format: nil, &block)
|
|
90
81
|
@verbose = !!verbose
|
|
82
|
+
@format = (format || 'JSON').upcase
|
|
91
83
|
block and instance_eval(&block)
|
|
92
84
|
end
|
|
93
85
|
|
|
@@ -99,24 +91,12 @@ module ContextSpook
|
|
|
99
91
|
def context(&block)
|
|
100
92
|
if block
|
|
101
93
|
@context and raise ArgumentError, "only one context allowed"
|
|
102
|
-
@context = Context.new(
|
|
94
|
+
@context = Context.new(generator: self, &block)
|
|
103
95
|
else
|
|
104
96
|
@context
|
|
105
97
|
end
|
|
106
98
|
end
|
|
107
99
|
|
|
108
|
-
# The output_context_size method prints the total size of the generated
|
|
109
|
-
# context JSON representation.
|
|
110
|
-
#
|
|
111
|
-
# This method calculates the size of the context object when serialized to
|
|
112
|
-
# JSON, formats it using binary units (KiB, MiB, etc.), and outputs the
|
|
113
|
-
# result to standard error.
|
|
114
|
-
def output_context_size
|
|
115
|
-
context_size = @context&.size.to_i
|
|
116
|
-
json_context_size = ContextSpook::Utils.format_size(context_size)
|
|
117
|
-
verbose_puts "Built #{json_context_size} of JSON context in total."
|
|
118
|
-
end
|
|
119
|
-
|
|
120
100
|
# The Context class represents and manages project context data, providing
|
|
121
101
|
# structured storage for file contents, command outputs, variables, and
|
|
122
102
|
# metadata that can be serialized to JSON for AI assistance.
|
|
@@ -134,11 +114,24 @@ module ContextSpook
|
|
|
134
114
|
# during processing, defaults to false.
|
|
135
115
|
# @param block [ Proc ] a block of code to be evaluated within the object's context
|
|
136
116
|
# If no block is given, the method does nothing.
|
|
137
|
-
def initialize(
|
|
138
|
-
@
|
|
117
|
+
def initialize(generator:, &block)
|
|
118
|
+
@generator = generator
|
|
139
119
|
block and instance_eval(&block)
|
|
120
|
+
meta(format: generator.format)
|
|
121
|
+
if generator.format == 'TOON'
|
|
122
|
+
meta(toon_example: toon_example)
|
|
123
|
+
end
|
|
140
124
|
end
|
|
141
125
|
|
|
126
|
+
# The generator method returns the generator object associated with this
|
|
127
|
+
# context.
|
|
128
|
+
#
|
|
129
|
+
# @return [ ContextSpook::Generator ] the generator instance that created
|
|
130
|
+
# this context
|
|
131
|
+
attr_reader :generator
|
|
132
|
+
|
|
133
|
+
delegate :verbose, to: :generator
|
|
134
|
+
|
|
142
135
|
# The namespace method creates a scoped block with a given name.
|
|
143
136
|
#
|
|
144
137
|
# @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,69 @@
|
|
|
1
|
+
# TOON (Token-Oriented Object Notation)
|
|
2
|
+
|
|
3
|
+
It is a compact, human-readable serialization format that reduces JSON token
|
|
4
|
+
count by 30-40% while maintaining lossless data fidelity. It uses CSV-style
|
|
5
|
+
tabular arrays for uniform data structures and YAML-like indentation for nested
|
|
6
|
+
objects, making it ideal for LLM input where token efficiency and parsing
|
|
7
|
+
reliability matter.
|
|
8
|
+
|
|
9
|
+
Key benefit: Same data, fewer tokens, better LLM understanding.
|
|
10
|
+
|
|
11
|
+
**Original JSON:**
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"database": {
|
|
15
|
+
"name": "user_management",
|
|
16
|
+
"tables": [
|
|
17
|
+
{
|
|
18
|
+
"name": "users",
|
|
19
|
+
"columns": [
|
|
20
|
+
{
|
|
21
|
+
"name": "id",
|
|
22
|
+
"type": "integer",
|
|
23
|
+
"primary_key": true
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "email",
|
|
27
|
+
"type": "string",
|
|
28
|
+
"unique": true
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
"row_count": 1000
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "orders",
|
|
35
|
+
"columns": [
|
|
36
|
+
{
|
|
37
|
+
"name": "id",
|
|
38
|
+
"type": "integer",
|
|
39
|
+
"primary_key": true
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "user_id",
|
|
43
|
+
"type": "integer"
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"row_count": 5000
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**TOON equivalent:**
|
|
54
|
+
```
|
|
55
|
+
database:
|
|
56
|
+
name: user_management
|
|
57
|
+
tables[2]{name,row_count}:
|
|
58
|
+
users,1000
|
|
59
|
+
orders,5000
|
|
60
|
+
tables[2]{name,columns}:
|
|
61
|
+
users:
|
|
62
|
+
columns[2]{name,type,primary_key}:
|
|
63
|
+
id,integer,true
|
|
64
|
+
email,string,true
|
|
65
|
+
orders:
|
|
66
|
+
columns[2]{name,type}:
|
|
67
|
+
id,integer,true
|
|
68
|
+
user_id,integer,false
|
|
69
|
+
```
|
data/lib/context_spook/toon.rb
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
1
|
require 'ruby_json_toon'
|
|
2
2
|
|
|
3
|
+
# The ContextSpook::TOON module provides TOON (Token-Oriented Object Notation)
|
|
4
|
+
# serialization functionality for context objects.
|
|
3
5
|
module ContextSpook::TOON
|
|
6
|
+
# Converts the context object to TOON format.
|
|
7
|
+
#
|
|
8
|
+
# @return [String] the TOON-encoded representation of the context
|
|
4
9
|
def to_toon
|
|
5
10
|
RubyJsonToon.encode(as_json)
|
|
6
11
|
end
|
|
7
12
|
memoize method: :to_toon
|
|
8
13
|
|
|
14
|
+
# Calculates the size of the TOON representation.
|
|
15
|
+
#
|
|
16
|
+
# @return [Integer] the size in bytes of the TOON representation
|
|
9
17
|
def toon_size
|
|
10
18
|
to_toon.size
|
|
11
19
|
end
|
|
20
|
+
|
|
21
|
+
# The toon_example method reads and returns the content of an example
|
|
22
|
+
# Markdown file that demonstrates the TOON (Token-Oriented Object Notation)
|
|
23
|
+
# format.
|
|
24
|
+
#
|
|
25
|
+
# This method is used to provide a sample representation of how context data
|
|
26
|
+
# can be formatted in TOON, which is an alternative serialization format
|
|
27
|
+
# supported by the ContextSpook library.
|
|
28
|
+
#
|
|
29
|
+
# @return [ String ] the content of the TOON example Markdown file
|
|
30
|
+
def toon_example
|
|
31
|
+
File.read(Pathname.new(__dir__) + 'toon/example.md')
|
|
32
|
+
end
|
|
12
33
|
end
|
data/lib/context_spook/utils.rb
CHANGED
|
@@ -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,56 +1,92 @@
|
|
|
1
1
|
require 'spec_helper'
|
|
2
2
|
|
|
3
3
|
describe ContextSpook::Generator do
|
|
4
|
-
let :
|
|
5
|
-
ContextSpook.generate_context('.contexts/project.rb', verbose:
|
|
4
|
+
let :my_context do
|
|
5
|
+
ContextSpook.generate_context('.contexts/project.rb', verbose: false)
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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(
|
|
85
|
+
expect(my_context.variables[:branch]).to be_present
|
|
50
86
|
end
|
|
51
87
|
|
|
52
88
|
it 'can have files' do
|
|
53
|
-
file =
|
|
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 =
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
4
|
+
version: 1.4.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.
|
|
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.
|
|
25
|
+
version: 2.17.0
|
|
26
26
|
- !ruby/object:Gem::Dependency
|
|
27
27
|
name: all_images
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -202,8 +202,10 @@ extra_rdoc_files:
|
|
|
202
202
|
- README.md
|
|
203
203
|
- lib/context_spook.rb
|
|
204
204
|
- lib/context_spook/generator.rb
|
|
205
|
+
- lib/context_spook/output_context.rb
|
|
205
206
|
- lib/context_spook/toon.rb
|
|
206
207
|
- lib/context_spook/utils.rb
|
|
208
|
+
- lib/context_spook/verbose_puts.rb
|
|
207
209
|
- lib/context_spook/version.rb
|
|
208
210
|
files:
|
|
209
211
|
- ".contexts/project.rb"
|
|
@@ -217,10 +219,14 @@ files:
|
|
|
217
219
|
- hey_world.yaml
|
|
218
220
|
- lib/context_spook.rb
|
|
219
221
|
- lib/context_spook/generator.rb
|
|
222
|
+
- lib/context_spook/output_context.rb
|
|
220
223
|
- lib/context_spook/toon.rb
|
|
224
|
+
- lib/context_spook/toon/example.md
|
|
221
225
|
- lib/context_spook/utils.rb
|
|
226
|
+
- lib/context_spook/verbose_puts.rb
|
|
222
227
|
- lib/context_spook/version.rb
|
|
223
228
|
- spec/context_spook/generator_spec.rb
|
|
229
|
+
- spec/context_spook/toon_spec.rb
|
|
224
230
|
- spec/spec_helper.rb
|
|
225
231
|
homepage: https://github.com/flori/context_spook
|
|
226
232
|
licenses:
|
|
@@ -249,4 +255,5 @@ specification_version: 4
|
|
|
249
255
|
summary: context_spook collects project context for AI
|
|
250
256
|
test_files:
|
|
251
257
|
- spec/context_spook/generator_spec.rb
|
|
258
|
+
- spec/context_spook/toon_spec.rb
|
|
252
259
|
- spec/spec_helper.rb
|