wp2txt 2.3.1 → 2.3.2
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/.gitignore +1 -0
- data/CHANGELOG.md +78 -281
- data/DEVELOPMENT.md +8 -0
- data/DEVELOPMENT_ja.md +7 -0
- data/README.md +12 -13
- data/README_ja.md +7 -7
- data/Rakefile +1 -1
- data/docs/{RESEARCH.md → INDEXES.md} +64 -63
- data/lib/wp2txt/version.rb +1 -1
- data/spec/docs_sync_spec.rb +27 -5
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 874d5f0c6463fc612080339cbbcbe560b51aba31a8a5e9478d95719c53744051
|
|
4
|
+
data.tar.gz: e8377c0df46cd7d35ca8a85cea4f52e6dc4ca57330004ce5fc6f11913aa1abcf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ce2ca7660379c37a3f36d6d731aa3a9c1798998c10dfb5ff012503f571653b26aef6324e579c1a8af7f0d2ff52cf90fc8c6a50f44f21d40b73d132277cb50a26
|
|
7
|
+
data.tar.gz: d8197213c46510742d015dcea77c8ff5c40d48dac7beff8cc409ce1e4ce5f3e138e47d7df9ce675a0614785b21bb266664f8c082c94080c4f3a29c712ffeab07
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -5,321 +5,118 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
Entries were rewritten in August 2026 to describe what changed for people using
|
|
9
|
+
wp2txt, rather than how it was implemented. The changes themselves are unaltered.
|
|
10
|
+
|
|
11
|
+
## [2.3.2] - 2026-08-13
|
|
12
|
+
|
|
13
|
+
- **Documentation release — no code changes.** The changelog and guides bundled with the gem and the container image are rewritten to say what each change means for someone using wp2txt, rather than how it was implemented. The guide formerly at `docs/RESEARCH.md` is now [docs/INDEXES.md](docs/INDEXES.md) ("Offline Indexes, Queries, and the MCP Server")
|
|
14
|
+
|
|
8
15
|
## [2.3.1] - 2026-08-12
|
|
9
16
|
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
14
|
-
- **Runaway-query hardening**: a `query_sql` child process now sets its own kernel-enforced CPU limit (`RLIMIT_CPU`, the query timeout plus a small grace) in addition to the parent's wall-clock kill. Previously a child orphaned by the parent's death — an interrupted test run, a closed terminal, a crashed server — kept executing forever: sqlite3 holds the GVL inside `sqlite3_step`, so Ruby never reaches a signal-safe point and even SIGTERM is ignored. Observed in the wild as two processes spinning at 99% CPU for over five days. The CPU limit is strictly more permissive than the existing wall-clock deadline, so no query that would otherwise succeed is affected
|
|
15
|
-
- **Test-suite hang guard**: each example now runs under a wall-clock timeout (120s default; `WP2TXT_SPEC_TIMEOUT=0` disables it, the `:no_timeout` tag exempts an example). The suite deliberately exercises runaway queries, so a wedged example must fail rather than spin
|
|
17
|
+
- **Fix: references leaked into extracted text**: reference contents — tag remnants like `ref…/ref` and the bibliographic text inside them — could end up in the extracted body text, breaking sentence splitting and mixing citation details into running prose. In a spot check of five English articles this affected about 70 places per article. References are now removed cleanly (or kept intact as `[ref]…[/ref]` with `--ref`), including references written across several lines and empty ones
|
|
18
|
+
- **Fix: `--extract-citations` ignored multi-line citation templates**: a `{{cite …}}` template split over several lines was left as raw markup instead of being formatted, so citations extracted from such references were silently missing. Single-line and multi-line templates now produce the same result
|
|
19
|
+
- **Long-running queries can no longer outlive their timeout**: an interrupted session could leave a `query_sql` process running at full CPU indefinitely. Queries now stop on their own even if the process that started them goes away. Queries that complete within the time limit are unaffected
|
|
20
|
+
- **Container images move to GitHub Container Registry**: images are published to `ghcr.io/yohasebe/wp2txt` only. The Docker Hub repository is no longer updated — please switch:
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
docker pull ghcr.io/yohasebe/wp2txt
|
|
18
23
|
|
|
19
|
-
|
|
20
|
-
- **Container images on GHCR**: images are now published to `ghcr.io/yohasebe/wp2txt` (Docker Hub `yohasebe/wp2txt` is maintained as a mirror)
|
|
24
|
+
## [2.3.0] - 2026-07-24
|
|
21
25
|
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
- **Interlanguage links (`--import-langlinks`)**:
|
|
25
|
-
- **
|
|
26
|
+
- **Extract an explicit list of articles**: `extract_corpus` accepts `titles:` — hand it the set you arrived at some other way (a `query_sql` result, your own list) instead of describing it with filters again. Titles are normalized the way MediaWiki treats them, duplicates are dropped, and one redirect hop is followed. Titles with no article are skipped and reported back as `not_found`, so a missing article is visible rather than silently absent. Up to 10,000 titles; cannot be combined with the filter arguments. Also available through `start_extract_job`
|
|
27
|
+
- **Write a large SQL result to a file**: `query_sql` accepts `output_path:` — all rows go to a JSONL file and the reply carries a summary plus three sample rows, instead of a row cap. The file is written completely or not at all: an interrupted or failing query leaves no partial output. Up to 5 million rows, cells longer than 64KB are clipped (both are reported back), and an existing file is only replaced with `overwrite: true`. A `.meta.json` beside the output records the SQL, the dump versions involved, and the row count
|
|
28
|
+
- **Interlanguage links (`--import-langlinks`)**: import the official `langlinks` dump into the metadata index to map articles across editions. The langlinks file must carry the same dump date as your index — a mismatch is refused, so links can never be mixed across versions. `--langlinks-langs en,de,fr,zh,ko` limits which target languages are imported; re-importing requires `-U`. After the import, a sample of titles per language is checked against any editions you have installed and the match rate is reported. `dump_info` shows what was imported and when
|
|
29
|
+
- **Query several editions in one statement**: `query_sql` accepts `attach: ["en"]`, which makes that language's index available as `en_meta` (and `en_fts` where a full-text index exists) alongside your own. You pass language codes, not paths, and only editions you have installed are accepted. The reply lists what was attached, including each edition's dump name, and says so when the dates differ. Together with the `langlinks` table this makes cross-edition comparisons a single query
|
|
30
|
+
- **Documentation split**: the README now covers text extraction; the index and MCP features (indexes, offline queries, full-text search, interlanguage links, cross-language SQL, MCP server) moved to a separate guide — now [docs/INDEXES.md](docs/INDEXES.md) — which lists every MCP tool
|
|
31
|
+
- **Container images on GitHub Container Registry**: images are now published to `ghcr.io/yohasebe/wp2txt`
|
|
26
32
|
|
|
27
33
|
## [2.2.0] - 2026-07-22
|
|
28
34
|
|
|
29
|
-
- **Index
|
|
30
|
-
- **MCP
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
33
|
-
- **Full-text search
|
|
34
|
-
- **Background extraction jobs**: `start_extract_job` / `job_status` / `cancel_job` / `list_jobs`
|
|
35
|
-
- **RAG
|
|
36
|
-
- **Alias
|
|
37
|
-
- **`discover_aliases`
|
|
38
|
-
- **MCP server (`wp2txt-mcp`)**:
|
|
39
|
-
- **
|
|
40
|
-
-
|
|
41
|
-
- **
|
|
42
|
-
- **Offline
|
|
35
|
+
- **Index format change — rebuild existing indexes with `--build-index -U`** (schema v2). Section headings are now recorded consistently, so headings written with decoration (`== '''X''' ==`) match section filters instead of being missed. Each index records the wp2txt version that built it, and `dump_info` warns when an index was built by a version whose text cleaning differs from the one you are running. A rebuild that fails partway no longer destroys the index you already had
|
|
36
|
+
- **Safer defaults for MCP use**: `query_sql` stops after 30 seconds and explains what made the query slow; queries containing words like `Update` inside a quoted value are no longer rejected by mistake. `extract_corpus` can only write under the server's output directory and will not replace an existing file unless you pass `overwrite: true`. Background jobs run one at a time
|
|
37
|
+
- **More ways to select articles from an LLM client**: a `get_categories` tool; `categories` (article must be in all of them) and `category_match` (substring on category names) on `find_articles` and `extract_corpus`; `content: "wikitext"` on `extract_corpus` for reading infoboxes and templates that cleaned text replaces with markers; `query_sql` for read-only SQL when the fixed tools cannot express a question, with `describe_schema` to see the tables. Article titles are matched the way MediaWiki treats them (underscores, capitalization). `get_article` stops at `max_chars` (default 40,000) and says when it truncated; `dump_info` reports whether the full-text index is up to date
|
|
38
|
+
- **Split the full-text build in two**: `--skip-fts-optimize` leaves out the final merge step — the slowest part on a full dump, 40–60 minutes and single-threaded — and still gives you a searchable index. Run `--fts-optimize` later when convenient; it is safe to repeat. `dump_info` shows whether the merge has been done
|
|
39
|
+
- **Full-text search**: `--build-index --fulltext` adds a searchable index over the cleaned article text. The tokenizer is chosen by language (character trigrams for Japanese, Chinese, Korean; word-based elsewhere, `--fts-tokenizer` to override). Search from the CLI with `--search` or from an LLM client with `search_text`, combined with category and section filters, as a literal phrase or a full-text query. Counts can be capped for speed or exact — an exact `0` means the term is absent from that dump. The index stores no article text (snippets are re-read from the dump when needed), so it costs far less disk than a copy of the corpus
|
|
40
|
+
- **Background extraction jobs**: `start_extract_job` / `job_status` / `cancel_job` / `list_jobs` for extractions beyond the 5,000-article limit of a direct call. Extraction writes to disk as it goes, so memory use no longer grows with the size of the job
|
|
41
|
+
- **Chunked output for RAG**: `extract_corpus` accepts `chunk_size` / `chunk_overlap` and emits one record per chunk with its `section_path`, `chunk_index`, and `chunk_count`. Chunks prefer to break at sentence and paragraph boundaries
|
|
42
|
+
- **Alias sets are checked before they are saved**: `save_alias_set` refuses a group whose headings frequently appear together in the same article — a sign they are different sections rather than synonyms — unless you pass `force`
|
|
43
|
+
- **`discover_aliases` prompt**: a bundled walkthrough of finding heading variants (`section_stats` → `section_cooccurrence` → save), so you do not have to remember the steps
|
|
44
|
+
- **MCP server (`wp2txt-mcp`)**: a new command that exposes a local dump to an LLM client over the Model Context Protocol. Tools cover dump identity, single articles and their sections, filtered listings, category trees, heading statistics, alias sets, and extraction to JSONL with a `.meta.json` recording the dump version and query. Requires the `mcp` gem (`gem install mcp`); wp2txt itself does not depend on it
|
|
45
|
+
- **Section aliases come from the dump, not from a bundled dictionary**: find the headings actually used with `section_stats`, check whether two of them mean the same thing with `section_cooccurrence`, and save the group with `save_alias_set`. Queries then refer to it by name via `alias_set`, and extractions record which group was used
|
|
46
|
+
- **Ruby API**: `Wp2txt::Corpus` provides the query and extraction layer used by the MCP server, for embedding wp2txt in your own code. Title lookup reads from SQLite on demand rather than loading a multi-million-entry index into memory
|
|
47
|
+
- **Offline metadata index (`--build-index`)**: builds a local index of a dump — each article's categories, section headings, redirects, and the category hierarchy — by scanning it in parallel. Stored under `~/.wp2txt/cache/` and tied to the dump version. No API access needed
|
|
48
|
+
- **Offline queries over a whole edition (`--find-articles`)**: list articles by `--in-category` (with `--depth` to include subcategories), `--has-section` (alias-aware), and `--title-match`. Supports `--limit` and JSON output (`-j json`); redirects are excluded. This is what makes questions like "all film articles that have a plot section" answerable against a pinned dump
|
|
43
49
|
|
|
44
50
|
## [2.1.2] - 2026-07-19
|
|
45
51
|
|
|
46
|
-
- **Fixed gem
|
|
52
|
+
- **Fixed unreadable files after `sudo gem install`**: files in the published gem carried owner-only permissions, so wp2txt failed to run for anyone but the installing user. Packaged files are now readable by everyone (0644, and 0755 for executables). If you hit this, reinstall the gem
|
|
47
53
|
|
|
48
54
|
## [2.1.1] - 2026-02-21
|
|
49
55
|
|
|
50
|
-
- **
|
|
51
|
-
- **
|
|
52
|
-
- **
|
|
56
|
+
- **Section aliases match in both directions**: asking for "Synopsis" now also finds sections headed "Plot", and the other way round, instead of only matching the name you happened to type
|
|
57
|
+
- **More section aliases out of the box**: 12 groups instead of 2, covering common English Wikipedia sections (Plot, Reception, References, Bibliography, Awards, Legacy, Early life, Career, and others)
|
|
58
|
+
- **Fixed options being ignored**: `--pre`, `--ref`, `--expand-templates`, and `--metadata-only` had no effect when used with `--articles` or `--from-category`. They now apply in those modes too
|
|
53
59
|
|
|
54
60
|
## [2.1.0] - 2026-02-19
|
|
55
61
|
|
|
56
|
-
- **
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- **
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
- Performance: Parallel gem (~3x) remains faster, Ractor (~2x) uses less memory
|
|
77
|
-
|
|
78
|
-
- **Template expansion**: New `--expand-templates` (`-E`) option expands common templates to readable text:
|
|
79
|
-
- Date templates: `{{birth date|1990|5|15}}` → "May 15, 1990"
|
|
80
|
-
- Convert templates: `{{convert|100|km|mi}}` → "100 km (62 mi)"
|
|
81
|
-
- Coordinate templates: `{{coord|35|41|N|139|41|E}}` → "35°41′N 139°41′E"
|
|
82
|
-
- Language templates: `{{lang|ja|日本語}}` → "日本語"
|
|
83
|
-
- Quote templates: `{{blockquote|text}}` → "text"
|
|
84
|
-
- And 20+ more template types
|
|
85
|
-
- **Enabled by default** - use `--no-expand-templates` to disable
|
|
86
|
-
- Parser functions support: `{{#if:}}`, `{{#switch:}}`, `{{#ifeq:}}`, `{{#expr:}}`
|
|
87
|
-
- Magic words support: `{{PAGENAME}}`, `{{CURRENTYEAR}}`, `{{NAMESPACE}}`
|
|
88
|
-
|
|
89
|
-
- **Removed legacy test data**: Deleted obsolete static test files:
|
|
90
|
-
- `data/testdata_en.bz2` (2.8MB, from 2022)
|
|
91
|
-
- `data/testdata_ja.bz2` (2.6MB, from 2022)
|
|
92
|
-
- `data/output_samples/` directory (~20MB)
|
|
93
|
-
- Tests now use live Wikipedia data with caching
|
|
94
|
-
|
|
95
|
-
- **Incremental dump downloads**: Smart handling of partial dump files when downloading full dumps:
|
|
96
|
-
- Detects existing partial downloads and offers to resume (download only remaining data)
|
|
97
|
-
- Validates dump dates - if dates match, can resume; if outdated, offers choices
|
|
98
|
-
- User options: resume download, download fresh, keep old partial, or use old as-is
|
|
99
|
-
- Automatic bz2 validation before and after incremental download
|
|
100
|
-
- Falls back to full download if server doesn't support HTTP Range headers
|
|
101
|
-
|
|
102
|
-
- **bz2 file validation**: New `Bz2Validator` module detects corrupt or invalid bz2 files before processing:
|
|
103
|
-
- Validates magic bytes (`BZ`), version byte (`h`), and block size (`1`-`9`)
|
|
104
|
-
- Optional decompression test to verify file integrity
|
|
105
|
-
- `StreamProcessor` validates bz2 files by default (configurable via `validate_bz2: false`)
|
|
106
|
-
- Detailed error types: `not_found`, `too_small`, `invalid_magic`, `invalid_version`, `invalid_block_size`, `decompression_failed`
|
|
107
|
-
|
|
108
|
-
- **Memory monitoring**: New `MemoryMonitor` module for adaptive resource management:
|
|
109
|
-
- Cross-platform memory detection (Linux, macOS, Windows)
|
|
110
|
-
- Adaptive buffer sizing based on available memory
|
|
111
|
-
- Memory statistics: `current_memory_usage`, `available_memory`, `memory_usage_percent`
|
|
112
|
-
- Automatic garbage collection when memory is low
|
|
113
|
-
|
|
114
|
-
- **Parallel article extraction**: `MultistreamReader` now supports parallel processing:
|
|
115
|
-
- `extract_articles_parallel(titles, num_processes: 4)` - Extract multiple articles in parallel
|
|
116
|
-
- `each_article_parallel(entries, num_processes: 4)` - Iterate with parallel processing
|
|
117
|
-
- Automatically groups articles by stream offset to minimize bz2 decompression overhead
|
|
118
|
-
|
|
119
|
-
- **Performance optimizations**:
|
|
120
|
-
- Pre-compiled 14 additional regex patterns for text cleanup
|
|
121
|
-
- Consolidated gsub chains (3 fewer calls per cleanup operation)
|
|
122
|
-
- Adaptive buffer sizing in `StreamProcessor` based on system memory
|
|
123
|
-
|
|
124
|
-
- **Cache staleness warnings**: Cache status now shows age and staleness information:
|
|
125
|
-
- Displays cache date and age (e.g., "2025-01-05 - 4 days ago")
|
|
126
|
-
- Warns when cache exceeds configured `dump_expiry_days` (default: 30 days)
|
|
127
|
-
- New `--update-cache` (`-U`) option to force refresh of cached dump files
|
|
128
|
-
- Users can choose to use stale cache or force update
|
|
129
|
-
|
|
130
|
-
- **Category-based extraction**: New `--from-category` option extracts all articles from a Wikipedia category:
|
|
131
|
-
- `wp2txt --lang=ja --from-category="日本の都市" -o ./output` extracts all articles in the category
|
|
132
|
-
- `--depth` option for subcategory recursion (e.g., `--depth=2` includes 2 levels of subcategories)
|
|
133
|
-
- `--dry-run` for preview mode (shows article counts without downloading)
|
|
134
|
-
- `--yes` to skip confirmation prompt for automation
|
|
135
|
-
- Circular reference prevention for category hierarchies
|
|
136
|
-
- Rate limiting for Wikipedia API requests
|
|
137
|
-
|
|
138
|
-
- **Configuration file**: New `--config-init` option creates persistent configuration:
|
|
139
|
-
- Settings stored in `~/.wp2txt/config.yml`
|
|
140
|
-
- Configurable: `dump_expiry_days`, `category_expiry_days`, `cache.directory`
|
|
141
|
-
- Default output format and subcategory depth
|
|
142
|
-
- CLI options override config file settings
|
|
143
|
-
|
|
144
|
-
- **Deprecated `--markers=none`**: Complete removal of special content is now deprecated
|
|
145
|
-
- Removing inline content (e.g., math formulas) makes surrounding text nonsensical
|
|
146
|
-
- `--markers=none` now shows a warning and behaves like `--markers=all`
|
|
147
|
-
- Use `--markers=math,code` to show only specific marker types
|
|
148
|
-
|
|
149
|
-
- **CLI option validation**: Extraction modes are now mutually exclusive with clear error messages:
|
|
150
|
-
- `--category-only`, `--summary-only`, `--metadata-only` cannot be combined
|
|
151
|
-
- `--sections` cannot be used with extraction modes
|
|
152
|
-
- `--section-stats` cannot be combined with extraction modes or `--sections`
|
|
153
|
-
|
|
154
|
-
- **Network retry with exponential backoff**: HTTP requests now retry on transient errors:
|
|
155
|
-
- Retries up to 3 times with exponential backoff (2, 4, 8 seconds)
|
|
156
|
-
- Handles timeouts, connection resets, and DNS failures
|
|
157
|
-
- CategoryFetcher API requests now log failures instead of silently returning nil
|
|
158
|
-
|
|
159
|
-
- **Disk full error handling**: OutputWriter now handles `Errno::ENOSPC` gracefully:
|
|
160
|
-
- Raises `Wp2txt::FileIOError` with descriptive message on disk full or I/O errors
|
|
161
|
-
- Properly closes file handles before raising
|
|
162
|
-
|
|
163
|
-
- **File rotation at article boundaries**: OutputWriter `write_from_file` now rotates output files only at blank lines (article boundaries):
|
|
164
|
-
- Prevents articles from being split across output files
|
|
165
|
-
- Eliminates UTF-8 character corruption at file boundaries (e.g., 3-byte Japanese characters split mid-byte)
|
|
166
|
-
- Uses line-by-line reading (`each_line` with `"r:UTF-8"`) instead of fixed-size byte chunks
|
|
167
|
-
- Verified with full Japanese Wikipedia (1.49M articles) and English Wikipedia (24.2 GB) dumps
|
|
168
|
-
|
|
169
|
-
- **HTTP timeout consistency**: All HTTP methods in `DumpManager` now use `DEFAULT_HTTP_TIMEOUT`:
|
|
170
|
-
- Added `open_timeout`/`read_timeout` to `download_incremental`, `get_remote_file_size`, `download_file_with_progress`, `download_file_range`
|
|
171
|
-
- Previously these methods had no timeout, risking indefinite hangs on network issues
|
|
172
|
-
|
|
173
|
-
- **Security: Command injection prevention**: All `IO.popen` calls now use array form:
|
|
174
|
-
- Fixed unsafe string interpolation in `wp2txt.rb`, `stream_processor.rb`, `bz2_validator.rb`, `memory_monitor.rb`
|
|
175
|
-
- Prevents shell metacharacter interpretation in file paths
|
|
176
|
-
|
|
177
|
-
- **Security: SSL certificate verification**: Restored proper TLS certificate validation:
|
|
178
|
-
- Removed `verify_callback` that unconditionally returned `true` (7 locations in `multistream.rb`)
|
|
179
|
-
- `VERIFY_PEER` now performs actual certificate verification
|
|
180
|
-
|
|
181
|
-
- **Security: Temp file handling**: `file_mod` now uses `Tempfile` instead of hardcoded `"temp"` filename:
|
|
182
|
-
- Prevents predictable file names and potential race conditions
|
|
183
|
-
- Temp files created in same directory as target file
|
|
184
|
-
|
|
185
|
-
- **CLI option fixes**:
|
|
186
|
-
- Added missing `--table` option (keep wiki table content)
|
|
187
|
-
- Added missing `--multiline` option (keep multi-line templates)
|
|
188
|
-
- Added missing `--pre` option (keep preformatted text blocks)
|
|
189
|
-
- Fixed `--ref` option not being transferred to processing config
|
|
190
|
-
- Reference removal is now conditional (respects `--ref` flag)
|
|
191
|
-
|
|
192
|
-
- **Ractor turbo mode warning**: Shows explicit warning when `--ractor` is used with turbo mode (unsupported combination)
|
|
193
|
-
|
|
194
|
-
- **Constants extraction**: Replaced magic numbers with named constants:
|
|
195
|
-
- `DEFAULT_HTTP_TIMEOUT`, `DEFAULT_PROGRESS_INTERVAL`, `INDEX_PROGRESS_THRESHOLD`
|
|
196
|
-
- `DEFAULT_TOP_N_SECTIONS`, `RESUME_METADATA_MAX_AGE_DAYS`, `MAX_HTTP_RETRIES`
|
|
197
|
-
|
|
198
|
-
- **Marker classification**: Markers now categorized as inline or block
|
|
199
|
-
- **Inline markers** (`[MATH]`, `[CODE]`, `[CHEM]`, `[IPA]`): Content that appears mid-sentence; removal would break grammar
|
|
200
|
-
- **Block markers** (`[TABLE]`, `[CODEBLOCK]`, `[INFOBOX]`, etc.): Standalone content that can be safely removed
|
|
201
|
-
- New `[CODEBLOCK]` marker for `<syntaxhighlight>`, `<source>`, `<pre>` tags (block-level code)
|
|
202
|
-
- `[CODE]` marker now only applies to inline `<code>` tags
|
|
62
|
+
- **Faster startup and repeated runs**: parsed data files, the Wikipedia category hierarchy (per language, 7-day expiry), and the multistream dump index are now cached under `~/.wp2txt/cache/`. Re-reading the index of a full dump used to take ~10 minutes; later runs now start in seconds
|
|
63
|
+
- **`--ractor` (Ruby 4.0+)**: thread-based parallel processing — about 2× faster than sequential, with a smaller memory footprint than the default process-based mode (which remains the fastest at ~3×). On Ruby 3.x the option falls back to sequential processing
|
|
64
|
+
- **Templates are expanded into readable text by default**: dates (`{{birth date|1990|5|15}}` → "May 15, 1990"), unit conversions (`{{convert|100|km|mi}}` → "100 km (62 mi)"), coordinates, language tags (`{{lang|ja|日本語}}` → 日本語), quotes, and 20+ more, plus common parser functions (`{{#if:}}`, `{{#switch:}}`) and magic words (`{{PAGENAME}}`). Turn off with `--no-expand-templates`
|
|
65
|
+
- **Interrupted downloads resume**: a partial dump download is detected and, when the server supports it, only the remaining data is fetched. If the dump date has changed you are asked whether to resume, redownload, or keep the old file; files are validated before and after
|
|
66
|
+
- **Corrupt dump files are detected before processing** instead of failing partway through, with the specific reason reported (file truncated, not a bz2 file, failed decompression test)
|
|
67
|
+
- **Output files rotate at article boundaries**: an article is never split across two output files, and multi-byte characters are no longer corrupted at file boundaries. Verified against the full Japanese (1.49M articles) and English (24.2 GB) dumps
|
|
68
|
+
- **Downloads can no longer hang indefinitely**: every network operation now has a timeout (several previously had none)
|
|
69
|
+
- **Transient network errors retry automatically** (3 attempts with 2/4/8-second backoff) instead of failing the run, and failed category API requests are logged instead of silently returning nothing
|
|
70
|
+
- **Running out of disk space raises a clear error** instead of leaving corrupt output behind
|
|
71
|
+
- **Cache age is visible**: cache listings show the date and age of each cached dump and warn when it exceeds `dump_expiry_days` (default 30); `--update-cache` (`-U`) forces a refresh
|
|
72
|
+
- **`--from-category`**: extract every article in a Wikipedia category via the Wikipedia API, with `--depth` for subcategory levels, `--dry-run` to preview article counts before downloading, and `--yes` for unattended runs
|
|
73
|
+
- **`--config-init`**: writes a persistent configuration file to `~/.wp2txt/config.yml` (cache lifetimes, cache directory, output defaults). Command-line options still take precedence
|
|
74
|
+
- **`--markers=none` is deprecated**: removing inline content (a formula mid-sentence) leaves broken text, so the option now warns and behaves like `--markers=all`. Use `--markers=math,code` to keep only specific marker types
|
|
75
|
+
- **Markers are classified as inline or block**: inline markers (`[MATH]`, `[CODE]`, `[CHEM]`, `[IPA]`) stand in for content whose removal would break the sentence; block markers (`[TABLE]`, `[INFOBOX]`, …) replace standalone content. New `[CODEBLOCK]` marker for code blocks; `[CODE]` now covers only inline code
|
|
76
|
+
- **Conflicting options are rejected with a clear message**: `--category-only`, `--summary-only`, and `--metadata-only` are mutually exclusive, and `--sections` / `--section-stats` cannot be combined with them
|
|
77
|
+
- **Option fixes**: `--table`, `--multiline`, and `--pre` were missing from the CLI and are now available; `--ref` was accepted but had no effect — reference removal now respects it
|
|
78
|
+
- `--ractor` combined with turbo mode now warns explicitly (the combination is unsupported)
|
|
79
|
+
- **Security fixes**: file paths are no longer passed through a shell, so crafted path names cannot inject commands; TLS certificate verification is actually performed (it was previously disabled by an accept-everything callback); temporary files use unpredictable names instead of a fixed name in the working directory
|
|
80
|
+
- **Performance**: text cleanup is faster (fewer passes over each article), and buffer sizes adapt to the memory actually available on the machine
|
|
81
|
+
- **Ruby API**: articles can be extracted from a multistream dump in parallel (`extract_articles_parallel`, `each_article_parallel`), grouped by stream to avoid decompressing the same block twice
|
|
203
82
|
|
|
204
83
|
## [2.0.0] - 2026-01-08
|
|
205
84
|
|
|
206
85
|
### Added
|
|
207
86
|
|
|
208
|
-
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
- **
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
- **
|
|
219
|
-
|
|
220
|
-
- `--cache-clear` - Clear all cache
|
|
221
|
-
- `--cache-clear --lang=ja` - Clear cache for specific language
|
|
222
|
-
- `--cache-dir` - Custom cache directory
|
|
223
|
-
|
|
224
|
-
- **Content type markers**: New `--markers` option marks special content:
|
|
225
|
-
- Supported types: `[MATH]`, `[CODE]`, `[CHEM]`, `[TABLE]`, `[SCORE]`, `[TIMELINE]`, `[GRAPH]`, `[IPA]`, `[INFOBOX]`, `[NAVBOX]`, `[GALLERY]`, `[SIDEBAR]`, `[MAPFRAME]`, `[IMAGEMAP]`, `[REFERENCES]`
|
|
226
|
-
- `--markers=all` (default) - Enable all markers
|
|
227
|
-
- `--markers=none` - Disable markers (content removed)
|
|
228
|
-
- `--markers=math,code` - Enable specific markers only
|
|
229
|
-
|
|
230
|
-
- **Citation extraction**: New `--extract-citations` (`-C`) option for formatted bibliography output:
|
|
231
|
-
- Extracts author, title, and year from `{{cite book}}`, `{{cite web}}`, `{{Citation}}` templates
|
|
232
|
-
- Formats citations as "Author. \"Title\". Year."
|
|
233
|
-
- Available via CLI (`--extract-citations`) and Ruby API (`extract_citations: true`)
|
|
234
|
-
|
|
235
|
-
- **Multistream support**: New classes for efficient Wikipedia dump processing:
|
|
236
|
-
- `MultistreamIndex` - Parse multistream index files
|
|
237
|
-
- `MultistreamReader` - Extract articles from multistream dumps
|
|
238
|
-
- `DumpManager` - Download and cache dump files
|
|
239
|
-
- Enables targeted article extraction without downloading full dump
|
|
240
|
-
|
|
241
|
-
- **Validation framework**: New rake tasks for validating Wikipedia dump processing:
|
|
242
|
-
- `testdata:prepare[lang,level]` - Download and cache test data
|
|
243
|
-
- `validate:run[lang,level]` - Run validation on cached data
|
|
244
|
-
- `validate:full[lang]` - Full dump validation
|
|
245
|
-
|
|
246
|
-
- **HTML entity management**: Comprehensive entity support from authoritative sources:
|
|
247
|
-
- 2125 entities from WHATWG HTML specification (`html_entities.json`)
|
|
248
|
-
- Wikipedia-specific entities (`wikipedia_entities.json`): `∶`, `‐`, `&nbso;`
|
|
249
|
-
- New script `scripts/fetch_html_entities.rb` to update from WHATWG
|
|
250
|
-
- Replaces hardcoded entity list with data-driven approach
|
|
251
|
-
|
|
252
|
-
- **MediaWiki data auto-generation**: Magic words and namespace aliases fetched from all Wikipedia APIs:
|
|
253
|
-
- New script `scripts/fetch_mediawiki_data.rb` queries 350+ Wikipedia language editions
|
|
254
|
-
- Data stored in `lib/wp2txt/data/mediawiki_aliases.json`
|
|
255
|
-
- 176 redirect keywords, 231 category aliases, 313 file aliases
|
|
256
|
-
- Run `ruby scripts/fetch_mediawiki_data.rb` to update
|
|
257
|
-
|
|
258
|
-
- **JSON/JSONL output format**: New `--format json` option outputs articles as JSONL (one JSON object per line) with `title`, `categories`, `text`, and `redirect` fields. Ideal for data pipelines and machine learning workflows.
|
|
259
|
-
|
|
260
|
-
- **Streaming processing**: Complete rewrite of the processing architecture:
|
|
261
|
-
- No longer creates intermediate XML files
|
|
262
|
-
- Directly streams from bz2 compressed files
|
|
263
|
-
- Reduced disk I/O and storage requirements
|
|
264
|
-
- New `StreamProcessor` and `OutputWriter` classes for modular design
|
|
265
|
-
|
|
266
|
-
- **Regex cache**: Dynamic regex patterns are now cached to avoid repeated compilation
|
|
267
|
-
|
|
268
|
-
- **Multilingual category support**: Added support for category namespaces in 30+ languages (European, Cyrillic, Asian, Middle Eastern)
|
|
269
|
-
|
|
270
|
-
- **Multilingual redirect support**: Added support for redirect keywords in 25+ languages
|
|
271
|
-
|
|
272
|
-
- **Comprehensive test suite**: 395 tests covering:
|
|
273
|
-
- Unicode handling (CJK, Cyrillic, Arabic, emoji)
|
|
274
|
-
- Edge cases (deeply nested templates, malformed markup)
|
|
275
|
-
- Multilingual category and redirect extraction
|
|
276
|
-
- Text processing utilities
|
|
277
|
-
- Integration tests with real Wikipedia content
|
|
278
|
-
|
|
279
|
-
- **SimpleCov integration**: Added code coverage reporting for development
|
|
280
|
-
|
|
281
|
-
- **Ruby 4.0 compatibility**: Full support for Ruby 4.0
|
|
87
|
+
- **`--lang`**: downloads the Wikipedia dump for any language code and processes it in one step (`wp2txt --lang=ja -o ./output`). Downloads are cached under `~/.wp2txt/cache/` for reuse
|
|
88
|
+
- **`--articles`**: extract specific articles by title (`wp2txt --lang=en --articles="Tokyo,Kyoto,Osaka" -o ./articles`), downloading only the index and the data streams that contain them rather than the whole dump
|
|
89
|
+
- **Cache management**: `--cache-status` (per-language overview), `--cache-clear` (all languages, or one with `--lang`), and `--cache-dir` (custom location)
|
|
90
|
+
- **Content markers (`--markers`)**: special content is replaced by a marker instead of disappearing silently: `[MATH]`, `[CODE]`, `[CHEM]`, `[TABLE]`, `[SCORE]`, `[TIMELINE]`, `[GRAPH]`, `[IPA]`, `[INFOBOX]`, `[NAVBOX]`, `[GALLERY]`, `[SIDEBAR]`, `[MAPFRAME]`, `[IMAGEMAP]`, `[REFERENCES]`. `--markers=all` is the default; `--markers=math,code` keeps only the listed types
|
|
91
|
+
- **Citation extraction (`--extract-citations`, `-C`)**: outputs a formatted bibliography ("Author. \"Title\". Year.") from `{{cite book}}`, `{{cite web}}`, and `{{Citation}}` templates. Also available from the Ruby API (`extract_citations: true`)
|
|
92
|
+
- **JSON/JSONL output (`--format json`)**: one JSON object per line with `title`, `categories`, `text`, and `redirect` fields, for data pipelines and machine-learning workflows
|
|
93
|
+
- **Multistream dumps are handled natively**, which is what makes targeted extraction work without downloading a full dump. Ruby API: `MultistreamIndex`, `MultistreamReader`, `DumpManager`
|
|
94
|
+
- **HTML character entities are converted comprehensively**: 2,125 entities from the WHATWG HTML specification, plus Wikipedia-specific ones (`∶`, `‐`, `&nbso;`)
|
|
95
|
+
- **Redirect, category, and file keywords fetched from 350+ Wikipedia language editions** (176 redirect keywords, 231 category aliases, 313 file aliases), replacing a small hardcoded list
|
|
96
|
+
- **Categories are recognized in 30+ languages** (European, Cyrillic, Asian, and Middle Eastern scripts) and **redirects in 25+**
|
|
97
|
+
- **Fully streamed processing**: dumps are processed directly from the compressed file, with no intermediate XML files — far less disk space and I/O than before
|
|
98
|
+
- **Ruby 4.0 support**
|
|
282
99
|
|
|
283
100
|
### Changed
|
|
284
101
|
|
|
285
|
-
- **
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
- `remove_complex`, `make_reference`: Optimized with `gsub!`
|
|
289
|
-
- Category deduplication: Changed from O(n²) to O(n) by calling `uniq!` once at end instead of every line
|
|
290
|
-
- `correct_separator`: Uses `tr` instead of `gsub` for single character replacement
|
|
291
|
-
- `remove_inbetween`: Dynamic regex patterns are now cached
|
|
292
|
-
|
|
293
|
-
- **BREAKING**: `REMOVE_HR_REGEX` now matches 4 or more hyphens (previously 3+) to align with MediaWiki specification where `----` is the minimum for horizontal rules
|
|
294
|
-
|
|
295
|
-
- **`chrref_to_utf` function**: Completely rewritten to support all Unicode codepoints (U+0001 to U+10FFFF), including:
|
|
296
|
-
- Supplementary plane characters (emoji, CJK Extension B, etc.)
|
|
297
|
-
- Proper handling of invalid codepoints (returns empty string)
|
|
298
|
-
|
|
299
|
-
- **`convert_characters` function**: Now uses `String#scrub` for safe handling of invalid UTF-8 sequences instead of calling `exit`
|
|
300
|
-
|
|
301
|
-
- **`command_exist?` function**: Updated to use `IO.popen` instead of `open("| ...")` for Ruby 4.0 compatibility
|
|
102
|
+
- **BREAKING: horizontal rules now require 4 or more hyphens** (previously 3), matching the MediaWiki specification — a line of `---` in an article is now treated as content, not as a rule
|
|
103
|
+
- **Character references convert across the entire Unicode range** (U+0001–U+10FFFF), including emoji and supplementary-plane CJK; invalid codepoints become empty strings instead of garbage characters
|
|
104
|
+
- **Faster text processing**: category deduplication went from quadratic to linear time, and string handling allocates far less memory on large articles
|
|
302
105
|
|
|
303
106
|
### Fixed
|
|
304
107
|
|
|
305
|
-
- **
|
|
306
|
-
|
|
307
|
-
- **
|
|
308
|
-
|
|
309
|
-
- **Horizontal rule detection**: Fixed `REMOVE_HR_REGEX` to correctly match MediaWiki horizontal rules (4+ hyphens)
|
|
310
|
-
|
|
311
|
-
- **Heading regex**: Fixed `IN_HEADING_REGEX` to allow trailing whitespace after closing equal signs
|
|
312
|
-
|
|
313
|
-
- **Ruby 4.0 compatibility**: Fixed `open("| which cmd")` pattern which no longer works in Ruby 4.0
|
|
108
|
+
- **Character references beyond U+FFFF now produce the right character**: `😀` becomes 😀 instead of an invalid character
|
|
109
|
+
- **Invalid UTF-8 in a dump no longer aborts the run**: bad byte sequences are scrubbed and processing continues (the tool previously exited mid-run)
|
|
110
|
+
- **Headings with trailing whitespace** after the closing `==` are now recognized as headings
|
|
314
111
|
|
|
315
112
|
### Deprecated
|
|
316
113
|
|
|
317
|
-
- **`--convert` / `-c
|
|
318
|
-
- **`--del-interfile` / `-x
|
|
114
|
+
- **`--convert` / `-c`**: no longer needed — streaming processing always converts
|
|
115
|
+
- **`--del-interfile` / `-x`**: no longer needed — intermediate files are no longer created
|
|
319
116
|
|
|
320
117
|
### Removed
|
|
321
118
|
|
|
322
|
-
- **Intermediate XML
|
|
119
|
+
- **Intermediate XML files**: processing no longer writes intermediate XML next to the output at any point
|
|
323
120
|
|
|
324
121
|
### Security
|
|
325
122
|
|
data/DEVELOPMENT.md
CHANGED
|
@@ -383,6 +383,14 @@ info = Wp2txt::Bz2Validator.file_info("/path/to/file.bz2")
|
|
|
383
383
|
2. Redirect keywords: `data/language_redirects.json`
|
|
384
384
|
3. Scripts: `scripts/generate_language_data.rb`
|
|
385
385
|
|
|
386
|
+
## Documentation
|
|
387
|
+
|
|
388
|
+
User-facing documents (README, README_ja, CHANGELOG, docs/) describe what a user can
|
|
389
|
+
observe and what they need to do — not how the code works internally or why it is
|
|
390
|
+
designed the way it is. Keep implementation detail in this file and in code comments,
|
|
391
|
+
and keep design rationale out of the public repository entirely. The MCP tool table in
|
|
392
|
+
docs/INDEXES.md is checked against the actual server surface by spec/docs_sync_spec.rb.
|
|
393
|
+
|
|
386
394
|
## Code Style
|
|
387
395
|
|
|
388
396
|
- Ruby 2.6+ compatibility
|
data/DEVELOPMENT_ja.md
CHANGED
|
@@ -383,6 +383,13 @@ info = Wp2txt::Bz2Validator.file_info("/path/to/file.bz2")
|
|
|
383
383
|
2. リダイレクトキーワード: `data/language_redirects.json`
|
|
384
384
|
3. スクリプト: `scripts/generate_language_data.rb`
|
|
385
385
|
|
|
386
|
+
## ドキュメンテーション
|
|
387
|
+
|
|
388
|
+
利用者向け文書(README, README_ja, CHANGELOG, docs/)には、利用者が観測できることと
|
|
389
|
+
利用者が取るべき行動だけを書く — コードが内部でどう動くか、なぜそう設計したかは書かない。
|
|
390
|
+
実装の詳細は本ファイルとコードコメントに、設計の理由づけは公開リポジトリの外に置く。
|
|
391
|
+
docs/INDEXES.md の MCP ツール表は spec/docs_sync_spec.rb が実際のサーバー表面と照合する。
|
|
392
|
+
|
|
386
393
|
## コードスタイル
|
|
387
394
|
|
|
388
395
|
- Ruby 2.6+互換性
|
data/README.md
CHANGED
|
@@ -255,12 +255,12 @@ By default, citation templates are removed. Use `--extract-citations` to extract
|
|
|
255
255
|
|
|
256
256
|
Supported: `{{cite book}}`, `{{cite web}}`, `{{cite news}}`, `{{cite journal}}`, `{{Citation}}`, etc.
|
|
257
257
|
|
|
258
|
-
##
|
|
258
|
+
## Offline Indexes, Queries, and the MCP Server
|
|
259
259
|
|
|
260
|
-
Beyond text extraction, wp2txt can turn a dump into a **local, version-pinned
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
260
|
+
Beyond text extraction, wp2txt can turn a dump into a **local, version-pinned database**:
|
|
261
|
+
SQLite indexes over categories, section headings, redirects, and (optionally) the full
|
|
262
|
+
article text, plus interlanguage links for cross-edition comparison — all queryable
|
|
263
|
+
offline, and usable from an LLM client via an MCP server.
|
|
264
264
|
|
|
265
265
|
```console
|
|
266
266
|
$ wp2txt --build-index --fulltext --lang=ja # build the indexes
|
|
@@ -270,13 +270,12 @@ $ wp2txt --import-langlinks -L ja --langlinks-langs en,de,fr,zh,ko
|
|
|
270
270
|
$ wp2txt-mcp --lang=ja # stdio MCP server for LLM agents
|
|
271
271
|
```
|
|
272
272
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
These queries scan every article — `0 matches` means the term is absent from that dump
|
|
274
|
+
version — and extractions record the dump version and query in a `.meta.json` sidecar,
|
|
275
|
+
so results can be reproduced later.
|
|
276
276
|
|
|
277
|
-
**→ See
|
|
278
|
-
|
|
279
|
-
MCP tool list, and design principles.
|
|
277
|
+
**→ See [docs/INDEXES.md](docs/INDEXES.md)** for index building, offline queries,
|
|
278
|
+
full-text search, interlanguage links, cross-language SQL, and the full MCP tool list.
|
|
280
279
|
|
|
281
280
|
## Command Line Options
|
|
282
281
|
|
|
@@ -354,11 +353,11 @@ MCP tool list, and design principles.
|
|
|
354
353
|
### Research infrastructure options
|
|
355
354
|
|
|
356
355
|
--build-index Build the metadata index (add --fulltext for FTS)
|
|
357
|
-
--find-articles / --search
|
|
356
|
+
--find-articles / --search Offline queries over a whole edition (see docs/INDEXES.md)
|
|
358
357
|
--import-langlinks Import interlanguage links (version-matched)
|
|
359
358
|
--fts-optimize Optimize an existing full-text index
|
|
360
359
|
|
|
361
|
-
See
|
|
360
|
+
See [docs/INDEXES.md](docs/INDEXES.md) for details.
|
|
362
361
|
|
|
363
362
|
## Configuration File
|
|
364
363
|
|
data/README_ja.md
CHANGED
|
@@ -333,9 +333,9 @@ defaults:
|
|
|
333
333
|
|
|
334
334
|
コマンドラインオプションは設定ファイルの設定を上書きします。
|
|
335
335
|
|
|
336
|
-
##
|
|
336
|
+
## オフライン索引・クエリ・MCPサーバー
|
|
337
337
|
|
|
338
|
-
テキスト抽出に加えて、wp2txt
|
|
338
|
+
テキスト抽出に加えて、wp2txtはダンプを**ローカルな版固定データベース**に変換できます。
|
|
339
339
|
カテゴリ・節見出し・リダイレクト・(オプションで)記事全文のSQLite索引、言語版横断比較のための
|
|
340
340
|
言語間リンク(langlinks)— すべてオフラインで悉皆的にクエリでき、MCPサーバー経由で
|
|
341
341
|
LLMエージェントにも公開できます。
|
|
@@ -348,12 +348,12 @@ $ wp2txt --import-langlinks -L ja --langlinks-langs en,de,fr,zh,ko
|
|
|
348
348
|
$ wp2txt-mcp --lang=ja # LLMエージェント向けMCPサーバー
|
|
349
349
|
```
|
|
350
350
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
351
|
+
これらのクエリは全記事を走査します(`0件` は当該ダンプ版にその語が存在しないことを
|
|
352
|
+
意味します)。抽出結果には dump 版とクエリを記録した `.meta.json` サイドカーが付き、
|
|
353
|
+
あとから再現できます。
|
|
354
354
|
|
|
355
|
-
**→ 詳細は [
|
|
356
|
-
|
|
355
|
+
**→ 詳細は [docs/INDEXES.md](docs/INDEXES.md)(英語)を参照**:
|
|
356
|
+
索引構築、オフラインクエリ、全文検索、言語間リンク、言語版横断SQL、MCPツール一覧。
|
|
357
357
|
|
|
358
358
|
## パフォーマンス
|
|
359
359
|
|
data/Rakefile
CHANGED
|
@@ -34,7 +34,7 @@ Rake::Task["build"].enhance([:normalize_permissions])
|
|
|
34
34
|
|
|
35
35
|
# Paths that must never reach a published image. The image is built from the
|
|
36
36
|
# working tree, so anything ignored locally (private notes, scratch files)
|
|
37
|
-
# would otherwise ride along
|
|
37
|
+
# would otherwise ride along.
|
|
38
38
|
IMAGE_FORBIDDEN_PATHS = %w[/wp2txt/research-notes /wp2txt/tmp /wp2txt/.git /wp2txt/CLAUDE.md /wp2txt/.claude].freeze
|
|
39
39
|
|
|
40
40
|
desc "Verify a built image contains no private material (run before pushing)"
|
|
@@ -1,40 +1,31 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Offline Indexes, Queries, and the MCP Server
|
|
2
2
|
|
|
3
|
-
This guide covers
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
This guide covers wp2txt's index-based features: local indexes over Wikipedia dumps,
|
|
4
|
+
offline queries across a whole edition, full-text search, interlanguage links,
|
|
5
|
+
cross-language SQL, and the MCP server for connecting an LLM client.
|
|
6
6
|
|
|
7
7
|
For plain-text extraction (the classic wp2txt), see the [README](../README.md).
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
wp2txt takes the opposite approach: build local indexes over an official dump file, so that
|
|
17
|
-
every query is
|
|
18
|
-
|
|
19
|
-
- **exhaustive** — it scans every article, not search-ranked results;
|
|
20
|
-
- **version-pinned** — results are tied to one dump (e.g. `jawiki-20260701`) and
|
|
21
|
-
reproducible later;
|
|
22
|
-
- **agent-operable** — the MCP server exposes the whole layer to LLM agents, with
|
|
23
|
-
guardrails and provenance records designed for autonomous use.
|
|
9
|
+
Everything below runs against a downloaded dump file: queries cover every article of the
|
|
10
|
+
edition rather than a page of search results, results are tied to one dump (e.g.
|
|
11
|
+
`jawiki-20260701`) and can be reproduced later, and nothing goes over the network once
|
|
12
|
+
the dump is downloaded. Typical uses: "which of the 1.5M articles have a plot section",
|
|
13
|
+
"how many film articles mention X, and which ones don't", "how do the section structures
|
|
14
|
+
of the same article differ between the English and Japanese editions".
|
|
24
15
|
|
|
25
16
|
## 1. Building the indexes
|
|
26
17
|
|
|
27
18
|
```console
|
|
28
|
-
#
|
|
19
|
+
# Metadata index (categories, section headings, redirects, category hierarchy)
|
|
29
20
|
$ wp2txt --build-index --lang=ja
|
|
30
21
|
|
|
31
|
-
#
|
|
22
|
+
# Metadata index + FTS5 full-text index over the cleaned article text
|
|
32
23
|
$ wp2txt --build-index --fulltext --lang=ja
|
|
33
24
|
```
|
|
34
25
|
|
|
35
26
|
The dump is downloaded automatically if needed and everything is cached under
|
|
36
27
|
`~/.wp2txt/cache/`. Ballpark figures (Apple Silicon laptop): Japanese Wikipedia ~15 min /
|
|
37
|
-
~1.7 GB for the metadata index, ~1.5 h / ~12 GB with full text; English roughly
|
|
28
|
+
~1.7 GB for the metadata index, ~1.5 h / ~12 GB with full text; English roughly 2.5–3× the
|
|
38
29
|
time, ~10 GB / ~12 GB respectively.
|
|
39
30
|
|
|
40
31
|
The full-text tokenizer is selected per language: character trigrams for Japanese,
|
|
@@ -58,8 +49,8 @@ $ wp2txt --find-articles --in-category "Films" -D 2 -j json --limit 100 --lang=e
|
|
|
58
49
|
$ wp2txt --search "タイムループ" --in-category "映画作品" -D 3 --lang=ja
|
|
59
50
|
```
|
|
60
51
|
|
|
61
|
-
Search totals
|
|
62
|
-
that dump version —
|
|
52
|
+
Search totals count every match in the edition, so `0 matches` means the term is absent
|
|
53
|
+
from that dump version — a result you can state and re-verify later.
|
|
63
54
|
|
|
64
55
|
## 3. Interlanguage links (langlinks)
|
|
65
56
|
|
|
@@ -114,7 +105,7 @@ $ claude mcp add wp2txt -- docker run -i --rm -v wp2txt:/root/.wp2txt ghcr.io/yo
|
|
|
114
105
|
|
|
115
106
|
| Tool | Purpose |
|
|
116
107
|
|------|---------|
|
|
117
|
-
| `dump_info` | Dump identity,
|
|
108
|
+
| `dump_info` | Dump identity, installed indexes, corpus statistics, langlinks provenance |
|
|
118
109
|
| `get_article` / `get_sections` / `list_headings` / `get_categories` | Single-article access (redirect-aware) |
|
|
119
110
|
| `find_articles` | Exhaustive filtered listing (category recursion, category AND / pattern match, section headings, title match) |
|
|
120
111
|
| `category_tree` / `section_stats` | Scope exploration and heading-frequency discovery |
|
|
@@ -126,17 +117,19 @@ $ claude mcp add wp2txt -- docker run -i --rm -v wp2txt:/root/.wp2txt ghcr.io/yo
|
|
|
126
117
|
| `extract_corpus` | Filtered or explicit-title extraction to JSONL + reproducibility sidecar; optional RAG chunking |
|
|
127
118
|
| `start_extract_job` / `job_status` / `cancel_job` / `list_jobs` | Background jobs for large extractions |
|
|
128
119
|
|
|
129
|
-
###
|
|
120
|
+
### What happens when your assistant uses these tools
|
|
130
121
|
|
|
131
|
-
- **
|
|
132
|
-
|
|
133
|
-
- **
|
|
134
|
-
|
|
135
|
-
- **
|
|
136
|
-
the query, and any alias sets
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
122
|
+
- **Filtering and counting run over the whole dump**, and your assistant reads the result
|
|
123
|
+
rather than tallying articles itself.
|
|
124
|
+
- **Large results are written to a file**; the reply carries a summary and a short sample.
|
|
125
|
+
Your corpus lands on disk intact instead of being paraphrased through the chat.
|
|
126
|
+
- **Extractions are traceable.** Extractions and file-writing queries leave a `.meta.json`
|
|
127
|
+
next to the output recording the dump version, the query, and any alias sets used, so
|
|
128
|
+
you can reproduce or cite the result later.
|
|
129
|
+
- **The tools cannot change your data.** Queries run read-only and are stopped after 30
|
|
130
|
+
seconds, saved alias sets are re-checked before being stored, and files can only be
|
|
131
|
+
written under the server's output directory — worth knowing if you plan to let an
|
|
132
|
+
assistant work unattended.
|
|
140
133
|
|
|
141
134
|
## 5. Cross-language SQL
|
|
142
135
|
|
|
@@ -157,12 +150,12 @@ query_sql(
|
|
|
157
150
|
```
|
|
158
151
|
|
|
159
152
|
Attached databases appear as `{lang}_meta` (and `{lang}_fts` when that language has a
|
|
160
|
-
full-text
|
|
161
|
-
|
|
162
|
-
|
|
153
|
+
full-text index) and share the main database's schema. You pass language codes, not paths,
|
|
154
|
+
and the SQL you write cannot attach anything itself. The response lists what was attached,
|
|
155
|
+
including each edition's dump name, and flags it when the dates differ.
|
|
163
156
|
|
|
164
|
-
|
|
165
|
-
|
|
157
|
+
This runs entirely offline against pinned dump versions, so a cross-edition comparison can
|
|
158
|
+
be re-run later and produce the same numbers.
|
|
166
159
|
|
|
167
160
|
## 6. Large results, explicit sets, and reproducibility
|
|
168
161
|
|
|
@@ -182,26 +175,34 @@ extract_corpus(titles: ["東京物語", "羅生門", ...], content: "summary",
|
|
|
182
175
|
redirect hop, and reports unmatched titles in `not_found` — closing the loop
|
|
183
176
|
*SQL decides the set → the tool materializes it → the LLM reads it*.
|
|
184
177
|
|
|
185
|
-
## 7.
|
|
186
|
-
|
|
187
|
-
Section headings vary ("Plot" vs "Synopsis"; 「あらすじ」 vs
|
|
188
|
-
no per-language dictionaries.
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
|
|
178
|
+
## 7. Section alias sets
|
|
179
|
+
|
|
180
|
+
Section headings vary by article and by language ("Plot" vs "Synopsis"; 「あらすじ」 vs
|
|
181
|
+
「ストーリー」), and wp2txt ships no per-language dictionaries. Three tools manage named
|
|
182
|
+
groups of equivalent headings instead:
|
|
183
|
+
|
|
184
|
+
- `section_stats` lists the headings actually used in a scope, with counts.
|
|
185
|
+
- `section_cooccurrence` reports how often two headings appear in the same article.
|
|
186
|
+
Headings that mean the same thing rarely co-occur, so a high ratio is evidence that
|
|
187
|
+
they are *different* sections (「概要」 and 「あらすじ」 co-occur often — not synonyms).
|
|
188
|
+
- `save_alias_set` stores a named group. The group is re-checked against co-occurrence
|
|
189
|
+
before saving; a failing group is not stored and the call returns `saved: false`
|
|
190
|
+
rather than an error. Pass `force` to override, and use `list_alias_sets` to see
|
|
191
|
+
what is stored.
|
|
192
|
+
|
|
193
|
+
Queries then accept `alias_set: "name"` in place of a heading list, and extractions
|
|
194
|
+
record the group's exact contents in their `.meta.json`. The bundled `discover_aliases`
|
|
195
|
+
prompt walks an LLM client through building and saving a set.
|
|
196
|
+
|
|
197
|
+
## 8. Limitations to keep in mind
|
|
198
|
+
|
|
199
|
+
- Searches run over the **cleaned text**: content that extraction replaces with a marker
|
|
200
|
+
(`[MATH]`, `[CODE]`, `[TABLE]`, …) cannot be matched. A count is a count of the cleaned
|
|
201
|
+
text, not of the raw wikitext.
|
|
202
|
+
- Japanese, Chinese, and Korean indexes cannot match queries shorter than 3 characters.
|
|
203
|
+
Word-based languages match exact forms only — `run` does not find `running`. Plan your
|
|
204
|
+
search terms accordingly, especially when you intend to report a zero result.
|
|
205
|
+
- Categories and interlanguage links are read from the dump as editors wrote them.
|
|
206
|
+
Categories added by a template rather than written in the article text are not visible
|
|
207
|
+
to wp2txt, which can make a category look much smaller than it is on the website —
|
|
208
|
+
check against the article text if a count looks wrong.
|
data/lib/wp2txt/version.rb
CHANGED
data/spec/docs_sync_spec.rb
CHANGED
|
@@ -5,7 +5,7 @@ require "spec_helper"
|
|
|
5
5
|
# The MCP tool surface and its public documentation drift apart easily
|
|
6
6
|
# (the 2.2.0 README shipped with a tool table missing four tools). This spec
|
|
7
7
|
# pins them together: every tool defined in bin/wp2txt-mcp must appear in the
|
|
8
|
-
# docs/
|
|
8
|
+
# docs/INDEXES.md tool table, and the table must not list phantom tools.
|
|
9
9
|
RSpec.describe "documentation surface sync" do
|
|
10
10
|
repo_root = File.expand_path("..", __dir__)
|
|
11
11
|
|
|
@@ -15,9 +15,9 @@ RSpec.describe "documentation surface sync" do
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
define_method(:documented_tools) do
|
|
18
|
-
doc = File.read(File.join(repo_root, "docs", "
|
|
18
|
+
doc = File.read(File.join(repo_root, "docs", "INDEXES.md"))
|
|
19
19
|
table = doc[/^### Tools\n(.*?)\n\n/m, 1]
|
|
20
|
-
raise "Tools table not found in docs/
|
|
20
|
+
raise "Tools table not found in docs/INDEXES.md" unless table
|
|
21
21
|
|
|
22
22
|
# Tool names live in the first column only (the purpose column may
|
|
23
23
|
# backtick argument names like `attach`)
|
|
@@ -30,10 +30,32 @@ RSpec.describe "documentation surface sync" do
|
|
|
30
30
|
expect(defined_tools.size).to be >= 15
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
it "documents every MCP tool in docs/
|
|
33
|
+
it "documents every MCP tool in docs/INDEXES.md, with no phantom entries" do
|
|
34
34
|
missing = defined_tools - documented_tools
|
|
35
35
|
phantom = documented_tools - defined_tools
|
|
36
|
-
expect(missing).to be_empty, "tools not documented in docs/
|
|
36
|
+
expect(missing).to be_empty, "tools not documented in docs/INDEXES.md: #{missing.join(', ')}"
|
|
37
37
|
expect(phantom).to be_empty, "documented tools that do not exist: #{phantom.join(', ')}"
|
|
38
38
|
end
|
|
39
|
+
|
|
40
|
+
# Tripwire: tracked files must not contain tokens listed in .private-doc-tokens,
|
|
41
|
+
# an untracked, machine-local file (one substring per line; # starts a comment).
|
|
42
|
+
# The file exists only on machines that maintain such a list; everywhere else
|
|
43
|
+
# (CI, other contributors) this example skips — loudly, so a silently dead
|
|
44
|
+
# check cannot be mistaken for a passing one.
|
|
45
|
+
it "keeps machine-local private tokens out of tracked files" do
|
|
46
|
+
token_file = File.join(repo_root, ".private-doc-tokens")
|
|
47
|
+
skip "SKIPPED: no .private-doc-tokens on this machine — tripwire not checked" unless File.exist?(token_file)
|
|
48
|
+
|
|
49
|
+
tokens = File.readlines(token_file, encoding: "UTF-8")
|
|
50
|
+
.map(&:strip).reject { |t| t.empty? || t.start_with?("#") }
|
|
51
|
+
tracked = `git -C #{repo_root} ls-files -z`.split("\x0")
|
|
52
|
+
hits = tracked.flat_map do |f|
|
|
53
|
+
path = File.join(repo_root, f)
|
|
54
|
+
next [] unless File.file?(path)
|
|
55
|
+
|
|
56
|
+
content = File.read(path, encoding: "BINARY")
|
|
57
|
+
tokens.filter_map { |t| "#{f}: #{t}" if content.include?(t.b) }
|
|
58
|
+
end
|
|
59
|
+
expect(hits).to be_empty, "private tokens found in tracked files:\n #{hits.join("\n ")}"
|
|
60
|
+
end
|
|
39
61
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wp2txt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.3.
|
|
4
|
+
version: 2.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yoichiro Hasebe
|
|
@@ -217,7 +217,7 @@ files:
|
|
|
217
217
|
- Rakefile
|
|
218
218
|
- bin/wp2txt
|
|
219
219
|
- bin/wp2txt-mcp
|
|
220
|
-
- docs/
|
|
220
|
+
- docs/INDEXES.md
|
|
221
221
|
- image/wp2txt-logo.svg
|
|
222
222
|
- image/wp2txt.svg
|
|
223
223
|
- lib/wp2txt.rb
|