291 lines
8.6 KiB
Markdown
291 lines
8.6 KiB
Markdown
# 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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```json
|
|
{
|
|
"name": "karpathywiki",
|
|
"version": "1.24.0",
|
|
"main": "main.js"
|
|
}
|
|
```
|
|
|
|
Fresh clone verification confirmed `package.json` has no `bin` entry:
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```ts
|
|
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.read`
|
|
- `app.vault.getMarkdownFiles`
|
|
- `app.vault.process`
|
|
- `app.vault.create`
|
|
- `app.vault.getAbstractFileByPath`
|
|
- `app.metadataCache.getFileCache`
|
|
- `TFile`, `TFolder`, `Notice`, `normalizePath`
|
|
|
|
Therefore, the engine cannot be called directly from Node/Python CLI without either:
|
|
|
|
1. running inside Obsidian, or
|
|
2. writing an adapter/shim that emulates enough of Obsidian's `App/Vault/metadataCache/TFile` behavior.
|
|
|
|
## 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:
|
|
|
|
```json
|
|
{
|
|
"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:
|
|
|
|
```ts
|
|
buildGraphFromContent(...)
|
|
getOrBuildGraph(...)
|
|
```
|
|
|
|
and query pipeline files exist under:
|
|
|
|
```text
|
|
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:
|
|
|
|
1. Mirror the plugin's source-analysis and page-generation logic in our own CLI-oriented tool.
|
|
2. Reuse its prompt structure and output schema ideas.
|
|
3. Write directly to the filesystem vault, but outside the plugin-owned `wiki/` tree by default.
|
|
4. Use gbrain for indexing/search after files are written.
|
|
5. Keep Redmine contact updates as a separate explicit apply phase.
|
|
6. 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 shape
|
|
- `src/wiki/prompts/generation.ts` — entity/concept/source page generation prompts
|
|
- `src/wiki/prompts/merge.ts` — classify-then-route merge strategy for updating existing pages
|
|
- `src/wiki/prompts/lint.ts` — wiki quality checks and contradiction review ideas
|
|
- `src/wiki/source-analyzer.ts` — JSON normalization, duplicate/blank source handling, provenance mentions
|
|
- `src/wiki/page-factory.ts` — slug/alias resolution, cross-type collision handling, merge-vs-append routing
|
|
- `src/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.
|