
Wiki Rewrite
- 1 installs
- 2 repo stars
- Updated July 19, 2026
- ievertan00/nanowiki
Reformats an existing file into the Obsidian wiki note schema in one pass, assigning domain/topic and preserving the Human Insight section.
About
Reformats an existing file into the Obsidian wiki note schema in a single pass, assigning domain/topic, linking only existing notes, and preserving the human-authored Human Insight section verbatim. A developer uses it to import a rough draft or literature file into their vault's standard schema.
- Single-pass reformat of a file into the wiki note schema
- Preserves the human-authored Human Insight section verbatim
Wiki Rewrite by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,366 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Jul 20, 2026 (Skillselion catalog sync)
npx skills add https://github.com/ievertan00/nanowiki --skill wiki-rewriteAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 2 |
| Last updated | July 19, 2026 |
| Repository | ievertan00/nanowiki ↗ |
What it does
Reformats an existing file into the Obsidian wiki note schema in one pass, assigning domain/topic and preserving the Human Insight section.
Files
wiki-rewrite
Reformat an existing file into the wiki schema. Single pass — the file content is the input, so there is no answer-generation pass. You are the LLM.
Bundled files
This skill ships note-schema.md and wiki-maintain.mjs in its own directory. Determine that directory once — written <SKILL_DIR> below — and substitute its real absolute path into every Read and node call (never hardcode a username, .claude, or drive letter). <SKILL_DIR> is the folder this SKILL.md was loaded from; if you don't already know it, find it with Glob **/wiki-rewrite/wiki-maintain.mjs (skills live under the host CLI's skills directory, e.g. ~/.claude/skills/ for Claude Code).
Then read <SKILL_DIR>\note-schema.md first — vault resolution, language rules, frontmatter, body skeleton, slug rule, and invariants. Everything below assumes it.
Steps
1. Resolve the vault path and output language — see note-schema.md. Parse --type, --lang, --vault; the remainder is the file argument. Normalize it: strip a leading @ (the file-reference marker CLIs like Claude Code prepend) and any surrounding quotes. Then resolve it to a real file — the same rule as `wiki-ingest`:
- Bare filename — no
/or\(e.g.rough-notes.md) →<vault>\sources\<name>. - Otherwise — a path (e.g.
@C:\drafts\x.md,@sources/x.md, or./drafts/x.md)
→ treat as a literal path (relative to cwd or absolute); if it doesn't exist there, also try <vault>\sources\<name>.
Error if nothing resolves.
Decide the source link from where the input lives:
- If the input file is already inside `<vault>\notes\` (you are re-normalizing an
existing note), it is not its own source — note its current source: value (if any) to carry over unchanged in step 4. Set sourceFile = none.
- Otherwise the input is a source document. A note can only link to a source that
lives in sources/. If the file is not already inside <vault>\sources\, copy it there now — slugify its basename (same slug rule) and keep the original extension: Copy-Item "<resolved path>" "<vault>\sources\<slug><ext>". Set sourceFile = the basename with extension of the file in sources/ (e.g. paper.pdf, My-Notes.md).
2. Read the input file. If it contains a ## Human Insight section with a non-empty body, capture that body now — you will restore it verbatim at the end.
3. Gather context. List notes/ basenames (existing-notes list) and read the domains taxonomy from wiki-config.json.
4. Format (single pass). Reshape the file's content into the note schema:
- Assign
domain/topicagainst the taxonomy (closest match, or a new concise one). type: use--typeif given, else inferatomicorliteraturefrom the content.- In
## Connections, link only to existing notes; otherwise leave empty. - Do not add information beyond what the file contains.
- Set `source:` from step 1: if
sourceFilewas set (input was a source
document), use a quoted wikilink to it — keep the extension for non-markdown files so Obsidian can resolve them, drop only a .md extension: source: "[[paper.pdf]]", source: "[[My-Notes]]". If you were re-normalizing an existing note, carry over its current source: value unchanged (empty if it had none).
5. Restore Human Insight. Replace the ## Human Insight section of your output with the body you captured in step 2 (verbatim). If there was none, leave it empty.
6. Write the note. Derive title from the frontmatter (fall back to the input file's basename). Compute the slug and write notes/<slug>.md.
7. Regenerate derived files:
node "<SKILL_DIR>\wiki-maintain.mjs" "<vaultPath>" --op rewrite --title "<noteTitle>"8. Report the saved note path and assigned domain/topic.
Wiki note schema & conventions
Shared reference for the note-writing skills (wiki-ask, wiki-rewrite, wiki-ingest). You are the LLM here — there is no external API. You do the generation yourself, then write files and run the maintenance helper.
Resolving the vault
The vault is the directory where the CLI was started — the current working directory. If the user runs Claude Code / Gemini CLI in D:\wiki, the vault is D:\wiki.
1. If an explicit --vault <path> argument is given, use it. 2. Otherwise, use the current working directory.
A vault contains: sources/, notes/, moc/, meta/, templates/personas/, templates/structures/, wiki-config.json, and WIKI.md. A fresh, empty directory is a valid vault — on the first run the maintenance helper scaffolds everything automatically: it creates the six subdirectories, writes a default wiki-config.json ({ "language": "zh", "domains": {} }), and generates a default WIKI.md from the bundled WIKI.template.md. This scaffolding is idempotent — existing files are never overwritten — so you don't need to create these yourself.
Output language
Resolve in this order: 1. --lang zh|en flag. 2. language field in wiki-config.json. 3. $env:WIKI_LANG. 4. Default zh (Simplified Chinese).
- zh — write all prose and frontmatter values (title, domain, topic, tags) in
Simplified Chinese, but keep widely-used technical terms and proper nouns in their original English form — do not translate them (AI, LLM, Prompt, Token, Docker, API, GPU, Transformer, product and company names, etc.).
- en — write everything in English.
Regardless of language, keep these structural tokens EXACTLY in English (the maintenance helper and Obsidian parse them by exact match):
- Section headings:
## Source Facts,## Synthesis,## Connections,
## Speculation, ## Open Questions, ## Human Insight
- Typed-link keywords:
extends::,contradicts::,requires::,examples::,related:: - YAML keys:
title:,type:,source:,domain:,topic:,tags:,aliases:,created:,updated:
Frontmatter
Output raw YAML between --- delimiters. No `\`\`\`yaml` fence, no code fence around the whole note.
---
title: <specific, unique noun phrase — Title Case, 3–7 words; distinctive enough to
stand alone in an index. Avoid generic one-word labels ("Gemini", "Attention").
Name the precise concept ("Scaled Dot-Product Attention"). For zh the value may be Chinese.>
type: <atomic | literature>
source: <a quoted wikilink to this note's source file in sources/, e.g. "[[My-Answer]]";
KEEP the extension for non-markdown files so Obsidian can resolve them — "[[paper.pdf]]",
NOT "[[paper]]" (which would resolve to paper.md). Empty when the note has no source file.>
domain: <closest match from the taxonomy, or a new concise domain>
topic: <closest match from the taxonomy, or a new concise topic>
tags: [tag-a, tag-b, tag-c] # 3–6 tags, each a SINGLE token with NO spaces,
# kebab-case, no quotes (Obsidian rejects spaces in tags)
aliases: [<0–3 alternative names other notes might link by — the title's counterpart
in the other language (English name for a Chinese title, or vice versa) and a
widely-used abbreviation, when they exist; spaces allowed; [] when none>]
created: <YYYY-MM-DD today>
updated: <YYYY-MM-DD today>
---Body skeleton (use these sections, in this order)
## Source Facts
Only what sources or established knowledge directly states. No interpretation here.
Present as a structured bulleted list — one discrete fact per bullet — and group
related bullets under bold sub-labels or short sub-headings when they cluster
naturally. Not a prose paragraph. Include inline citations as (Source: title) where
applicable.
## Synthesis
Cross-source interpretation — what the facts add up to. Clearly LLM-generated
inference, not source statements.
## Connections
Typed links ONLY. Relationship types:
extends:: [[note]] — this note builds on another
contradicts:: [[note]] — these claims conflict
requires:: [[note]] — this concept depends on another
examples:: [[note]] — concrete instances of this concept
related:: [[note]] — loose association
Use only the types that genuinely apply. Multiple links of one type are fine.
Every link must earn its place. Atomic notes: aim for 2–4 links. Literature notes:
up to 8 is reasonable.
## Speculation
Unverified but interesting inferences. Clearly marked as not established.
## Open Questions
What this note does not resolve. Gaps worth investigating.
## Human Insight
Leave this section completely empty (heading only). Reserved for the human author.Filename (slug)
slug = <domain>-<topic>-<title> with every run of characters not in [a-zA-Z0-9一-鿿] replaced by -, then trim leading/trailing -. Alphanumerics and CJK ideographs are preserved, so Chinese domains/topics/titles produce Chinese filenames. Save the note to notes/<slug>.md. If the note has no domain or topic, fall back to the title alone. Naming is ultimately code-owned: wiki-maintain.mjs re-derives this name on every run and renames any note that drifts, rewriting inbound links — so just write your best name here and let the maintenance script normalize it.
Invariants (these hold regardless of the model behind this skill)
- Human Insight is sacred. Never write content under
## Human Insight. New notes:
leave it empty. When rewriting/updating an existing note, copy its existing Human Insight body back verbatim.
- No dead links. In
## Connections, only[[link]]to notes that **already
exist in `notes/` — matched by filename or** by a name in a note's aliases:. If none genuinely apply, leave Connections empty. Never invent a link to a note that does not exist.
- Citation markers are preserved verbatim. A Source Facts bullet may end with a
^[<source-name>] marker tying the fact to a file in sources/. When rewriting or updating a note, copy every existing marker unchanged with its bullet. When ingesting, append ^[<source-file-basename>] to each bullet you add to Source Facts.
- No code fences around the note or its frontmatter. Output clean Markdown only.
After writing — regenerate derived files
Run the bundled helper (next to this skill's SKILL.md) once at the end:
node "<this skill folder>\wiki-maintain.mjs" "<vaultPath>" --op <ask|rewrite|ingest> --title "<noteTitle>"It rebuilds moc/*.md, meta/index.md, the wiki-config.json taxonomy, the WIKI.md domains block, and appends meta/log.md. Never hand-edit those files.
#!/usr/bin/env node
// Zero-dependency vault maintenance for the wiki-* skills.
// Rebuilds moc/*.md, meta/index.md, the wiki-config.json taxonomy, the WIKI.md
// domains block, and appends meta/log.md. Mirrors the wiki CLI's meta.js so the
// derived files stay byte-compatible. These files are owned by the tooling —
// the human never hand-edits them.
//
// node wiki-maintain.mjs <vaultPath> [--op <name>] [--title <title>]
//
// --op/--title are optional; when both are given a log line is appended.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const args = process.argv.slice(2);
const vaultPath = args[0];
if (!vaultPath) {
console.error('Usage: node wiki-maintain.mjs <vaultPath> [--op <name>] [--title <title>]');
process.exit(1);
}
const opIdx = args.indexOf('--op');
const titleIdx = args.indexOf('--title');
const op = opIdx !== -1 ? args[opIdx + 1] : null;
const title = titleIdx !== -1 ? args[titleIdx + 1] : null;
const today = new Date().toISOString().slice(0, 10);
const notesDir = path.join(vaultPath, 'notes');
const mocDir = path.join(vaultPath, 'moc');
const metaDir = path.join(vaultPath, 'meta');
function parseFrontmatter(content) {
const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
if (!m) return {};
const result = {};
for (const raw of m[1].split(/\r?\n/)) {
const line = raw.replace(/^```ya?ml?|```$/g, '').trim();
const i = line.indexOf(':');
if (i === -1) continue;
const key = line.slice(0, i).trim();
const value = line.slice(i + 1).trim().replace(/^['"]|['"]$/g, '');
if (value) result[key] = value;
}
return result;
}
// ── Schema rename: enforce <domain>-<topic>-<title> filenames (code-owned naming) ──
// Mirrors the CLI's renameToSchema (src/lint.js). Renames any note whose filename is
// not <domain>-<topic>-<title>, rewriting inbound [[links]]. Notes missing domain or
// topic are left as-is. Same slugify/normalize rules as the CLI — keep in sync.
const schemaSlugify = s => String(s).replace(/[^a-zA-Z0-9一-鿿]+/g, '-').replace(/^-|-$/g, '');
const schemaNormalize = s => String(s).toLowerCase().replace(/[\s\-_::、,。!?]+/g, '').replace(/[^\w一-鿿]/g, '');
function rewriteInboundLinks(fromSlug, toSlug) {
const target = schemaNormalize(fromSlug);
for (const f of fs.readdirSync(notesDir).filter(f => f.endsWith('.md'))) {
const p = path.join(notesDir, f);
const content = fs.readFileSync(p, 'utf8');
const updated = content.replace(/\[\[([^\]|]+)(\|[^\]]*)?\]\]/g, (m, t, disp) =>
schemaNormalize(t) === target ? `[[${toSlug}${disp || ''}]]` : m);
if (updated !== content) fs.writeFileSync(p, updated);
}
}
function renameToSchema() {
if (!fs.existsSync(notesDir)) return 0;
const files = fs.readdirSync(notesDir).filter(f => f.endsWith('.md'));
const taken = new Set(files.map(f => path.basename(f, '.md')));
let count = 0;
for (const file of files) {
const currentSlug = path.basename(file, '.md');
const fm = parseFrontmatter(fs.readFileSync(path.join(notesDir, file), 'utf8'));
if (!fm.domain || !fm.topic) continue;
const title = fm.title || currentSlug;
let desired = schemaSlugify(`${fm.domain}-${fm.topic}-${title}`);
if (!desired || desired === currentSlug) continue;
if (taken.has(desired)) {
// The bare schema name is occupied by another note. If currentSlug is already a
// stable `<desired>-N` disambiguation of it, leave it — renaming would just pick a
// different suffix every run and oscillate forever. Otherwise claim the next free
// suffix. (When the bare name is free, we fall through and rename to it, which also
// promotes a stale `<desired>-N` back to the bare name.)
if (currentSlug.replace(/-\d+$/, '') === desired) continue;
let n = 2;
while (taken.has(`${desired}-${n}`)) n++;
desired = `${desired}-${n}`;
}
fs.renameSync(path.join(notesDir, file), path.join(notesDir, `${desired}.md`));
taken.delete(currentSlug);
taken.add(desired);
rewriteInboundLinks(currentSlug, desired);
count++;
}
return count;
}
function readNotes() {
if (!fs.existsSync(notesDir)) return [];
return fs.readdirSync(notesDir).filter(f => f.endsWith('.md')).map(f => {
const content = fs.readFileSync(path.join(notesDir, f), 'utf8');
return { slug: path.basename(f, '.md'), fm: parseFrontmatter(content) };
});
}
// ── First-run scaffold: create the vault skeleton + default config + WIKI.md ──
// Idempotent — existing files are never touched.
const DEFAULT_CONFIG = { language: 'zh', domains: {} };
const DEFAULT_TEMPLATES = {
'personas/skeptical-reviewer.md': `以一名挑剔、要求证据的审稿人视角作答:
* 对每一个关键论断,主动追问"证据是什么"、"样本/数据是否足够"、"是否存在选择性引用"。
* 明确区分"已证实的事实"、"作者的推论"和"未经验证的猜测",三者不可混淆。
* 主动指出可能的利益冲突、研究局限、方法缺陷,即使原始来源未提及。
* 对过于绝对或缺乏限定条件的结论,标注该结论的适用边界。
* 语气审慎、克制,不夸大也不轻易否定,但绝不回避指出弱点。`,
'personas/beginner-explainer.md': `假设读者第一次接触这个主题,按照"新手友好"的方式作答:
* 先给出一句话的整体定位(这是什么、为什么重要),再逐步展开。
* 遇到专业术语或缩写时,第一次出现必须给出简明解释,不能假设读者已经知道。
* 多用类比 and 具体例子,把抽象概念落到可感知的场景。
* 按照"由浅入深"的顺序组织内容:先讲核心概念,再讲细节和边缘情况。
* 在适当的地方提示后续可以深入了解的方向,为进一步学习指路。`,
'personas/investor-decisionmaker.md': `以投资者/决策者的视角作答,关注"这对决策意味着什么":
* 每一段分析尽量落到机会、风险、所需行动或决策影响上,而不是停留在纯描述。
* 明确标注时间维度:短期(影响当下决策)vs 中长期(影响战略方向)。
* 对关键不确定性,说明如果该假设被证明错误,结论会如何变化。
* 在合适的地方给出风险收益的定性判断(如:高风险高回报 / 稳健但增长有限)。
* 避免空泛的"值得关注"之类表述,尽量给出可执行的下一步(如:需要进一步验证的数据、需要观察的指标、需要等待的事件)。`,
'personas/systems-architect.md': `You are a pragmatic, veteran Principal Systems Architect.
When answering or summarizing:
- Focus heavily on operational constraints, scalability bottlenecks, and failure modes.
- Look at security implications (threat vectors, data boundaries).
- Keep descriptions dry, technical, and concrete.
- Prioritize real-world engineering trade-offs (e.g., maintenance overhead vs. performance gains) over theoretical ideals.`,
'personas/feynman-tutor.md': `You are an expert tutor who explains complex concepts using the Feynman Technique.
- Break down jargon into plain, clear language.
- Use intuitive, real-world analogies to ground abstract concepts.
- Explain the "why" before the "how."
- Keep the tone encouraging, clear, and accessible, without sounding condescending.`,
'structures/api-eval.md': `- **Developer Experience (DX):** Setup friction, quality of docs, type safety.
- **Performance:** Runtime overhead, memory usage, latency profiles, dependency footprint.
- **Ecosystem Fit:** Versioning frequency, community support, ease of testing/mocking.
- **Alternatives:** How it compares directly to the leading industry standard.`,
'structures/system-design.md': `- **Bottlenecks:** Network, disk I/O, or CPU limits; scaling limitations.
- **State & Storage:** Database choices, consistency guarantees, cache-invalidation strategies.
- **Failover:** What happens if a node or region goes down?
- **Trade-offs:** Which coordinates of the CAP theorem, cost, or complexity were sacrificed?`,
'structures/paper-summary.md': `- **Core Hypothesis:** The exact problem statement and proposed solution.
- **Methodology Summary:** How they tested it, variables controlled, and metrics measured.
- **Key Benchmarks:** Exact percentage improvements, speeds, or parameters.
- **Limitations:** Self-admitted or obvious flaws in the research or implementation.`,
'structures/company-competitor-deepdive.md': `## 公司概况
* 成立时间、所在地、规模(员工数、营收量级)
* 主营业务与产品线
* 股权结构与控制人
* 发展历程中的关键里程碑
## 商业模式
* 收入构成与占比
* 成本结构与毛利率
* 客户结构(B端/C端、集中度)
* 定价策略与变现方式
## 产品与技术
* 核心产品/服务的竞争力来源
* 技术壁垒与专利布局
* product-迭代节奏与路线图
## 市场地位与竞争格局
* 市场份额与排名
* 主要竞争对手及其差异化
* 与竞品的对比表格(产品、价格、渠道、用户规模)
## 财务表现
* 营收、利润、增长率(近3年趋势)
* 现金流状况与融资历史
* 估值水平(如有公开数据,标注来源;否则标注"估算"并说明依据)
## 团队与治理
* 创始人/管理层背景
* 组织文化与人才策略
* 治理结构中的潜在风险点
## SWOT分析
* 优势(Strengths)
* 劣势(Weaknesses)
* 机会(Opportunities)
* 威胁(Threats)
## 风险与不确定性
* 经营风险、法律合规风险、技术风险、市场风险
* 评估影响程度:高 / 中 / 低
## 近期动态与展望
* 最近6-12个月的重大事件(融资、并购、产品发布、人事变动)
* 未来1-3年的关键看点与判断依据`,
'structures/industry-research-report.md': `# 一、执行摘要(Executive Summary)
包括:
* 行业现状概述
* 核心发现
* 关键数据
* 未来趋势判断
* 主要机会与风险
要求:用500字以内总结整份报告。
---
# 二、行业概况
## 2.1 行业定义
说明:
* 行业边界
* 主要产品与服务
* 行业分类标准
* 上下游关系
## 2.2 行业发展历程
分析:
* 萌芽期
* 成长期
* 成熟期
* 当前所处阶段
## 2.3 行业价值链
识别上游、中游、下游,分析各环节价值分布。
---
# 三、宏观环境分析(PEST)
## 政策(Policy)
分析:国家政策、监管环境、产业扶回政策、行业准入门槛
## 经济(Economic)
分析:GDP影响、消费能力变化、利率环境、宏观经济周期影响
## 社会(Social)
分析:人口结构变化、消费习惯变化、社会需求变化
## 技术(Technology)
分析:技术演进路线、核心技术壁垒、技术替代风险
---
# 四、市场规模分析
## 当前市场规模
输出:市场规模、增长率、历史变化趋势
## TAM / SAM / SOM分析
分别估算总市场规模(TAM)、可服务市场(SAM)、可获取市场(SOM),明确假设依据。
## 市场增长驱动因素
分析:政策驱动、技术驱动、用户需求驱动、商业模式驱动
---
# 五、产业链分析
## 上游
分析:核心资源、供应商格局、议价能力
## 中游
分析:主要企业、行业集中度、盈利模式
## 下游
分析:客户类型、采购逻辑、需求变化趋势
识别产业链关键利润环节。
---
# 六、竞争格局分析
## 市场结构
判断:完全竞争 / 垄断竞争 / 寡头竞争 / 垄断市场
## 市场集中度
分析:CR3、CR5、CR10
## 主要企业分析
对于TOP企业分别分析:公司简介、市场份额、产品结构、核心优势、核心风险、财务表现、战略方向。输出对比表格。
---
# 七、商业模式分析
分析行业主要盈利模式:收入来源、成本结构、利润来源、关键成功因素。分析行业价值创造逻辑。
---
# 八、用户需求分析
识别:核心用户群体、用户画像、用户痛点、用户决策逻辑。分析未来需求变化趋势。
---
# 九、行业趋势分析
从以下维度分析:技术趋势、产品趋势、商业模式趋势、竞争趋势、政策趋势。预测未来3年、5年、10年行业发展方向。
---
# 十、风险分析
包括:政策风险、技术风险、市场风险、竞争风险、替代风险。评估影响程度:高 / 中 / 低。
---
# 十一、机会分析
识别:增量市场、新兴赛道、技术变革机会、商业模式创新机会。说明机会形成原因。
---
# 十二、结论与建议
分别从企业经营者视角、创业者视角、投资者视角提出具体建议。
---
输出要求:
1. 使用咨询公司(麦肯锡、贝恩、BCG)、券商研究所及投资机构的研究风格写作。
2. 优先使用数据和事实支撑观点。
3. 明确区分事实、推论和预测。
4. 每个结论必须给出依据。
5. 使用表格呈现关键数据。
6. 使用 SWOT、PEST、波特五力模型等经典框架。
7. 最后给出未来三年行业展望及关键判断。
8. 若缺少真实数据,请明确标注"估算"并说明估算逻辑。`,
'structures/paper-book-summary.md': `## 基本信息
* 作者、发表时间/出版年份、来源(期刊/会议/出版社)
* 研究领域与所属学术脉络
## 核心论点/主张
* 作者试图回答的问题或证明的论点
* 主要结论一句话概括
## 研究方法
* 采用的方法、数据来源、样本规模
* 方法本身的优势与局限
## 关键发现与证据
* 支撑论点的核心数据/案例
* 区分"作者声称的事实"与"作者的推论"
## 与既有知识的关系
* 这项工作如何延续、修正或反驳已有研究
* 与本知识库中已有笔记的关联点
## 局限性与批判性评估
* 作者自述的局限
* 你认为未被充分讨论的弱点(方法、样本、推广性等)
## 实践启示
* 对实际工作/决策有何指导意义
* 若结论被采纳,会改变什么
## 延伸问题
* 这项工作引出的、值得进一步研究的问题`,
'structures/technology-deepdive.md': `## 技术原理
* 核心工作机制与关键概念
* 与相关/前驱技术的区别
## 技术架构
* 系统组成与关键模块
* 依赖的基础设施或前置条件
## 性能与基准
* 关键性能指标(速度、精度、成本、能耗等)
* 与替代方案对比数据(标注数据来源;数据缺失时标注"估算"并说明依据)
## 成熟度与应用现状
* 当前所处发展阶段(研究/原型/早期应用/规模化)
* 已落地的典型应用场景与案例
## 替代方案与竞争技术
* 主要竞品技术及其优劣势
* 选型决策的关键权衡因素
## 局限性与开放问题
* 当前已知的技术瓶颈
* 尚未解决的关键问题
## 生态与社区
* 主要参与者(公司、开源项目、标准组织)
* 工具链与开发者生态成熟度
## 发展趋势与路线图
* 短期(1年内)、中期(1-3年)、长期(3年以上)的演进方向
* 可能引发范式转变的因素`
};
function ensureScaffold() {
for (const d of ['sources', 'notes', 'moc', 'meta', 'templates/personas', 'templates/structures']) {
const p = path.join(vaultPath, d);
if (!fs.existsSync(p)) fs.mkdirSync(p, { recursive: true });
}
for (const [relPath, content] of Object.entries(DEFAULT_TEMPLATES)) {
const p = path.join(vaultPath, 'templates', relPath);
if (!fs.existsSync(p)) {
fs.writeFileSync(p, content);
}
}
const cfgPath = path.join(vaultPath, 'wiki-config.json');
if (!fs.existsSync(cfgPath)) {
fs.writeFileSync(cfgPath, JSON.stringify(DEFAULT_CONFIG, null, 2));
}
const wikiFile = path.join(vaultPath, 'WIKI.md');
if (!fs.existsSync(wikiFile)) {
const tpl = path.join(__dirname, 'WIKI.template.md');
if (fs.existsSync(tpl)) fs.copyFileSync(tpl, wikiFile);
}
}
ensureScaffold();
const renamedCount = renameToSchema();
const notes = readNotes();
// ── MOC: one file per domain, grouped by topic, sorted by title ──────────────
function rebuildMOC() {
if (!fs.existsSync(mocDir)) fs.mkdirSync(mocDir, { recursive: true });
// Wipe stale .md first so removed/merged domains leave no orphan MOC file.
for (const f of fs.readdirSync(mocDir).filter(f => f.endsWith('.md'))) {
fs.rmSync(path.join(mocDir, f));
}
const byDomain = {};
for (const { slug, fm } of notes) {
const domain = fm.domain || 'uncategorized';
const topic = fm.topic || '';
const title = fm.title || slug;
(byDomain[domain] ??= {});
(byDomain[domain][topic] ??= []).push({ slug, title });
}
for (const [domain, topics] of Object.entries(byDomain)) {
let out = '';
for (const [topic, list] of Object.entries(topics)) {
if (topic) out += `## ${topic}\n`;
list.sort((a, b) => a.title.localeCompare(b.title)).forEach(({ slug, title }) => {
out += slug === title ? `- [[${slug}]]\n` : `- [[${slug}|${title}]]\n`;
});
out += '\n';
}
fs.writeFileSync(path.join(mocDir, `${domain}.md`), out);
}
}
// ── meta/index.md: flat sorted catalog of every note slug ────────────────────
function rebuildIndex() {
if (!fs.existsSync(metaDir)) fs.mkdirSync(metaDir, { recursive: true });
const slugs = notes.map(n => n.slug).sort();
let out = '# Index\n\n';
for (const s of slugs) out += `- [[${s}]]\n`;
fs.writeFileSync(path.join(metaDir, 'index.md'), out);
}
// ── wiki-config.json taxonomy: additive merge from note frontmatter ──────────
function rebuildTaxonomy() {
const cfgPath = path.join(vaultPath, 'wiki-config.json');
let cfg = {};
if (fs.existsSync(cfgPath)) {
try { cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8')); } catch { cfg = {}; }
}
const domains = cfg.domains || {};
for (const { fm } of notes) {
if (!fm.domain) continue;
const topics = (domains[fm.domain] ||= []);
if (fm.topic && !topics.includes(fm.topic)) topics.push(fm.topic);
}
cfg.domains = domains;
fs.writeFileSync(cfgPath, JSON.stringify(cfg, null, 2));
}
// ── WIKI.md domains block (only maintained when WIKI.md already exists) ───────
function updateWikiDomains() {
const wikiFile = path.join(vaultPath, 'WIKI.md');
if (!fs.existsSync(wikiFile)) return;
const set = new Set(notes.map(n => n.fm.domain).filter(Boolean));
const sorted = [...set].sort((a, b) => a.localeCompare(b));
const list = sorted.length ? sorted.map(d => `- [[${d}]]`).join('\n') : '_No domains yet._';
const block = `<!-- domains:start (auto-generated — do not edit) -->\n${list}\n<!-- domains:end -->`;
let content = fs.readFileSync(wikiFile, 'utf8');
const re = /<!-- domains:start[\s\S]*?<!-- domains:end -->/;
content = re.test(content)
? content.replace(re, block)
: `${content.trimEnd()}\n\n---\n\n## Domains\n\n${block}\n`;
fs.writeFileSync(wikiFile, content);
}
// ── meta/log.md: append-only, grep-friendly operation log ────────────────────
function appendLog() {
if (!op || !title) return;
if (!fs.existsSync(metaDir)) fs.mkdirSync(metaDir, { recursive: true });
fs.appendFileSync(path.join(metaDir, 'log.md'), `## [${today}] ${op} | ${title}\n\n`);
}
rebuildMOC();
rebuildIndex();
rebuildTaxonomy();
updateWikiDomains();
appendLog();
console.log(`Maintained: ${notes.length} note(s)${renamedCount ? `, ${renamedCount} renamed to schema` : ''} → moc/, meta/index.md, taxonomy${op ? `, logged ${op}` : ''}.`);
Wiki Schema
This document governs how the wiki is structured and maintained. The LLM reads it at the start of every session. You and the LLM co-evolve it over time.
This file was generated automatically on first use of the wiki skills. Edit it freely — only the auto-generated Domains block below is overwritten.
---
Philosophy
You source and ask questions. The LLM writes and maintains.
- Sources (
sources/) — raw inputs, immutable. Never modified after ingestion. - Notes (
notes/) — LLM-generated pages. The LLM owns this layer entirely. - MOCs (
moc/) — per-domain navigation indexes. Auto-generated, never hand-edited. - Meta (
meta/) —index.md(catalog) andlog.md(chronological record).
---
Note Structure
Every note follows this skeleton, in order:
## Source Facts
Only what sources or established knowledge directly states.
Inline citations as (Source: title) where applicable. No interpretation.
## Synthesis
Cross-source interpretation. What the facts add up to.
Clearly LLM-generated inference, not source statements.
## Connections
Typed links only:
extends:: [[note]] — this note builds on another
contradicts:: [[note]] — these claims conflict
requires:: [[note]] — this concept depends on another
examples:: [[note]] — concrete instances of this concept
related:: [[note]] — loose association
Use only the types that genuinely apply — not all types are required.
Multiple links of the same type are fine.
Every link should earn its place: if removing it costs nothing, drop it.
Atomic notes: aim for 2–4 links. Literature notes: up to 8 is reasonable.
## Speculation
Unverified but interesting inferences. Clearly marked as not established.
## Open Questions
What this note does not resolve. Gaps worth investigating.
## Human Insight
[Reserved for the human author. Never written to or modified by the LLM.]---
Frontmatter Schema
Frontmatter is plain YAML between --- delimiters — no code fences inside:
---
title: concise noun-phrase title
type: atomic | literature
source: filename or title of the source document (literature notes only; empty for atomic)
domain: top-level domain (e.g. ai, engineering, language)
topic: sub-field within domain (e.g. llm, databases, grammar)
tags: [keyword-1, keyword-2, keyword-3]
created: YYYY-MM-DD
updated: YYYY-MM-DD
---type:
atomic— one concept, permanently valuable. Generated byask.literature— tied to a specific source (paper, article, book). Generated byingest.
source: the filename or title of the source document this note was derived from. Set automatically by ingest. Empty for atomic notes.
tags: 3–6 tags. Each tag MUST be a single token with no spaces — Obsidian rejects spaces in tags. Join multi-word concepts with hyphens (kebab-case), e.g. prompt-caching, kv-cache. Do not wrap tags in quotes. Technical terms stay in their original form (e.g. llm, docker).
---
Output Language
The output language is set by language in wiki-config.json (zh for Simplified Chinese, en for English).
- Prose and frontmatter values (title, domain, topic, tags) are written in the configured language.
- Technical terms and proper nouns stay in their original English form — never translate them (AI, LLM, Prompt, Token, Docker, API, GPU, Transformer, product and company names, …).
- Structural tokens stay in English, always, regardless of language. These are parsed by exact match when MOCs and the index are regenerated, so localizing them would break the wiki:
- Section headings:
## Source Facts,## Synthesis,## Connections,## Speculation,## Open Questions,## Human Insight - Typed-link keywords:
extends::,contradicts::,requires::,examples::,related:: - YAML frontmatter keys:
title:,type:,source:,domain:,topic:,tags:,created:,updated:
---
Taxonomy
Domains and topics are stored in wiki-config.json. The LLM uses existing entries when they fit and adds new ones when they don't. Edit wiki-config.json directly to rename or consolidate.
---
Domains
The current domains in the vault, regenerated from note frontmatter after every mutating command. Each links to its MOC.
<!-- domains:start (auto-generated — do not edit) --> _No domains yet._ <!-- domains:end -->
---
Operations
ask — user asks a question. LLM answers naturally (Layer 1), then formats into a note (Layer 2). Saves to notes/, updates moc/ and meta/index.md.
rewrite — user provides an existing file. LLM reformats it into the note schema. ## Human Insight is preserved exactly.
ingest — user drops a source into sources/. LLM reads it, writes a literature note, updates relevant existing notes, appends to meta/log.md.
lint — LLM scans the wiki for contradictions, stale claims, orphan notes, missing links, and concepts without pages. Report saved to meta/lint-YYYY-MM-DD.md.
---
Log Format
Entries in meta/log.md use this prefix for grep-ability:
## [YYYY-MM-DD] operation | Title---
Conventions
- Note filenames: derived from the title by collapsing every run of characters outside
[a-zA-Z0-9+ CJK ideographs]to a single-, then trimming leading/trailing-. Alphanumerics and Chinese characters are preserved, so Chinese titles produce Chinese filenames (e.g.LLM-Prompt-Caching-KV缓存复用机制.md). English titles end up kebab-case. - All notes live flat in
notes/— no subfolders - Body links use
[[note-title]](Obsidian wikilink format) - MOC links use
[[slug|Title]]— slug is the safe filename, title is human-readable display text ## Human Insightis never overwritten by the LLM- Sources in
sources/are never modified after being added