context_spook 1.6.0 → 1.7.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/README.md +103 -12
- data/Rakefile +0 -1
- data/bin/context_spook +6 -3
- data/context_spook.gemspec +7 -8
- data/lib/context_spook/generator.rb +75 -45
- data/lib/context_spook/output_context.rb +2 -2
- data/lib/context_spook/toon/encoder.rb +628 -0
- data/lib/context_spook/toon/example.md +36 -66
- data/lib/context_spook/toon.rb +13 -15
- data/lib/context_spook/utils.rb +14 -9
- data/lib/context_spook/version.rb +1 -1
- data/spec/context_spook/generator_spec.rb +63 -2
- data/spec/context_spook/toon_encoder_spec.rb +90 -0
- data/spec/context_spook/toon_spec.rb +15 -16
- data/spec/examples/README.md +79 -0
- data/spec/examples/conversions/keyed-tabular-objects.json +19 -0
- data/spec/examples/conversions/keyed-tabular-objects.toon +4 -0
- data/spec/examples/conversions/nested-field-groups.json +28 -0
- data/spec/examples/conversions/nested-field-groups.toon +4 -0
- data/spec/examples/conversions/tabular-arrays.json +22 -0
- data/spec/examples/conversions/tabular-arrays.toon +4 -0
- data/spec/examples/invalid/delimiter-mismatch.toon +3 -0
- data/spec/examples/invalid/length-mismatch.toon +3 -0
- data/spec/examples/invalid/multiple-root-primitives.toon +2 -0
- data/spec/examples/valid/comments.toon +5 -0
- data/spec/examples/valid/delimiter-scoping.toon +5 -0
- data/spec/examples/valid/mixed-arrays.toon +5 -0
- data/spec/examples/valid/nested-objects.toon +9 -0
- data/spec/examples/valid/objects.toon +6 -0
- data/spec/examples/valid/pipe-delimiter.toon +3 -0
- data/spec/examples/valid/primitive-arrays.toon +3 -0
- data/spec/examples/valid/tab-delimiter.toon +3 -0
- data/spec/fixtures/encode/arrays-nested.json +120 -0
- data/spec/fixtures/encode/arrays-objects.json +160 -0
- data/spec/fixtures/encode/arrays-primitive.json +95 -0
- data/spec/fixtures/encode/arrays-tabular.json +182 -0
- data/spec/fixtures/encode/delimiters.json +214 -0
- data/spec/fixtures/encode/objects-keyed.json +160 -0
- data/spec/fixtures/encode/objects.json +288 -0
- data/spec/fixtures/encode/primitives.json +262 -0
- data/spec/fixtures/encode/whitespace.json +54 -0
- metadata +35 -18
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3992c09d3428a0268385de6fbf268f96b92ff8cc8f84cc11d464e3c1cd9c1cfe
|
|
4
|
+
data.tar.gz: d8cbd80e0ca8a3df54f464d8eb120dd8d9864b51e1aafc12b23f60e9e8b42eef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d186a38e1e0285498700f73d790ed0caa3fd4e19e38565c21f39657320311d1bf37bf32405ea176c44e8d7e4b8ef3978d853ce35f89a1e922ecbbf739b2dafa6
|
|
7
|
+
data.tar.gz: 9a86582d2c45a68a913ae82f64e76ca2235e96d4d3250901b49eced799ff7d236b08f869fe1dc20fe4c3453c21d35e4cb7e9c8b42c67d8ed67c7bb79d3e469f5
|
data/README.md
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
ContextSpook is a Ruby library that collects and organizes project information
|
|
4
4
|
to help AI assistants understand codebases better. It provides a
|
|
5
5
|
domain-specific language (DSL) for describing project context, which can then
|
|
6
|
-
be exported as structured JSON data.
|
|
6
|
+
be exported as structured JSON or TOON (Token-Oriented Object Notation) data.
|
|
7
|
+
|
|
8
|
+
TOON output is produced by a native encoder conformant to the TOON v4.4
|
|
9
|
+
specification, validated against 186 official conformance fixtures.
|
|
7
10
|
|
|
8
11
|
The DSL is general-purpose and can be used for any kind of project or
|
|
9
12
|
collection of files, whether software development, documentation, research
|
|
@@ -14,7 +17,8 @@ domains.
|
|
|
14
17
|
|
|
15
18
|
## Documentation
|
|
16
19
|
|
|
17
|
-
Complete API documentation is available at:
|
|
20
|
+
Complete API documentation is available at:
|
|
21
|
+
[GitHub.io](https://flori.github.io/context_spook/)
|
|
18
22
|
|
|
19
23
|
## Installation
|
|
20
24
|
|
|
@@ -25,11 +29,13 @@ gem 'context_spook'
|
|
|
25
29
|
```
|
|
26
30
|
|
|
27
31
|
And then execute:
|
|
32
|
+
|
|
28
33
|
```bash
|
|
29
34
|
$ bundle install
|
|
30
35
|
```
|
|
31
36
|
|
|
32
37
|
Or install it yourself as:
|
|
38
|
+
|
|
33
39
|
```bash
|
|
34
40
|
$ gem install context_spook
|
|
35
41
|
```
|
|
@@ -72,7 +78,18 @@ application.
|
|
|
72
78
|
```ruby
|
|
73
79
|
File.write 'context.json', context.to_json
|
|
74
80
|
```
|
|
81
|
+
You can also generate a manifest-only context by passing `manifest: true`.
|
|
82
|
+
File entries are reduced to their path plus metadata (size, tokens, MIME
|
|
83
|
+
types, tags), giving the model a lightweight index of file paths it can
|
|
84
|
+
use with `read_file` to load only what it needs:
|
|
75
85
|
|
|
86
|
+
```ruby
|
|
87
|
+
context = ContextSpook::generate_context(manifest: true) do
|
|
88
|
+
context do
|
|
89
|
+
file 'lib/context_spook.rb'
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
```
|
|
76
93
|
#### From a context definition file
|
|
77
94
|
|
|
78
95
|
Alternatively store the block's content above to a file `.contexts/project.rb`:
|
|
@@ -136,6 +153,28 @@ Or pipe directly to another tool:
|
|
|
136
153
|
context_spook .contexts/project.rb | ollama_chat_send
|
|
137
154
|
```
|
|
138
155
|
|
|
156
|
+
You can choose the output format with the `-F` flag (JSON is the default):
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Generate context as TOON
|
|
160
|
+
context_spook .contexts/project.rb -F TOON > context.toon
|
|
161
|
+
|
|
162
|
+
# Save TOON directly to a file
|
|
163
|
+
context_spook .contexts/project.rb -F TOON -o context.toon
|
|
164
|
+
```
|
|
165
|
+
You can use manifest mode with `-m` to omit file content. Each file entry
|
|
166
|
+
is reduced to its path plus metadata (size, tokens, MIME types, tags),
|
|
167
|
+
giving the model a lightweight index of file paths it can use with
|
|
168
|
+
`read_file` to load only what it needs:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
# Generate manifest-only context as TOON
|
|
172
|
+
context_spook .contexts/project.rb -F TOON -m > context.toon
|
|
173
|
+
|
|
174
|
+
# Generate manifest-only context as JSON
|
|
175
|
+
context_spook .contexts/project.rb -m > context.json
|
|
176
|
+
```
|
|
177
|
+
|
|
139
178
|
The CLI tool also supports verbose output:
|
|
140
179
|
|
|
141
180
|
```bash
|
|
@@ -178,7 +217,8 @@ To estimate the size of your context without generating the large output file:
|
|
|
178
217
|
context_spook .contexts/project.rb -S
|
|
179
218
|
```
|
|
180
219
|
|
|
181
|
-
This is useful for optimizing context size before committing to large output
|
|
220
|
+
This is useful for optimizing context size before committing to large output
|
|
221
|
+
files.
|
|
182
222
|
|
|
183
223
|
This is how you can show the usage message:
|
|
184
224
|
|
|
@@ -192,8 +232,10 @@ context_spook -h
|
|
|
192
232
|
The DSL collects various types of project information:
|
|
193
233
|
|
|
194
234
|
- **Variables**: Key-value pairs (e.g., git branch)
|
|
195
|
-
- **Files**:
|
|
196
|
-
|
|
235
|
+
- **Files**: Line-numbered content, byte size, token estimate, MIME types, line
|
|
236
|
+
count, and tags (or metadata-only in manifest mode)
|
|
237
|
+
- **Commands**: Shell command outputs with size, token estimate, exit codes,
|
|
238
|
+
working directory, and tags
|
|
197
239
|
- **Metadata**: Project-wide attributes (e.g., Ruby version, coverage data)
|
|
198
240
|
|
|
199
241
|
## Intended Use Case
|
|
@@ -215,9 +257,20 @@ assistants understand:
|
|
|
215
257
|
"files": {
|
|
216
258
|
"lib/context_spook.rb": {
|
|
217
259
|
"namespace": "lib",
|
|
218
|
-
"content":
|
|
219
|
-
|
|
220
|
-
|
|
260
|
+
"content": [
|
|
261
|
+
{ "linenumber": 1, "line": "module ContextSpook" },
|
|
262
|
+
{ "linenumber": 2, "line": "" },
|
|
263
|
+
{ "linenumber": 3, "line": "require 'context_spook/version'" },
|
|
264
|
+
{ "linenumber": 4, "line": "require 'context_spook/utils'" },
|
|
265
|
+
{ "linenumber": 5, "line": "require 'context_spook/generator'" }
|
|
266
|
+
],
|
|
267
|
+
"size": 120,
|
|
268
|
+
"tokens": 35,
|
|
269
|
+
"lines": 5,
|
|
270
|
+
"content_types": [
|
|
271
|
+
"application/x-ruby",
|
|
272
|
+
"text/x-ruby"
|
|
273
|
+
],
|
|
221
274
|
"tags": [
|
|
222
275
|
"lib"
|
|
223
276
|
]
|
|
@@ -227,20 +280,58 @@ assistants understand:
|
|
|
227
280
|
"tree": {
|
|
228
281
|
"namespace": "structure",
|
|
229
282
|
"output": "lib\n├── context_spook\n│ └── generator.rb\n└── context_spook.rb\n\n2 directories, 3 files",
|
|
283
|
+
"size": 88,
|
|
284
|
+
"tokens": 26,
|
|
230
285
|
"exit_code": 0,
|
|
231
|
-
"working_directory": "/path/to/project"
|
|
286
|
+
"working_directory": "/path/to/project",
|
|
287
|
+
"tags": [
|
|
288
|
+
"project_structure"
|
|
289
|
+
]
|
|
232
290
|
}
|
|
233
291
|
},
|
|
234
292
|
"metadata": {
|
|
235
|
-
"ruby": "ruby
|
|
236
|
-
"code_coverage":
|
|
293
|
+
"ruby": "ruby 4.0.7 (2026-09-15 ...) +PRISM [x86_64-linux]",
|
|
294
|
+
"code_coverage": null
|
|
237
295
|
},
|
|
238
296
|
"variables": {
|
|
239
|
-
"branch": "
|
|
297
|
+
"branch": "master"
|
|
240
298
|
}
|
|
241
299
|
}
|
|
242
300
|
```
|
|
243
301
|
|
|
302
|
+
The same context in TOON v4 format:
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
files:
|
|
306
|
+
"lib/context_spook.rb":
|
|
307
|
+
namespace: lib
|
|
308
|
+
content[5]{linenumber,line}:
|
|
309
|
+
1,module ContextSpook
|
|
310
|
+
2,""
|
|
311
|
+
3,require 'context_spook/version'
|
|
312
|
+
4,require 'context_spook/utils'
|
|
313
|
+
5,require 'context_spook/generator'
|
|
314
|
+
size: 120
|
|
315
|
+
tokens: 35
|
|
316
|
+
lines: 5
|
|
317
|
+
content_types[2]: application/x-ruby,text/x-ruby
|
|
318
|
+
tags[1]: lib
|
|
319
|
+
commands:
|
|
320
|
+
tree:
|
|
321
|
+
namespace: structure
|
|
322
|
+
output: "lib\n├── context_spook\n│ └── generator.rb\n└── context_spook.rb\n\n2 directories, 3 files"
|
|
323
|
+
size: 88
|
|
324
|
+
tokens: 26
|
|
325
|
+
exit_code: 0
|
|
326
|
+
working_directory: /path/to/project
|
|
327
|
+
tags[1]: project_structure
|
|
328
|
+
metadata:
|
|
329
|
+
ruby: "ruby 4.0.7 (2026-09-15 ...) +PRISM [x86_64-linux]"
|
|
330
|
+
code_coverage: null
|
|
331
|
+
variables:
|
|
332
|
+
branch: master
|
|
333
|
+
```
|
|
334
|
+
|
|
244
335
|
## License
|
|
245
336
|
|
|
246
337
|
The gem is available as open source under the terms of the [MIT License](LICENSE)
|
data/Rakefile
CHANGED
|
@@ -38,7 +38,6 @@ GemHadar do
|
|
|
38
38
|
dependency 'mime-types', '~> 3.0'
|
|
39
39
|
dependency 'yaml', '~> 0.4'
|
|
40
40
|
dependency 'pathname', '~> 0.4'
|
|
41
|
-
dependency 'toon-ruby', '>= 0.1'
|
|
42
41
|
development_dependency 'all_images', '~> 0.6'
|
|
43
42
|
development_dependency 'rspec', '~> 3.2'
|
|
44
43
|
development_dependency 'debug'
|
data/bin/context_spook
CHANGED
|
@@ -27,6 +27,7 @@ def usage
|
|
|
27
27
|
-i PATTERN Include files matching PATTERN (can be used multiple times)
|
|
28
28
|
Supports glob patterns like **, *, ?, [abc], {a,b,c}
|
|
29
29
|
-F FORMAT Output context as format JSON (the default) or TOON
|
|
30
|
+
-m Manifest mode: omit file content, carry only metadata
|
|
30
31
|
-h Show this help message
|
|
31
32
|
|
|
32
33
|
Examples:
|
|
@@ -49,19 +50,21 @@ def usage
|
|
|
49
50
|
# Generate informational output only to estimate context size
|
|
50
51
|
#{File.basename($0)} .contexts/project.rb -S
|
|
51
52
|
|
|
53
|
+
# Generate context in manifest mode (metadata only, no file content)
|
|
54
|
+
#{File.basename($0)} .contexts/project.rb -m
|
|
52
55
|
# Generate context and save to file
|
|
53
56
|
#{File.basename($0)} .contexts/project.rb -o context.json
|
|
54
57
|
EOT
|
|
55
58
|
exit 0
|
|
56
59
|
end
|
|
57
60
|
|
|
58
|
-
opts = go 'o:i:F:
|
|
61
|
+
opts = go 'o:i:F:mvSh', defaults: { ?v => true }
|
|
59
62
|
opts[?h] and usage
|
|
60
63
|
context = nil
|
|
61
64
|
output = nil
|
|
62
65
|
|
|
63
66
|
if opts[?i]
|
|
64
|
-
context = ContextSpook.generate_context(verbose: opts[?v], format: opts[?F]) do
|
|
67
|
+
context = ContextSpook.generate_context(verbose: opts[?v], format: opts[?F], manifest: opts[?m]) do
|
|
65
68
|
context do
|
|
66
69
|
opts[?i].to_a.each do |glob|
|
|
67
70
|
glob = File.expand_path(glob)
|
|
@@ -74,7 +77,7 @@ if opts[?i]
|
|
|
74
77
|
end
|
|
75
78
|
else
|
|
76
79
|
filename = ARGV.shift or fail 'require context definition file as an argument'
|
|
77
|
-
context = ContextSpook.generate_context(filename, verbose: opts[?v], format: opts[?F])
|
|
80
|
+
context = ContextSpook.generate_context(filename, verbose: opts[?v], format: opts[?F], manifest: opts[?m])
|
|
78
81
|
end
|
|
79
82
|
|
|
80
83
|
output = STDOUT
|
data/context_spook.gemspec
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# stub: context_spook 1.
|
|
2
|
+
# stub: context_spook 1.7.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.7.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/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]
|
|
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/toon/encoder.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/encoder.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_encoder_spec.rb".freeze, "spec/context_spook/toon_spec.rb".freeze, "spec/examples/README.md".freeze, "spec/examples/conversions/keyed-tabular-objects.json".freeze, "spec/examples/conversions/keyed-tabular-objects.toon".freeze, "spec/examples/conversions/nested-field-groups.json".freeze, "spec/examples/conversions/nested-field-groups.toon".freeze, "spec/examples/conversions/tabular-arrays.json".freeze, "spec/examples/conversions/tabular-arrays.toon".freeze, "spec/examples/invalid/delimiter-mismatch.toon".freeze, "spec/examples/invalid/length-mismatch.toon".freeze, "spec/examples/invalid/multiple-root-primitives.toon".freeze, "spec/examples/valid/comments.toon".freeze, "spec/examples/valid/delimiter-scoping.toon".freeze, "spec/examples/valid/mixed-arrays.toon".freeze, "spec/examples/valid/nested-objects.toon".freeze, "spec/examples/valid/objects.toon".freeze, "spec/examples/valid/pipe-delimiter.toon".freeze, "spec/examples/valid/primitive-arrays.toon".freeze, "spec/examples/valid/tab-delimiter.toon".freeze, "spec/fixtures/encode/arrays-nested.json".freeze, "spec/fixtures/encode/arrays-objects.json".freeze, "spec/fixtures/encode/arrays-primitive.json".freeze, "spec/fixtures/encode/arrays-tabular.json".freeze, "spec/fixtures/encode/delimiters.json".freeze, "spec/fixtures/encode/objects-keyed.json".freeze, "spec/fixtures/encode/objects.json".freeze, "spec/fixtures/encode/primitives.json".freeze, "spec/fixtures/encode/whitespace.json".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
|
-
s.rubygems_version = "4.0.
|
|
21
|
+
s.rubygems_version = "4.0.22".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/context_spook/toon_spec.rb".freeze, "spec/spec_helper.rb".freeze]
|
|
23
|
+
s.test_files = ["spec/context_spook/generator_spec.rb".freeze, "spec/context_spook/toon_encoder_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.18.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,5 +36,4 @@ 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<toon-ruby>.freeze, [">= 0.1".freeze])
|
|
40
39
|
end
|
|
@@ -28,6 +28,8 @@ module ContextSpook
|
|
|
28
28
|
# during processing
|
|
29
29
|
# @param format [ String, nil ] the output format for the context, either
|
|
30
30
|
# 'JSON' or 'TOON'
|
|
31
|
+
# @param manifest [ TrueClass, FalseClass ] if true, file entries
|
|
32
|
+
# omit their content and only carry metadata
|
|
31
33
|
# @param block [ Proc ] a block containing the context definition, used if
|
|
32
34
|
# filename is nil
|
|
33
35
|
#
|
|
@@ -37,15 +39,18 @@ module ContextSpook
|
|
|
37
39
|
# @raise [ ArgumentError ] if neither a filename nor a block is provided
|
|
38
40
|
# @raise [ ArgumentError ] if an invalid format is specified
|
|
39
41
|
# @raise [ ArgumentError ] if the context definition file cannot be read or parsed
|
|
40
|
-
def self.generate_context(filename = nil, verbose: false, format: nil,
|
|
42
|
+
def self.generate_context(filename = nil, verbose: false, format: nil,
|
|
43
|
+
manifest: false, &block)
|
|
41
44
|
verbose = !!verbose
|
|
42
45
|
filename.present? ^ block or
|
|
43
46
|
raise ArgumentError, 'need either a filename or a &block argument'
|
|
44
|
-
generator =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
generator =
|
|
48
|
+
if filename
|
|
49
|
+
source = File.read(filename)
|
|
50
|
+
Generator.send(:new, verbose:, format:, manifest:).send(:parse, source)
|
|
51
|
+
else
|
|
52
|
+
Generator.send(:new, verbose:, format:, manifest:, &block)
|
|
53
|
+
end
|
|
49
54
|
generator.output_context_size
|
|
50
55
|
generator.context
|
|
51
56
|
end
|
|
@@ -70,6 +75,12 @@ module ContextSpook
|
|
|
70
75
|
# @return [ String ] the format identifier, either 'JSON' or 'TOON'
|
|
71
76
|
attr_reader :format
|
|
72
77
|
|
|
78
|
+
# The manifest method returns whether the generator is in manifest mode,
|
|
79
|
+
# where file entries omit their content.
|
|
80
|
+
#
|
|
81
|
+
# @return [ TrueClass, FalseClass ] true if in manifest mode
|
|
82
|
+
attr_reader :manifest
|
|
83
|
+
|
|
73
84
|
private_class_method :new
|
|
74
85
|
|
|
75
86
|
# The token_estimator attribute allows setting the logic used to estimate
|
|
@@ -86,9 +97,10 @@ module ContextSpook
|
|
|
86
97
|
# output during processing, defaults to lfalse.
|
|
87
98
|
# @param block [ Proc ] a block of code to be evaluated within the object's context
|
|
88
99
|
# If no block is given, the method does nothing.
|
|
89
|
-
def initialize(verbose: false, format: nil, &block)
|
|
90
|
-
@verbose
|
|
91
|
-
@format
|
|
100
|
+
def initialize(verbose: false, format: nil, manifest: false, &block)
|
|
101
|
+
@verbose = !!verbose
|
|
102
|
+
@format = (format || 'JSON').upcase
|
|
103
|
+
@manifest = !!manifest
|
|
92
104
|
%w[ TOON JSON ].include?(@format) or
|
|
93
105
|
raise ArgumentError,
|
|
94
106
|
"format needs to be either JSON or TOON, was #{@format.inspect}"
|
|
@@ -119,21 +131,18 @@ module ContextSpook
|
|
|
119
131
|
include Tins::DSLAccessor
|
|
120
132
|
include Term::ANSIColor
|
|
121
133
|
include ContextSpook::TOON
|
|
134
|
+
include ContextSpook::Utils
|
|
122
135
|
|
|
123
136
|
# The initialize method sets up the object by evaluating the provided block
|
|
124
137
|
# in the object's context.
|
|
125
138
|
#
|
|
126
|
-
# @param verbose [ TrueClass, FalseClass ] flag to enable verbose output
|
|
127
|
-
# during processing, defaults to false.
|
|
128
139
|
# @param block [ Proc ] a block of code to be evaluated within the object's context
|
|
129
140
|
# If no block is given, the method does nothing.
|
|
130
141
|
def initialize(generator:, &block)
|
|
131
142
|
@generator = generator
|
|
132
143
|
block and instance_eval(&block)
|
|
133
144
|
meta(format: generator.format)
|
|
134
|
-
if generator.
|
|
135
|
-
meta(toon_example: toon_example)
|
|
136
|
-
end
|
|
145
|
+
meta(manifest: true) if generator.manifest
|
|
137
146
|
end
|
|
138
147
|
|
|
139
148
|
# The generator method returns the generator object associated with this
|
|
@@ -145,6 +154,8 @@ module ContextSpook
|
|
|
145
154
|
|
|
146
155
|
delegate :verbose, to: :generator
|
|
147
156
|
|
|
157
|
+
delegate :manifest, to: :generator
|
|
158
|
+
|
|
148
159
|
# The namespace method creates a scoped block with a given name.
|
|
149
160
|
#
|
|
150
161
|
# @param name [ Object ] the name to scope the block with
|
|
@@ -205,11 +216,10 @@ module ContextSpook
|
|
|
205
216
|
#
|
|
206
217
|
# @param filename [ String ] the path to the JSON file to be read and parsed
|
|
207
218
|
#
|
|
208
|
-
# @return [ Object, nil ] the parsed JSON data structure or nil if the
|
|
219
|
+
# @return [ Object, nil ] the parsed JSON data structure or nil if the
|
|
220
|
+
# file cannot be read
|
|
209
221
|
def json(filename)
|
|
210
|
-
file_size =
|
|
211
|
-
File.size(filename), format: '%.2f %U', unit: ?b, prefix: 1024
|
|
212
|
-
)
|
|
222
|
+
file_size = format_size(File.size(filename))
|
|
213
223
|
verbose_puts "Read #{filename.inspect} as JSON (%s) for context." % file_size
|
|
214
224
|
JSON.load_file(filename)
|
|
215
225
|
rescue Errno::ENOENT, JSON::ParserError => e
|
|
@@ -231,9 +241,7 @@ module ContextSpook
|
|
|
231
241
|
# @return [ Object, nil ] the parsed YAML data structure or nil if the
|
|
232
242
|
# file cannot be read
|
|
233
243
|
def yaml(filename)
|
|
234
|
-
file_size =
|
|
235
|
-
File.size(filename), format: '%.2f %U', unit: ?b, prefix: 1024
|
|
236
|
-
)
|
|
244
|
+
file_size = format_size(File.size(filename))
|
|
237
245
|
verbose_puts "Read #{filename.inspect} as YAML (%s) for context." % file_size
|
|
238
246
|
YAML.load_file(filename)
|
|
239
247
|
rescue Errno::ENOENT, Psych::SyntaxError => e
|
|
@@ -252,32 +260,51 @@ module ContextSpook
|
|
|
252
260
|
# content.
|
|
253
261
|
#
|
|
254
262
|
# It reads the specified file and creates an entry in the files hash with
|
|
255
|
-
# the file's content, along with its namespace and optional tags.
|
|
263
|
+
# the file's content, along with its namespace and optional tags. In
|
|
264
|
+
# manifest mode, only metadata fields are stored.
|
|
256
265
|
#
|
|
257
266
|
# @param filename [ String ] the path to the file to be read and stored
|
|
258
267
|
# @param tags [ Array<String>, nil ] optional array of tags to associate with the file
|
|
259
268
|
#
|
|
260
269
|
# @return [ Hash ] the created file entry with content, namespace, and tags
|
|
261
270
|
def file(filename, tags: nil)
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
271
|
+
size = File.size(filename)
|
|
272
|
+
tokens = estimate_tokens(size)
|
|
273
|
+
if manifest
|
|
274
|
+
files[filename] = {
|
|
275
|
+
namespace: scope_top,
|
|
276
|
+
size: ,
|
|
277
|
+
tokens: ,
|
|
278
|
+
content_types: MIME::Types.type_for(filename)
|
|
279
|
+
.map(&:content_type).full?,
|
|
280
|
+
tags: (Array(tags) if tags),
|
|
281
|
+
}.compact
|
|
282
|
+
else
|
|
283
|
+
content = File.read(filename)
|
|
284
|
+
content_lines = content.lines
|
|
285
|
+
numbered_lines = content_lines.each_with_index.map {
|
|
286
|
+
{ linenumber: _2 + 1, line: _1.chomp }
|
|
287
|
+
}
|
|
288
|
+
files[filename] = {
|
|
289
|
+
namespace: scope_top,
|
|
290
|
+
content: numbered_lines,
|
|
291
|
+
size: ,
|
|
292
|
+
tokens: ,
|
|
293
|
+
lines: content_lines.size,
|
|
294
|
+
content_types: MIME::Types.type_for(filename)
|
|
295
|
+
.map(&:content_type).full?,
|
|
296
|
+
tags: (Array(tags) if tags),
|
|
297
|
+
}.compact
|
|
298
|
+
end
|
|
299
|
+
verbose_puts "Read #{filename.inspect} (%s, %s) for context." %
|
|
300
|
+
[ format_size(size), format_tokens(tokens) ]
|
|
275
301
|
nil
|
|
276
302
|
rescue Errno::ENOENT => e
|
|
277
303
|
verbose_puts color(208) { "Reading #{filename.inspect} caused #{e.class}: #{e}" }
|
|
278
304
|
end
|
|
279
305
|
|
|
280
|
-
# The commands method sets up a DSL accessor for provided command
|
|
306
|
+
# The commands method sets up a DSL accessor for provided command
|
|
307
|
+
# outputs.
|
|
281
308
|
#
|
|
282
309
|
# @param value [ Hash ] the hash of commands to be set
|
|
283
310
|
dsl_accessor :commands do
|
|
@@ -286,8 +313,8 @@ module ContextSpook
|
|
|
286
313
|
|
|
287
314
|
# The command method executes a shell command and stores its result.
|
|
288
315
|
#
|
|
289
|
-
# This method runs a given shell command and records the output, exit
|
|
290
|
-
# working directory, and optional tags in the commands hash.
|
|
316
|
+
# This method runs a given shell command and records the output, exit
|
|
317
|
+
# code, working directory, and optional tags in the commands hash.
|
|
291
318
|
#
|
|
292
319
|
# @param shell_command [ String ] the shell command to execute
|
|
293
320
|
# @param tags [ Array<String>, nil ] optional array of tags to associate
|
|
@@ -303,17 +330,20 @@ module ContextSpook
|
|
|
303
330
|
"Executing #{shell_command.inspect} resulted in exit code #{exit_code}."
|
|
304
331
|
}
|
|
305
332
|
end
|
|
333
|
+
output_size = output.bytesize
|
|
334
|
+
output_tokens = estimate_tokens(output_size)
|
|
306
335
|
commands[shell_command] = {
|
|
307
|
-
namespace:
|
|
336
|
+
namespace: scope_top,
|
|
308
337
|
output:,
|
|
338
|
+
size: output_size,
|
|
339
|
+
tokens: output_tokens,
|
|
309
340
|
exit_code:,
|
|
310
341
|
working_directory: Dir.pwd,
|
|
311
|
-
tags:
|
|
342
|
+
tags: (Array(tags) if tags),
|
|
312
343
|
}.compact
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
verbose_puts "Executed #{shell_command.inspect} with output (%s) for context." % output_size
|
|
344
|
+
verbose_puts "Executed #{shell_command.inspect} with output "\
|
|
345
|
+
"(%s, %s) for context." %
|
|
346
|
+
[ format_size(output_size), format_tokens(output_tokens) ]
|
|
317
347
|
nil
|
|
318
348
|
end
|
|
319
349
|
|
|
@@ -351,7 +381,7 @@ module ContextSpook
|
|
|
351
381
|
# @return [ Integer ] the size in bytes of the JSON representation of the
|
|
352
382
|
# context
|
|
353
383
|
def size
|
|
354
|
-
to_json.
|
|
384
|
+
to_json.bytesize
|
|
355
385
|
end
|
|
356
386
|
end
|
|
357
387
|
|
|
@@ -15,7 +15,7 @@ module ContextSpook::OutputContext
|
|
|
15
15
|
if @context
|
|
16
16
|
context = @format == 'TOON' ? @context.to_toon : @context.to_json
|
|
17
17
|
end
|
|
18
|
-
formatted_size = ContextSpook::Utils.format_size(context.
|
|
18
|
+
formatted_size = ContextSpook::Utils.format_size(context.bytesize)
|
|
19
19
|
tokens = @token_estimator.call(context)
|
|
20
20
|
formatted_tokens = ContextSpook::Utils.format_tokens(tokens)
|
|
21
21
|
|
|
@@ -33,7 +33,7 @@ module ContextSpook::OutputContext
|
|
|
33
33
|
# defaults to STDOUT
|
|
34
34
|
def output_context(output: STDOUT)
|
|
35
35
|
if @format == 'TOON'
|
|
36
|
-
|
|
36
|
+
@context.to_toon_stream(output)
|
|
37
37
|
else
|
|
38
38
|
JSON.dump(@context.as_json, output)
|
|
39
39
|
end
|