Track the source-of-truth code, skills, bootstrap rules, docs, and packaging files before adding new features. Exclude local vault config, generated skill mirrors, temporary exports, virtual environments, and business data files from version control. Validation: inspected staged file list and git ignored status before commit.
8.1 KiB
name, description
| name | description |
|---|---|
| wiki-import | Import a wiki knowledge graph from a graph.json export file into the current vault. Use this skill when the user says "import wiki", "import from export", "load graph.json", "import vault", "/wiki-import", or wants to transfer pages from one vault to another using the output of wiki-export. |
Wiki Import — Reconstruct Pages from graph.json
You are importing a vault's knowledge graph from a graph.json export (produced by wiki-export) into the current vault. This reconstructs page stubs with correct frontmatter, wikilinks, and typed relationships, then updates all vault metadata.
Before You Start
- Resolve config — follow the Config Resolution Protocol in
llm-wiki/SKILL.md(walk up CWD for.env→~/.obsidian-wiki/config→ prompt setup). This givesOBSIDIAN_VAULT_PATH. - Read
$OBSIDIAN_VAULT_PATH/AGENTS.mdif it exists — apply any owner-specific conventions.
Step 1: Locate and Validate Source
Find the import source:
- If the user provided a path argument, use it directly.
- Otherwise auto-detect
./wiki-export/graph.jsonin the current directory. - If neither exists, ask the user for the path.
Validate the file:
- Must be valid JSON
- Must have top-level keys:
nodes(array),links(array),graph(object) - Must have at least 1 node
If validation fails, report what's wrong and stop.
Show a preview before importing:
Import preview
Source: <path> (exported at <graph.exported_at>)
Nodes: N total (concepts: A, entities: B, skills: C, references: D, ...)
Links: M edges (X typed, Y untyped)
Target: $OBSIDIAN_VAULT_PATH
Step 2: Determine Conflict Resolution Mode
Read the user's phrasing to determine mode. Default is merge.
| Mode | Trigger phrases | Behaviour |
|---|---|---|
merge |
(default, no special phrasing) | Existing pages: update frontmatter tags/summary/relationships and add missing wikilinks; new pages: create stub. |
skip |
"skip existing", "don't overwrite", "only new pages" | Leave existing pages completely untouched; only create pages that don't exist yet. |
overwrite |
"overwrite", "replace existing", "force import" | Replace all matched pages with freshly reconstructed stubs regardless of existing content. |
Step 3: Build Internal Maps
Before writing anything, build two maps from the links array:
Adjacency map — for each node id, collect all neighbour ids (edges in either direction):
adjacency["concepts/transformers"] = ["entities/vaswani", "concepts/lstm", ...]
Typed edge map — for each node id, collect outgoing typed edges only (typed: true):
typed_edges["concepts/transformers"] = [
{target: "concepts/lstm", relation: "contradicts"},
...
]
Step 4: Reconstruct Pages
Record counts: created = 0, skipped = 0, merged = 0.
For each node in nodes:
- Compute
page_path = $VAULT/<node.id>.md - Ensure the parent directory exists (e.g.
$VAULT/concepts/) - Check if the file already exists:
- merge mode (default) + exists → read existing file, apply merge logic (see below), increment
merged - merge mode (default) + doesn't exist → proceed to create stub, increment
created - skip mode + exists → increment
skipped, continue to next node - skip mode + doesn't exist → proceed to create stub, increment
created - overwrite mode + exists → proceed to write fresh stub (overwrite), increment
merged - overwrite mode + doesn't exist → proceed to create stub, increment
created
- merge mode (default) + exists → read existing file, apply merge logic (see below), increment
Page template (new or overwrite)
---
title: <node.label>
category: <node.category>
tags: <node.tags as YAML list>
sources:
- "imported from <graph.json path>"
<if node.summary exists>
summary: "<node.summary>"
</if>
<if typed_edges[node.id] is non-empty>
relationships:
<for each {target, relation} in typed_edges[node.id]>
- target: "[[<target>]]"
type: <relation>
</for>
</if>
lifecycle: draft
lifecycle_changed: <today YYYY-MM-DD>
base_confidence: 0.5
tier: supporting
created: <ISO timestamp>
updated: <ISO timestamp>
---
# <node.label>
<node.summary paragraph if available, else omit>
## Related
<for each neighbour in adjacency[node.id], sorted alphabetically>
<if edge is typed>
- [[<neighbour>]] — <relation>
<else>
- [[<neighbour>]]
</if>
</for>
If adjacency[node.id] is empty, omit the ## Related section entirely.
Merge logic (merge mode, existing page)
- Read the existing page's frontmatter.
- Tags: union of existing tags and
node.tags(deduplicated, keep existing order, append new ones). - Summary: if the existing page has no
summaryfield andnode.summaryexists, add it. - Relationships: union of existing
relationships:entries andtyped_edges[node.id]— skip entries where the same(target, type)pair already exists. - Updated: set
updatedto the current ISO timestamp. - Body: scan for a
## Relatedsection. If it exists, append any missing wikilinks fromadjacency[node.id]that aren't already linked anywhere in the body. If no## Relatedsection exists, append one with the missing links. - Leave the rest of the body untouched.
Step 5: Update Vault Metadata
.manifest.json
Add a new entry keyed by the canonical path of the graph.json file:
"<absolute path to graph.json>": {
"ingested_at": "<ISO timestamp>",
"source_type": "wiki-export",
"pages_created": ["list/of/created/pages.md"],
"pages_updated": ["list/of/merged/pages.md"]
}
Also increment:
stats.total_sources_ingestedby 1stats.total_pagesby the count of pages actually created (not skipped/merged)
If .manifest.json doesn't exist, create it with the standard structure:
{
"stats": {
"total_sources_ingested": 1,
"total_pages": <created count>
},
"<graph.json path>": { ... }
}
index.md
For each created or merged page:
- Add or update the entry under its category section using the format:
- [[<id>]] — <summary or title> ( #tag1 #tag2)(Note: space before(—description ( #tag)notdescription(#tag))
Keep categories sorted alphabetically. Create the category section if it doesn't exist.
log.md
Append one line:
- [<ISO timestamp>] IMPORT source="<graph.json path>" pages_created=<N> pages_skipped=<K> pages_merged=<M>
hot.md
Rewrite the Recent Activity section to include this import as the latest entry:
- [<timestamp>] IMPORT from <graph.json path> — created X, merged Z pages
Update the updated: frontmatter timestamp. Leave other hot.md sections (Active Threads, Key Takeaways) intact unless they reference pages that were just created — in which case add brief mentions.
Step 6: Print Summary
Wiki import complete → $OBSIDIAN_VAULT_PATH
Source: <graph.json path>
exported at <graph.exported_at>, <N> nodes, <M> links
Created: <X> pages
Merged: <Z> pages (existing pages updated)
Skipped: <Y> pages (only when --skip mode was used)
Only show the Skipped line if skip mode was explicitly requested. If overwrite mode was used, label merged pages as Replaced instead.
Notes
- Stub quality: Imported pages are stubs — they have structure and wikilinks but no full body content. They're starting points for future ingestion, not finished pages.
- Re-running is safe: The default
mergemode is idempotent — re-running on an unchanged export will update timestamps but won't destroy content. Useoverwriteonly when you want to fully reset pages to stubs. - Directory creation: Always create missing category directories before writing pages.
- Broken wikilinks: Since pages are being created together from the same export, most links will resolve. Any node referenced in
linksbut absent fromnodes(broken in the original export) will still appear as a wikilink — it just won't have a corresponding page file, which is valid. - Filtered exports: If the source
graph.jsonwas produced with visibility filtering (noted ingraph.metadata), imported pages will only reflect the filtered set. Note this in the summary ifgraph.graphcontains afilteredkey.