lumina-wiki 1.5.0 → 1.6.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,90 @@
3
3
  All notable changes to Lumina-Wiki are documented here.
4
4
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
5
 
6
+ ## [Unreleased]
7
+
8
+ ## [1.6.0] - 2026-05-15
9
+
10
+ ### Added — Multi-provider PDF resolution + RSS / Atom feeds (research pack)
11
+
12
+ - **OpenAlex fetcher** activated as the metadata anchor across the new ladder
13
+ (Phase 1–2). `external_ids.openalex` namespace now persists Work IDs
14
+ (`^W\d+$`).
15
+ - **`fetch_unpaywall.py`** — DOI → best OA PDF URL. Requires
16
+ `UNPAYWALL_EMAIL` (free email-of-record).
17
+ - **`fetch_core.py`** — CORE search + download-url. Optional
18
+ `CORE_API_KEY`; ladder skips CORE on 429 and warns once.
19
+ - **`resolve_pdf.py`** — 2-layer orchestrator. Layer A always runs OpenAlex
20
+ (cross-walks DOI ↔ arXiv ↔ OpenAlex). Layer B is a stop-on-first-200 PDF
21
+ ladder: `oa_url → unpaywall → core → arxiv`. Each provider attempt is
22
+ logged to stderr; the final shape carries `external_ids`, `sources[]`,
23
+ `pdf_path`, and `status` (`ok` | `metadata_only` | `failed`).
24
+ - **`fetch_rss.py`** — RSS / Atom poller with etag caching, defusedxml-based
25
+ XXE rejection, per-feed state files under `_lumina/_state/feeds/<id>.json`,
26
+ spill-aware `max_new` cap, and 5000-entry / 90-day `last_seen_guids`
27
+ eviction.
28
+ - **Watchlist `type: feed`** items extend `_lumina/config/watchlist.yml`
29
+ additively. v1 files without `type` keep validating (defaults to
30
+ `topic`). Feed URLs are gated to `^https://` and rejected if they start
31
+ with `--` (flag-injection defense-in-depth).
32
+ - **`/lumi-research-watch-run`** skill orchestrates a single pass over the
33
+ consolidated watchlist (topics + feeds). User owns scheduling — three
34
+ patterns documented (cron, launchd, Task Scheduler).
35
+ - **`cron-daily.sh` wrapper** ships under
36
+ `_lumina/scripts/scheduler-samples/`. Inert until the user wires it into
37
+ their scheduler. Sets `umask 077`, `chmod 600` on the log,
38
+ rotates at 1 MB.
39
+ - **`extract_ids_from_text()`** in `id_utils.py` — reusable free-text
40
+ identifier harvester for feed entry titles / summaries / link hrefs.
41
+
42
+ ### Added — Project governance
43
+
44
+ - `CONTRIBUTING.md` at the repo root: workflow checklists for adding skills,
45
+ fetchers, schema changes, installer changes, and entry-point stubs; the
46
+ trilingual user-docs convention; CI gates; exit-code contract; and a section
47
+ specifically scoped to AI-agent contributors that points at
48
+ `docs/project-context.md`, `CLAUDE.md`, and `docs/DEVELOPMENT.md` as
49
+ load-bearing context.
50
+ - `CODE_OF_CONDUCT.md` at the repo root: Contributor Covenant v2.1, contact
51
+ `tronghieu.luu@gmail.com`. Linked from `CONTRIBUTING.md` §2.
52
+ - `SECURITY.md` at the repo root: supported-versions table, private
53
+ reporting channels (GitHub Private Vulnerability Reporting + email),
54
+ in-scope / out-of-scope surfaces, severity bands, and coordinated
55
+ disclosure expectations.
56
+ - `.github/PULL_REQUEST_TEMPLATE.md`: per-change-type checklists that
57
+ mirror `CONTRIBUTING.md` §5 (skill / fetcher / schema / installer),
58
+ trilingual docs checkpoint, and a rule-deviation prompt.
59
+
60
+ ### Security
61
+
62
+ - **SSRF guard** (`_safe_url`) on every PDF candidate URL: rejects RFC1918,
63
+ loopback, link-local, multicast, cloud-metadata (169.254.169.254).
64
+ Re-validated post-redirect.
65
+ - **`fetch_pdf.py` mid-stream size cap** (`MAX_PDF_BYTES = 100 MiB`) — a
66
+ malicious endpoint that lies about Content-Length now aborts mid-download
67
+ and cleans up `.tmp`.
68
+ - **DOI filename hashing** — DOIs are hashed to a 16-char SHA-256 prefix on
69
+ disk to neutralize Windows-reserved-name collisions (CON, PRN, AUX, NUL).
70
+ - **XXE pre-parse** — every RSS / Atom body is run through
71
+ `defusedxml.ElementTree.fromstring` before feedparser sees it; DOCTYPE /
72
+ billion-laughs payloads are rejected without state mutation.
73
+
74
+ ### Requirements
75
+
76
+ - New optional env vars: `UNPAYWALL_EMAIL`, `CORE_API_KEY`. Both gracefully
77
+ skip (ladder continues) when unset.
78
+ - `requirements.txt` adds `feedparser>=6.0` (research pack only).
79
+
80
+ ### Backwards compatibility
81
+
82
+ - `external_ids.openalex` is additive — existing pages continue to validate.
83
+ - `sources[]` is additive — entries without an entry stay valid.
84
+ `ns/value` fields drop silently if either is missing or invalid (same
85
+ forgiveness model as the existing `url` field).
86
+ - Watchlist v1 (no `type` field) still validates and runs unchanged.
87
+ - `fetch_pdf.py` CLI is stable; new helpers (`_safe_url`, `head_check`,
88
+ `MAX_PDF_BYTES`) are additions only.
89
+
6
90
  ## [1.5.0] - 2026-05-10
7
91
 
8
92
  ### Added — Learning Pack: `/lumi-learning-reflect` self-reflection skill (PRs #16, #17)
@@ -398,7 +482,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
398
482
 
399
483
  ---
400
484
 
401
- [Unreleased]: https://github.com/tronghieu/lumina-wiki/compare/v1.4.0...HEAD
485
+ [Unreleased]: https://github.com/tronghieu/lumina-wiki/compare/v1.5.0...HEAD
486
+ [1.5.0]: https://github.com/tronghieu/lumina-wiki/compare/v1.4.0...v1.5.0
402
487
  [1.4.0]: https://github.com/tronghieu/lumina-wiki/compare/v1.3.0...v1.4.0
403
488
  [1.3.0]: https://github.com/tronghieu/lumina-wiki/compare/v1.2.0...v1.3.0
404
489
  [1.2.0]: https://github.com/tronghieu/lumina-wiki/compare/v1.1.0...v1.2.0
package/README.md CHANGED
@@ -184,6 +184,7 @@ These are the commands you can use when chatting with your AI agent.
184
184
  | | `/lumi-research-prefill` | Seed foundational concepts to avoid duplicates. |
185
185
  | | `/lumi-research-topic` | Create a topic page at `wiki/topics/<slug>.md` by gathering related concepts and sources already in your wiki. The AI proposes what to include and you confirm before anything is written. Use this after several `/lumi-ingest` runs when you want to give a theme its own page. |
186
186
  | | `/lumi-research-setup` | Help configure API keys for research tools. |
187
+ | | `/lumi-research-watch-run` | Run one scheduled-discovery pass over your watchlist (topics + RSS / Atom feeds). Polls only when you ask. |
187
188
  | **Reading** | `/lumi-reading-chapter-ingest` | Ingest a book chapter by chapter. |
188
189
  | | `/lumi-reading-character-track` | Track characters and their relationships in a story. |
189
190
  | | `/lumi-reading-theme-map` | Identify and map themes in a narrative. |
@@ -204,8 +205,8 @@ Lumina-Wiki is evolving rapidly. Here is our user-facing roadmap:
204
205
  - [x] **Multilingual setup:** Choose English, Vietnamese, or Chinese as your primary language during install. *(shipped in v1.2)*
205
206
  - [x] **Native DOCX, RTF & EPUB ingestion:** Pull Word, Rich Text, and EPUB books straight into your wiki via the research pack. *(shipped in v1.x)*
206
207
  - [x] **Improved CI/CD:** Native support for Bun and Node 22 environments. *(shipped in v1.2)*
207
- - [ ] **Global Source Expansion:** Direct integration with OpenAlex, CORE, and Unpaywall for reliable DOI-to-PDF resolution.
208
- - [ ] **RSS & Blog Monitoring:** Automatically surface new papers from your favorite lab blogs and journals.
208
+ - [x] **Global Source Expansion:** Direct integration with OpenAlex, CORE, and Unpaywall for reliable DOI-to-PDF resolution. *(shipped in v1.6)*
209
+ - [x] **RSS & Blog Monitoring:** Automatically surface new papers from your favorite lab blogs and journals via `type: feed` watchlist items. *(shipped in v1.6)*
209
210
  - [ ] **Advanced Paper Ranking:** See influence scores and quality signals for your research papers.
210
211
 
211
212
  **Long-term (Deep Research & Integration)**
@@ -250,3 +251,198 @@ npm run test:all
250
251
  - [简体中文 (Chinese)](README.zh.md)
251
252
 
252
253
  **License:** [MIT](LICENSE) © Lưu Trọng Hiếu.
254
+
255
+ <!-- lumina:schema -->
256
+
257
+ ## Roles
258
+
259
+ You are the wiki maintainer. The user curates sources, asks questions, and directs analysis. You do everything else: read, summarize, connect pages, file notes, run health checks, and keep the wiki coherent. You write the wiki; the user reads it.
260
+
261
+ Always communicate with the user in **English**. Always write wiki pages in **English**.
262
+
263
+ ### User Communication
264
+
265
+ - Default to a clear, everyday style suitable for most users. You are a helpful knowledge assistant, not a software engineer explaining implementation details.
266
+ - Use **English** for every conversational message. Do not mix languages unless quoting source text, file names, commands, or proper nouns.
267
+ - Translate workflow terms into the user's language. If a source uses an important domain term, write the translated term first and put the original term in parentheses on first use.
268
+ - Speak to non-technical users. Use short, natural sentences. Say what the user gets, what changed, what needs attention, or what decision is needed; keep internal tool details quiet unless the user asks.
269
+ - Prefer plain phrases such as "checking links", "checking against the source", "saving the page", and "I found something to review" over tool-centric words like lint, schema, frontmatter, checkpoint, verify, or JSON in user-facing messages.
270
+ - If technical detail is necessary, give the plain-language meaning first, then the technical term in parentheses.
271
+ - Ask the user only when their judgment is needed: approving a draft, choosing between ambiguous sources, allowing an overwrite/restart, handling source-check findings, accepting lower confidence, or deciding how to fix an issue the tools cannot fix safely.
272
+
273
+ ---
274
+
275
+ ## Repository Layout
276
+
277
+ Keep this mental map in immediate context:
278
+
279
+ ### `wiki/` is the main product surface
280
+
281
+ - `wiki/index.md` — catalog of all wiki pages, updated on every ingest
282
+ - `wiki/log.md` — append-only activity log
283
+ - `wiki/concepts/` — reusable knowledge structure
284
+ - `wiki/sources/` — per-source summaries (papers, articles, books, podcasts, notes)
285
+ - `wiki/people/` — people referenced across sources
286
+ - `wiki/summary/` — area-level syntheses
287
+ - `wiki/outputs/` — generated artifacts (comparisons, exports)
288
+ - `wiki/graph/` — derived state; never edit manually
289
+
290
+ ### `raw/` is user-owned
291
+
292
+ - `raw/sources/` — `.pdf`, `.tex`, `.html`, `.md`, transcripts, anything ingested
293
+ - `raw/notes/` — user's own markdown notes
294
+ - `raw/assets/` — images and binary attachments
295
+ - `raw/tmp/` — sidecar files generated by skills (transient; do not store canonical sources here)
296
+ - `raw/download/<resource>/` — full-text artifacts auto-fetched by skills, partitioned by source
297
+ (e.g. `raw/download/arxiv/2604.03501v2.pdf`, `raw/download/doi/<doi>.pdf`).
298
+ Permanent agent-writable zone — keep separate from `raw/sources/` (human-curated).
299
+
300
+ **Rule:** never modify or delete an existing file under `raw/`. Files added by the user are authoritative and immutable to the agent. New files may only be *added*, only by a skill that documents this behavior, and only into `raw/tmp/`, `raw/download/`. Every other path under `raw/` is read-only.
301
+
302
+ ### `.agents/` is the skill source of truth
303
+
304
+ - `.agents/skills/lumi-*/` — installed skills (flat, one directory per skill)
305
+
306
+ ### `_lumina/` is the installer-managed sidecar
307
+
308
+ - `_lumina/config/lumina.config.yaml` — workspace config; editable
309
+ - `_lumina/schema/` — deeper reference docs; open when this file points you there
310
+ - `_lumina/scripts/` — Node engine (`wiki.mjs`, `lint.mjs`, `reset.mjs`, `schemas.mjs`)
311
+ - `_lumina/tools/` — Python tools (always: `extract_pdf.py`, `fetch_pdf.py`, `requirements.txt`)
312
+ - `_lumina/_state/` — installer/skill checkpoint state; gitignored
313
+ - `_lumina/manifest.json` — installer state; never edit by hand
314
+
315
+ ---
316
+
317
+ ## Page Types
318
+
319
+ Every wiki page has a defined type, frontmatter, and section structure. **Open `_lumina/schema/page-templates.md` before drafting a new page or repairing an existing one** — it has the full templates and required frontmatter fields.
320
+
321
+ | Type | Directory | Purpose |
322
+ |------------|---------------|----------------------------------------------------------------------|
323
+ | Source | `sources/` | Per-document summary: key claims, evidence, takeaways, questions |
324
+ | Concept | `concepts/` | Cross-source idea or technique with variants and comparisons |
325
+ | Person | `people/` | Profile of a referenced person with key sources and relationships |
326
+ | Summary | `summary/` | Area-level synthesis spanning multiple sources and concepts |
327
+
328
+ ---
329
+
330
+ ## Link Syntax
331
+
332
+ All internal links use Obsidian wikilinks:
333
+
334
+ ```markdown
335
+ [[slug]] — link to any page in this wiki
336
+ [[chain-of-thought]] — links to concepts/chain-of-thought.md
337
+ [[1984-orwell]] — links to sources/1984-orwell.md
338
+ ```
339
+
340
+ **Slug rule**: lowercase, hyphen-separated, no spaces, no diacritics.
341
+
342
+ ---
343
+
344
+ ## Cross-Reference Rules (Bidirectional Links)
345
+
346
+ When you write a forward link, **always write the reverse link in the same operation**. This is the heart of why the wiki compounds. Skipping it leaves the graph half-built.
347
+
348
+ | Forward action | Required reverse action |
349
+ |---------------------------------------------|---------------------------------------------|
350
+ | `sources/A` writes `Related: [[concept-B]]` | `concepts/B` appends A to `Key sources` |
351
+ | `sources/A` writes `[[person-C]]` | `people/C` appends A to `Key sources` |
352
+ | `concepts/K` writes `[[source-E]]` | `sources/E` appends K to `Related concepts`|
353
+ | `summary/S` writes `[[concept-K]]` | `concepts/K` appends S to `Mentioned in` |
354
+
355
+ ### Exemptions (mode: `exempt-only`, default)
356
+
357
+ Some links are intentionally one-way. Defaults:
358
+
359
+ - **`outputs/**`** — ephemeral artifacts
360
+ - **External URLs** (`*://*`) — out of wiki scope
361
+
362
+ Anything outside an exemption glob must be bidirectional.
363
+
364
+ ---
365
+
366
+ ## Log Format
367
+
368
+ Append-only. One line per skill invocation. Format:
369
+
370
+ ```markdown
371
+ ## [YYYY-MM-DD] skill | details
372
+ ```
373
+
374
+ `grep "^## \[" wiki/log.md | tail -10` gives you recent activity.
375
+
376
+ ---
377
+
378
+ ## Graph
379
+
380
+ `wiki/graph/edges.jsonl` and `wiki/graph/citations.jsonl` are auto-generated. Never edit manually. The full set of edge types lives in `_lumina/scripts/schemas.mjs` — open it when you need to pick a type or check what is allowed.
381
+
382
+ ---
383
+
384
+ ## Constraints (Non-Negotiable)
385
+
386
+ - **`raw/` is user-owned**: never modify or delete existing files; additions only via the two named paths above.
387
+ - **`graph/` is auto-generated**: only modify via the graph rebuild step.
388
+ - **Bidirectional links are mandatory**: forward link and reverse link in the same operation.
389
+ - **`index.md` updated on every ingest**: every new page must be cataloged immediately.
390
+ - **`log.md` is append-only**: never rewrite history.
391
+ - **Skill flags are user-owned**: never invent, flip, or drop a flag based on repo state alone. If the user omitted a parameter, only fill it in when the skill explicitly documents a default; otherwise ask.
392
+ - **No silent overwrites**: preserve sections marked with `<!-- user-edited -->` comment.
393
+ - **Cite when uncertain**: link the source explicitly for low-confidence claims.
394
+
395
+ ---
396
+
397
+ ## Skills
398
+
399
+ Skills live in `.agents/skills/` and are invoked via slash commands. Active install recorded in `_lumina/manifest.json`.
400
+
401
+ ### Core skills (always present)
402
+
403
+ | Skill | Trigger | What it does |
404
+ |---------------|---------------|-------------------------------------------------------|
405
+ | `/lumi-init` | manual, first | Bootstrap wiki from existing `raw/` content |
406
+ | `/lumi-ingest` | manual | Read a source and write a wiki page. It asks you to review the draft, then continues on its own unless something needs your judgment |
407
+ | `/lumi-ask` | manual | Query wiki, synthesize answer, optionally file page |
408
+ | `/lumi-edit` | manual | Add/remove/revise wiki content per user request |
409
+ | `/lumi-check` | manual/weekly | Lint: broken links, orphans, missing reverse links |
410
+ | `/lumi-reset` | manual | Scoped destructive cleanup |
411
+ | `/lumi-verify` | manual | Check that wiki pages match the sources they cite; reports suspicious statements for the user to review; never auto-edits |
412
+
413
+
414
+ ---
415
+
416
+ ## Tooling Conventions
417
+
418
+ - **`_lumina/scripts/lint.mjs`** — pure-Node markdown linter, runs offline.
419
+ - **`_lumina/scripts/wiki.mjs`** — wiki engine (frontmatter, graph mutation, slug, log).
420
+ - **`_lumina/scripts/reset.mjs`** — scoped destructive reset.
421
+ - **`_lumina/tools/extract_pdf.py`** — PDF text extractor (pypdf-based); used by `/lumi-ingest` and `/lumi-reading-chapter-ingest` when the host IDE cannot read PDFs natively.
422
+ - **`_lumina/tools/fetch_pdf.py`** — URL → `raw/download/<resource>/` PDF downloader (streaming, atomic, idempotent); used by `/lumi-ingest` Mode B when the input is a URL or paper identifier.
423
+ - **`_lumina/tools/requirements.txt`** — Python dependencies for bundled tools. Run `pip install -r _lumina/tools/requirements.txt` when a tool reports a missing package.
424
+
425
+ ---
426
+
427
+ ## How To Use This Wiki (For New LLM Sessions)
428
+
429
+ 1. Read this file (you are doing it now).
430
+ 2. Read `wiki/index.md` to learn what already exists.
431
+ 3. Read `wiki/log.md`'s last 20 entries to learn what happened recently.
432
+ 4. When the user invokes a skill, read the skill's `SKILL.md` first.
433
+ 5. When in doubt about page structure, open `_lumina/schema/page-templates.md`.
434
+ 6. When in doubt about scope, ask the user — never silently expand it.
435
+
436
+ The wiki is a long-running collaboration. Maintain it patiently.
437
+
438
+ <!-- /lumina:schema -->
439
+
440
+ ---
441
+
442
+ ## Contributors
443
+
444
+ Thanks to everyone who has contributed to Lumina Wiki!
445
+
446
+ [![Contributors](https://contrib.rocks/image?repo=tronghieu/lumina-wiki)](https://github.com/tronghieu/lumina-wiki/graphs/contributors)
447
+
448
+ **Want to contribute?** Read [CONTRIBUTING.md](CONTRIBUTING.md) to get started — bug reports, new skills, tool integrations, and translations are all welcome.
package/README.vi.md CHANGED
@@ -184,6 +184,7 @@ Xem [Hướng dẫn Nâng cao](docs/user-guide/advanced-qmd.vi.md) để biết
184
184
  | | `/lumi-research-prefill` | Tạo trước các khái niệm nền tảng để tránh trùng lặp. |
185
185
  | | `/lumi-research-topic` | Gom các khái niệm và nguồn liên quan trong wiki thành một trang chủ đề tại `wiki/topics/<slug>.md`. AI đề xuất danh sách để bạn xem và xác nhận trước khi trang được tạo. Dùng sau khi đã nạp nhiều tài liệu và muốn tổng hợp một nhóm ý tưởng thành trang riêng. |
186
186
  | | `/lumi-research-setup` | Giúp cấu hình API key cho các công cụ nghiên cứu. |
187
+ | | `/lumi-research-watch-run` | Chạy một lượt khám phá theo lịch dựa trên watchlist (chủ đề + nguồn RSS / Atom). Chỉ chạy khi bạn yêu cầu. |
187
188
  | **Reading** | `/lumi-reading-chapter-ingest`| Nạp kiến thức sách theo từng chương. |
188
189
  | | `/lumi-reading-character-track`| Theo dõi các nhân vật và mối quan hệ của họ trong truyện. |
189
190
  | | `/lumi-reading-theme-map` | Xác định và lập bản đồ các chủ đề trong một câu chuyện. |
@@ -204,8 +205,8 @@ Lumina-Wiki đang phát triển nhanh chóng. Dưới đây là lộ trình hư
204
205
  - [x] **Cài đặt đa ngôn ngữ:** Chọn Tiếng Anh, Tiếng Việt hoặc Tiếng Trung làm ngôn ngữ chính khi cài đặt. *(đã phát hành trong v1.2)*
205
206
  - [x] **Nạp DOCX, RTF & EPUB native:** Đưa thẳng file Word, Rich Text và sách EPUB vào wiki qua research pack. *(đã phát hành trong v1.x)*
206
207
  - [x] **Cải thiện CI/CD:** Hỗ trợ chính thức cho môi trường Bun và Node 22. *(đã phát hành trong v1.2)*
207
- - [ ] **Mở rộng nguồn dữ liệu toàn cầu:** Tích hợp trực tiếp với OpenAlex, CORE và Unpaywall để tra cứu DOI-to-PDF đáng tin cậy.
208
- - [ ] **Theo dõi RSS & Blog:** Tự động phát hiện bài báo mới từ các blog phòng thí nghiệm và tạp chí yêu thích.
208
+ - [x] **Mở rộng nguồn dữ liệu toàn cầu:** Tích hợp trực tiếp với OpenAlex, CORE và Unpaywall để tra cứu DOI-to-PDF đáng tin cậy. *(ra mắt trong v1.6)*
209
+ - [x] **Theo dõi RSS & Blog:** Tự động phát hiện bài báo mới từ các blog phòng thí nghiệm và tạp chí yêu thích qua các mục `type: feed` trong watchlist. *(ra mắt trong v1.6)*
209
210
  - [ ] **Xếp hạng bài báo nâng cao:** Xem điểm số ảnh hưởng và tín hiệu chất lượng cho các nghiên cứu của bạn.
210
211
 
211
212
  **Dài hạn (Nghiên cứu sâu & Tích hợp)**
@@ -249,3 +250,13 @@ npm run test:all
249
250
  - [简体中文 (Tiếng Trung)](README.zh.md)
250
251
 
251
252
  **Giấy phép:** [MIT](LICENSE) © Lưu Trọng Hiếu.
253
+
254
+ ---
255
+
256
+ ## Người đóng góp
257
+
258
+ Cảm ơn tất cả những người đã đóng góp cho Lumina Wiki!
259
+
260
+ [![Contributors](https://contrib.rocks/image?repo=tronghieu/lumina-wiki)](https://github.com/tronghieu/lumina-wiki/graphs/contributors)
261
+
262
+ **Muốn đóng góp?** Đọc [CONTRIBUTING.md](CONTRIBUTING.md) để bắt đầu — báo lỗi, thêm skill mới, tích hợp công cụ, hay dịch thuật đều được chào đón.
package/README.zh.md CHANGED
@@ -185,6 +185,7 @@ npx skills add https://github.com/tobi/qmd --skill qmd
185
185
  | | `/lumi-research-prefill` | 预先生成基础概念,避免重复。 |
186
186
  | | `/lumi-research-topic` | 把 wiki 中已有的相关概念和来源汇聚成一个主题页,保存在 `wiki/topics/<slug>.md`。AI 会提议收录哪些内容,由你确认后再生成页面。多次 `/lumi-ingest` 之后,用它把一组相关想法整理成独立的主题页。 |
187
187
  | | `/lumi-research-setup` | 帮助配置研究工具的 API key。 |
188
+ | | `/lumi-research-watch-run` | 基于 watchlist 运行一次计划式发现(主题 + RSS / Atom 源)。仅在你要求时才运行。 |
188
189
  | **Reading** | `/lumi-reading-chapter-ingest`| 按章节导入书籍知识。 |
189
190
  | | `/lumi-reading-character-track`| 追踪故事中的角色及其关系。 |
190
191
  | | `/lumi-reading-theme-map` | 识别并映射故事主题。 |
@@ -205,8 +206,8 @@ Lumina-Wiki 正在快速演进。这是我们的用户路线图:
205
206
  - [x] **多语言安装:** 安装时可选英文、越南文或中文作为主语言。*(v1.2 已发布)*
206
207
  - [x] **原生 DOCX、RTF 与 EPUB 导入:** 通过 research pack 将 Word、Rich Text 与 EPUB 电子书直接导入维基。*(v1.x 已发布)*
207
208
  - [x] **改进的 CI/CD:** 正式支持 Bun 和 Node 22 环境。*(v1.2 已发布)*
208
- - [ ] **全球数据源扩展:** 直接集成 OpenAlex、CORE 和 Unpaywall,实现可靠的 DOI-to-PDF 解析。
209
- - [ ] **RSS 与博客监控:** 自动从您喜爱的实验室博客和期刊中发现新论文。
209
+ - [x] **全球数据源扩展:** 直接集成 OpenAlex、CORE 和 Unpaywall,实现可靠的 DOI-to-PDF 解析。*(在 v1.6 中发布)*
210
+ - [x] **RSS 与博客监控:** 通过 watchlist 中的 `type: feed` 项,自动从您喜爱的实验室博客和期刊中发现新论文。*(在 v1.6 中发布)*
210
211
  - [ ] **高级论文排名:** 查看研究论文的影响力评分和质量信号。
211
212
 
212
213
  **长期计划(深度研究与集成)**
@@ -251,3 +252,13 @@ npm run test:all
251
252
  - [Tiếng Việt(越南语)](README.vi.md)
252
253
 
253
254
  **许可:** [MIT](LICENSE) © Lưu Trọng Hiếu.
255
+
256
+ ---
257
+
258
+ ## 贡献者
259
+
260
+ 感谢所有为 Lumina Wiki 做出贡献的人!
261
+
262
+ [![Contributors](https://contrib.rocks/image?repo=tronghieu/lumina-wiki)](https://github.com/tronghieu/lumina-wiki/graphs/contributors)
263
+
264
+ **想要贡献?** 请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 开始参与——欢迎提交 Bug 报告、新 skill、工具集成以及翻译。
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "lumina-wiki",
4
- "version": "1.5.0",
4
+ "version": "1.6.0",
5
5
  "description": "Domain-agnostic, multi-IDE wiki scaffolder — Karpathy's LLM-Wiki vision, cross-platform and pack-based.",
6
6
  "keywords": [
7
7
  "llm-wiki",
@@ -952,26 +952,25 @@ function buildIdeStub(target, vars) {
952
952
  async function copyScripts(projectRoot) {
953
953
  const destDir = join(projectRoot, '_lumina', 'scripts');
954
954
  const scriptFiles = ['wiki.mjs', 'lint.mjs', 'reset.mjs', 'schemas.mjs', 'discover-runner.mjs', 'external-ids.mjs', 'parse-ids.mjs', 'merge-ids.mjs', 'build-source.mjs'];
955
- for (const file of scriptFiles) {
956
- const src = join(SCRIPTS_DIR, file);
957
- const dest = join(destDir, file);
955
+ // Parallel: each copy is independent; destDir is created earlier.
956
+ await Promise.all(scriptFiles.map(async file => {
958
957
  try {
959
- await copyFile(src, dest);
958
+ await copyFile(join(SCRIPTS_DIR, file), join(destDir, file));
960
959
  } catch (_) {
961
960
  // Scripts may not exist yet (P4+ work); skip gracefully
962
961
  }
963
- }
962
+ }));
963
+ // Ensure the lib subdir once, then parallelize the file copies.
964
+ const libDir = join(destDir, 'lib');
965
+ await ensureDir(libDir);
964
966
  const libFiles = ['watchlist-config.mjs', 'discovery-state.mjs'];
965
- for (const file of libFiles) {
966
- const src = join(SCRIPTS_DIR, 'lib', file);
967
- const dest = join(destDir, 'lib', file);
967
+ await Promise.all(libFiles.map(async file => {
968
968
  try {
969
- await ensureDir(dirname(dest));
970
- await copyFile(src, dest);
969
+ await copyFile(join(SCRIPTS_DIR, 'lib', file), join(libDir, file));
971
970
  } catch (_) {
972
971
  // Script libs may not exist yet; skip gracefully
973
972
  }
974
- }
973
+ }));
975
974
  }
976
975
 
977
976
  async function copyChangelog(projectRoot) {
@@ -1052,6 +1051,7 @@ function getSkillDefs(packs) {
1052
1051
  { name: 'setup', canonicalId: 'lumi-research-setup', displayName: '/lumi-research-setup' },
1053
1052
  { name: 'topic', canonicalId: 'lumi-research-topic', displayName: '/lumi-research-topic' },
1054
1053
  { name: 'watchlist', canonicalId: 'lumi-research-watchlist', displayName: '/lumi-research-watchlist' },
1054
+ { name: 'watch-run', canonicalId: 'lumi-research-watch-run', displayName: '/lumi-research-watch-run' },
1055
1055
  ];
1056
1056
  for (const s of researchSkills) {
1057
1057
  defs.push({ ...s, pack: 'research', srcPackPath: 'packs/research' });
@@ -1086,19 +1086,22 @@ async function copyTools(projectRoot, { research }) {
1086
1086
  const destDir = join(projectRoot, '_lumina', 'tools');
1087
1087
  const coreTools = ['extract_pdf.py', 'fetch_pdf.py', 'id_utils.py'];
1088
1088
  const researchTools = [
1089
- '_env.py', 'discover.py', 'init_discovery.py', 'prepare_source.py',
1089
+ '_env.py', '_cache.py', 'discover.py', 'init_discovery.py', 'prepare_source.py',
1090
1090
  'fetch_arxiv.py', 'fetch_wikipedia.py', 'fetch_s2.py', 'fetch_deepxiv.py',
1091
+ 'fetch_openalex.py', 'fetch_unpaywall.py', 'fetch_core.py', 'resolve_pdf.py',
1092
+ 'fetch_rss.py',
1091
1093
  ];
1092
1094
  const toolFiles = research ? [...coreTools, ...researchTools] : coreTools;
1093
- for (const file of toolFiles) {
1094
- const src = join(TOOLS_DIR, file);
1095
- const dest = join(destDir, file);
1095
+ // Parallelize: each copy is independent and destDir already exists.
1096
+ // Sequential awaits were the main Windows cold-start regression in v1.4
1097
+ // (~30 ms per file × 14 files dominates on NTFS + Defender).
1098
+ await Promise.all(toolFiles.map(async file => {
1096
1099
  try {
1097
- await copyFile(src, dest);
1100
+ await copyFile(join(TOOLS_DIR, file), join(destDir, file));
1098
1101
  } catch (_) {
1099
1102
  // Tool not yet authored; skip
1100
1103
  }
1101
- }
1104
+ }));
1102
1105
  try {
1103
1106
  await copyFile(join(TOOLS_DIR, 'requirements.txt'), join(destDir, 'requirements.txt'));
1104
1107
  } catch (_) {
@@ -1110,7 +1113,9 @@ async function renderSchemaDocs(projectRoot, templateVars) {
1110
1113
  const schemaDir = join(projectRoot, '_lumina', 'schema');
1111
1114
  const schemaDocs = ['page-templates.md', 'cross-reference-packs.md', 'graph-packs.md', 'lumi-help.csv', 'lumi-help-runbook.md'];
1112
1115
 
1113
- for (const doc of schemaDocs) {
1116
+ // Parallel render + atomicWrite. Each doc is independent; schemaDir is
1117
+ // already created by the dirsToCreate loop in installCommand.
1118
+ await Promise.all(schemaDocs.map(async doc => {
1114
1119
  const templatePath = join(TEMPLATES_DIR, '_lumina', 'schema', doc);
1115
1120
  const destPath = join(schemaDir, doc);
1116
1121
  let content;
@@ -1121,7 +1126,7 @@ async function renderSchemaDocs(projectRoot, templateVars) {
1121
1126
  content = `# ${doc}\n\n_This file is managed by the Lumina installer._\n`;
1122
1127
  }
1123
1128
  await atomicWrite(destPath, content);
1124
- }
1129
+ }));
1125
1130
  }
1126
1131
 
1127
1132
  async function renderEnvExample(projectRoot) {
@@ -151,7 +151,7 @@ export function buildPromptList(existingManifest, defaultLocale = 'en') {
151
151
  * @param {Function} [opts.t] - Locale translator function.
152
152
  * @returns {Promise<InstallAnswers>}
153
153
  */
154
- export async function runInstallPrompts({ acceptDefaults = false, cwd = process.cwd(), existingManifest = null, defaultLocale = 'en', t = null } = {}) {
154
+ export async function runInstallPrompts({ acceptDefaults = false, cwd = process.cwd(), existingManifest = null, defaultLocale = 'en', t: initialT = null } = {}) {
155
155
  if (acceptDefaults) {
156
156
  const loc = existingManifest?.locale ?? defaultLocale;
157
157
  return defaultAnswers(cwd, loc);
@@ -159,6 +159,11 @@ export async function runInstallPrompts({ acceptDefaults = false, cwd = process.
159
159
 
160
160
  const { intro, outro, text, multiselect, select, confirm, isCancel, cancel } = await getClack();
161
161
 
162
+ // t starts as whatever the caller passed (may be null on fresh install where
163
+ // locale is unknown). After Prompt 0 selects the locale, we rebind t to the
164
+ // user-chosen locale so prompts 1-5 are localized.
165
+ let t = initialT;
166
+
162
167
  // t may not be available yet at intro time (locale not yet selected);
163
168
  // use hardcoded EN for intro — this is the chicken-and-egg prompt.
164
169
  intro('Lumina Wiki Installer');
@@ -179,14 +184,26 @@ export async function runInstallPrompts({ acceptDefaults = false, cwd = process.
179
184
  const locale = localeRaw;
180
185
  const langDefault = LOCALE_LANGUAGE_NAME[locale] ?? 'English';
181
186
 
187
+ // Rebind t to the just-selected locale so the remaining prompts render in
188
+ // the user's chosen language. Without this, prompts 1-5 silently fall back
189
+ // to EN literals even when the user picked vi/zh at Prompt 0.
190
+ try {
191
+ const { loadLocale } = await import('./locales.js');
192
+ const localeMod = await loadLocale(locale);
193
+ t = localeMod.t;
194
+ } catch {
195
+ // Keep whatever t we had (possibly null → EN literals); never block install.
196
+ }
197
+
182
198
  // Interactive locale-switch confirmation (Phase 5 §60-63):
183
199
  // If user picks a locale different from the installed one on an upgrade,
184
200
  // require explicit confirmation before destructively rewriting README.md
185
201
  // and IDE stubs. Default N — protects user content.
186
202
  if (existingManifest?.locale && existingManifest.locale !== locale) {
187
203
  const proceed = await confirm({
188
- // Use trilingual literal — t() not yet bound to chosen locale and the
189
- // user is mid-switch; both old and new locales are relevant context.
204
+ // Use trilingual literal — user is mid-switch, so both old and new
205
+ // locales are relevant context. t() is available here but intentionally
206
+ // not used so the warning is legible in either locale.
190
207
  message: `Locale change ${existingManifest.locale} -> ${locale} will rewrite README.md and IDE stubs in the new locale. Outside-schema edits are preserved. Continue?`,
191
208
  initialValue: false,
192
209
  });