html2rss 0.27.2 → 0.29.1

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.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +82 -25
  3. data/html2rss.gemspec +0 -1
  4. data/lib/html2rss/article.rb +1 -2
  5. data/lib/html2rss/auto_source/README.md +12 -9
  6. data/lib/html2rss/auto_source/cleanup.rb +92 -1
  7. data/lib/html2rss/auto_source/scraper/json_state/document_scanner.rb +3 -5
  8. data/lib/html2rss/auto_source/scraper/meta_oembed.rb +15 -5
  9. data/lib/html2rss/auto_source/scraper/microdata.rb +8 -6
  10. data/lib/html2rss/auto_source/scraper/native_feed.rb +93 -0
  11. data/lib/html2rss/auto_source/scraper/schema.rb +39 -21
  12. data/lib/html2rss/auto_source/scraper.rb +6 -4
  13. data/lib/html2rss/auto_source/segmenter/cluster.rb +2 -3
  14. data/lib/html2rss/auto_source/segmenter/primary_link.rb +1 -2
  15. data/lib/html2rss/auto_source/segmenter.rb +1 -2
  16. data/lib/html2rss/auto_source.rb +32 -11
  17. data/lib/html2rss/batch.rb +169 -0
  18. data/lib/html2rss/capture/README.md +15 -6
  19. data/lib/html2rss/capture.rb +158 -15
  20. data/lib/html2rss/channel.rb +1 -2
  21. data/lib/html2rss/cli/probe_view.rb +66 -0
  22. data/lib/html2rss/cli/render.rb +124 -0
  23. data/lib/html2rss/cli/validate.rb +91 -0
  24. data/lib/html2rss/cli.rb +318 -193
  25. data/lib/html2rss/config/auto_source_contract.rb +7 -0
  26. data/lib/html2rss/config/request_headers.rb +4 -14
  27. data/lib/html2rss/config/schema.rb +7 -5
  28. data/lib/html2rss/config/validator.rb +1 -0
  29. data/lib/html2rss/config.rb +64 -18
  30. data/lib/html2rss/doctor/botasaurus.rb +130 -0
  31. data/lib/html2rss/feed_builder/rss.rb +1 -2
  32. data/lib/html2rss/feed_pipeline/README.md +7 -5
  33. data/lib/html2rss/feed_pipeline/auto_fallback.rb +78 -19
  34. data/lib/html2rss/feed_pipeline/runtime_policy.rb +1 -0
  35. data/lib/html2rss/feed_pipeline.rb +48 -21
  36. data/lib/html2rss/feed_resolution/README.md +52 -0
  37. data/lib/html2rss/feed_resolution/candidate_generator.rb +139 -0
  38. data/lib/html2rss/feed_resolution/diag.rb +32 -0
  39. data/lib/html2rss/feed_resolution/options.rb +31 -0
  40. data/lib/html2rss/feed_resolution/policy.rb +47 -0
  41. data/lib/html2rss/feed_resolution/probe.rb +59 -0
  42. data/lib/html2rss/feed_resolution/scorer.rb +49 -0
  43. data/lib/html2rss/feed_resolution.rb +268 -0
  44. data/lib/html2rss/html/article_extractor/heading_extractor.rb +24 -14
  45. data/lib/html2rss/html/article_extractor.rb +4 -9
  46. data/lib/html2rss/html/feed_link.rb +4 -2
  47. data/lib/html2rss/html/navigator.rb +4 -39
  48. data/lib/html2rss/html/probe.rb +82 -0
  49. data/lib/html2rss/html/sst_article_extractor.rb +1 -2
  50. data/lib/html2rss/link_destination/noise_policy.rb +1 -2
  51. data/lib/html2rss/link_destination/path_classifier.rb +20 -19
  52. data/lib/html2rss/mcp/README.md +80 -0
  53. data/lib/html2rss/mcp/contract.rb +135 -13
  54. data/lib/html2rss/mcp/outcome/playbook.rb +122 -0
  55. data/lib/html2rss/mcp/outcome.rb +112 -40
  56. data/lib/html2rss/mcp/runtime.rb +45 -0
  57. data/lib/html2rss/mcp/server/tools.rb +277 -0
  58. data/lib/html2rss/mcp/server.rb +91 -232
  59. data/lib/html2rss/mcp.rb +3 -0
  60. data/lib/html2rss/page_recon/README.md +69 -0
  61. data/lib/html2rss/page_recon/diagnostics.rb +211 -0
  62. data/lib/html2rss/page_recon.rb +297 -0
  63. data/lib/html2rss/recon.rb +252 -0
  64. data/lib/html2rss/request_service/blocked_surface.rb +1 -0
  65. data/lib/html2rss/request_service/botasaurus_contract.rb +8 -1
  66. data/lib/html2rss/request_service/botasaurus_strategy.rb +1 -1
  67. data/lib/html2rss/request_service/faraday_strategy.rb +11 -7
  68. data/lib/html2rss/request_service/policy.rb +1 -2
  69. data/lib/html2rss/request_service/response.rb +27 -4
  70. data/lib/html2rss/request_service/strategy.rb +1 -2
  71. data/lib/html2rss/request_service.rb +15 -1
  72. data/lib/html2rss/request_session.rb +4 -2
  73. data/lib/html2rss/scrape_target.rb +24 -0
  74. data/lib/html2rss/selectors/post_processors/sanitize_html.rb +2 -4
  75. data/lib/html2rss/selectors/post_processors.rb +0 -1
  76. data/lib/html2rss/selectors.rb +33 -10
  77. data/lib/html2rss/sst/attrs.rb +1 -2
  78. data/lib/html2rss/sst/normalizer.rb +49 -11
  79. data/lib/html2rss/status.rb +48 -24
  80. data/lib/html2rss/surface_category.rb +63 -0
  81. data/lib/html2rss/syndication/README.md +21 -0
  82. data/lib/html2rss/syndication/candidate_catalog.rb +26 -0
  83. data/lib/html2rss/syndication/discovery.rb +215 -0
  84. data/lib/html2rss/syndication/parser.rb +137 -0
  85. data/lib/html2rss/syndication.rb +10 -0
  86. data/lib/html2rss/test/enhance_audit.rb +192 -0
  87. data/lib/html2rss/test.rb +458 -0
  88. data/lib/html2rss/url.rb +23 -2
  89. data/lib/html2rss/version.rb +1 -1
  90. data/lib/html2rss.rb +157 -35
  91. data/schema/html2rss-config.schema.json +47 -29
  92. metadata +34 -18
  93. data/lib/html2rss/mcp/inspect.rb +0 -241
  94. data/lib/html2rss/selectors/post_processors/html_to_markdown.rb +0 -59
data/lib/html2rss.rb CHANGED
@@ -16,7 +16,7 @@ require 'html2rss/defaults'
16
16
 
17
17
  ##
18
18
  # The Html2rss namespace.
19
- module Html2rss
19
+ module Html2rss # rubocop:disable Metrics/ModuleLength
20
20
  ##
21
21
  # The logger instance.
22
22
  module Log
@@ -28,6 +28,160 @@ module Html2rss
28
28
  end
29
29
  end
30
30
 
31
+ ##
32
+ # Cheap diagnostics for a URL (final URL, status, alternates, surface).
33
+ # Golden path step 1 (optional); use {.recon} for verdict and native_feed.
34
+ #
35
+ # @param url [String] source page URL
36
+ # @param strategy [Symbol] request strategy (:auto, :faraday, :botasaurus)
37
+ # @param deep [Boolean] when true and strategy is :auto, one Botasaurus hop if configured
38
+ # @return [Html2rss::PageRecon::Diagnostics::Report]
39
+ def self.inspect(url, strategy: :auto, deep: false, **)
40
+ PageRecon::Diagnostics.call(url:, strategy:, deep:, **)
41
+ end
42
+
43
+ ##
44
+ # Curation verdict and native_feed preference for a URL.
45
+ # Golden path step 2 (optional); adds verdict beyond {.inspect}.
46
+ #
47
+ # @param url [String, Html2rss::Url] source page URL
48
+ # @param strategy [Symbol] request strategy (:auto, :faraday, :botasaurus)
49
+ # @return [Html2rss::Recon::Result]
50
+ def self.recon(url, strategy: :auto, **)
51
+ Recon.call(url, strategy:, **)
52
+ end
53
+
54
+ ##
55
+ # Derives a reusable YAML-ready feed config from a URL.
56
+ # Golden path step 3.
57
+ #
58
+ # @param url [String] source page URL
59
+ # @param strategy [Symbol] request strategy (+:auto+, +:faraday+, +:botasaurus+)
60
+ # @option options [String, nil] :items_selector optional CSS selector hint for items
61
+ # @option options [Array<String>, nil] :topics optional directory topics override
62
+ # @option options [String, nil] :title optional title override
63
+ # @option options [String, nil] :summary optional summary override
64
+ # @option options [Boolean] :force whether to ignore native feed detection
65
+ # @option options [Boolean, nil] :enhance whether to force enhance: true
66
+ # @option options [Integer, nil] :max_redirects optional redirect limit override
67
+ # @option options [Integer, nil] :max_requests optional request budget override
68
+ # @option options [Integer, nil] :limit max articles to keep
69
+ # @option options [String, nil] :local_file_path optional local HTML file path
70
+ # @return [Html2rss::Capture::CaptureResult]
71
+ def self.capture(url, strategy: :auto, **)
72
+ Capture.build(url, strategy:, **)
73
+ end
74
+
75
+ ##
76
+ # Validates a config hash, YAML string, or file path against the schema.
77
+ # Side door: schema-only check without live extraction.
78
+ #
79
+ # @param config_input [Hash, String]
80
+ # @param feed_name [String, nil]
81
+ # @param params [Hash]
82
+ # @return [Dry::Validation::Result, Html2rss::Config::ValidationResult]
83
+ def self.validate(config_input, feed_name = nil, params: {})
84
+ _raw, validation = Config.resolve_and_validate(config_input, feed_name:, params:)
85
+ validation
86
+ end
87
+
88
+ ##
89
+ # Validates schema and asserts live item extraction.
90
+ # Golden path step 4.
91
+ #
92
+ # @param config_input [Hash, String] config hash, YAML string, or file path
93
+ # @param feed_name [String, nil] optional feed name in multi-feed file
94
+ # @param min_items [Integer] minimum required items (default: 1)
95
+ # @param params [Hash] dynamic feed params
96
+ # @param strategy [Symbol, nil] optional strategy override
97
+ # @param strict_quality [Boolean] when true, fail on ship-quality audit thresholds
98
+ # @param compare_enhance [Boolean] diagnostic enhance off vs on comparison on cached HTML
99
+ # @return [Html2rss::Test::Result]
100
+ def self.test(config_input, feed_name = nil, min_items: 1, params: {}, strategy: nil, strict_quality: false, # rubocop:disable Metrics/ParameterLists
101
+ compare_enhance: false)
102
+ Test.call(config_input, feed_name, min_items:, params:, strategy:, strict_quality:, compare_enhance:)
103
+ end
104
+
105
+ ##
106
+ # Ships RSS from a validated config (user-facing verb for {.feed_result}).
107
+ # Golden path step 5.
108
+ #
109
+ # @param raw_config [Hash{Symbol => Object}] feed configuration
110
+ # @return [Html2rss::FeedResult]
111
+ def self.apply(raw_config)
112
+ feed_result(raw_config)
113
+ end
114
+
115
+ # rubocop:disable Metrics/ParameterLists
116
+
117
+ ##
118
+ # One-shot auto-source scrape from a URL (user-facing verb for {.auto_feed_result}).
119
+ #
120
+ # @param url [String] source page URL
121
+ # @param strategy [Symbol] request strategy to use
122
+ # @param items_selector [String, nil] optional selector hint for item extraction
123
+ # @param max_redirects [Integer, nil] optional redirect limit override
124
+ # @param max_requests [Integer] optional request budget override (default: 4 for sitemap sub-fetches)
125
+ # @param local_file_path [String, nil] optional local HTML file path
126
+ # @param limit [Integer, nil] max articles to keep (default: {AutoSource::DEFAULT_LIMIT})
127
+ # @return [Html2rss::FeedResult]
128
+ def self.scrape(url,
129
+ strategy: :auto,
130
+ items_selector: nil,
131
+ max_redirects: nil,
132
+ max_requests: 4,
133
+ local_file_path: nil,
134
+ limit: nil)
135
+ auto_feed_result(url, strategy:, items_selector:, max_redirects:, max_requests:,
136
+ local_file_path:, limit:)
137
+ end
138
+
139
+ ##
140
+ # Scrapes multiple URLs in parallel using auto-source article discovery.
141
+ #
142
+ # @param urls [Enumerable<String>] list of URLs to scrape
143
+ # @param strategy [Symbol] request strategy (:auto, :faraday, :botasaurus)
144
+ # @param limit [Integer] max articles to keep per URL (default: 10)
145
+ # @param concurrency [Integer] max worker threads (default: 5)
146
+ # @return [Html2rss::Batch::BatchResult]
147
+ def self.batch_scrape(urls, strategy: :auto, limit: 10, concurrency: Batch::DEFAULT_CONCURRENCY)
148
+ Batch.batch_scrape(urls:, strategy:, limit:, concurrency:)
149
+ end
150
+
151
+ ##
152
+ # Inspects multiple URLs in parallel with per-URL error isolation.
153
+ #
154
+ # @param urls [Enumerable<String>] list of URLs to inspect
155
+ # @param strategy [Symbol] request strategy (:auto, :faraday, :botasaurus)
156
+ # @param concurrency [Integer] max worker threads (default: 5)
157
+ # @return [Html2rss::Batch::BatchResult]
158
+ def self.batch_inspect(urls, strategy: :auto, concurrency: Batch::DEFAULT_CONCURRENCY)
159
+ Batch.batch_inspect(urls:, strategy:, concurrency:)
160
+ end
161
+
162
+ ##
163
+ # Runs recon across multiple URLs in parallel with per-URL error isolation.
164
+ #
165
+ # @param urls [Enumerable<String>] list of URLs to recon
166
+ # @param strategy [Symbol] request strategy (:auto, :faraday, :botasaurus)
167
+ # @param concurrency [Integer] max worker threads (default: 5)
168
+ # @option options [String, nil] :cache_dir optional HTML cache directory
169
+ # @return [Html2rss::Batch::BatchResult]
170
+ def self.batch_recon(urls, strategy: :auto, concurrency: Batch::DEFAULT_CONCURRENCY, **)
171
+ Batch.batch_recon(urls:, strategy:, concurrency:, **)
172
+ end
173
+
174
+ # rubocop:enable Metrics/ParameterLists
175
+
176
+ ##
177
+ # Exports the configuration JSON Schema as JSON string.
178
+ #
179
+ # @param pretty [Boolean] whether to pretty-print JSON
180
+ # @return [String]
181
+ def self.schema_json(pretty: true)
182
+ Config.json_schema_json(pretty:)
183
+ end
184
+
31
185
  ##
32
186
  # Loads a feed configuration from YAML.
33
187
  #
@@ -143,39 +297,9 @@ module Html2rss
143
297
  local_file_path:, limit:).to_json_feed
144
298
  end
145
299
 
146
- ##
147
- # Analyzes a URL and produces a reusable YAML-ready feed config hash.
148
- #
149
- # Uses auto-source discovery to extract articles, then derives CSS selectors
150
- # from the structural analysis.
151
- #
152
- # @param url [String] source page URL
153
- # @param strategy [Symbol] request strategy (+:auto+, +:faraday+, +:botasaurus+)
154
- # @param items_selector [String, nil] optional CSS selector hint for items
155
- # @param max_redirects [Integer, nil] optional redirect limit override
156
- # @param max_requests [Integer, nil] optional request budget override
157
- # @param limit [Integer, nil] max articles to keep
158
- # @param local_file_path [String, nil] optional local HTML file path
159
- # @return [Hash] feed config hash with +:channel+ and +:selectors+
160
- def self.capture(url,
161
- strategy: :auto,
162
- items_selector: nil,
163
- max_redirects: nil,
164
- max_requests: nil,
165
- limit: nil,
166
- local_file_path: nil)
167
- Capture.build(url,
168
- strategy:,
169
- items_selector:,
170
- max_redirects:,
171
- max_requests:,
172
- limit:,
173
- local_file_path:).config
174
- end
175
-
176
300
  # rubocop:enable Metrics/ParameterLists
177
301
 
178
- # rubocop:disable ThreadSafety/ClassInstanceVariable
302
+ # rubocop:disable-next ThreadSafety/ClassInstanceVariable
179
303
  class << self
180
304
  ##
181
305
  # @return [Html2rss::Defaults] the global defaults instance
@@ -217,12 +341,11 @@ module Html2rss
217
341
  logger.level = defaults.log_level if logger.respond_to?(:level=)
218
342
  end
219
343
  end
220
- # rubocop:enable ThreadSafety/ClassInstanceVariable
221
344
 
222
345
  class << self
223
346
  private
224
347
 
225
- # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
348
+ # rubocop:disable-next Metrics/ParameterLists, Metrics/MethodLength
226
349
  def build_auto_source_config(url:, strategy:, items_selector:, max_redirects:, max_requests:,
227
350
  local_file_path: nil, limit: nil)
228
351
  config = Config.auto_source_config(
@@ -237,7 +360,6 @@ module Html2rss
237
360
  end
238
361
  config
239
362
  end
240
- # rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
241
363
  end
242
364
 
243
365
  logger.level = defaults.log_level if logger.respond_to?(:level=)
@@ -97,7 +97,12 @@
97
97
  "consumer",
98
98
  "civic",
99
99
  "product",
100
- "research"
100
+ "research",
101
+ "health",
102
+ "culture",
103
+ "education",
104
+ "rights",
105
+ "transport"
101
106
  ]
102
107
  },
103
108
  "minItems": 1
@@ -157,9 +162,40 @@
157
162
  },
158
163
  "exclusiveMinimum": 0
159
164
  },
165
+ "entry_resolution": {
166
+ "type": "object",
167
+ "properties": {
168
+ "enabled": {
169
+ "type": "boolean",
170
+ "not": {
171
+ "type": "null"
172
+ }
173
+ },
174
+ "max_probes": {
175
+ "type": "integer",
176
+ "not": {
177
+ "type": "null"
178
+ },
179
+ "exclusiveMinimum": 0
180
+ }
181
+ },
182
+ "required": []
183
+ },
160
184
  "scraper": {
161
185
  "type": "object",
162
186
  "properties": {
187
+ "native_feed": {
188
+ "type": "object",
189
+ "properties": {
190
+ "enabled": {
191
+ "type": "boolean",
192
+ "not": {
193
+ "type": "null"
194
+ }
195
+ }
196
+ },
197
+ "required": []
198
+ },
163
199
  "wordpress_api": {
164
200
  "type": "object",
165
201
  "properties": {
@@ -338,7 +374,14 @@
338
374
  "required": [],
339
375
  "default": {
340
376
  "limit": 25,
377
+ "entry_resolution": {
378
+ "enabled": true,
379
+ "max_probes": 5
380
+ },
341
381
  "scraper": {
382
+ "native_feed": {
383
+ "enabled": true
384
+ },
342
385
  "wordpress_api": {
343
386
  "enabled": true
344
387
  },
@@ -405,7 +448,8 @@
405
448
  "type": "boolean",
406
449
  "not": {
407
450
  "type": "null"
408
- }
451
+ },
452
+ "description": "List-card enrichment: run Html::ArticleExtractor on each matched item node to fill missing fields from the card HTML."
409
453
  },
410
454
  "pagination": {
411
455
  "description": "Pagination configuration or maximum page count integer.",
@@ -466,7 +510,7 @@
466
510
  "required": [
467
511
  "selector"
468
512
  ],
469
- "description": "Defines the items selector and optional enhancement settings."
513
+ "description": "Defines the items selector and list-card enhance settings."
470
514
  },
471
515
  "enclosure": {
472
516
  "$schema": "http://json-schema.org/draft-06/schema#",
@@ -508,9 +552,6 @@
508
552
  {
509
553
  "$ref": "#/$defs/post_processors/gsub"
510
554
  },
511
- {
512
- "$ref": "#/$defs/post_processors/html_to_markdown"
513
- },
514
555
  {
515
556
  "$ref": "#/$defs/post_processors/markdown_to_html"
516
557
  },
@@ -600,9 +641,6 @@
600
641
  {
601
642
  "$ref": "#/$defs/post_processors/gsub"
602
643
  },
603
- {
604
- "$ref": "#/$defs/post_processors/html_to_markdown"
605
- },
606
644
  {
607
645
  "$ref": "#/$defs/post_processors/markdown_to_html"
608
646
  },
@@ -837,26 +875,6 @@
837
875
  ],
838
876
  "additionalProperties": true
839
877
  },
840
- "html_to_markdown": {
841
- "type": "object",
842
- "title": "html_to_markdown",
843
- "description": "Sanitize HTML then convert it to Markdown (via ReverseMarkdown).",
844
- "examples": [
845
- {
846
- "name": "html_to_markdown"
847
- }
848
- ],
849
- "properties": {
850
- "name": {
851
- "type": "string",
852
- "const": "html_to_markdown"
853
- }
854
- },
855
- "required": [
856
- "name"
857
- ],
858
- "additionalProperties": true
859
- },
860
878
  "markdown_to_html": {
861
879
  "type": "object",
862
880
  "title": "markdown_to_html",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: html2rss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.2
4
+ version: 0.29.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gil Desmarais
@@ -203,20 +203,6 @@ dependencies:
203
203
  - - ">="
204
204
  - !ruby/object:Gem::Version
205
205
  version: '0'
206
- - !ruby/object:Gem::Dependency
207
- name: reverse_markdown
208
- requirement: !ruby/object:Gem::Requirement
209
- requirements:
210
- - - "~>"
211
- - !ruby/object:Gem::Version
212
- version: '3.0'
213
- type: :runtime
214
- prerelease: false
215
- version_requirements: !ruby/object:Gem::Requirement
216
- requirements:
217
- - - "~>"
218
- - !ruby/object:Gem::Version
219
- version: '3.0'
220
206
  - !ruby/object:Gem::Dependency
221
207
  name: rss
222
208
  requirement: !ruby/object:Gem::Requirement
@@ -331,6 +317,7 @@ files:
331
317
  - lib/html2rss/auto_source/scraper/meta_oembed.rb
332
318
  - lib/html2rss/auto_source/scraper/microdata.rb
333
319
  - lib/html2rss/auto_source/scraper/microformats2.rb
320
+ - lib/html2rss/auto_source/scraper/native_feed.rb
334
321
  - lib/html2rss/auto_source/scraper/schema.rb
335
322
  - lib/html2rss/auto_source/scraper/schema/category_extractor.rb
336
323
  - lib/html2rss/auto_source/scraper/schema/item_list.rb
@@ -350,10 +337,14 @@ files:
350
337
  - lib/html2rss/auto_source/segmenter/list.rb
351
338
  - lib/html2rss/auto_source/segmenter/primary_link.rb
352
339
  - lib/html2rss/auto_source/segmenter/semantic.rb
340
+ - lib/html2rss/batch.rb
353
341
  - lib/html2rss/capture.rb
354
342
  - lib/html2rss/capture/README.md
355
343
  - lib/html2rss/channel.rb
356
344
  - lib/html2rss/cli.rb
345
+ - lib/html2rss/cli/probe_view.rb
346
+ - lib/html2rss/cli/render.rb
347
+ - lib/html2rss/cli/validate.rb
357
348
  - lib/html2rss/config.rb
358
349
  - lib/html2rss/config/auto_source_contract.rb
359
350
  - lib/html2rss/config/dynamic_params.rb
@@ -364,6 +355,7 @@ files:
364
355
  - lib/html2rss/config/selectors_validator.rb
365
356
  - lib/html2rss/config/validator.rb
366
357
  - lib/html2rss/defaults.rb
358
+ - lib/html2rss/doctor/botasaurus.rb
367
359
  - lib/html2rss/error.rb
368
360
  - lib/html2rss/feed_builder.rb
369
361
  - lib/html2rss/feed_builder/item_presentation.rb
@@ -376,6 +368,14 @@ files:
376
368
  - lib/html2rss/feed_pipeline/auto_fallback.rb
377
369
  - lib/html2rss/feed_pipeline/runtime_policy.rb
378
370
  - lib/html2rss/feed_pipeline/strategy_plan.rb
371
+ - lib/html2rss/feed_resolution.rb
372
+ - lib/html2rss/feed_resolution/README.md
373
+ - lib/html2rss/feed_resolution/candidate_generator.rb
374
+ - lib/html2rss/feed_resolution/diag.rb
375
+ - lib/html2rss/feed_resolution/options.rb
376
+ - lib/html2rss/feed_resolution/policy.rb
377
+ - lib/html2rss/feed_resolution/probe.rb
378
+ - lib/html2rss/feed_resolution/scorer.rb
379
379
  - lib/html2rss/feed_result.rb
380
380
  - lib/html2rss/hash_util.rb
381
381
  - lib/html2rss/html.rb
@@ -396,6 +396,7 @@ files:
396
396
  - lib/html2rss/html/feed_link.rb
397
397
  - lib/html2rss/html/navigator.rb
398
398
  - lib/html2rss/html/navigator/text_extractor.rb
399
+ - lib/html2rss/html/probe.rb
399
400
  - lib/html2rss/html/rendering.rb
400
401
  - lib/html2rss/html/rendering/audio_renderer.rb
401
402
  - lib/html2rss/html/rendering/description_builder.rb
@@ -411,11 +412,18 @@ files:
411
412
  - lib/html2rss/link_destination/path_classifier.rb
412
413
  - lib/html2rss/link_destination/text_classifier.rb
413
414
  - lib/html2rss/mcp.rb
415
+ - lib/html2rss/mcp/README.md
414
416
  - lib/html2rss/mcp/config_argument.rb
415
417
  - lib/html2rss/mcp/contract.rb
416
- - lib/html2rss/mcp/inspect.rb
417
418
  - lib/html2rss/mcp/outcome.rb
419
+ - lib/html2rss/mcp/outcome/playbook.rb
420
+ - lib/html2rss/mcp/runtime.rb
418
421
  - lib/html2rss/mcp/server.rb
422
+ - lib/html2rss/mcp/server/tools.rb
423
+ - lib/html2rss/page_recon.rb
424
+ - lib/html2rss/page_recon/README.md
425
+ - lib/html2rss/page_recon/diagnostics.rb
426
+ - lib/html2rss/recon.rb
419
427
  - lib/html2rss/request_service.rb
420
428
  - lib/html2rss/request_service/blocked_surface.rb
421
429
  - lib/html2rss/request_service/botasaurus_contract.rb
@@ -446,6 +454,7 @@ files:
446
454
  - lib/html2rss/scoring/observation.rb
447
455
  - lib/html2rss/scoring/ranked_segment.rb
448
456
  - lib/html2rss/scoring/score.rb
457
+ - lib/html2rss/scrape_target.rb
449
458
  - lib/html2rss/selectors.rb
450
459
  - lib/html2rss/selectors/extractors.rb
451
460
  - lib/html2rss/selectors/extractors/attribute.rb
@@ -458,7 +467,6 @@ files:
458
467
  - lib/html2rss/selectors/post_processors.rb
459
468
  - lib/html2rss/selectors/post_processors/base.rb
460
469
  - lib/html2rss/selectors/post_processors/gsub.rb
461
- - lib/html2rss/selectors/post_processors/html_to_markdown.rb
462
470
  - lib/html2rss/selectors/post_processors/html_transformers/transform_urls_to_absolute_ones.rb
463
471
  - lib/html2rss/selectors/post_processors/html_transformers/wrap_img_in_a.rb
464
472
  - lib/html2rss/selectors/post_processors/markdown_to_html.rb
@@ -477,6 +485,14 @@ files:
477
485
  - lib/html2rss/sst/tags.rb
478
486
  - lib/html2rss/sst/text.rb
479
487
  - lib/html2rss/status.rb
488
+ - lib/html2rss/surface_category.rb
489
+ - lib/html2rss/syndication.rb
490
+ - lib/html2rss/syndication/README.md
491
+ - lib/html2rss/syndication/candidate_catalog.rb
492
+ - lib/html2rss/syndication/discovery.rb
493
+ - lib/html2rss/syndication/parser.rb
494
+ - lib/html2rss/test.rb
495
+ - lib/html2rss/test/enhance_audit.rb
480
496
  - lib/html2rss/url.rb
481
497
  - lib/html2rss/version.rb
482
498
  - lib/tasks/config_schema.rake
@@ -486,7 +502,7 @@ licenses:
486
502
  - MIT
487
503
  metadata:
488
504
  allowed_push_host: https://rubygems.org
489
- changelog_uri: https://github.com/html2rss/html2rss/releases/tag/v0.27.2
505
+ changelog_uri: https://github.com/html2rss/html2rss/releases/tag/v0.29.1
490
506
  rubygems_mfa_required: 'true'
491
507
  rdoc_options: []
492
508
  require_paths: