context_spook 0.0.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 +7 -0
- data/Gemfile +5 -0
- data/LICENSE +19 -0
- data/README.md +155 -0
- data/Rakefile +40 -0
- data/bin/context_spook +8 -0
- data/context_spook.gemspec +35 -0
- data/contexts/project.rb +37 -0
- data/lib/context_spook/generator.rb +230 -0
- data/lib/context_spook/version.rb +8 -0
- data/lib/context_spook.rb +4 -0
- data/spec/context_spook/generator_spec.rb +52 -0
- data/spec/spec_helper.rb +19 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ea37f8f7f6d8653e96ccf73795be9702b4f10401c0a5ff13ae78d1ae89adabdd
|
4
|
+
data.tar.gz: 14a65612ee0e4598bd5797d9d542fee108f143452520752b4047281724bb92fb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8d4ca846a041fd61065d31e533cce3c56b06aaaa9eb438b9f462df8e58397d61588b1b8f734f36a9dec182444839763c8248fa375ed1df393c71fc679d613781
|
7
|
+
data.tar.gz: f9b13a0d717542832c44db2fefe328188780c9f465424631594da657478db15747df7f7a4ccd7335bad90c5d6611a77c87cbcae2264188e478997ba77eede026
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright Florian Frank
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the “Software”), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# ContextSpook
|
2
|
+
|
3
|
+
ContextSpook is a Ruby library that collects and organizes project information
|
4
|
+
to help AI assistants understand codebases better. It provides a
|
5
|
+
domain-specific language (DSL) for describing project context, which can then
|
6
|
+
be exported as structured JSON data.
|
7
|
+
|
8
|
+
The DSL is general-purpose and can be used for any kind of project or
|
9
|
+
collection of files, whether software development, documentation, research
|
10
|
+
data, educational materials, creative projects, or any other type of organized
|
11
|
+
information. The `contexts/project.rb` example below demonstrates how to
|
12
|
+
describe a Ruby project, but the same principles apply across many different
|
13
|
+
domains.
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
|
17
|
+
Add this line to your application's Gemfile:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem 'context_spook'
|
21
|
+
```
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
```bash
|
25
|
+
$ bundle install
|
26
|
+
```
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
```bash
|
30
|
+
$ gem install context_spook
|
31
|
+
```
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
Create a `contexts/project.rb` file that describes your project context using
|
36
|
+
the DSL in a context definition file:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
# contexts/project.rb
|
40
|
+
context do
|
41
|
+
variable branch: `git rev-parse --abbrev-ref HEAD`.chomp
|
42
|
+
|
43
|
+
namespace "structure" do
|
44
|
+
command "tree lib", tags: %w[ project_structure ]
|
45
|
+
end
|
46
|
+
|
47
|
+
namespace "lib" do
|
48
|
+
Dir['lib/**/*.rb'].each do |filename|
|
49
|
+
file filename, tags: 'lib'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
namespace "spec" do
|
54
|
+
Dir['spec/**/*.rb'].each do |filename|
|
55
|
+
file filename, tags: 'spec'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
namespace "gems" do
|
60
|
+
file 'context_spook.gemspec'
|
61
|
+
file 'Gemfile'
|
62
|
+
file 'Gemfile.lock'
|
63
|
+
end
|
64
|
+
|
65
|
+
file 'Rakefile', tags: 'gem_hadar'
|
66
|
+
|
67
|
+
file 'README.md', tags: 'documentation'
|
68
|
+
|
69
|
+
meta ruby: RUBY_DESCRIPTION
|
70
|
+
|
71
|
+
meta code_coverage: JSON.load_file('coverage/coverage_context.json') rescue nil
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
### Programmatic Usage
|
76
|
+
|
77
|
+
Now you can generate the context from the file, and store it as JSON in Ruby.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
context = ContextSpook::generate_context('contexts/project.rb')
|
81
|
+
File.write 'context.json', context.to_json
|
82
|
+
```
|
83
|
+
|
84
|
+
### CLI Usage
|
85
|
+
|
86
|
+
Generate context and save to file:
|
87
|
+
|
88
|
+
```bash
|
89
|
+
./bin/context_spook contexts/project.rb > context.json
|
90
|
+
```
|
91
|
+
|
92
|
+
Or pipe directly to another tool:
|
93
|
+
|
94
|
+
```
|
95
|
+
./bin/context_spook contexts/project.rb | ollama_chat_send
|
96
|
+
```
|
97
|
+
|
98
|
+
You will see two orange warning messages, that demonstrates how errors like
|
99
|
+
missing files or commands with failing exit codes are handled.
|
100
|
+
|
101
|
+
## What Gets Collected
|
102
|
+
|
103
|
+
The DSL collects various types of project information:
|
104
|
+
|
105
|
+
- **Variables**: Key-value pairs (e.g., git branch)
|
106
|
+
- **Files**: Content, size, line count, and tags for each file
|
107
|
+
- **Commands**: Shell command outputs with exit codes and working directory
|
108
|
+
- **Metadata**: Project-wide attributes (e.g., Ruby version, coverage data)
|
109
|
+
|
110
|
+
## Intended Use Case
|
111
|
+
|
112
|
+
The generated JSON is designed to be sent to Language Model assistants to
|
113
|
+
provide them with comprehensive context about your codebase. This helps AI
|
114
|
+
assistants understand:
|
115
|
+
|
116
|
+
- The project structure and file organization
|
117
|
+
- Key source files and their contents
|
118
|
+
- Command outputs that reveal project state
|
119
|
+
- Metadata about the development environment
|
120
|
+
- Coverage information for code quality
|
121
|
+
|
122
|
+
## Example Output Structure
|
123
|
+
|
124
|
+
```json
|
125
|
+
{
|
126
|
+
"files": {
|
127
|
+
"lib/context_spook.rb": {
|
128
|
+
"namespace": "lib",
|
129
|
+
"content": "...",
|
130
|
+
"size": 1234,
|
131
|
+
"lines": 56,
|
132
|
+
"tags": ["lib"]
|
133
|
+
}
|
134
|
+
},
|
135
|
+
"commands": {
|
136
|
+
"tree lib": {
|
137
|
+
"namespace": "structure",
|
138
|
+
"output": "lib\n├── context_spook\n│ └── generator.rb\n└── context_spook.rb\n\n2 directories, 3 files",
|
139
|
+
"exit_code": 0,
|
140
|
+
"working_directory": "/path/to/project"
|
141
|
+
}
|
142
|
+
},
|
143
|
+
"metadata": {
|
144
|
+
"ruby": "ruby 3.1.0 ...",
|
145
|
+
"code_coverage": { ... }
|
146
|
+
},
|
147
|
+
"variables": {
|
148
|
+
"branch": "main"
|
149
|
+
}
|
150
|
+
}
|
151
|
+
```
|
152
|
+
|
153
|
+
## License
|
154
|
+
|
155
|
+
The gem is available as open source under the terms of the [MIT License](./LICENSE)
|
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# vim: set filetype=ruby et sw=2 ts=2:
|
2
|
+
|
3
|
+
require 'gem_hadar'
|
4
|
+
|
5
|
+
GemHadar do
|
6
|
+
name 'context_spook'
|
7
|
+
module_type :module
|
8
|
+
author 'Florian Frank'
|
9
|
+
email 'flori@ping.de'
|
10
|
+
homepage "https://github.com/flori/context_spook"
|
11
|
+
summary "#{name} collects project context for AI"
|
12
|
+
executables 'context_spook'
|
13
|
+
|
14
|
+
description <<~EOT
|
15
|
+
#{name} is a library that collects and organizes project
|
16
|
+
information to help AI assistants understand codebases better.
|
17
|
+
EOT
|
18
|
+
|
19
|
+
test_dir 'spec'
|
20
|
+
ignore '.*.sw[pon]', 'pkg', 'Gemfile.lock', '.AppleDouble', '.bundle',
|
21
|
+
'.yardoc', 'doc', 'tags', 'errors.lst', 'cscope.out', 'coverage', 'tmp',
|
22
|
+
'yard'
|
23
|
+
package_ignore '.all_images.yml', '.tool-versions', '.gitignore', 'VERSION',
|
24
|
+
'.rspec', *Dir.glob('.github/**/*', File::FNM_DOTMATCH)
|
25
|
+
readme 'README.md'
|
26
|
+
|
27
|
+
required_ruby_version '~> 3.1'
|
28
|
+
|
29
|
+
dependency 'tins', '~>1.39'
|
30
|
+
dependency 'json', '~>2.0'
|
31
|
+
dependency 'term-ansicolor', '~> 1.11'
|
32
|
+
development_dependency 'all_images', '~> 0.6'
|
33
|
+
development_dependency 'rspec', '~> 3.2'
|
34
|
+
development_dependency 'debug'
|
35
|
+
development_dependency 'simplecov'
|
36
|
+
|
37
|
+
licenses << 'MIT'
|
38
|
+
|
39
|
+
clobber 'coverage'
|
40
|
+
end
|
data/bin/context_spook
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'context_spook'
|
4
|
+
|
5
|
+
filename = ARGV.shift or fail 'require context definition file as first argument'
|
6
|
+
context_json = ContextSpook.generate_context(filename).to_json
|
7
|
+
STDERR.puts "Now outputting #{context_json.size} bytes of JSON context."
|
8
|
+
puts context_json
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
# stub: context_spook 0.0.0 ruby lib
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "context_spook".freeze
|
6
|
+
s.version = "0.0.0".freeze
|
7
|
+
|
8
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
9
|
+
s.require_paths = ["lib".freeze]
|
10
|
+
s.authors = ["Florian Frank".freeze]
|
11
|
+
s.date = "1980-01-02"
|
12
|
+
s.description = "context_spook is a library that collects and organizes project\ninformation to help AI assistants understand codebases better.\n".freeze
|
13
|
+
s.email = "flori@ping.de".freeze
|
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 = ["Gemfile".freeze, "LICENSE".freeze, "README.md".freeze, "Rakefile".freeze, "bin/context_spook".freeze, "context_spook.gemspec".freeze, "contexts/project.rb".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]
|
17
|
+
s.homepage = "https://github.com/flori/context_spook".freeze
|
18
|
+
s.licenses = ["MIT".freeze]
|
19
|
+
s.rdoc_options = ["--title".freeze, "ContextSpook - context_spook collects project context for AI".freeze, "--main".freeze, "README.md".freeze]
|
20
|
+
s.required_ruby_version = Gem::Requirement.new("~> 3.1".freeze)
|
21
|
+
s.rubygems_version = "3.6.9".freeze
|
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]
|
24
|
+
|
25
|
+
s.specification_version = 4
|
26
|
+
|
27
|
+
s.add_development_dependency(%q<gem_hadar>.freeze, ["~> 2.0".freeze])
|
28
|
+
s.add_development_dependency(%q<all_images>.freeze, ["~> 0.6".freeze])
|
29
|
+
s.add_development_dependency(%q<rspec>.freeze, ["~> 3.2".freeze])
|
30
|
+
s.add_development_dependency(%q<debug>.freeze, [">= 0".freeze])
|
31
|
+
s.add_development_dependency(%q<simplecov>.freeze, [">= 0".freeze])
|
32
|
+
s.add_runtime_dependency(%q<tins>.freeze, ["~> 1.39".freeze])
|
33
|
+
s.add_runtime_dependency(%q<json>.freeze, ["~> 2.0".freeze])
|
34
|
+
s.add_runtime_dependency(%q<term-ansicolor>.freeze, ["~> 1.11".freeze])
|
35
|
+
end
|
data/contexts/project.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
context do
|
2
|
+
variable branch: `git rev-parse --abbrev-ref HEAD`.chomp
|
3
|
+
|
4
|
+
namespace "structure" do
|
5
|
+
command "tree lib", tags: %w[ project_structure ]
|
6
|
+
end
|
7
|
+
|
8
|
+
namespace "lib" do
|
9
|
+
Dir['lib/**/*.rb'].each do |filename|
|
10
|
+
file filename, tags: 'lib'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
namespace "spec" do
|
15
|
+
Dir['spec/**/*.rb'].each do |filename|
|
16
|
+
file filename, tags: 'spec'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
namespace "gems" do
|
21
|
+
file 'context_spook.gemspec'
|
22
|
+
file 'Gemfile'
|
23
|
+
file 'Gemfile.lock'
|
24
|
+
end
|
25
|
+
|
26
|
+
command 'false', tags: 'failure'
|
27
|
+
|
28
|
+
file 'nixda', tags: 'failure'
|
29
|
+
|
30
|
+
file 'Rakefile', tags: [ 'gem_hadar' ]
|
31
|
+
|
32
|
+
file 'README.md', tags: [ 'documentation' ]
|
33
|
+
|
34
|
+
meta ruby: RUBY_DESCRIPTION
|
35
|
+
|
36
|
+
meta code_coverage: JSON.load_file('coverage/coverage_context.json') rescue nil
|
37
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require 'tins/xt'
|
2
|
+
require 'term/ansicolor'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
|
6
|
+
# The ContextSpook module serves as a namespace container for collecting and
|
7
|
+
# organizing project information for AI assistance.
|
8
|
+
module ContextSpook
|
9
|
+
include DSLKit::Interpreter
|
10
|
+
|
11
|
+
# The generate_context method processes a context definition file and returns
|
12
|
+
# the resulting context object.
|
13
|
+
#
|
14
|
+
# This method reads the content of a specified file, parses it using the
|
15
|
+
# generator's parsing mechanism, and extracts the configured context from the
|
16
|
+
# parsed result.
|
17
|
+
#
|
18
|
+
# @param filename [ String ] the path to the context definition file to be processed
|
19
|
+
#
|
20
|
+
# @return [ ContextSpook::Generator::Context ] the context object generated
|
21
|
+
# from the file contents
|
22
|
+
def self.generate_context(filename)
|
23
|
+
Generator.send(:new).send(:parse, File.read(filename)).context
|
24
|
+
end
|
25
|
+
|
26
|
+
# The Generator class provides a DSL parser that interprets context
|
27
|
+
# definition files and constructs structured context objects containing
|
28
|
+
# project metadata, file contents, command outputs, and variables for AI
|
29
|
+
# assistance.
|
30
|
+
class Generator
|
31
|
+
private_class_method :new
|
32
|
+
|
33
|
+
# The initialize method sets up the object by evaluating the provided block in the object's context.
|
34
|
+
#
|
35
|
+
# @param block [ Proc ] A block of code to be evaluated within the object's context.
|
36
|
+
# If no block is given, the method does nothing.
|
37
|
+
def initialize(&block)
|
38
|
+
block and instance_eval(&block)
|
39
|
+
end
|
40
|
+
|
41
|
+
# The context method creates or returns a context object.
|
42
|
+
#
|
43
|
+
# @param block [ Proc ] optional block to initialize the context
|
44
|
+
#
|
45
|
+
# @return [ Context ] the context object
|
46
|
+
def context(&block)
|
47
|
+
if block
|
48
|
+
@context and raise ArgumentError, "only one context allowed"
|
49
|
+
@context = Context.new(&block)
|
50
|
+
else
|
51
|
+
@context
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# The Context class represents and manages project context data, providing
|
56
|
+
# structured storage for file contents, command outputs, variables, and
|
57
|
+
# metadata that can be serialized to JSON for AI assistance.
|
58
|
+
class Context
|
59
|
+
include Tins::Scope
|
60
|
+
include Tins::DSLAccessor
|
61
|
+
include Term::ANSIColor
|
62
|
+
|
63
|
+
# The initialize method sets up the object by evaluating a block in the
|
64
|
+
# object's context.
|
65
|
+
#
|
66
|
+
# @param block [ Proc ] A block to be evaluated within the object's context.
|
67
|
+
def initialize(&block)
|
68
|
+
block and instance_eval(&block)
|
69
|
+
end
|
70
|
+
|
71
|
+
# The namespace method creates a scoped block with a given name.
|
72
|
+
#
|
73
|
+
# @param name [ Object ] the name to scope the block with
|
74
|
+
#
|
75
|
+
# @yield [ block ] executes the block within the created scope
|
76
|
+
def namespace(name, &block)
|
77
|
+
name = name.to_sym
|
78
|
+
scope_block(name) do
|
79
|
+
instance_eval(&block)
|
80
|
+
end
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# The variables method sets up a DSL accessor for managing a collection of
|
85
|
+
# variables.
|
86
|
+
#
|
87
|
+
# @param default [ Hash ] the default variables to initialize with
|
88
|
+
dsl_accessor :variables do
|
89
|
+
{}
|
90
|
+
end
|
91
|
+
|
92
|
+
# The variable method assigns key-value pairs to the variables hash.
|
93
|
+
#
|
94
|
+
# @param v [ Hash ] a hash containing variable names as keys and their
|
95
|
+
# values
|
96
|
+
def variable(**v)
|
97
|
+
v.each do |name, value|
|
98
|
+
variables[name.to_sym] = value
|
99
|
+
end
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# The metadata method sets up a DSL accessor for metadata configuration.
|
104
|
+
#
|
105
|
+
# @param default [ Object ] the default value for the metadata, `{}`
|
106
|
+
dsl_accessor :metadata do
|
107
|
+
{}
|
108
|
+
end
|
109
|
+
|
110
|
+
# The meta method assigns metadata key-value pairs to the metadata hash.
|
111
|
+
#
|
112
|
+
# @param m [ Hash ] a hash of metadata key-value pairs to be added
|
113
|
+
def meta(**m)
|
114
|
+
m.each do |name, value|
|
115
|
+
metadata[name.to_sym] = value
|
116
|
+
end
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
|
120
|
+
# The files method sets up a DSL accessor for providing files.
|
121
|
+
#
|
122
|
+
# @param default [ Hash ] the default files hash
|
123
|
+
dsl_accessor :files do
|
124
|
+
{}
|
125
|
+
end
|
126
|
+
|
127
|
+
# The file method associates a file with the current scope and stores its
|
128
|
+
# content.
|
129
|
+
#
|
130
|
+
# It reads the specified file and creates an entry in the files hash with
|
131
|
+
# the file's content, along with its namespace and optional tags.
|
132
|
+
#
|
133
|
+
# @param filename [ String ] the path to the file to be read and stored
|
134
|
+
# @param tags [ Array<String>, nil ] optional array of tags to associate with the file
|
135
|
+
#
|
136
|
+
# @return [ Hash ] the created file entry with content, namespace, and tags
|
137
|
+
def file(filename, tags: nil)
|
138
|
+
content = File.read(filename)
|
139
|
+
files[filename] = {
|
140
|
+
namespace: scope_top,
|
141
|
+
content:,
|
142
|
+
size: content.size,
|
143
|
+
lines: content.lines.size,
|
144
|
+
tags: (Array(tags) if tags),
|
145
|
+
}.compact
|
146
|
+
nil
|
147
|
+
rescue Errno::ENOENT => e
|
148
|
+
warn "Reading #{filename.inspect} caused #{e.class}: #{e}"
|
149
|
+
end
|
150
|
+
|
151
|
+
# The commands method sets up a DSL accessor for provided command outputs.
|
152
|
+
#
|
153
|
+
# @param value [ Hash ] the hash of commands to be set
|
154
|
+
dsl_accessor :commands, {}
|
155
|
+
|
156
|
+
# The command method executes a shell command and stores its result.
|
157
|
+
#
|
158
|
+
# This method runs a given shell command and records the output, exit code,
|
159
|
+
# working directory, and optional tags in the commands hash.
|
160
|
+
#
|
161
|
+
# @param shell_command [ String ] the shell command to execute
|
162
|
+
# @param tags [ Array<String>, nil ] optional array of tags to associate
|
163
|
+
# with the command
|
164
|
+
#
|
165
|
+
# @return [ Hash ] the stored command result including output, exit code,
|
166
|
+
# and metadata
|
167
|
+
def command(shell_command, tags: nil)
|
168
|
+
output = `#{shell_command}`
|
169
|
+
exit_code = $?&.exitstatus.to_i
|
170
|
+
if exit_code != 0
|
171
|
+
warn "Executing #{shell_command.inspect} resulted in exit code #{exit_code}."
|
172
|
+
end
|
173
|
+
commands[shell_command] = {
|
174
|
+
namespace: scope_top,
|
175
|
+
output:,
|
176
|
+
exit_code:,
|
177
|
+
working_directory: Dir.pwd,
|
178
|
+
tags: (Array(tags) if tags),
|
179
|
+
}.compact
|
180
|
+
nil
|
181
|
+
end
|
182
|
+
|
183
|
+
# The to_json method converts the object to a JSON representation by
|
184
|
+
# first generating its hash form and then serializing that hash into JSON
|
185
|
+
# format.
|
186
|
+
def to_json(*)
|
187
|
+
as_json.to_json(*)
|
188
|
+
end
|
189
|
+
|
190
|
+
# The as_json method converts the context's files, commands, and metadata
|
191
|
+
# into a hash representation.
|
192
|
+
#
|
193
|
+
# @return [ Hash ] a hash containing the files, commands, and metadata
|
194
|
+
def as_json(*)
|
195
|
+
{
|
196
|
+
files:,
|
197
|
+
commands:,
|
198
|
+
metadata:,
|
199
|
+
variables:
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
# The warn method enhances warning messages by applying colored
|
206
|
+
# formatting before passing them to the superclass implementation.
|
207
|
+
#
|
208
|
+
# @param msgs [ Array ] an array of message objects to be formatted and warned
|
209
|
+
def warn(*msgs)
|
210
|
+
msgs.map! { |m| color(208) { m } }
|
211
|
+
super(*msgs, uplevel: 1)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
# The parse method processes the given source code by interpreting it
|
218
|
+
# within the current binding context, allowing for dynamic evaluation and
|
219
|
+
# configuration setup.
|
220
|
+
#
|
221
|
+
# @param source [ String ] the source code to be parsed and interpreted
|
222
|
+
#
|
223
|
+
# @return [ ContextSpook::Generator ] returns self to allow for method
|
224
|
+
# chaining after parsing
|
225
|
+
def parse(source)
|
226
|
+
interpret_with_binding source, binding
|
227
|
+
self
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ContextSpook::Generator do
|
4
|
+
let :context do
|
5
|
+
ContextSpook.generate_context('contexts/project.rb')
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'context can be generated' do
|
9
|
+
expect(context).to be_a described_class::Context
|
10
|
+
expect(context.metadata[:ruby]).to eq RUBY_DESCRIPTION
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'context be transformed to JSON if loaded' do
|
14
|
+
context_as_json = context.to_json
|
15
|
+
expect(context_as_json.size).to be > 1024
|
16
|
+
expect(JSON(context_as_json)).to be_a Hash
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'Context' do
|
20
|
+
|
21
|
+
it 'can have variables' do
|
22
|
+
expect(context.variables[:branch]).to be_present
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can have files' do
|
26
|
+
file = context.files['lib/context_spook.rb']
|
27
|
+
expect(file).to be_present
|
28
|
+
expect(file[:content]).to be_present
|
29
|
+
expect(file[:size]).to be > 0
|
30
|
+
expect(file[:lines]).to be > 0
|
31
|
+
expect(file[:namespace]).to eq :lib
|
32
|
+
expect(file[:tags]).to eq %w[ lib ]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'can have commands' do
|
36
|
+
command = context.commands['tree lib']
|
37
|
+
expect(command).to be_present
|
38
|
+
expect(command[:working_directory]).to eq Dir.pwd
|
39
|
+
expect(command[:exit_code]).to be_present
|
40
|
+
if command[:exit_code] == 0
|
41
|
+
expect(command[:output]).to be_present
|
42
|
+
else
|
43
|
+
expect(command[:output]).not_to be_present
|
44
|
+
end
|
45
|
+
expect(command[:tags]).to eq %w[ project_structure ]
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'can have metada' do
|
49
|
+
expect(context.metadata[:ruby]).to eq RUBY_DESCRIPTION
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'gem_hadar/simplecov'
|
2
|
+
GemHadar::SimpleCov.start
|
3
|
+
require 'rspec'
|
4
|
+
require 'tins/xt/expose'
|
5
|
+
begin
|
6
|
+
require 'debug'
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
require 'context_spook'
|
10
|
+
|
11
|
+
# The asset method constructs a file path by joining the directory of the
|
12
|
+
# current file, an 'assets' subdirectory, and the provided filename.
|
13
|
+
#
|
14
|
+
# @param name [ String ] the name of the asset file to be located
|
15
|
+
#
|
16
|
+
# @return [ String ] the complete file path to the asset
|
17
|
+
def asset(name)
|
18
|
+
File.join(__dir__, 'assets', name)
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: context_spook
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Florian Frank
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: gem_hadar
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '2.0'
|
19
|
+
type: :development
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '2.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: all_images
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0.6'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.6'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: rspec
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.2'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.2'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: debug
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: simplecov
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: tins
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '1.39'
|
89
|
+
type: :runtime
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1.39'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: json
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '2.0'
|
103
|
+
type: :runtime
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '2.0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: term-ansicolor
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '1.11'
|
117
|
+
type: :runtime
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '1.11'
|
124
|
+
description: |
|
125
|
+
context_spook is a library that collects and organizes project
|
126
|
+
information to help AI assistants understand codebases better.
|
127
|
+
email: flori@ping.de
|
128
|
+
executables:
|
129
|
+
- context_spook
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files:
|
132
|
+
- README.md
|
133
|
+
- lib/context_spook.rb
|
134
|
+
- lib/context_spook/generator.rb
|
135
|
+
- lib/context_spook/version.rb
|
136
|
+
files:
|
137
|
+
- Gemfile
|
138
|
+
- LICENSE
|
139
|
+
- README.md
|
140
|
+
- Rakefile
|
141
|
+
- bin/context_spook
|
142
|
+
- context_spook.gemspec
|
143
|
+
- contexts/project.rb
|
144
|
+
- lib/context_spook.rb
|
145
|
+
- lib/context_spook/generator.rb
|
146
|
+
- lib/context_spook/version.rb
|
147
|
+
- spec/context_spook/generator_spec.rb
|
148
|
+
- spec/spec_helper.rb
|
149
|
+
homepage: https://github.com/flori/context_spook
|
150
|
+
licenses:
|
151
|
+
- MIT
|
152
|
+
metadata: {}
|
153
|
+
rdoc_options:
|
154
|
+
- "--title"
|
155
|
+
- ContextSpook - context_spook collects project context for AI
|
156
|
+
- "--main"
|
157
|
+
- README.md
|
158
|
+
require_paths:
|
159
|
+
- lib
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - "~>"
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '3.1'
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
requirements: []
|
171
|
+
rubygems_version: 3.6.9
|
172
|
+
specification_version: 4
|
173
|
+
summary: context_spook collects project context for AI
|
174
|
+
test_files:
|
175
|
+
- spec/context_spook/generator_spec.rb
|
176
|
+
- spec/spec_helper.rb
|