lumina-wiki 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,139 @@
1
+ # Changelog
2
+
3
+ All notable changes to Lumina-Wiki are documented here.
4
+ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5
+
6
+ ## [Unreleased]
7
+
8
+ ### Added
9
+ - `/lumi-migrate-legacy` core skill — LLM-driven backfill of provenance/confidence
10
+ - `CHANGELOG.md` shipped to `_lumina/CHANGELOG.md` for skill consumption
11
+ - Post-upgrade installer banner with lint summary (errors/warnings) when version bumps
12
+ - Manifest `schemaVersion` bump 1 → 2 with `legacyMigrationNeeded` flag
13
+
14
+ ### Migration
15
+ - Upgrades from <0.6 set `legacyMigrationNeeded: true` in manifest. Run `/lumi-migrate-legacy` or `wiki.mjs migrate --add-defaults` to backfill `provenance` and `confidence` fields on existing sources/concepts.
16
+
17
+ ---
18
+
19
+ ## [0.6.0] - 2026-05-03
20
+
21
+ ### Added
22
+ - Schema: `provenance` field (required enum: `replayable|partial|missing`) on source nodes
23
+ - Schema: `confidence` field (optional float 0–1) on source and concept nodes
24
+ - Lint check L11: warns when `confidence` is missing on sources/concepts
25
+ - Lint `--summary` flag outputs stable JSON shape `{ by_check: { L01..L11 } }` for machine consumption
26
+ - `wiki.mjs`: 8-hex `session_id` segment in log entries; `LUMINA_SESSION_ID` env override for multi-write correlation
27
+ - Installer `migrateManifest` helper for forward-compatible `schemaVersion` upgrades (1→1 no-op today, ready for 1→2)
28
+ - Skills: provenance/confidence rubric added to `/lumi-ingest`, `/lumi-discover`, `/lumi-prefill`
29
+ - ROADMAP: v1.0 `/lumi-verify` pass planned (3 stages: grounding A wiki↔raw, drift B raw↔URL, external C wiki↔web)
30
+
31
+ ### Fixed
32
+ - CI: enumerate test files explicitly; use `fileURLToPath` for CLI path resolution
33
+ - CI: quote test globs for Windows; install `requests` for Python tests
34
+ - CI: spawn `npm.cmd` via shell on Windows
35
+ - Scripts: resolve `reset.mjs` and `wiki.mjs` paths with `fileURLToPath`
36
+ - Tools: strip Windows-illegal characters from discovery source IDs
37
+ - Docs: recommend `qmd` skill for local search across all README language files
38
+
39
+ ### Migration
40
+ - Sources need `provenance` (required) added; concepts and sources may add `confidence` (optional).
41
+ - Run `wiki.mjs migrate --add-defaults` for deterministic backfill (sets `provenance: missing`, omits `confidence`), or `/lumi-migrate-legacy` (v0.7+) for LLM-driven backfill.
42
+ - `log.md` entries now include `session:<8hex>` segment — backward-compatible parser; no migration needed for existing log entries.
43
+ - `lint --summary` JSON shape is stable from this version forward; scripts consuming raw lint output should migrate to `--summary`.
44
+
45
+ ---
46
+
47
+ ## [0.5.0] - 2026-05-03
48
+
49
+ ### Added
50
+ - Foundation aliases in wiki: named aliases for foundation nodes enable cross-skill deduplication
51
+ - `wiki.mjs resolve-alias` command for alias lookup
52
+ - Research: `/lumi-prefill` handles Wikipedia disambiguation pages and title collisions gracefully
53
+ - Research: `/lumi-discover` surfaces entry purpose field and deduplicates ingested papers; logs discovery phases
54
+
55
+ ### Fixed
56
+ - `wiki.mjs resolve-alias` no-match error now unwrapped to correct stderr format
57
+
58
+ ### Changed
59
+ - Policy: cross-model review framed around bundled infra, not bias — second-model review is user choice, not blocked
60
+
61
+ ### Migration
62
+ - No schema changes. No migration needed.
63
+
64
+ ---
65
+
66
+ ## [0.4.0] - 2026-05-02
67
+
68
+ ### Added
69
+ - GEMINI.md agent entry-point stub for Gemini IDE targets
70
+ - README restructured into multi-language files; skill names updated throughout
71
+ - Obsidian vault setup documented across all language READMEs; agent entry-point stubs excluded from vault
72
+ - Contributor guide added (`docs/`)
73
+
74
+ ### Changed
75
+ - Skill `canonicalId` values namespaced with pack prefix (e.g. `research:lumi-discover`)
76
+ - Installer: BMAD-style directory prompt; `project_name` auto-derived from directory
77
+ - Installer: broadened Codex target; added `qwen` and `iflow` CLI targets
78
+ - Installer: yellow LUMINA WIKI banner shown on install
79
+
80
+ ### Migration
81
+ - If referencing skill `canonicalId` values in custom tooling, update to pack-prefixed form (e.g. `lumi-discover` → `research:lumi-discover`). Skill filenames and slash-command names are unchanged.
82
+
83
+ ---
84
+
85
+ ## [0.3.0] - 2026-05-02
86
+
87
+ ### Added
88
+ - `extract_pdf.py` shipped as core PDF extractor for all installs (no opt-in required)
89
+
90
+ ### Migration
91
+ - No schema or API changes. No migration needed.
92
+
93
+ ---
94
+
95
+ ## [0.2.0] - 2026-05-02
96
+
97
+ ### Added
98
+ - Full installer with pack system (`core`, `research`, `reading`)
99
+ - CI matrix and package readiness checks (`ci:idempotency`, `ci:package`)
100
+ - Agent context files: `CLAUDE.md`, `AGENTS.md`, dev guide, sandbox helper
101
+ - `.gitignore` for `node_modules`, `__pycache__`, `.env`, editor files
102
+ - Skills output flattened to `.agents/skills/lumi-*` layout
103
+ - Installer: yellow LUMINA WIKI banner, 5-prompt flow, 3-file manifest
104
+ - README: badges, language links, end-user docs
105
+ - Tagline: "Where Knowledge Starts to Glow"
106
+ - ROADMAP: v1 daily-fetch and v2 source/ranking expansion plans
107
+
108
+ ### Changed
109
+ - `.agents/skills/` output renamed from nested layout to flat `lumi-*` prefix
110
+
111
+ ### Migration
112
+ - Fresh install from v0.1: re-run `npx lumina-wiki install --yes`. Existing `raw/` and `wiki/` content is preserved.
113
+
114
+ ---
115
+
116
+ ## [0.1.0] - 2026-05-01
117
+
118
+ ### Added
119
+ - Initial npm package scaffold
120
+ - Core scripts: `wiki.mjs` (graph/frontmatter engine), `lint.mjs` (9 checks), `reset.mjs`, `schemas.mjs`
121
+ - 14 skills locked: 6 core (`/lumi-init`, `/lumi-ingest`, `/lumi-ask`, `/lumi-edit`, `/lumi-check`, `/lumi-reset`), 4 research, 4 reading
122
+ - Installer entry point `bin/lumina.js` (ESM, lazy imports, <300 ms cold start)
123
+ - Atomic write discipline (`atomicWrite` with `fd.datasync()` + rename) throughout
124
+ - `safePath()` path validation rejecting `..`, absolute paths, Windows drive letters
125
+ - Bidirectional link enforcement; `raw/` read-only except `raw/tmp/` and `raw/discovered/`
126
+ - PRD, architecture docs, and v0.1 quick-spec
127
+
128
+ ### Migration
129
+ - First release. No prior version to migrate from.
130
+
131
+ ---
132
+
133
+ [Unreleased]: https://github.com/tronghieu/lumina-wiki/compare/v0.6.0...HEAD
134
+ [0.6.0]: https://github.com/tronghieu/lumina-wiki/compare/v0.5.0...v0.6.0
135
+ [0.5.0]: https://github.com/tronghieu/lumina-wiki/compare/v0.4.0...v0.5.0
136
+ [0.4.0]: https://github.com/tronghieu/lumina-wiki/compare/v0.3.0...v0.4.0
137
+ [0.3.0]: https://github.com/tronghieu/lumina-wiki/compare/v0.2.0...v0.3.0
138
+ [0.2.0]: https://github.com/tronghieu/lumina-wiki/compare/v0.1.0...v0.2.0
139
+ [0.1.0]: https://github.com/tronghieu/lumina-wiki/releases/tag/v0.1.0
package/README.md CHANGED
@@ -66,6 +66,37 @@ If you installed the `research` pack, some skills need API keys to search online
66
66
 
67
67
  The agent will guide you through an interactive setup to save your keys to a local `.env` file.
68
68
 
69
+ ### **Step 3 (Upgrades): Migrate Legacy Wiki Entries**
70
+
71
+ If you are **re-installing Lumina-Wiki on a project that already has a `wiki/` from an earlier version**, run the installer the same way:
72
+
73
+ ```bash
74
+ npx lumina-wiki install
75
+ ```
76
+
77
+ The installer detects the version bump and updates scripts, schemas, and skills atomically. **Your wiki content (`wiki/`, `raw/`, `log.md`) is never modified by the installer.** When the installer finds frontmatter fields added by newer versions but missing on older entries, it prints a `[warn]` banner with the count and the next step.
78
+
79
+ You then have two ways to backfill:
80
+
81
+ **Option A — LLM-driven (recommended):** Open your AI chat and run:
82
+
83
+ > **You:**
84
+ > `/lumi-migrate-legacy`
85
+
86
+ The skill reads `_lumina/CHANGELOG.md` to learn which fields each version added, runs `lint --json` to find affected entries, and infers per-entry values (e.g. `provenance: replayable | partial | missing`, `confidence: high | medium | low | unverified`) from `raw/` snapshots, citation edges, and entry metadata. Idempotent — safe to run multiple times.
87
+
88
+ **Option B — Quick deterministic backfill:** From your terminal:
89
+
90
+ ```bash
91
+ node _lumina/scripts/wiki.mjs migrate --add-defaults
92
+ ```
93
+
94
+ This applies conservative defaults (`provenance: missing`, `confidence: unverified`) to every entry that lacks them. Lint goes green immediately, but values are placeholders — you can refine later with Option A or by editing entries by hand.
95
+
96
+ You can combine: run Option B first for a clean lint, then Option A when you want higher-quality values. Both write atomically and leave a trail in `wiki/log.md`.
97
+
98
+ For the full list of schema changes per version, see [`CHANGELOG.md`](CHANGELOG.md) or the local copy at `_lumina/CHANGELOG.md` after install.
99
+
69
100
  ## 3. Your First Commands (Core Skills)
70
101
 
71
102
  Interact with your wiki using these commands in your AI chat interface (Gemini CLI, Claude, etc.).
@@ -136,6 +167,27 @@ Lumina creates a workspace with a clear purpose for each directory.
136
167
 
137
168
  > The `wiki/graph/` folder contains `edges.jsonl` and `citations.jsonl` (machine-readable data files, not markdown). Excluding it keeps the graph view clean.
138
169
 
170
+ ### **Local Search with qmd (Optional)**
171
+
172
+ As your wiki grows, you may want faster full-text search than `index.md` plus `grep` can offer. We recommend [qmd](https://github.com/tobi/qmd) — a local, on-device search engine for markdown files with hybrid BM25/vector search and LLM re-ranking. It pairs nicely with Lumina-Wiki and your AI agent.
173
+
174
+ **How to wire it into your agent:**
175
+
176
+ 1. Install qmd following the instructions in its repo, then index the project root so it sees both `wiki/` and `raw/`.
177
+ 2. **CLI route** — your agent can call `qmd <query>` via `Bash`. Just mention in your prompts that the command is available.
178
+ 3. **MCP route (handy for Claude Code, Codex, Cursor)** — register qmd's MCP server in your IDE's MCP config. The agent picks it up as a native tool and can call it from `/lumi-ask` or any follow-up.
179
+ 4. Re-index after `/lumi-ingest` (manually or via a hook) so new pages become searchable.
180
+
181
+ qmd sits alongside `index.md` and the wiki graph — a fast retrieval layer that feeds your agent better candidates before it reads pages in full.
182
+
183
+ **Bonus — tobi's official qmd skill.** tobi also publishes a dedicated skill that teaches your agent how to use qmd effectively (when to pick `lex` vs `vec` vs `hyde`, how to write `intent` to disambiguate, lex syntax for phrases and exclusions). If your IDE supports the skill format, you can install it with:
184
+
185
+ ```bash
186
+ npx skills add https://github.com/tobi/qmd --skill qmd
187
+ ```
188
+
189
+ We recommend reading [the skill page on skills.sh](https://skills.sh/tobi/qmd/qmd) first to see exactly what it adds.
190
+
139
191
  ---
140
192
 
141
193
  ## 5. Available Skills and Tools (v0.1)
package/README.vi.md CHANGED
@@ -64,6 +64,37 @@ Nếu bạn đã cài đặt gói `research`, một số kỹ năng sẽ cần A
64
64
 
65
65
  Agent sẽ hướng dẫn bạn qua một quy trình cài đặt tương tác để lưu các key của bạn vào file `.env` cục bộ.
66
66
 
67
+ ### **Bước 3 (Khi nâng cấp): Migrate các entry wiki cũ**
68
+
69
+ Nếu bạn **cài lại Lumina-Wiki trên một dự án đã có sẵn `wiki/` từ phiên bản trước**, chạy installer như bình thường:
70
+
71
+ ```bash
72
+ npx lumina-wiki install
73
+ ```
74
+
75
+ Installer phát hiện version mới và cập nhật scripts, schemas, skills atomically. **Nội dung wiki của bạn (`wiki/`, `raw/`, `log.md`) không bị installer chỉnh sửa.** Khi installer thấy các trường frontmatter mới được thêm bởi version mới nhưng thiếu trên các entry cũ, nó sẽ in banner `[warn]` kèm số lượng và bước tiếp theo.
76
+
77
+ Bạn có hai cách để backfill:
78
+
79
+ **Cách A — LLM-driven (khuyến nghị):** Mở chat AI và chạy:
80
+
81
+ > **Bạn:**
82
+ > `/lumi-migrate-legacy`
83
+
84
+ Skill đọc `_lumina/CHANGELOG.md` để biết phiên bản nào thêm trường gì, chạy `lint --json` để tìm entry bị ảnh hưởng, và infer giá trị cho từng entry (ví dụ: `provenance: replayable | partial | missing`, `confidence: high | medium | low | unverified`) dựa trên snapshot trong `raw/`, citation edges, và metadata. Idempotent — chạy lại nhiều lần vẫn an toàn.
85
+
86
+ **Cách B — Backfill nhanh, deterministic:** Từ terminal:
87
+
88
+ ```bash
89
+ node _lumina/scripts/wiki.mjs migrate --add-defaults
90
+ ```
91
+
92
+ Lệnh này gán default bảo thủ (`provenance: missing`, `confidence: unverified`) cho mọi entry còn thiếu. Lint xanh ngay, nhưng giá trị chỉ là placeholder — bạn có thể tinh chỉnh sau bằng Cách A hoặc sửa tay.
93
+
94
+ Có thể kết hợp: chạy Cách B trước để lint xanh, sau đó Cách A khi muốn giá trị chính xác hơn. Cả hai đều atomic và để lại trail trong `wiki/log.md`.
95
+
96
+ Xem [`CHANGELOG.md`](CHANGELOG.md) hoặc bản local `_lumina/CHANGELOG.md` sau khi cài để biết toàn bộ thay đổi schema theo version.
97
+
67
98
  ## 3. Các lệnh đầu tiên của bạn (Kỹ năng cốt lõi)
68
99
 
69
100
  Tương tác với wiki của bạn bằng cách sử dụng các lệnh này trong giao diện trò chuyện với AI Agent (ví dụ: Gemini CLI, Claude, v.v.).
@@ -134,6 +165,27 @@ Lumina tạo ra một không gian làm việc với mục đích rõ ràng cho t
134
165
 
135
166
  > Thư mục `wiki/graph/` chứa `edges.jsonl` và `citations.jsonl` (dữ liệu máy đọc, không phải markdown). Exclude thư mục này giúp graph view không bị nhiễu.
136
167
 
168
+ ### **Tìm kiếm cục bộ với qmd (Tùy chọn)**
169
+
170
+ Khi wiki của bạn lớn dần, bạn có thể muốn tìm kiếm full-text nhanh hơn so với `index.md` + `grep`. Chúng tôi gợi ý [qmd](https://github.com/tobi/qmd) — một search engine chạy hoàn toàn local trên file markdown, kết hợp BM25, vector search và LLM re-ranking. qmd phối hợp rất hợp với Lumina-Wiki và AI Agent của bạn.
171
+
172
+ **Cách tích hợp với AI Agent:**
173
+
174
+ 1. Cài qmd theo hướng dẫn trong repo của nó, sau đó index thư mục gốc của project để qmd thấy cả `wiki/` lẫn `raw/`.
175
+ 2. **Qua CLI** — agent có thể gọi `qmd <query>` bằng `Bash`. Chỉ cần nhắc trong prompt rằng câu lệnh này đã sẵn sàng.
176
+ 3. **Qua MCP (rất tiện cho Claude Code, Codex, Cursor)** — đăng ký MCP server của qmd vào file cấu hình MCP của IDE. Agent sẽ nhận nó như một tool gốc và có thể gọi từ `/lumi-ask` hay câu hỏi tiếp theo.
177
+ 4. Re-index sau mỗi lần `/lumi-ingest` (thủ công hoặc qua hook) để các trang mới có thể tìm được.
178
+
179
+ qmd đứng song song với `index.md` và wiki graph — một lớp truy xuất nhanh giúp agent có candidate tốt hơn trước khi đọc đầy đủ từng trang.
180
+
181
+ **Tuỳ chọn thêm — skill chính thức của tobi cho qmd.** tobi cũng phát hành một skill chuyên dụng, dạy agent cách dùng qmd hiệu quả (khi nào chọn `lex` vs `vec` vs `hyde`, cách viết `intent` để khử nhập nhằng, cú pháp lex cho phrase và exclude). Nếu IDE của bạn hỗ trợ định dạng skill, bạn có thể cài bằng:
182
+
183
+ ```bash
184
+ npx skills add https://github.com/tobi/qmd --skill qmd
185
+ ```
186
+
187
+ Khuyến khích đọc [trang skill trên skills.sh](https://skills.sh/tobi/qmd/qmd) trước để xem chính xác skill này bổ sung những gì.
188
+
137
189
  ---
138
190
 
139
191
  ## 5. Các Kỹ năng và Công cụ có sẵn (v0.1)
package/README.zh.md CHANGED
@@ -64,6 +64,37 @@ npx lumina-wiki install
64
64
 
65
65
  Agent 将引导您通过交互式设置将密钥保存到本地 `.env` 文件中。
66
66
 
67
+ ### **第三步(升级时):迁移旧版 Wiki 条目**
68
+
69
+ 如果您**在已经有 `wiki/` 的旧项目上重新安装 Lumina-Wiki**,照常运行安装器:
70
+
71
+ ```bash
72
+ npx lumina-wiki install
73
+ ```
74
+
75
+ 安装器检测到版本升级后会原子性地更新 scripts、schemas 和 skills。**您的 wiki 内容(`wiki/`、`raw/`、`log.md`)不会被安装器修改。** 当安装器发现新版本添加的 frontmatter 字段在旧条目中缺失时,会打印 `[warn]` 横幅,显示数量和下一步操作。
76
+
77
+ 您有两种方式进行回填:
78
+
79
+ **方案 A — LLM 驱动(推荐):** 打开 AI 聊天并运行:
80
+
81
+ > **您:**
82
+ > `/lumi-migrate-legacy`
83
+
84
+ 该技能读取 `_lumina/CHANGELOG.md` 了解每个版本添加了哪些字段,运行 `lint --json` 找出受影响的条目,并基于 `raw/` 快照、引用边和条目元数据为每个条目推断合适的值(例如 `provenance: replayable | partial | missing`,`confidence: high | medium | low | unverified`)。幂等 — 多次运行也是安全的。
85
+
86
+ **方案 B — 快速确定性回填:** 从终端运行:
87
+
88
+ ```bash
89
+ node _lumina/scripts/wiki.mjs migrate --add-defaults
90
+ ```
91
+
92
+ 此命令为所有缺失字段的条目设置保守的默认值(`provenance: missing`,`confidence: unverified`)。lint 立即变绿,但值只是占位符 — 您可以稍后通过方案 A 或手动编辑来优化。
93
+
94
+ 您可以组合使用:先运行方案 B 让 lint 变绿,需要更准确的值时再运行方案 A。两种方式都是原子写入并在 `wiki/log.md` 留下记录。
95
+
96
+ 完整的版本 schema 变更列表请见 [`CHANGELOG.md`](CHANGELOG.md) 或安装后的本地副本 `_lumina/CHANGELOG.md`。
97
+
67
98
  ## 3. 常用指令(核心技能 Core Skills)
68
99
 
69
100
  在您的 AI 聊天界面(Gemini CLI, Claude 等)中使用这些指令与维基进行交互。
@@ -134,6 +165,27 @@ Lumina 创建的工作区为每个目录都设定了明确的用途。
134
165
 
135
166
  > `wiki/graph/` 文件夹包含 `edges.jsonl` 和 `citations.jsonl`(机器可读数据文件,非 Markdown)。排除该文件夹可保持图谱视图整洁。
136
167
 
168
+ ### **使用 qmd 进行本地搜索(可选)**
169
+
170
+ 随着您的 Wiki 不断增长,您可能希望获得比 `index.md` + `grep` 更快的全文搜索体验。我们推荐 [qmd](https://github.com/tobi/qmd) —— 一个完全本地、设备上的 Markdown 搜索引擎,结合 BM25、向量搜索和 LLM 重排序。qmd 与 Lumina-Wiki 和您的 AI Agent 配合得非常顺畅。
171
+
172
+ **如何与 AI Agent 集成:**
173
+
174
+ 1. 按照 qmd 仓库中的说明安装,然后对项目根目录建立索引,使 qmd 能同时看到 `wiki/` 和 `raw/`。
175
+ 2. **CLI 方式** — Agent 可以通过 `Bash` 调用 `qmd <query>`。只需在提示中提及该命令已可用。
176
+ 3. **MCP 方式(在 Claude Code、Codex、Cursor 中很方便)** — 在 IDE 的 MCP 配置中注册 qmd 的 MCP 服务器。Agent 会将其识别为原生工具,可以从 `/lumi-ask` 或后续提问中调用。
177
+ 4. 在每次 `/lumi-ingest` 后重新索引(手动或通过 hook),让新页面可被搜索到。
178
+
179
+ qmd 与 `index.md` 和 wiki 图谱并行工作 —— 作为一个快速检索层,在 Agent 完整阅读页面之前为它提供更好的候选结果。
180
+
181
+ **额外推荐 —— tobi 的官方 qmd skill。** tobi 还发布了一个专用 skill,教您的 Agent 如何高效使用 qmd(何时选择 `lex` vs `vec` vs `hyde`、如何编写 `intent` 进行消歧、lex 短语与排除项语法)。如果您的 IDE 支持 skill 格式,可以通过以下命令安装:
182
+
183
+ ```bash
184
+ npx skills add https://github.com/tobi/qmd --skill qmd
185
+ ```
186
+
187
+ 建议先阅读 [skills.sh 上的 skill 页面](https://skills.sh/tobi/qmd/qmd),了解它具体添加了什么。
188
+
137
189
  ---
138
190
 
139
191
  ## 5. 可用技能与工具 (v0.1)
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": "0.5.0",
4
+ "version": "0.7.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",
@@ -55,6 +55,7 @@
55
55
  "src/tools/fetch_s2.py",
56
56
  "src/tools/fetch_deepxiv.py",
57
57
  "src/tools/requirements.txt",
58
+ "CHANGELOG.md",
58
59
  "README.md",
59
60
  "LICENSE"
60
61
  ],
@@ -71,8 +72,8 @@
71
72
  "devDependencies": {},
72
73
  "scripts": {
73
74
  "test": "npm run test:installer",
74
- "test:installer": "node --test src/installer/*.test.js",
75
- "test:scripts": "node --test src/scripts/*.test.mjs",
75
+ "test:installer": "node --test src/installer/commands.test.js src/installer/fs.test.js src/installer/manifest.test.js src/installer/template-engine.test.js src/installer/update-check.test.js",
76
+ "test:scripts": "node --test src/scripts/lint.test.mjs src/scripts/reset.test.mjs src/scripts/wiki.test.mjs",
76
77
  "test:python": "python3 -m pytest src/tools/tests -q",
77
78
  "test:all": "npm run test:installer && npm run test:scripts && npm run test:python",
78
79
  "test:fs": "node --test src/installer/fs.test.js",
@@ -17,6 +17,7 @@
17
17
  import { readFile, writeFile, rename, unlink, rm, access, copyFile } from 'node:fs/promises';
18
18
  import { join, resolve, relative, dirname, basename } from 'node:path';
19
19
  import { constants as fsConstants } from 'node:fs';
20
+ import { spawnSync } from 'node:child_process';
20
21
  import { createRequire } from 'node:module';
21
22
  import { fileURLToPath } from 'node:url';
22
23
 
@@ -31,6 +32,7 @@ import {
31
32
  import {
32
33
  readManifest,
33
34
  writeManifest,
35
+ migrateManifest,
34
36
  readSkillsManifest,
35
37
  writeSkillsManifest,
36
38
  readFilesManifest,
@@ -223,6 +225,9 @@ export async function installCommand(opts = {}) {
223
225
  // 8. Copy scripts
224
226
  await copyScripts(projectRoot);
225
227
 
228
+ // 8.5. Copy CHANGELOG.md so /lumi-migrate-legacy can read it offline
229
+ await copyChangelog(projectRoot);
230
+
226
231
  // 9. Copy skills
227
232
  const skillRows = await copySkills(projectRoot, packs);
228
233
 
@@ -259,7 +264,13 @@ export async function installCommand(opts = {}) {
259
264
 
260
265
  // 17. Write three state files atomically
261
266
  const now = new Date().toISOString();
267
+ // Run schema migrations on the existing manifest first so flags like
268
+ // legacyMigrationNeeded are preserved into the final write.
269
+ const migrated = existingManifest
270
+ ? migrateManifest(existingManifest, MANIFEST_SCHEMA_VERSION)
271
+ : {};
262
272
  const manifest = {
273
+ ...migrated,
263
274
  schemaVersion: MANIFEST_SCHEMA_VERSION,
264
275
  packageVersion: PKG.version,
265
276
  installedAt: existingManifest?.installedAt ?? now,
@@ -280,6 +291,16 @@ export async function installCommand(opts = {}) {
280
291
  await writeSkillsManifest(projectRoot, skillRows);
281
292
  await writeFilesManifest(projectRoot, fileRows);
282
293
 
294
+ // 17.5. Post-upgrade: spawn lint --summary, print banner if findings exist
295
+ if (isUpgrade && existingManifest.packageVersion !== PKG.version) {
296
+ await printPostUpgradeBanner({
297
+ projectRoot,
298
+ fromVersion: existingManifest.packageVersion,
299
+ toVersion: PKG.version,
300
+ colors,
301
+ });
302
+ }
303
+
283
304
  // 18. Print summary
284
305
  console.log('');
285
306
  console.log(colors.green('[done] Lumina Wiki installed successfully.'));
@@ -395,6 +416,76 @@ export async function versionCommand(opts = {}) {
395
416
  // Internal helpers
396
417
  // ---------------------------------------------------------------------------
397
418
 
419
+ /**
420
+ * Run lint --summary in the project root and, if findings exist, print a
421
+ * post-upgrade banner to stderr.
422
+ *
423
+ * @param {object} opts
424
+ * @param {string} opts.projectRoot
425
+ * @param {string} opts.fromVersion
426
+ * @param {string} opts.toVersion
427
+ * @param {object} opts.colors
428
+ */
429
+ export async function printPostUpgradeBanner({ projectRoot, fromVersion, toVersion, colors }) {
430
+ let summary;
431
+ try {
432
+ const lintScript = join(projectRoot, '_lumina', 'scripts', 'lint.mjs');
433
+ const result = spawnSync(
434
+ process.execPath,
435
+ [lintScript, '--summary'],
436
+ { cwd: projectRoot, encoding: 'utf8', timeout: 30000 },
437
+ );
438
+
439
+ // Exit codes: 0 = clean, 1 = findings. Anything else (crash / ENOENT) → skip.
440
+ if (result.error || (result.status !== 0 && result.status !== 1)) {
441
+ return;
442
+ }
443
+
444
+ try {
445
+ summary = JSON.parse(result.stdout.trim());
446
+ } catch {
447
+ return;
448
+ }
449
+ } catch {
450
+ return;
451
+ }
452
+
453
+ const { errors = 0, warnings = 0 } = summary;
454
+ if (errors === 0 && warnings === 0) {
455
+ return;
456
+ }
457
+
458
+ const banner = [
459
+ '',
460
+ colors.yellow(`[warn] Lumina upgraded v${fromVersion} -> v${toVersion} — schema gap detected:`),
461
+ colors.yellow(` ${errors} error(s), ${warnings} warning(s) across legacy entries.`),
462
+ '',
463
+ ' Quick fix (deterministic):',
464
+ ' node _lumina/scripts/wiki.mjs migrate --add-defaults',
465
+ '',
466
+ ' Smart fix (LLM-driven, recommended):',
467
+ ' /lumi-migrate-legacy',
468
+ '',
469
+ ' Both are idempotent. See _lumina/CHANGELOG.md for details.',
470
+ '',
471
+ ].join('\n');
472
+
473
+ process.stderr.write(banner + '\n');
474
+
475
+ // Spawn-and-forget: append upgrade log entry (ignore failures, e.g., wiki/ missing)
476
+ try {
477
+ const wikiScript = join(projectRoot, '_lumina', 'scripts', 'wiki.mjs');
478
+ const logMsg = `upgrade v${fromVersion}->v${toVersion}: ${errors} errors, ${warnings} warnings — run /lumi-migrate-legacy`;
479
+ spawnSync(
480
+ process.execPath,
481
+ [wikiScript, 'log', 'installer', logMsg],
482
+ { cwd: projectRoot, encoding: 'utf8', timeout: 10000 },
483
+ );
484
+ } catch {
485
+ // Ignore: wiki/ may not exist for incomplete installs
486
+ }
487
+ }
488
+
398
489
  async function readAnswersFromConfig(projectRoot, existingManifest) {
399
490
  // Try reading lumina.config.yaml for stored answers
400
491
  try {
@@ -690,6 +781,16 @@ async function copyScripts(projectRoot) {
690
781
  }
691
782
  }
692
783
 
784
+ async function copyChangelog(projectRoot) {
785
+ const src = join(PACKAGE_ROOT, 'CHANGELOG.md');
786
+ const dest = join(projectRoot, '_lumina', 'CHANGELOG.md');
787
+ try {
788
+ await copyFile(src, dest);
789
+ } catch (_) {
790
+ // CHANGELOG may not exist in older snapshots; skip gracefully
791
+ }
792
+ }
793
+
693
794
  async function copySkills(projectRoot, packs) {
694
795
  const skillRows = [];
695
796
  const skillDefs = getSkillDefs(packs);
@@ -735,12 +836,13 @@ function getSkillDefs(packs) {
735
836
 
736
837
  if (packs.includes('core')) {
737
838
  const coreSkills = [
738
- { name: 'init', canonicalId: 'lumi-init', displayName: '/lumi-init' },
739
- { name: 'ingest', canonicalId: 'lumi-ingest', displayName: '/lumi-ingest' },
740
- { name: 'ask', canonicalId: 'lumi-ask', displayName: '/lumi-ask' },
741
- { name: 'edit', canonicalId: 'lumi-edit', displayName: '/lumi-edit' },
742
- { name: 'check', canonicalId: 'lumi-check', displayName: '/lumi-check' },
743
- { name: 'reset', canonicalId: 'lumi-reset', displayName: '/lumi-reset' },
839
+ { name: 'init', canonicalId: 'lumi-init', displayName: '/lumi-init' },
840
+ { name: 'ingest', canonicalId: 'lumi-ingest', displayName: '/lumi-ingest' },
841
+ { name: 'ask', canonicalId: 'lumi-ask', displayName: '/lumi-ask' },
842
+ { name: 'edit', canonicalId: 'lumi-edit', displayName: '/lumi-edit' },
843
+ { name: 'check', canonicalId: 'lumi-check', displayName: '/lumi-check' },
844
+ { name: 'reset', canonicalId: 'lumi-reset', displayName: '/lumi-reset' },
845
+ { name: 'migrate-legacy', canonicalId: 'lumi-migrate-legacy', displayName: '/lumi-migrate-legacy' },
744
846
  ];
745
847
  for (const s of coreSkills) {
746
848
  defs.push({ ...s, pack: 'core', srcPackPath: 'core' });
@@ -19,7 +19,7 @@ import { atomicWrite, ensureDir } from './fs.js';
19
19
  // Constants
20
20
  // ---------------------------------------------------------------------------
21
21
 
22
- export const MANIFEST_SCHEMA_VERSION = 1;
22
+ export const MANIFEST_SCHEMA_VERSION = 2;
23
23
 
24
24
  export const SKILLS_CSV_HEADER = 'canonical_id,display_name,pack,source,relative_path,target_link_path,version';
25
25
  export const FILES_CSV_HEADER = 'relative_path,sha256,source_pack,installed_version';
@@ -274,6 +274,78 @@ export async function writeFilesManifest(projectRoot, rows) {
274
274
  await atomicWrite(csvPath, serializeCsv(rows, cols));
275
275
  }
276
276
 
277
+ // ---------------------------------------------------------------------------
278
+ // Manifest migration
279
+ // ---------------------------------------------------------------------------
280
+
281
+ /**
282
+ * Migration registry shape (for future entries):
283
+ *
284
+ * const MIGRATIONS = {
285
+ * '1->2': (m) => ({ ...m, newField: 'default' }),
286
+ * '2->3': (m) => { ... return updatedManifest; },
287
+ * };
288
+ *
289
+ * To add a migration from version N to N+1:
290
+ * 1. Bump MANIFEST_SCHEMA_VERSION to N+1.
291
+ * 2. Add `'N->N+1': (m) => { ...transform... }` to MIGRATIONS.
292
+ * 3. The loop below will apply it automatically.
293
+ */
294
+ const MIGRATIONS = {
295
+ '1->2': (m) => ({ ...m, legacyMigrationNeeded: true }),
296
+ };
297
+
298
+ /**
299
+ * Migrate a manifest object to the target schema version.
300
+ *
301
+ * - If `manifest.schemaVersion === targetVersion` — no-op, returns manifest unchanged.
302
+ * - If `manifest.schemaVersion` is missing (legacy install) — sets it to 1 and returns.
303
+ * - If `manifest.schemaVersion > targetVersion` — throws (downgrade not supported, code 3).
304
+ * - If `manifest.schemaVersion < targetVersion` — applies registered migrations in order.
305
+ *
306
+ * @param {object} manifest - Parsed manifest object (may lack schemaVersion).
307
+ * @param {number} targetVersion - The schema version to migrate to.
308
+ * @returns {object} - The migrated manifest (new object if changed).
309
+ */
310
+ export function migrateManifest(manifest, targetVersion) {
311
+ // Legacy install: schemaVersion was not recorded before this constant existed.
312
+ if (manifest.schemaVersion === undefined || manifest.schemaVersion === null) {
313
+ return { ...manifest, schemaVersion: 1 };
314
+ }
315
+
316
+ const current = manifest.schemaVersion;
317
+
318
+ if (current === targetVersion) {
319
+ return manifest;
320
+ }
321
+
322
+ if (current > targetVersion) {
323
+ const err = new Error(
324
+ `Manifest schemaVersion ${current} is newer than installer target ${targetVersion}. ` +
325
+ 'Downgrade is not supported. Update lumina-wiki to the latest version.',
326
+ );
327
+ err.code = 3;
328
+ throw err;
329
+ }
330
+
331
+ // Apply migrations step-by-step (current → current+1 → … → targetVersion).
332
+ let m = manifest;
333
+ for (let v = current; v < targetVersion; v++) {
334
+ const key = `${v}->${v + 1}`;
335
+ const migrate = MIGRATIONS[key];
336
+ if (!migrate) {
337
+ const err = new Error(
338
+ `No migration found for schemaVersion ${key}. ` +
339
+ 'This is an internal error — please file an issue.',
340
+ );
341
+ err.code = 3;
342
+ throw err;
343
+ }
344
+ m = { ...migrate(m), schemaVersion: v + 1 };
345
+ }
346
+ return m;
347
+ }
348
+
277
349
  // ---------------------------------------------------------------------------
278
350
  // State file paths helper
279
351
  // ---------------------------------------------------------------------------