pkm-mcp-server 1.0.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 +52 -0
- package/LICENSE +21 -0
- package/README.md +246 -0
- package/activity.js +147 -0
- package/embeddings.js +672 -0
- package/graph.js +340 -0
- package/handlers.js +871 -0
- package/helpers.js +855 -0
- package/index.js +498 -0
- package/package.json +63 -0
- package/sample-project/CLAUDE.md +193 -0
- package/templates/adr.md +52 -0
- package/templates/daily-note.md +19 -0
- package/templates/devlog.md +35 -0
- package/templates/fleeting-note.md +11 -0
- package/templates/literature-note.md +25 -0
- package/templates/meeting-notes.md +28 -0
- package/templates/moc.md +22 -0
- package/templates/permanent-note.md +26 -0
- package/templates/project-index.md +38 -0
- package/templates/research-note.md +35 -0
- package/templates/task.md +22 -0
- package/templates/troubleshooting-log.md +32 -0
- package/utils.js +31 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# Project: [Your Project Name]
|
|
2
|
+
|
|
3
|
+
> Place this file in your code repository root. It configures Claude Code to integrate with your Obsidian PKM system via the `obsidian-pkm` MCP server.
|
|
4
|
+
|
|
5
|
+
## PKM Integration
|
|
6
|
+
|
|
7
|
+
- **Vault project path**: `01-Projects/[YourProjectName]/`
|
|
8
|
+
- **MCP Server**: `obsidian-pkm` (configured in `~/.claude/settings.json`)
|
|
9
|
+
|
|
10
|
+
### Session Start: Load Context
|
|
11
|
+
|
|
12
|
+
Before starting work, load project context:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
vault_read("01-Projects/[YourProjectName]/_index.md")
|
|
16
|
+
vault_recent({ folder: "01-Projects/[YourProjectName]", limit: 5 })
|
|
17
|
+
vault_activity({ path: "01-Projects/[YourProjectName]", limit: 10 })
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Use `vault_activity` to recall what was done in previous sessions (files read, written, searched). This gives continuity across conversations.
|
|
21
|
+
|
|
22
|
+
### Before Creating or Researching
|
|
23
|
+
|
|
24
|
+
Always search before creating new notes to avoid duplication:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
vault_search({ query: "your topic" })
|
|
28
|
+
vault_semantic_search({ query: "your concept", folder: "01-Projects/[YourProjectName]" })
|
|
29
|
+
vault_query({ tags: ["relevant-tag"], folder: "01-Projects/[YourProjectName]" })
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`vault_semantic_search` finds conceptually related notes even when different words are used (e.g., searching "managing overwhelm" finds notes about "cognitive load"). Use it for discovery alongside exact-text `vault_search`.
|
|
33
|
+
|
|
34
|
+
## Documentation Rules
|
|
35
|
+
|
|
36
|
+
### Metadata
|
|
37
|
+
|
|
38
|
+
All notes MUST include YAML frontmatter. See `06-System/metadata-schema.md` for the full schema.
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
---
|
|
42
|
+
type: <type> # Required
|
|
43
|
+
created: YYYY-MM-DD # Required (auto-filled by vault_write)
|
|
44
|
+
tags: [...] # Required: at least one tag
|
|
45
|
+
status: <status> # If applicable
|
|
46
|
+
---
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Never create notes without frontmatter** — use `vault_write` with a template.
|
|
50
|
+
|
|
51
|
+
### Architecture Decisions
|
|
52
|
+
|
|
53
|
+
When a significant technical decision is made:
|
|
54
|
+
|
|
55
|
+
1. Create an ADR using `vault_write` with the `adr` template:
|
|
56
|
+
```
|
|
57
|
+
vault_write({
|
|
58
|
+
template: "adr",
|
|
59
|
+
path: "01-Projects/[YourProjectName]/development/decisions/ADR-{NNN}-{kebab-title}.md",
|
|
60
|
+
frontmatter: { tags: ["decision", "relevant-topic"], deciders: "Team" }
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
2. Fill in Context, Decision, Options Considered, and Consequences sections
|
|
64
|
+
3. Update `_index.md` to link the new ADR:
|
|
65
|
+
```
|
|
66
|
+
vault_append({
|
|
67
|
+
path: "01-Projects/[YourProjectName]/_index.md",
|
|
68
|
+
heading: "## Architecture Decisions",
|
|
69
|
+
position: "end_of_section",
|
|
70
|
+
content: "- [[ADR-{NNN}-{kebab-title}]]"
|
|
71
|
+
})
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Development Progress
|
|
75
|
+
|
|
76
|
+
After implementing features or making significant progress, append to the devlog:
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
vault_append({
|
|
80
|
+
path: "01-Projects/[YourProjectName]/development/devlog.md",
|
|
81
|
+
heading: "## Recent Activity",
|
|
82
|
+
position: "after_heading",
|
|
83
|
+
content: "## YYYY-MM-DD\n\n### Session Summary\n- What was done\n\n### Key Decisions\n- Decisions made (link to ADRs)\n\n### Next Steps\n- What's next\n\n---\n"
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Use `position: "after_heading"` to prepend new entries (most recent first) or `position: "end_of_section"` to append.
|
|
88
|
+
|
|
89
|
+
### Reusable Knowledge
|
|
90
|
+
|
|
91
|
+
When solving a problem that has general applicability:
|
|
92
|
+
|
|
93
|
+
1. Search for existing related notes first:
|
|
94
|
+
```
|
|
95
|
+
vault_semantic_search({ query: "the concept you learned" })
|
|
96
|
+
```
|
|
97
|
+
2. Create a permanent note if nothing exists:
|
|
98
|
+
```
|
|
99
|
+
vault_write({
|
|
100
|
+
template: "permanent-note",
|
|
101
|
+
path: "03-Resources/Development/{topic-name}.md",
|
|
102
|
+
frontmatter: { tags: ["relevant-tags"] }
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
3. Find and add bidirectional links:
|
|
106
|
+
```
|
|
107
|
+
vault_suggest_links({ path: "03-Resources/Development/{topic-name}.md" })
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`vault_suggest_links` uses semantic similarity to find notes worth linking to, and excludes notes already linked via `[[wikilinks]]`.
|
|
111
|
+
|
|
112
|
+
### Troubleshooting
|
|
113
|
+
|
|
114
|
+
When debugging a complex issue, create a troubleshooting log:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
vault_write({
|
|
118
|
+
template: "troubleshooting-log",
|
|
119
|
+
path: "01-Projects/[YourProjectName]/development/debug/{issue-name}.md",
|
|
120
|
+
frontmatter: { tags: ["debugging", "relevant-topic"] }
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Fill in: Problem, Symptoms, Root Cause, Solution, Prevention.
|
|
125
|
+
|
|
126
|
+
## Context Queries
|
|
127
|
+
|
|
128
|
+
### Find Related Notes
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
vault_search({ query: "keywords", folder: "01-Projects/[YourProjectName]" })
|
|
132
|
+
vault_semantic_search({ query: "concept description" })
|
|
133
|
+
vault_query({ type: "adr", folder: "01-Projects/[YourProjectName]" })
|
|
134
|
+
vault_tags({ folder: "01-Projects/[YourProjectName]" })
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Explore Connections
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
vault_neighborhood({ path: "01-Projects/[YourProjectName]/_index.md", depth: 2 })
|
|
141
|
+
vault_links({ path: "path/to/note.md", direction: "both" })
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`vault_neighborhood` traverses wikilinks via BFS and returns notes grouped by hop distance — useful for understanding how notes relate to each other.
|
|
145
|
+
|
|
146
|
+
### Cross-Session Memory
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
vault_activity({ path: "01-Projects/[YourProjectName]" })
|
|
150
|
+
vault_activity({ tool: "vault_write", since: "2026-01-01" })
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Check what was done in previous sessions to maintain continuity.
|
|
154
|
+
|
|
155
|
+
## Available Templates
|
|
156
|
+
|
|
157
|
+
Use these with `vault_write({ template: "name", path: "...", frontmatter: { tags: [...] } })`:
|
|
158
|
+
|
|
159
|
+
| Template | Use For |
|
|
160
|
+
|---|---|
|
|
161
|
+
| `project-index` | Project overview (`_index.md`) |
|
|
162
|
+
| `adr` | Architecture Decision Records |
|
|
163
|
+
| `devlog` | Development logs |
|
|
164
|
+
| `permanent-note` | Atomic evergreen knowledge notes |
|
|
165
|
+
| `research-note` | Research findings and analysis |
|
|
166
|
+
| `troubleshooting-log` | Bug investigations and fixes |
|
|
167
|
+
| `fleeting-note` | Quick captures and temporary thoughts |
|
|
168
|
+
| `literature-note` | Notes on articles, books, talks |
|
|
169
|
+
| `meeting-notes` | Meeting records |
|
|
170
|
+
| `moc` | Maps of Content (index/hub notes) |
|
|
171
|
+
| `daily-note` | Daily notes |
|
|
172
|
+
| `task` | Structured task notes with status, priority, due date |
|
|
173
|
+
|
|
174
|
+
## Session End
|
|
175
|
+
|
|
176
|
+
Before ending a development session:
|
|
177
|
+
|
|
178
|
+
1. Append a summary to the devlog
|
|
179
|
+
2. Update project status in `_index.md` if changed
|
|
180
|
+
3. Note any created files for future reference
|
|
181
|
+
|
|
182
|
+
## Project-Specific Notes
|
|
183
|
+
|
|
184
|
+
<!-- Customize the sections below for your project -->
|
|
185
|
+
|
|
186
|
+
### Tech Stack
|
|
187
|
+
-
|
|
188
|
+
|
|
189
|
+
### Key Patterns
|
|
190
|
+
-
|
|
191
|
+
|
|
192
|
+
### Important Constraints
|
|
193
|
+
-
|
package/templates/adr.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: adr
|
|
3
|
+
status: proposed
|
|
4
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
5
|
+
deciders:
|
|
6
|
+
tags:
|
|
7
|
+
- decision
|
|
8
|
+
- architecture
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# ADR-XXX: <% tp.file.title %>
|
|
12
|
+
|
|
13
|
+
## Context
|
|
14
|
+
<!-- What is the issue that we're seeing that motivates this decision? -->
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
<!-- What is the change that we're proposing and/or doing? -->
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Options Considered
|
|
22
|
+
|
|
23
|
+
### Option 1:
|
|
24
|
+
**Pros:**
|
|
25
|
+
-
|
|
26
|
+
|
|
27
|
+
**Cons:**
|
|
28
|
+
-
|
|
29
|
+
|
|
30
|
+
### Option 2:
|
|
31
|
+
**Pros:**
|
|
32
|
+
-
|
|
33
|
+
|
|
34
|
+
**Cons:**
|
|
35
|
+
-
|
|
36
|
+
|
|
37
|
+
## Consequences
|
|
38
|
+
<!-- What becomes easier or more difficult because of this change? -->
|
|
39
|
+
|
|
40
|
+
### Positive
|
|
41
|
+
-
|
|
42
|
+
|
|
43
|
+
### Negative
|
|
44
|
+
-
|
|
45
|
+
|
|
46
|
+
### Risks
|
|
47
|
+
-
|
|
48
|
+
|
|
49
|
+
## Related
|
|
50
|
+
<!-- Links to related notes, prior decisions, or research -->
|
|
51
|
+
-
|
|
52
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: daily
|
|
3
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
4
|
+
tags:
|
|
5
|
+
- daily
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# <% tp.file.title %>
|
|
9
|
+
|
|
10
|
+
## Tasks
|
|
11
|
+
- [ ]
|
|
12
|
+
|
|
13
|
+
## Notes
|
|
14
|
+
<!-- Thoughts, observations, quick captures -->
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Log
|
|
18
|
+
<!-- What happened today -->
|
|
19
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: devlog
|
|
3
|
+
project:
|
|
4
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
5
|
+
tags:
|
|
6
|
+
- devlog
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Development Log
|
|
10
|
+
|
|
11
|
+
This log tracks development progress, decisions made, and lessons learned.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## <% tp.date.now("YYYY-MM-DD") %>
|
|
16
|
+
|
|
17
|
+
### Session Summary
|
|
18
|
+
<!-- What was accomplished in this session? -->
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### Key Decisions
|
|
22
|
+
<!-- Any decisions made (link to ADRs if created) -->
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Blockers / Issues
|
|
26
|
+
<!-- What problems were encountered? -->
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Next Steps
|
|
30
|
+
<!-- What should be done in the next session? -->
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
<!-- New entries are added above this line -->
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: literature
|
|
3
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
4
|
+
source:
|
|
5
|
+
tags:
|
|
6
|
+
- literature
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# <% tp.file.title %>
|
|
10
|
+
|
|
11
|
+
## Summary
|
|
12
|
+
<!-- Key points from the source material -->
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## Notes
|
|
16
|
+
<!-- Your own thoughts and reactions -->
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Quotes
|
|
20
|
+
<!-- Notable passages (with page/section references) -->
|
|
21
|
+
-
|
|
22
|
+
|
|
23
|
+
## Related
|
|
24
|
+
<!-- Links to related notes or concepts -->
|
|
25
|
+
-
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: meeting
|
|
3
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
4
|
+
attendees:
|
|
5
|
+
tags:
|
|
6
|
+
- meeting
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Meeting: <% tp.file.title %>
|
|
10
|
+
|
|
11
|
+
## Agenda
|
|
12
|
+
-
|
|
13
|
+
|
|
14
|
+
## Discussion
|
|
15
|
+
<!-- Key points discussed -->
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Decisions
|
|
19
|
+
<!-- What was decided -->
|
|
20
|
+
-
|
|
21
|
+
|
|
22
|
+
## Action Items
|
|
23
|
+
<!-- Who does what by when -->
|
|
24
|
+
- [ ]
|
|
25
|
+
|
|
26
|
+
## Related
|
|
27
|
+
<!-- Links to relevant notes, projects, or prior meetings -->
|
|
28
|
+
-
|
package/templates/moc.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: moc
|
|
3
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
4
|
+
tags:
|
|
5
|
+
- moc
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# <% tp.file.title %>
|
|
9
|
+
|
|
10
|
+
<!-- Map of Content: an index/hub that organizes notes around a topic. -->
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
<!-- Brief description of what this MOC covers -->
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Core Notes
|
|
17
|
+
<!-- Primary notes on this topic -->
|
|
18
|
+
-
|
|
19
|
+
|
|
20
|
+
## Related Topics
|
|
21
|
+
<!-- Links to other MOCs or areas -->
|
|
22
|
+
-
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: permanent
|
|
3
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
4
|
+
tags: [permanent]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# <% tp.file.title %>
|
|
8
|
+
|
|
9
|
+
<!--
|
|
10
|
+
This is a permanent/evergreen note. Guidelines:
|
|
11
|
+
- One idea per note (atomic)
|
|
12
|
+
- Written in your own words
|
|
13
|
+
- Should make sense on its own
|
|
14
|
+
- Link liberally to related notes
|
|
15
|
+
-->
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Related
|
|
20
|
+
<!-- Links to related concepts, source materials, projects where this was learned -->
|
|
21
|
+
-
|
|
22
|
+
|
|
23
|
+
## References
|
|
24
|
+
<!-- If this came from a specific source -->
|
|
25
|
+
-
|
|
26
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: project-index
|
|
3
|
+
status: active
|
|
4
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
5
|
+
updated: <% tp.date.now("YYYY-MM-DD") %>
|
|
6
|
+
tags:
|
|
7
|
+
- project
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# <% tp.file.title %>
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
<!-- Brief description of what this project is and why it exists -->
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Objectives
|
|
17
|
+
<!-- What does success look like? What are we trying to achieve? -->
|
|
18
|
+
- [ ]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Key Links
|
|
22
|
+
<!-- Important documents, external resources, related projects -->
|
|
23
|
+
- **Repository**:
|
|
24
|
+
- **Requirements**: [[<% tp.file.title %>/planning/requirements|Requirements]]
|
|
25
|
+
- **Roadmap**: [[<% tp.file.title %>/planning/roadmap|Roadmap]]
|
|
26
|
+
- **Dev Log**: [[<% tp.file.title %>/development/devlog|Development Log]]
|
|
27
|
+
|
|
28
|
+
## Architecture Decisions
|
|
29
|
+
<!-- Links to ADRs as they're created -->
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Current Status
|
|
33
|
+
<!-- What's the current state? What's being worked on? -->
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Recent Activity
|
|
37
|
+
<!-- Auto-updated by Claude Code during sessions -->
|
|
38
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: research
|
|
3
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
4
|
+
status: active
|
|
5
|
+
tags:
|
|
6
|
+
- research
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# <% tp.file.title %>
|
|
10
|
+
|
|
11
|
+
## What It Is
|
|
12
|
+
<!-- Brief description of the topic or technology -->
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## Why Consider It
|
|
16
|
+
<!-- What problem does it solve? Why is it relevant? -->
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Pros
|
|
20
|
+
-
|
|
21
|
+
|
|
22
|
+
## Cons
|
|
23
|
+
-
|
|
24
|
+
|
|
25
|
+
## Key Findings
|
|
26
|
+
<!-- Important details, benchmarks, or observations -->
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Links
|
|
30
|
+
<!-- External references: docs, articles, repos -->
|
|
31
|
+
-
|
|
32
|
+
|
|
33
|
+
## Verdict
|
|
34
|
+
<!-- Summary assessment and recommendation -->
|
|
35
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: task
|
|
3
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
4
|
+
status: pending
|
|
5
|
+
priority: normal
|
|
6
|
+
due: null
|
|
7
|
+
project: null
|
|
8
|
+
source: null
|
|
9
|
+
tags:
|
|
10
|
+
- task
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# <% tp.file.title %>
|
|
14
|
+
|
|
15
|
+
## Description
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Context
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Acceptance Criteria
|
|
22
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: bug
|
|
3
|
+
created: <% tp.date.now("YYYY-MM-DD") %>
|
|
4
|
+
status: investigating
|
|
5
|
+
tags:
|
|
6
|
+
- debugging
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# <% tp.file.title %>
|
|
10
|
+
|
|
11
|
+
## Problem
|
|
12
|
+
<!-- What went wrong? -->
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## Symptoms
|
|
16
|
+
<!-- Observable behavior, error messages, logs -->
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Root Cause
|
|
20
|
+
<!-- What actually caused the issue -->
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
## Solution
|
|
24
|
+
<!-- How it was fixed -->
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## Prevention
|
|
28
|
+
<!-- How to avoid this in the future -->
|
|
29
|
+
|
|
30
|
+
## Related
|
|
31
|
+
<!-- Links to related notes, ADRs, or issues -->
|
|
32
|
+
-
|
package/utils.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
|
|
5
|
+
/** Extract YAML frontmatter from markdown content. */
|
|
6
|
+
export function extractFrontmatter(content) {
|
|
7
|
+
if (!content.startsWith("---")) return null;
|
|
8
|
+
const endIndex = content.indexOf("\n---", 3);
|
|
9
|
+
if (endIndex === -1) return null;
|
|
10
|
+
const yamlContent = content.slice(4, endIndex);
|
|
11
|
+
try {
|
|
12
|
+
return yaml.load(yamlContent, { schema: yaml.JSON_SCHEMA });
|
|
13
|
+
} catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Recursively get all markdown files in a directory (skips dotfiles/dirs). */
|
|
19
|
+
export async function getAllMarkdownFiles(dir, baseDir = dir) {
|
|
20
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
21
|
+
const files = [];
|
|
22
|
+
for (const entry of entries) {
|
|
23
|
+
const fullPath = path.join(dir, entry.name);
|
|
24
|
+
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
25
|
+
files.push(...await getAllMarkdownFiles(fullPath, baseDir));
|
|
26
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
27
|
+
files.push(path.relative(baseDir, fullPath));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return files;
|
|
31
|
+
}
|