8.6 KiB
KarpathyWiki Obsidian Plugin Inspection
Date: 2026-07-09
Plugin: karpathywiki / green-dalii/obsidian-llm-wiki
Repo inspected: https://github.com/green-dalii/obsidian-llm-wiki
Fresh clone verification: commit 0979d50
License: Apache-2.0
Version observed: 1.24.0
Why inspected
Jason observed that the Obsidian Karpathy LLM Wiki plugin is doing much of what the Redmine ticket decomposition process should do: entity/concept extraction, knowledge graph page generation, links, indexes, and conversational query. The question was whether we can trigger it headlessly/CLI or mirror its logic.
Repository / structure findings
The plugin is a compact TypeScript Obsidian plugin.
Important files:
src/main.ts
src/wiki/wiki-engine.ts
src/wiki/source-analyzer.ts
src/wiki/page-factory.ts
src/wiki/prompts/ingestion.ts
src/wiki/prompts/generation.ts
src/wiki/prompts/merge.ts
src/wiki/prompts/lint.ts
src/wiki/query-engine/
src/core/
Package metadata:
{
"name": "karpathywiki",
"version": "1.24.0",
"main": "main.js"
}
Fresh clone verification confirmed package.json has no bin entry:
PACKAGE {'name': 'karpathywiki', 'version': '1.24.0', 'main': 'main.js', 'bin': None, 'license': 'Apache-2.0'}
There is no package bin entry and no obvious standalone CLI exposed by the project.
Obsidian command entry points
src/main.ts registers Obsidian commands:
ingest-source
ingest-folder
ingest-multiple-files
query-wiki
lint-wiki
regenerate-index
cancel-ingestion
ingest-active-file
view-ingestion-history
recreate-welcome-note
These are Obsidian command palette commands, not shell commands.
If Obsidian is running, it may be possible to trigger commands through Obsidian/Advanced URI style automation, but that would still depend on Obsidian being open and the plugin being loaded. It is not a true headless pipeline.
Core engine dependency
The core class is WikiEngine in src/wiki/wiki-engine.ts.
It is separate from the plugin UI, but its constructor requires Obsidian types and APIs:
constructor(
app: App,
settings: LLMWikiSettings,
getLLMClient: () => LLMClient | null,
schemaManager: SchemaManager,
onFileWrite?: (path: string) => void,
onProgress?: (message: string) => void,
onDone?: (report: IngestReport) => void
)
It uses:
app.vault.readapp.vault.getMarkdownFilesapp.vault.processapp.vault.createapp.vault.getAbstractFileByPathapp.metadataCache.getFileCacheTFile,TFolder,Notice,normalizePath
Therefore, the engine cannot be called directly from Node/Python CLI without either:
- running inside Obsidian, or
- writing an adapter/shim that emulates enough of Obsidian's
App/Vault/metadataCache/TFilebehavior.
Portable logic worth mirroring
The useful logic is not locked to Obsidian conceptually. It can be mirrored or adapted:
Source analysis
src/wiki/source-analyzer.ts:
- reads source content
- checks blank/duplicate content
- prompts LLM for structured JSON
- extracts entities and concepts
- includes aliases
- collects verbatim quotes / provenance
- identifies contradictions, related pages, key points
- normalizes irregular LLM JSON
The core ingestion prompt is in src/wiki/prompts/ingestion.ts.
Notable extraction output shape:
{
"source_title": "...",
"summary": "...",
"entities": [
{
"name": "...",
"type": "person|organization|project|product|event|place|other",
"aliases": [],
"summary": "...",
"mentions_in_source": [],
"mentions_with_provenance": [],
"related_entities": [],
"related_concepts": []
}
],
"concepts": [],
"contradictions": [],
"related_pages": [],
"key_points": []
}
Page generation
src/wiki/page-factory.ts:
- slugifies entities/concepts
- checks existing pages
- resolves duplicates via slug/alias/LLM semantic match
- generates entity/concept pages
- merges into existing pages
- protects reviewed pages
- corrects polluted wikilinks
- injects provenance/mentions
Prompts
src/wiki/prompts/generation.ts includes high-value generation prompts for:
- entity pages
- concept pages
- source summary pages
- preserving reviewed pages
Query / graph retrieval
The README claims zero-embedding graph retrieval via Personalized PageRank over the wiki link graph. Source references in WikiEngine show:
buildGraphFromContent(...)
getOrBuildGraph(...)
and query pipeline files exist under:
src/wiki/query-engine/pipeline/
This is useful design inspiration, but not required for the first Redmine decomposition tool because gbrain already supplies search/indexing and the vault graph itself supplies wikilinks.
Recommendation
Do not depend on Obsidian running for Redmine ticket decomposition.
Instead:
- Mirror the plugin's source-analysis and page-generation logic in our own CLI-oriented tool.
- Reuse its prompt structure and output schema ideas.
- Write directly to the filesystem vault, but outside the plugin-owned
wiki/tree by default. - Use gbrain for indexing/search after files are written.
- Keep Redmine contact updates as a separate explicit apply phase.
- Preserve compatibility with Obsidian/KarpathyWiki by using normal Markdown, wikilinks, and frontmatter fields the plugin understands where practical.
Compatibility rule: the plugin owns wiki/entities, wiki/concepts, wiki/sources, wiki/index.md, and wiki/log.md. The headless decomposer should default to LDR-owned paths like Timeline/, entities/organizations/, and Processes/Redmine/, linking to wiki/... pages when useful but not editing them.
This gives us the same broad effect without requiring a GUI Obsidian session and without competing with the existing plugin state.
Possible implementation paths
Path A — Thin TypeScript headless port
Build a Node/TypeScript CLI in /home/jason/projects/redmine or a sibling project that imports/copied logic from the plugin and supplies a fake Obsidian adapter.
Pros:
- Closer to upstream code.
- Easier to vendor future prompt improvements.
- Can reuse zod/AI SDK provider setup.
Cons:
- Need to emulate enough of Obsidian
Vault,TFile, metadata cache, and settings. - Adds Node/TypeScript toolchain complexity to Redmine workflow.
Path B — Python mirror using existing Redmine tooling
Implement the same conceptual pipeline in Python alongside redmine_tickets.py:
- source normalization
- LLM JSON extraction
- entity/concept dedup via filesystem search + slug rules
- page generation templates
- timeline/entity writers
- gbrain sync hooks
Pros:
- Fits current Redmine tooling.
- Easier to integrate with ticket cache and contact updates.
- No Obsidian runtime needed.
Cons:
- We do not directly reuse TypeScript implementation.
- Need to maintain our own prompt/schema logic.
Path C — Obsidian-driven automation
Use Obsidian command automation / Advanced URI to trigger plugin commands.
Pros:
- Minimal code if it works.
Cons:
- Requires Obsidian running.
- Harder to make deterministic/headless/cron-safe.
- Harder to feed Redmine tickets and capture structured contact-update proposals.
Best fit for LDR Redmine decomposition
Path B is the best first implementation path.
Reasons:
- Redmine tickets are already fetched/cached in Python.
- We need Redmine contact updates and safety gates.
- We need Onyx URLs and gbrain sync hooks.
- We need headless/cron/agent execution.
- We already proved manual note generation format in the decomposition pilot.
Use the plugin as a reference for:
src/wiki/prompts/ingestion.ts— source analysis prompt and entity/concept output shapesrc/wiki/prompts/generation.ts— entity/concept/source page generation promptssrc/wiki/prompts/merge.ts— classify-then-route merge strategy for updating existing pagessrc/wiki/prompts/lint.ts— wiki quality checks and contradiction review ideassrc/wiki/source-analyzer.ts— JSON normalization, duplicate/blank source handling, provenance mentionssrc/wiki/page-factory.ts— slug/alias resolution, cross-type collision handling, merge-vs-append routingsrc/wiki/query-engine/— graph retrieval ideas for later, not first pass
Do not try to invoke it via Obsidian as the primary production pipeline; use the source as a design reference for a headless Python mirror.
Immediate next action
Update ticket_decomposition_knowledge_ingestion_plan.md so Phase 2 says:
- use KarpathyWiki plugin prompt/schema as reference
- implement a Python headless mirror first
- consider TypeScript adapter later only if Python mirror becomes too costly
Then implement a decompose --dry-run engine using the pilot tickets as fixtures.