llms-txt-ruby 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +71 -0
- data/.github/workflows/push.yml +35 -0
- data/.gitignore +10 -1
- data/.rubocop.yml +27 -0
- data/.ruby-version +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +88 -0
- data/README.md +279 -30
- data/Rakefile +10 -0
- data/bin/llms-txt +242 -0
- data/lib/llms_txt/bulk_transformer.rb +137 -0
- data/lib/llms_txt/config.rb +113 -0
- data/lib/llms_txt/errors.rb +31 -0
- data/lib/llms_txt/generator.rb +234 -0
- data/lib/llms_txt/markdown_transformer.rb +90 -0
- data/lib/llms_txt/parser.rb +223 -0
- data/lib/llms_txt/validator.rb +222 -0
- data/lib/llms_txt/version.rb +6 -0
- data/lib/llms_txt.rb +130 -0
- data/llms-txt-ruby.gemspec +18 -9
- data/llms-txt.yml.example +26 -0
- data/mise.toml +2 -0
- data/renovate.json +30 -0
- metadata +115 -10
- data/lib/llms-txt/version.rb +0 -3
@@ -0,0 +1,222 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LlmsTxt
|
4
|
+
# Validates llms.txt content against the llms.txt specification
|
5
|
+
#
|
6
|
+
# Ensures that llms.txt content follows proper formatting rules including:
|
7
|
+
# - Required H1 title header
|
8
|
+
# - Optional description blockquote
|
9
|
+
# - Proper section ordering (Documentation, Examples, Optional)
|
10
|
+
# - Valid markdown syntax and link formats
|
11
|
+
# - File size and line length limits
|
12
|
+
#
|
13
|
+
# @example Validate llms.txt content
|
14
|
+
# validator = LlmsTxt::Validator.new(content)
|
15
|
+
# validator.valid? # => true or false
|
16
|
+
# validator.errors # => Array of error messages
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
class Validator
|
20
|
+
# @return [String] the llms.txt content being validated
|
21
|
+
attr_reader :content
|
22
|
+
|
23
|
+
# @return [Array<String>] array of validation error messages
|
24
|
+
attr_reader :errors
|
25
|
+
|
26
|
+
# Required sections that must appear in llms.txt
|
27
|
+
REQUIRED_SECTIONS = ['# '].freeze
|
28
|
+
|
29
|
+
# Optional sections that may appear in llms.txt
|
30
|
+
OPTIONAL_SECTIONS = ['> ', '## Documentation', '## Examples', '## Optional'].freeze
|
31
|
+
|
32
|
+
# Maximum length for a single line in characters
|
33
|
+
MAX_LINE_LENGTH = 120
|
34
|
+
|
35
|
+
# Maximum file size in bytes
|
36
|
+
MAX_FILE_SIZE = 50_000
|
37
|
+
|
38
|
+
# Initialize a new validator
|
39
|
+
#
|
40
|
+
# @param content [String] the llms.txt content to validate
|
41
|
+
def initialize(content)
|
42
|
+
@content = content
|
43
|
+
@errors = []
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check if content is valid
|
47
|
+
#
|
48
|
+
# Runs all validation checks and returns whether the content is valid.
|
49
|
+
# Use {#errors} to access validation error messages.
|
50
|
+
#
|
51
|
+
# @return [Boolean] true if content is valid, false otherwise
|
52
|
+
def valid?
|
53
|
+
validate!
|
54
|
+
errors.empty?
|
55
|
+
end
|
56
|
+
|
57
|
+
# Validate content and return result
|
58
|
+
#
|
59
|
+
# Runs all validation checks, populates {#errors} array, and returns whether
|
60
|
+
# the content is valid.
|
61
|
+
#
|
62
|
+
# @return [Boolean] true if content is valid, false otherwise
|
63
|
+
def validate!
|
64
|
+
@errors = []
|
65
|
+
|
66
|
+
validate_required_sections
|
67
|
+
validate_structure
|
68
|
+
validate_markdown_syntax
|
69
|
+
validate_links
|
70
|
+
validate_file_size
|
71
|
+
|
72
|
+
errors.empty?
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# Checks for required H1 title header and validates title length
|
78
|
+
#
|
79
|
+
# Adds errors if title is missing or exceeds 80 characters
|
80
|
+
def validate_required_sections
|
81
|
+
lines = content.lines
|
82
|
+
|
83
|
+
unless lines.first&.start_with?('# ')
|
84
|
+
errors << 'Missing required H1 title (must start with "# ")'
|
85
|
+
end
|
86
|
+
|
87
|
+
return unless lines.first&.strip&.length.to_i > 80
|
88
|
+
|
89
|
+
errors << 'Title is too long (max 80 characters)'
|
90
|
+
end
|
91
|
+
|
92
|
+
# Validates H1 uniqueness, description length, and section ordering
|
93
|
+
#
|
94
|
+
# Ensures only one H1, description under 200 chars, and proper section order
|
95
|
+
def validate_structure
|
96
|
+
lines = content.lines
|
97
|
+
h1_count = lines.count { |line| line.start_with?('# ') }
|
98
|
+
|
99
|
+
errors << 'Multiple H1 headers found (only one allowed)' if h1_count > 1
|
100
|
+
|
101
|
+
if lines[1]&.start_with?('> ') && lines[1].strip.length > 200
|
102
|
+
errors << 'Description blockquote is too long (max 200 characters)'
|
103
|
+
end
|
104
|
+
|
105
|
+
validate_section_order
|
106
|
+
end
|
107
|
+
|
108
|
+
# Verifies sections appear in correct order: Documentation, Examples, Optional
|
109
|
+
#
|
110
|
+
# Detects out-of-order sections and adds validation errors
|
111
|
+
def validate_section_order
|
112
|
+
sections = content.scan(/^## (.+)$/).flatten
|
113
|
+
expected_order = %w[Documentation Examples Optional]
|
114
|
+
|
115
|
+
current_index = -1
|
116
|
+
sections.each do |section|
|
117
|
+
index = expected_order.index(section)
|
118
|
+
next unless index
|
119
|
+
|
120
|
+
errors << "Section '#{section}' is out of order" if index < current_index
|
121
|
+
current_index = index
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Validates markdown syntax including links, lists, and headers
|
126
|
+
#
|
127
|
+
# Delegates to specialized validators for different markdown elements
|
128
|
+
def validate_markdown_syntax
|
129
|
+
validate_link_format
|
130
|
+
validate_list_format
|
131
|
+
validate_headers
|
132
|
+
end
|
133
|
+
|
134
|
+
# Checks markdown links for empty text/URLs and valid URL formats
|
135
|
+
#
|
136
|
+
# Validates URLs follow expected patterns for relative/absolute paths
|
137
|
+
def validate_link_format
|
138
|
+
content.scan(/\[([^\]]*)\]\(([^)]*)\)/) do |text, url|
|
139
|
+
errors << 'Empty link text found' if text.empty?
|
140
|
+
|
141
|
+
errors << 'Empty link URL found' if url.empty?
|
142
|
+
|
143
|
+
# Allow relative paths, absolute paths, HTTP(S) URLs, and common file extensions
|
144
|
+
url_pattern = %r{
|
145
|
+
^(?:
|
146
|
+
https?://|
|
147
|
+
/|
|
148
|
+
\.\.?/|
|
149
|
+
[a-zA-Z0-9_.-]+(?:/|\.md|\.txt|\.rb|\.html)?|
|
150
|
+
[A-Z]+[a-zA-Z]*|
|
151
|
+
docs/|
|
152
|
+
examples/|
|
153
|
+
lib/
|
154
|
+
).*$
|
155
|
+
}x
|
156
|
+
unless url =~ url_pattern
|
157
|
+
errors << "Invalid URL format: #{url}"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Validates list items match expected markdown link format
|
163
|
+
#
|
164
|
+
# Ensures list items with links have proper syntax with optional descriptions
|
165
|
+
def validate_list_format
|
166
|
+
content.lines.each_with_index do |line, index|
|
167
|
+
next unless line =~ /^[-*]\s+\[/
|
168
|
+
|
169
|
+
# Allow both with and without descriptions
|
170
|
+
next if line =~ /^[-*]\s+\[.+\]\(.+\)(?::\s*.+)?$/
|
171
|
+
|
172
|
+
errors << "Invalid list item format at line #{index + 1}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Validates header levels and content
|
177
|
+
#
|
178
|
+
# Checks for empty H1 headers and warns about headers deeper than H2
|
179
|
+
def validate_headers
|
180
|
+
content.scan(/^(#+)\s+(.+)$/) do |hashes, text|
|
181
|
+
level = hashes.length
|
182
|
+
|
183
|
+
if level == 1 && text.strip.empty?
|
184
|
+
errors << 'Empty H1 header text'
|
185
|
+
elsif level > 2
|
186
|
+
errors << "Headers deeper than H2 not recommended (found H#{level})"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Validates link security and format requirements
|
192
|
+
#
|
193
|
+
# Warns about non-HTTPS URLs and URLs containing spaces
|
194
|
+
def validate_links
|
195
|
+
links = content.scan(/\[([^\]]+)\]\(([^)]+)\)/)
|
196
|
+
|
197
|
+
links.each do |_text, url|
|
198
|
+
if url.start_with?('http') && !url.start_with?('https')
|
199
|
+
errors << "Non-HTTPS URL found: #{url} (consider using HTTPS)"
|
200
|
+
end
|
201
|
+
|
202
|
+
errors << "URL contains spaces: #{url}" if url.include?(' ')
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Checks file size and individual line lengths against limits
|
207
|
+
#
|
208
|
+
# Enforces 50KB file size limit and 120 character line length limit
|
209
|
+
def validate_file_size
|
210
|
+
if content.bytesize > MAX_FILE_SIZE
|
211
|
+
errors << "File size exceeds maximum (#{MAX_FILE_SIZE} bytes)"
|
212
|
+
end
|
213
|
+
|
214
|
+
lines = content.lines
|
215
|
+
lines.each_with_index do |line, index|
|
216
|
+
if line.chomp.length > MAX_LINE_LENGTH
|
217
|
+
errors << "Line #{index + 1} exceeds maximum length (#{MAX_LINE_LENGTH} characters)"
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
data/lib/llms_txt.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'zeitwerk'
|
4
|
+
require 'pathname'
|
5
|
+
require 'find'
|
6
|
+
|
7
|
+
loader = Zeitwerk::Loader.for_gem
|
8
|
+
loader.setup
|
9
|
+
|
10
|
+
module LlmsTxt
|
11
|
+
|
12
|
+
class << self
|
13
|
+
# Generates llms.txt from existing markdown documentation
|
14
|
+
#
|
15
|
+
# @param docs_path [String, nil] path to documentation directory or file (optional if
|
16
|
+
# config_file provided)
|
17
|
+
# @param options [Hash] generation options
|
18
|
+
# @option options [String] :config_file path to YAML config file (auto-finds llms-txt.yml)
|
19
|
+
# @option options [String] :base_url base URL for converting relative links (overrides config)
|
20
|
+
# @option options [String] :title project title (auto-detected if not provided, overrides
|
21
|
+
# config)
|
22
|
+
# @option options [String] :description project description (auto-detected if not provided,
|
23
|
+
# overrides config)
|
24
|
+
# @option options [String] :output output file path (default: 'llms.txt', overrides config)
|
25
|
+
# @option options [Boolean] :convert_urls convert HTML URLs to markdown format (overrides
|
26
|
+
# config)
|
27
|
+
# @option options [Boolean] :verbose enable verbose output (overrides config)
|
28
|
+
# @return [String] generated llms.txt content
|
29
|
+
#
|
30
|
+
# @example Generate from docs directory
|
31
|
+
# LlmsTxt.generate_from_docs('./docs')
|
32
|
+
#
|
33
|
+
# @example Generate using config file
|
34
|
+
# LlmsTxt.generate_from_docs(config_file: 'llms-txt.yml')
|
35
|
+
#
|
36
|
+
# @example Generate with config file and overrides
|
37
|
+
# LlmsTxt.generate_from_docs('./docs',
|
38
|
+
# config_file: 'my-config.yml',
|
39
|
+
# title: 'Override Title'
|
40
|
+
# )
|
41
|
+
def generate_from_docs(docs_path = nil, options = {})
|
42
|
+
# Support config-first usage: generate_from_docs(config_file: 'path.yml')
|
43
|
+
if docs_path.is_a?(Hash) && docs_path.key?(:config_file)
|
44
|
+
options = docs_path
|
45
|
+
docs_path = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
config = Config.new(options[:config_file])
|
49
|
+
merged_options = config.merge_with_options(options)
|
50
|
+
|
51
|
+
# Use docs_path param or config file docs setting
|
52
|
+
final_docs_path = docs_path || merged_options[:docs]
|
53
|
+
|
54
|
+
Generator.new(final_docs_path, merged_options).generate
|
55
|
+
end
|
56
|
+
|
57
|
+
# Transforms a markdown file to be AI-friendly
|
58
|
+
#
|
59
|
+
# @param file_path [String] path to markdown file
|
60
|
+
# @param options [Hash] transformation options
|
61
|
+
# @option options [String] :config_file path to YAML config file (auto-finds llms-txt.yml)
|
62
|
+
# @option options [String] :base_url base URL for expanding relative links (overrides config)
|
63
|
+
# @option options [Boolean] :convert_urls convert HTML URLs to markdown format (overrides
|
64
|
+
# config)
|
65
|
+
# @option options [Boolean] :verbose enable verbose output (overrides config)
|
66
|
+
# @return [String] transformed markdown content
|
67
|
+
#
|
68
|
+
# @example Transform with direct options
|
69
|
+
# LlmsTxt.transform_markdown('README.md',
|
70
|
+
# base_url: 'https://myproject.io',
|
71
|
+
# convert_urls: true
|
72
|
+
# )
|
73
|
+
#
|
74
|
+
# @example Transform using config file
|
75
|
+
# LlmsTxt.transform_markdown('README.md', config_file: 'llms-txt.yml')
|
76
|
+
def transform_markdown(file_path, options = {})
|
77
|
+
config = Config.new(options[:config_file])
|
78
|
+
merged_options = config.merge_with_options(options)
|
79
|
+
|
80
|
+
MarkdownTransformer.new(file_path, merged_options).transform
|
81
|
+
end
|
82
|
+
|
83
|
+
# Bulk transforms multiple markdown files to be AI-friendly
|
84
|
+
#
|
85
|
+
# @param docs_path [String] path to documentation directory
|
86
|
+
# @param options [Hash] transformation options
|
87
|
+
# @option options [String] :config_file path to YAML config file (auto-finds llms-txt.yml)
|
88
|
+
# @option options [String] :base_url base URL for expanding relative links (overrides config)
|
89
|
+
# @option options [Boolean] :convert_urls convert HTML URLs to markdown format (overrides
|
90
|
+
# config)
|
91
|
+
# @option options [String] :suffix suffix for transformed files (default: '.llm', overrides
|
92
|
+
# config)
|
93
|
+
# @option options [Array<String>] :excludes glob patterns for files to exclude (overrides
|
94
|
+
# config)
|
95
|
+
# @option options [Boolean] :verbose enable verbose output (overrides config)
|
96
|
+
# @return [Array<String>] paths of transformed files
|
97
|
+
#
|
98
|
+
# @example Bulk transform with direct options
|
99
|
+
# LlmsTxt.bulk_transform('./docs',
|
100
|
+
# base_url: 'https://myproject.io',
|
101
|
+
# suffix: '.ai',
|
102
|
+
# excludes: ['**/private/**', 'draft-*.md']
|
103
|
+
# )
|
104
|
+
#
|
105
|
+
# @example Bulk transform using config file
|
106
|
+
# LlmsTxt.bulk_transform('./docs', config_file: 'llms-txt.yml')
|
107
|
+
def bulk_transform(docs_path, options = {})
|
108
|
+
config = Config.new(options[:config_file])
|
109
|
+
merged_options = config.merge_with_options(options)
|
110
|
+
|
111
|
+
BulkTransformer.new(docs_path, merged_options).transform_all
|
112
|
+
end
|
113
|
+
|
114
|
+
# Parses an existing llms.txt file
|
115
|
+
#
|
116
|
+
# @param file_path [String] path to the llms.txt file to parse
|
117
|
+
# @return [Parser] parsed llms.txt object
|
118
|
+
def parse(file_path)
|
119
|
+
Parser.new(file_path).parse
|
120
|
+
end
|
121
|
+
|
122
|
+
# Validates llms.txt content
|
123
|
+
#
|
124
|
+
# @param content [String] the llms.txt content to validate
|
125
|
+
# @return [Boolean] true if content is valid, false otherwise
|
126
|
+
def validate(content)
|
127
|
+
Validator.new(content).valid?
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/llms-txt-ruby.gemspec
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'lib/
|
3
|
+
require_relative 'lib/llms_txt/version'
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'llms-txt-ruby'
|
7
|
-
spec.version =
|
7
|
+
spec.version = LlmsTxt::VERSION
|
8
8
|
spec.authors = ['Maciej Mensfeld']
|
9
9
|
spec.email = %w[maciej@mensfeld.pl]
|
10
10
|
|
11
|
-
spec.summary = '
|
11
|
+
spec.summary = 'Simple tool for generating llms.txt files from existing markdown documentation'
|
12
12
|
spec.description = <<~DESC
|
13
|
-
A Ruby gem
|
14
|
-
|
15
|
-
and
|
16
|
-
|
13
|
+
A simple Ruby gem for generating llms.txt files from existing markdown documentation.
|
14
|
+
Transform your docs to be AI-friendly with two core functions: generate llms.txt from
|
15
|
+
documentation directories and transform individual markdown files by expanding relative
|
16
|
+
links and converting URLs.
|
17
17
|
DESC
|
18
18
|
|
19
19
|
spec.homepage = 'https://github.com/mensfeld/llms-txt-ruby'
|
@@ -25,10 +25,19 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.metadata['source_code_uri'] = 'https://github.com/mensfeld/llms-txt-ruby'
|
26
26
|
spec.metadata['changelog_uri'] = 'https://github.com/mensfeld/llms-txt-ruby/blob/main/CHANGELOG.md'
|
27
27
|
spec.metadata['documentation_uri'] = 'https://github.com/mensfeld/llms-txt-ruby'
|
28
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
28
29
|
|
29
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
|
30
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec|test)/}) }
|
30
31
|
|
31
32
|
spec.bindir = 'bin'
|
32
|
-
spec.executables = spec.files.grep(%r{\
|
33
|
+
spec.executables = spec.files.grep(%r{\Abin/}) { |f| File.basename(f) }
|
33
34
|
spec.require_paths = ['lib']
|
35
|
+
|
36
|
+
spec.add_dependency 'zeitwerk', '~> 2.6'
|
37
|
+
|
38
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
39
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
40
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
41
|
+
spec.add_development_dependency 'rubocop', '~> 1.0'
|
42
|
+
spec.add_development_dependency 'simplecov', '~> 0.21'
|
34
43
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Example llms-txt.yml configuration file
|
2
|
+
# Copy to llms-txt.yml and customize for your project
|
3
|
+
|
4
|
+
# Path to documentation directory or file
|
5
|
+
docs: ./docs
|
6
|
+
|
7
|
+
# Base URL for expanding relative links (optional)
|
8
|
+
base_url: https://myproject.io
|
9
|
+
|
10
|
+
# Project information (optional - will auto-detect from docs if not provided)
|
11
|
+
title: My Awesome Project
|
12
|
+
description: A Ruby library that helps developers build amazing applications
|
13
|
+
|
14
|
+
# Output file path (optional)
|
15
|
+
output: llms.txt
|
16
|
+
|
17
|
+
# Transform options (optional)
|
18
|
+
convert_urls: true # Convert .html links to .md
|
19
|
+
verbose: false # Verbose output
|
20
|
+
|
21
|
+
# Bulk transformation options (optional)
|
22
|
+
suffix: .llm # Suffix for transformed files (creates file.llm.md)
|
23
|
+
excludes: # Patterns for files to exclude from bulk transformation
|
24
|
+
- "**/private/**" # Exclude private directories
|
25
|
+
- "**/draft-*.md" # Exclude draft files
|
26
|
+
- "**/.*" # Exclude hidden files
|
data/mise.toml
ADDED
data/renovate.json
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
3
|
+
"extends": [
|
4
|
+
"config:recommended"
|
5
|
+
],
|
6
|
+
"ignorePaths": [
|
7
|
+
"spec/integrations"
|
8
|
+
],
|
9
|
+
"github-actions": {
|
10
|
+
"enabled": true,
|
11
|
+
"pinDigests": true
|
12
|
+
},
|
13
|
+
"ruby": {
|
14
|
+
"enabled": true
|
15
|
+
},
|
16
|
+
"packageRules": [
|
17
|
+
{
|
18
|
+
"matchManagers": [
|
19
|
+
"github-actions"
|
20
|
+
],
|
21
|
+
"minimumReleaseAge": "7 days"
|
22
|
+
},
|
23
|
+
{
|
24
|
+
"matchManagers": [
|
25
|
+
"bundler"
|
26
|
+
],
|
27
|
+
"minimumReleaseAge": "7 days"
|
28
|
+
}
|
29
|
+
]
|
30
|
+
}
|
metadata
CHANGED
@@ -1,30 +1,134 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: llms-txt-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maciej Mensfeld
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
-
dependencies:
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: zeitwerk
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '2.6'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '2.6'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: bundler
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: rake
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '13.0'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '13.0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: rspec
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3.0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '3.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rubocop
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '1.0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: simplecov
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0.21'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0.21'
|
12
96
|
description: |
|
13
|
-
A Ruby gem
|
14
|
-
|
15
|
-
and
|
16
|
-
|
97
|
+
A simple Ruby gem for generating llms.txt files from existing markdown documentation.
|
98
|
+
Transform your docs to be AI-friendly with two core functions: generate llms.txt from
|
99
|
+
documentation directories and transform individual markdown files by expanding relative
|
100
|
+
links and converting URLs.
|
17
101
|
email:
|
18
102
|
- maciej@mensfeld.pl
|
19
|
-
executables:
|
103
|
+
executables:
|
104
|
+
- llms-txt
|
20
105
|
extensions: []
|
21
106
|
extra_rdoc_files: []
|
22
107
|
files:
|
108
|
+
- ".github/workflows/ci.yml"
|
109
|
+
- ".github/workflows/push.yml"
|
23
110
|
- ".gitignore"
|
111
|
+
- ".rubocop.yml"
|
112
|
+
- ".ruby-version"
|
113
|
+
- Gemfile
|
114
|
+
- Gemfile.lock
|
24
115
|
- LICENSE
|
25
116
|
- README.md
|
26
|
-
-
|
117
|
+
- Rakefile
|
118
|
+
- bin/llms-txt
|
119
|
+
- lib/llms_txt.rb
|
120
|
+
- lib/llms_txt/bulk_transformer.rb
|
121
|
+
- lib/llms_txt/config.rb
|
122
|
+
- lib/llms_txt/errors.rb
|
123
|
+
- lib/llms_txt/generator.rb
|
124
|
+
- lib/llms_txt/markdown_transformer.rb
|
125
|
+
- lib/llms_txt/parser.rb
|
126
|
+
- lib/llms_txt/validator.rb
|
127
|
+
- lib/llms_txt/version.rb
|
27
128
|
- llms-txt-ruby.gemspec
|
129
|
+
- llms-txt.yml.example
|
130
|
+
- mise.toml
|
131
|
+
- renovate.json
|
28
132
|
homepage: https://github.com/mensfeld/llms-txt-ruby
|
29
133
|
licenses:
|
30
134
|
- MIT
|
@@ -34,6 +138,7 @@ metadata:
|
|
34
138
|
source_code_uri: https://github.com/mensfeld/llms-txt-ruby
|
35
139
|
changelog_uri: https://github.com/mensfeld/llms-txt-ruby/blob/main/CHANGELOG.md
|
36
140
|
documentation_uri: https://github.com/mensfeld/llms-txt-ruby
|
141
|
+
rubygems_mfa_required: 'true'
|
37
142
|
rdoc_options: []
|
38
143
|
require_paths:
|
39
144
|
- lib
|
@@ -48,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
48
153
|
- !ruby/object:Gem::Version
|
49
154
|
version: '0'
|
50
155
|
requirements: []
|
51
|
-
rubygems_version: 3.6.
|
156
|
+
rubygems_version: 3.6.9
|
52
157
|
specification_version: 4
|
53
|
-
summary:
|
158
|
+
summary: Simple tool for generating llms.txt files from existing markdown documentation
|
54
159
|
test_files: []
|
data/lib/llms-txt/version.rb
DELETED