@jayjiang/byoao 1.1.2 → 2.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/dist/__tests__/plugin-config.test.js +7 -10
- package/dist/__tests__/plugin-config.test.js.map +1 -1
- package/dist/assets/obsidian-skills/byoao-conventions.md +30 -54
- package/dist/assets/obsidian-skills/vault-thinking.md +6 -5
- package/dist/assets/presets/common/AGENTS.md.hbs +29 -46
- package/dist/assets/presets/common/SCHEMA.md.hbs +57 -0
- package/dist/assets/presets/common/Start Here.md.hbs +29 -40
- package/dist/assets/presets/minimal/preset.json +3 -3
- package/dist/assets/presets/pm-tpm/preset.json +2 -2
- package/dist/assets/skills/ask.md +28 -27
- package/dist/assets/skills/challenge.md +79 -121
- package/dist/assets/skills/connect.md +75 -163
- package/dist/assets/skills/cook.md +167 -0
- package/dist/assets/skills/diagnose.md +102 -43
- package/dist/assets/skills/drift.md +64 -165
- package/dist/assets/skills/health.md +63 -0
- package/dist/assets/skills/ideas.md +11 -10
- package/dist/assets/skills/organize.md +56 -155
- package/dist/assets/skills/prep.md +63 -0
- package/dist/assets/skills/trace.md +75 -90
- package/dist/assets/skills/wiki.md +77 -178
- package/dist/cli/cli-program.js +17 -14
- package/dist/cli/cli-program.js.map +1 -1
- package/dist/cli/installer.js +10 -4
- package/dist/cli/installer.js.map +1 -1
- package/dist/hooks/idle-suggestions.js +4 -4
- package/dist/hooks/idle-suggestions.js.map +1 -1
- package/dist/hooks/system-transform.js +35 -1
- package/dist/hooks/system-transform.js.map +1 -1
- package/dist/index.js +395 -623
- package/dist/index.js.map +1 -1
- package/dist/plugin-config.js +6 -32
- package/dist/plugin-config.js.map +1 -1
- package/dist/tools/init-vault.js +8 -38
- package/dist/tools/init-vault.js.map +1 -1
- package/dist/tools/vault-doctor.js +1 -1
- package/dist/tools/vault-doctor.js.map +1 -1
- package/dist/tools/vault-status.js +1 -1
- package/dist/tools/vault-status.js.map +1 -1
- package/dist/vault/__tests__/create.test.js +47 -115
- package/dist/vault/__tests__/create.test.js.map +1 -1
- package/dist/vault/__tests__/doctor.test.js +14 -2
- package/dist/vault/__tests__/doctor.test.js.map +1 -1
- package/dist/vault/__tests__/manifest.test.js +2 -2
- package/dist/vault/__tests__/manifest.test.js.map +1 -1
- package/dist/vault/__tests__/status.test.js +12 -0
- package/dist/vault/__tests__/status.test.js.map +1 -1
- package/dist/vault/__tests__/upgrade.test.js +3 -3
- package/dist/vault/__tests__/upgrade.test.js.map +1 -1
- package/dist/vault/create.js +75 -188
- package/dist/vault/create.js.map +1 -1
- package/dist/vault/doctor.js +49 -0
- package/dist/vault/doctor.js.map +1 -1
- package/dist/vault/manifest.js +1 -1
- package/dist/vault/preset.js +10 -4
- package/dist/vault/preset.js.map +1 -1
- package/dist/vault/self-update.js +1 -1
- package/dist/vault/status.js +24 -0
- package/dist/vault/status.js.map +1 -1
- package/dist/vault/upgrade.js +120 -16
- package/dist/vault/upgrade.js.map +1 -1
- package/package.json +1 -1
- package/src/assets/obsidian-skills/byoao-conventions.md +9 -6
- package/src/assets/obsidian-skills/vault-thinking.md +6 -5
- package/src/assets/presets/common/AGENTS.md.hbs +23 -19
- package/src/assets/presets/common/SCHEMA.md.hbs +57 -0
- package/src/assets/presets/common/Start Here.md.hbs +29 -40
- package/src/assets/presets/minimal/preset.json +3 -3
- package/src/assets/presets/pm-tpm/preset.json +2 -2
- package/src/skills/ask.md +28 -27
- package/src/skills/challenge.md +79 -121
- package/src/skills/connect.md +75 -163
- package/src/skills/cook.md +167 -0
- package/src/skills/diagnose.md +102 -43
- package/src/skills/drift.md +64 -165
- package/src/skills/health.md +63 -0
- package/src/skills/ideas.md +11 -10
- package/src/skills/organize.md +56 -155
- package/src/skills/prep.md +63 -0
- package/src/skills/trace.md +75 -90
- package/src/skills/wiki.md +77 -178
- package/dist/assets/presets/common/Glossary.md.hbs +0 -16
- package/dist/assets/presets/common/obsidian/daily-notes.json +0 -5
- package/dist/assets/presets/common/obsidian/templates.json +0 -3
- package/dist/assets/presets/common/templates/Daily Note.md +0 -19
- package/dist/assets/presets/common/templates/Decision Record.md +0 -32
- package/dist/assets/presets/common/templates/Investigation.md +0 -34
- package/dist/assets/presets/common/templates/Meeting Notes.md +0 -25
- package/dist/assets/skills/emerge.md +0 -168
- package/dist/assets/skills/weave.md +0 -287
- package/dist/tools/add-glossary-term.js +0 -21
- package/dist/tools/add-glossary-term.js.map +0 -1
- package/dist/tools/add-person.js +0 -21
- package/dist/tools/add-person.js.map +0 -1
- package/dist/tools/add-project.js +0 -24
- package/dist/tools/add-project.js.map +0 -1
- package/dist/tools/graph-health.js +0 -25
- package/dist/tools/graph-health.js.map +0 -1
- package/dist/tools/note-read.js +0 -19
- package/dist/tools/note-read.js.map +0 -1
- package/dist/tools/search-vault.js +0 -22
- package/dist/tools/search-vault.js.map +0 -1
- package/dist/vault/__tests__/glossary.test.js +0 -68
- package/dist/vault/__tests__/glossary.test.js.map +0 -1
- package/dist/vault/__tests__/graph-health.test.js +0 -102
- package/dist/vault/__tests__/graph-health.test.js.map +0 -1
- package/dist/vault/__tests__/member.test.js +0 -85
- package/dist/vault/__tests__/member.test.js.map +0 -1
- package/dist/vault/__tests__/note-read.test.js +0 -71
- package/dist/vault/__tests__/note-read.test.js.map +0 -1
- package/dist/vault/__tests__/obsidian-cli.test.js +0 -108
- package/dist/vault/__tests__/obsidian-cli.test.js.map +0 -1
- package/dist/vault/__tests__/search-vault.test.js +0 -93
- package/dist/vault/__tests__/search-vault.test.js.map +0 -1
- package/dist/vault/glossary.js +0 -27
- package/dist/vault/glossary.js.map +0 -1
- package/dist/vault/graph-health.js +0 -83
- package/dist/vault/graph-health.js.map +0 -1
- package/dist/vault/member.js +0 -67
- package/dist/vault/member.js.map +0 -1
- package/dist/vault/note-read.js +0 -70
- package/dist/vault/note-read.js.map +0 -1
- package/dist/vault/project.js +0 -68
- package/dist/vault/project.js.map +0 -1
- package/dist/vault/retrieval-types.js +0 -5
- package/dist/vault/retrieval-types.js.map +0 -1
- package/dist/vault/search-vault.js +0 -87
- package/dist/vault/search-vault.js.map +0 -1
- package/src/assets/presets/common/obsidian/daily-notes.json +0 -5
- package/src/assets/presets/common/obsidian/templates.json +0 -3
- package/src/assets/presets/common/templates/Daily Note.md +0 -19
- package/src/assets/presets/common/templates/Decision Record.md +0 -32
- package/src/assets/presets/common/templates/Investigation.md +0 -34
- package/src/assets/presets/common/templates/Meeting Notes.md +0 -25
- package/src/skills/emerge.md +0 -168
- package/src/skills/weave.md +0 -287
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: weave
|
|
3
|
-
description: Scan vault notes, enrich with frontmatter + wikilinks, suggest permanent concept notes, and create a connected knowledge graph. Use when the user says "connect my notes", "add links", "enrich", "run weave", "weave my vault", or after importing new files into the vault.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# /weave — Connect Your Notes
|
|
7
|
-
|
|
8
|
-
You are a knowledge graph builder. Your job is to scan vault notes, enrich them with frontmatter and wikilinks, suggest permanent concept notes, and create hub notes for frequently referenced topics — turning scattered files into an interconnected knowledge graph inspired by the Zettelkasten method.
|
|
9
|
-
|
|
10
|
-
## Prerequisites Check
|
|
11
|
-
|
|
12
|
-
**Before doing anything else**, verify Obsidian CLI is available:
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
obsidian --version
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
If this fails, STOP and display:
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
Obsidian CLI is not available. Please ensure:
|
|
22
|
-
1. Obsidian is running
|
|
23
|
-
2. This vault is open in Obsidian
|
|
24
|
-
3. CLI is enabled: Settings → General → Advanced → Command-line interface
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Do NOT proceed with degraded results — graph queries are essential.
|
|
28
|
-
|
|
29
|
-
## Parameters
|
|
30
|
-
|
|
31
|
-
- **file** (optional): Path to a single file to weave. If omitted, scan the entire vault.
|
|
32
|
-
- **folder** (optional): Path to a folder to scan. If omitted, scan the entire vault.
|
|
33
|
-
- **dry-run** (optional): If set, show proposed changes without applying them.
|
|
34
|
-
|
|
35
|
-
## File Exclusion Rules
|
|
36
|
-
|
|
37
|
-
When scanning files, skip:
|
|
38
|
-
|
|
39
|
-
| Pattern | Reason |
|
|
40
|
-
|---------|--------|
|
|
41
|
-
| `.obsidian/` | Obsidian internal config |
|
|
42
|
-
| `.git/` | Git internals |
|
|
43
|
-
| `.byoao/` | BYOAO internal data |
|
|
44
|
-
| `node_modules/` | Dependencies |
|
|
45
|
-
| `.env`, `credentials.*`, `*.key` | Sensitive files |
|
|
46
|
-
| Binary files (images, PDFs, etc.) | Cannot add frontmatter/wikilinks |
|
|
47
|
-
| `AGENTS.md` | BYOAO-managed file |
|
|
48
|
-
| `*.base` | Obsidian Base files — not user content notes |
|
|
49
|
-
|
|
50
|
-
Report skipped non-markdown files at the end: "Skipped N non-markdown files".
|
|
51
|
-
|
|
52
|
-
## Process
|
|
53
|
-
|
|
54
|
-
Execute these steps in order. Be explicit about each tool call — different LLM providers must execute this consistently.
|
|
55
|
-
|
|
56
|
-
### Step 1: Build Vault Map
|
|
57
|
-
|
|
58
|
-
```bash
|
|
59
|
-
obsidian list
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
This gives you the full list of notes. Also run:
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
obsidian properties sort=count counts
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
This reveals the vault's structure — which properties are used, how many notes have frontmatter.
|
|
69
|
-
|
|
70
|
-
### Step 2: Scan Target Files
|
|
71
|
-
|
|
72
|
-
For each markdown file in scope (respecting exclusion rules):
|
|
73
|
-
|
|
74
|
-
#### 2a. Read the file
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
obsidian read "<note name>"
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
#### 2b. Identify entities
|
|
81
|
-
|
|
82
|
-
Scan the content for concepts using semantic understanding (not a predefined list):
|
|
83
|
-
|
|
84
|
-
- **People names** — proper nouns that appear to be people
|
|
85
|
-
- **Project/product names** — capitalized multi-word phrases that recur
|
|
86
|
-
- **Domain concepts** — technical terms, acronyms, recurring themes
|
|
87
|
-
- **Tool/system names** — software, services, platforms mentioned
|
|
88
|
-
- **Dates and events** — meetings, deadlines, milestones
|
|
89
|
-
- **Methodologies/frameworks** — named approaches like "Zettelkasten", "Agile", etc.
|
|
90
|
-
|
|
91
|
-
#### 2c. Cross-reference against existing notes
|
|
92
|
-
|
|
93
|
-
For each entity found:
|
|
94
|
-
|
|
95
|
-
1. Does a vault note with this name exist? → Mark as wikilink candidate
|
|
96
|
-
2. Is it a new, unrecognized concept? → Track for permanent note suggestion (Step 4)
|
|
97
|
-
|
|
98
|
-
#### 2d. Propose frontmatter
|
|
99
|
-
|
|
100
|
-
If the file has no frontmatter, or has incomplete frontmatter, propose additions:
|
|
101
|
-
|
|
102
|
-
```yaml
|
|
103
|
-
---
|
|
104
|
-
title: "<inferred from content or filename>"
|
|
105
|
-
note_type: <fleeting | literature | permanent>
|
|
106
|
-
type: "<inferred: meeting, idea, reference, daily, project, person, etc>"
|
|
107
|
-
date: YYYY-MM-DD
|
|
108
|
-
domain: "<knowledge area: analytics, infrastructure, design, etc>"
|
|
109
|
-
references:
|
|
110
|
-
- "[[Related Note]]"
|
|
111
|
-
tags: [<relevant tags>]
|
|
112
|
-
status: <draft | active | completed | archived>
|
|
113
|
-
source: "<URL if this note originates from a cloud document>"
|
|
114
|
-
---
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**note_type classification (Zettelkasten):**
|
|
118
|
-
|
|
119
|
-
| `note_type` | When to use |
|
|
120
|
-
|-------------|-------------|
|
|
121
|
-
| `fleeting` | Raw inputs: quick notes, meeting minutes, clipped articles, thoughts not yet processed |
|
|
122
|
-
| `literature` | Processed references: summaries of papers, books, articles, or external sources |
|
|
123
|
-
| `permanent` | Atomic concepts: single-idea notes that synthesize understanding from multiple sources |
|
|
124
|
-
|
|
125
|
-
If unsure, default to `fleeting` — the user can reclassify later.
|
|
126
|
-
|
|
127
|
-
**Date resolution (mandatory — never leave empty):**
|
|
128
|
-
|
|
129
|
-
1. Extract from content — explicit dates in the text, meeting dates, file name patterns (e.g. `2026-03-27-meeting.md`)
|
|
130
|
-
2. If no date in content, get the file creation time:
|
|
131
|
-
```bash
|
|
132
|
-
stat -f '%SB' -t '%Y-%m-%d' "<file path>" # macOS
|
|
133
|
-
stat -c '%w' "<file path>" # Linux (birth time)
|
|
134
|
-
```
|
|
135
|
-
If birth time is unavailable (`-` or empty), fall back to modification time:
|
|
136
|
-
```bash
|
|
137
|
-
stat -f '%Sm' -t '%Y-%m-%d' "<file path>" # macOS
|
|
138
|
-
stat -c '%y' "<file path>" # Linux
|
|
139
|
-
```
|
|
140
|
-
3. Never leave `date` empty in the proposed frontmatter.
|
|
141
|
-
|
|
142
|
-
**Source field (optional):**
|
|
143
|
-
|
|
144
|
-
- Add `source` only when the note clearly originates from a cloud document (e.g. contains Confluence export markers, Google Docs formatting, or a URL in the content pointing to the original).
|
|
145
|
-
- If the file already has a `source` field, always preserve it.
|
|
146
|
-
|
|
147
|
-
**Frontmatter preservation rules:**
|
|
148
|
-
- **Never overwrite** existing fields
|
|
149
|
-
- **Only add** missing fields
|
|
150
|
-
- **Merge arrays** — if file has `tags: [meeting]` and you suggest `tags: [meeting, migration]`, result is `[meeting, migration]`
|
|
151
|
-
- **Warn on conflicts** — if existing value seems wrong, note it but don't change it
|
|
152
|
-
|
|
153
|
-
#### 2e. Propose wikilinks
|
|
154
|
-
|
|
155
|
-
Convert plain text mentions to `[[wikilinks]]`:
|
|
156
|
-
|
|
157
|
-
- Existing note names → `[[Note Name]]`
|
|
158
|
-
- People → `[[Person Name]]`
|
|
159
|
-
- Domain concepts → `[[Concept Name]]`
|
|
160
|
-
|
|
161
|
-
Rules:
|
|
162
|
-
- Only link the **first occurrence** of each term in a file
|
|
163
|
-
- Don't link inside code blocks, frontmatter, or existing wikilinks
|
|
164
|
-
- Don't link common English words even if they happen to match a note name
|
|
165
|
-
- Preserve the original text when the casing differs: `rate limiting` → `[[Rate Limiting|rate limiting]]`
|
|
166
|
-
|
|
167
|
-
### Step 3: Backup Before Modification
|
|
168
|
-
|
|
169
|
-
Before modifying any file, create a backup:
|
|
170
|
-
|
|
171
|
-
```bash
|
|
172
|
-
mkdir -p .byoao/backups/<timestamp>
|
|
173
|
-
cp "<file path>" ".byoao/backups/<timestamp>/<filename>"
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Use the current date-time as the timestamp (e.g., `2026-03-27T20-45`).
|
|
177
|
-
|
|
178
|
-
This is critical for existing folder adoption where files are user-created and irreplaceable.
|
|
179
|
-
|
|
180
|
-
### Step 4: Apply Changes
|
|
181
|
-
|
|
182
|
-
For each file with proposed changes:
|
|
183
|
-
1. Show the user a summary of proposed changes (frontmatter additions, wikilinks to add)
|
|
184
|
-
2. Wait for confirmation before applying
|
|
185
|
-
3. Apply changes using file edit tools
|
|
186
|
-
|
|
187
|
-
### Step 5: Suggest Permanent Notes
|
|
188
|
-
|
|
189
|
-
After scanning all files, analyze concept frequency across the vault:
|
|
190
|
-
|
|
191
|
-
**When to suggest a permanent note:**
|
|
192
|
-
- A concept appears in 3+ notes
|
|
193
|
-
- No dedicated note exists for that concept
|
|
194
|
-
- The concept has a clear, non-ambiguous definition
|
|
195
|
-
|
|
196
|
-
For each candidate, present to the user:
|
|
197
|
-
|
|
198
|
-
```markdown
|
|
199
|
-
### Permanent Note Candidate: [[Concept Name]]
|
|
200
|
-
|
|
201
|
-
**Appears in:** [[Note A]], [[Note B]], [[Note C]]
|
|
202
|
-
|
|
203
|
-
**Proposed content:**
|
|
204
|
-
|
|
205
|
-
---
|
|
206
|
-
title: "Concept Name"
|
|
207
|
-
note_type: permanent
|
|
208
|
-
type: reference
|
|
209
|
-
domain: <inferred from source notes>
|
|
210
|
-
date: <today>
|
|
211
|
-
tags: [<inferred>]
|
|
212
|
-
references:
|
|
213
|
-
- "[[Note A]]"
|
|
214
|
-
- "[[Note B]]"
|
|
215
|
-
---
|
|
216
|
-
|
|
217
|
-
# Concept Name
|
|
218
|
-
|
|
219
|
-
*Auto-generated by /weave — this concept appears across multiple notes. Review and refine.*
|
|
220
|
-
|
|
221
|
-
## Summary
|
|
222
|
-
<1-2 sentence summary synthesized from source notes>
|
|
223
|
-
|
|
224
|
-
## References
|
|
225
|
-
- [[Note A]] — <context>
|
|
226
|
-
- [[Note B]] — <context>
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
Ask the user: "Create this permanent note?" Only create if confirmed.
|
|
230
|
-
|
|
231
|
-
### Step 6: Suggest Note Splitting (Zettelkasten Atomicity)
|
|
232
|
-
|
|
233
|
-
Check for notes that contain multiple independent concepts. For each candidate:
|
|
234
|
-
|
|
235
|
-
```markdown
|
|
236
|
-
### Split Suggestion: [[Multi-Concept Note]]
|
|
237
|
-
|
|
238
|
-
This note appears to cover multiple distinct concepts:
|
|
239
|
-
1. **Concept A** — <brief description>
|
|
240
|
-
2. **Concept B** — <brief description>
|
|
241
|
-
3. **Concept C** — <brief description>
|
|
242
|
-
|
|
243
|
-
Consider splitting these into separate atomic notes for better knowledge graph connectivity.
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
**Do NOT split automatically.** Only suggest; the user decides.
|
|
247
|
-
|
|
248
|
-
### Step 7: Directory Organization (optional)
|
|
249
|
-
|
|
250
|
-
If the vault has many files in flat or disorganized directories, suggest:
|
|
251
|
-
|
|
252
|
-
"Your vault has files that could benefit from reorganization.
|
|
253
|
-
Run `/organize` to see a proposed directory structure based on
|
|
254
|
-
the frontmatter metadata we just added. It uses `obsidian move`
|
|
255
|
-
to safely relocate files while automatically updating all links."
|
|
256
|
-
|
|
257
|
-
Do NOT move files during /weave — directory reorganization is
|
|
258
|
-
a separate step handled by `/organize`.
|
|
259
|
-
|
|
260
|
-
### Step 8: Report
|
|
261
|
-
|
|
262
|
-
After all changes are applied, provide a summary:
|
|
263
|
-
|
|
264
|
-
```
|
|
265
|
-
Weave complete:
|
|
266
|
-
- Scanned: N files
|
|
267
|
-
- Enriched: N files (frontmatter + wikilinks)
|
|
268
|
-
- Wikilinks added: N
|
|
269
|
-
- Permanent notes created: N
|
|
270
|
-
- Split suggestions: N (pending user review)
|
|
271
|
-
- Orphaned files (no links): N
|
|
272
|
-
- Skipped: N non-markdown files
|
|
273
|
-
- Backups: .byoao/backups/<timestamp>/
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
## Single File Mode
|
|
277
|
-
|
|
278
|
-
When `file=` is provided, run the same process but only for that one file. Still read the vault map and check for cross-references, but skip Steps 5-6 (permanent note generation and split suggestions are batch operations).
|
|
279
|
-
|
|
280
|
-
## Important Guidelines
|
|
281
|
-
|
|
282
|
-
- **Be conservative**: When in doubt about a wikilink or frontmatter value, skip it. False positives degrade trust.
|
|
283
|
-
- **Ask, don't assume**: Always present changes for user confirmation before applying.
|
|
284
|
-
- **Preserve user content**: Never delete, rewrite, or reorganize existing text. Only add metadata and convert mentions to links.
|
|
285
|
-
- **Domain inference**: Use existing note domains to infer the domain for new notes. Consistency matters.
|
|
286
|
-
- **Idempotent**: Running /weave twice on the same file should not add duplicate wikilinks or frontmatter fields.
|
|
287
|
-
- **Zettelkasten principle**: Favor atomicity. One idea per note. Suggest splits for multi-concept notes.
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin/tool";
|
|
2
|
-
import { addGlossaryTerm } from "../vault/glossary.js";
|
|
3
|
-
export const byoao_add_glossary_term = tool({
|
|
4
|
-
description: "Add a new term to the vault's Glossary.md. Appends a row to the Core Terms table.",
|
|
5
|
-
args: {
|
|
6
|
-
vaultPath: tool.schema.string().describe("Path to the Obsidian vault"),
|
|
7
|
-
term: tool.schema.string().describe("The domain term to add"),
|
|
8
|
-
definition: tool.schema.string().describe("Brief definition (1-2 sentences)"),
|
|
9
|
-
domain: tool.schema.string().optional().describe("Knowledge domain (e.g. analytics, infrastructure)"),
|
|
10
|
-
},
|
|
11
|
-
async execute(args) {
|
|
12
|
-
const result = await addGlossaryTerm({
|
|
13
|
-
vaultPath: args.vaultPath,
|
|
14
|
-
term: args.term,
|
|
15
|
-
definition: args.definition,
|
|
16
|
-
domain: args.domain ?? "",
|
|
17
|
-
});
|
|
18
|
-
return `✓ Added glossary term: ${result.termAdded}\n File: ${result.glossaryPath}`;
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
//# sourceMappingURL=add-glossary-term.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add-glossary-term.js","sourceRoot":"","sources":["../../src/tools/add-glossary-term.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,CAAC,MAAM,uBAAuB,GAAG,IAAI,CAAC;IAC1C,WAAW,EACT,mFAAmF;IACrF,IAAI,EAAE;QACJ,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;QACtE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;QAC7D,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;QAC7E,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;KACtG;IACD,KAAK,CAAC,OAAO,CAAC,IAAI;QAChB,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC;YACnC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,EAAE;SAC1B,CAAC,CAAC;QAEH,OAAO,0BAA0B,MAAM,CAAC,SAAS,aAAa,MAAM,CAAC,YAAY,EAAE,CAAC;IACtF,CAAC;CACF,CAAC,CAAC"}
|
package/dist/tools/add-person.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin/tool";
|
|
2
|
-
import { addMember } from "../vault/member.js";
|
|
3
|
-
export const byoao_add_person = tool({
|
|
4
|
-
description: "Add a person note to an existing vault. Creates a person note in People/ and updates the team index and AGENTS.md wikilinks.",
|
|
5
|
-
args: {
|
|
6
|
-
vaultPath: tool.schema.string().describe("Path to the Obsidian vault"),
|
|
7
|
-
name: tool.schema.string().describe("Person's full name"),
|
|
8
|
-
role: tool.schema.string().optional().describe("Person's role/title"),
|
|
9
|
-
team: tool.schema.string().optional().describe("Team or KB name"),
|
|
10
|
-
},
|
|
11
|
-
async execute(args) {
|
|
12
|
-
const result = await addMember({
|
|
13
|
-
vaultPath: args.vaultPath,
|
|
14
|
-
name: args.name,
|
|
15
|
-
role: args.role || "",
|
|
16
|
-
team: args.team || "",
|
|
17
|
-
});
|
|
18
|
-
return `Added person: ${args.name}\n File: ${result.filePath}\n Wikilinks updated: ${result.wikilinksAdded}`;
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
//# sourceMappingURL=add-person.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add-person.js","sourceRoot":"","sources":["../../src/tools/add-person.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;IACnC,WAAW,EACT,8HAA8H;IAChI,IAAI,EAAE;QACJ,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;QACtE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oBAAoB,CAAC;QACzD,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;QACrE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC;KAClE;IACD,KAAK,CAAC,OAAO,CAAC,IAAI;QAChB,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC;YAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;YACrB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;SACtB,CAAC,CAAC;QAEH,OAAO,iBAAiB,IAAI,CAAC,IAAI,aAAa,MAAM,CAAC,QAAQ,0BAA0B,MAAM,CAAC,cAAc,EAAE,CAAC;IACjH,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin/tool";
|
|
2
|
-
import { addProject } from "../vault/project.js";
|
|
3
|
-
export const byoao_add_project = tool({
|
|
4
|
-
description: "Add a new project note to an existing vault. Creates a project note in Projects/ and updates the team index and AGENTS.md wikilinks.",
|
|
5
|
-
args: {
|
|
6
|
-
vaultPath: tool.schema.string().describe("Path to the Obsidian vault"),
|
|
7
|
-
name: tool.schema.string().describe("Project name"),
|
|
8
|
-
description: tool.schema
|
|
9
|
-
.string()
|
|
10
|
-
.optional()
|
|
11
|
-
.describe("One-line project description"),
|
|
12
|
-
team: tool.schema.string().optional().describe("Team name"),
|
|
13
|
-
},
|
|
14
|
-
async execute(args) {
|
|
15
|
-
const result = await addProject({
|
|
16
|
-
vaultPath: args.vaultPath,
|
|
17
|
-
name: args.name,
|
|
18
|
-
description: args.description || "",
|
|
19
|
-
team: args.team || "",
|
|
20
|
-
});
|
|
21
|
-
return `✓ Added project: ${args.name}\n File: ${result.filePath}\n Wikilinks updated: ${result.wikilinksAdded}`;
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
//# sourceMappingURL=add-project.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"add-project.js","sourceRoot":"","sources":["../../src/tools/add-project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,CAAC,MAAM,iBAAiB,GAAG,IAAI,CAAC;IACpC,WAAW,EACT,sIAAsI;IACxI,IAAI,EAAE;QACJ,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;QACtE,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;QACnD,WAAW,EAAE,IAAI,CAAC,MAAM;aACrB,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,8BAA8B,CAAC;QAC3C,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC;KAC5D;IACD,KAAK,CAAC,OAAO,CAAC,IAAI;QAChB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC;YAC9B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;YACnC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE;SACtB,CAAC,CAAC;QAEH,OAAO,oBAAoB,IAAI,CAAC,IAAI,aAAa,MAAM,CAAC,QAAQ,0BAA0B,MAAM,CAAC,cAAc,EAAE,CAAC;IACpH,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin/tool";
|
|
2
|
-
import { getGraphHealth } from "../vault/graph-health.js";
|
|
3
|
-
export const byoao_graph_health = tool({
|
|
4
|
-
description: "Diagnose Obsidian vault graph health — find orphan notes (no links in or out), unresolved links (broken wikilinks), and dead-end notes. Uses Obsidian CLI for vault-aware analysis.",
|
|
5
|
-
args: {
|
|
6
|
-
vaultPath: tool.schema.string().describe("Absolute path to the Obsidian vault"),
|
|
7
|
-
check: tool.schema
|
|
8
|
-
.enum(["all", "orphans", "unresolved", "deadends"])
|
|
9
|
-
.optional()
|
|
10
|
-
.describe("Which check to run: 'all' (default), 'orphans', 'unresolved', or 'deadends'"),
|
|
11
|
-
limit: tool.schema
|
|
12
|
-
.number()
|
|
13
|
-
.optional()
|
|
14
|
-
.describe("Maximum number of results to return (default: 20)"),
|
|
15
|
-
},
|
|
16
|
-
async execute(args) {
|
|
17
|
-
const result = await getGraphHealth({
|
|
18
|
-
vaultPath: args.vaultPath,
|
|
19
|
-
check: args.check,
|
|
20
|
-
limit: args.limit,
|
|
21
|
-
});
|
|
22
|
-
return JSON.stringify(result, null, 2);
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
//# sourceMappingURL=graph-health.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"graph-health.js","sourceRoot":"","sources":["../../src/tools/graph-health.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;IACrC,WAAW,EACT,qLAAqL;IACvL,IAAI,EAAE;QACJ,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;QAC/E,KAAK,EAAE,IAAI,CAAC,MAAM;aACf,IAAI,CAAC,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC;aAClD,QAAQ,EAAE;aACV,QAAQ,CACP,6EAA6E,CAC9E;QACH,KAAK,EAAE,IAAI,CAAC,MAAM;aACf,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,mDAAmD,CAAC;KACjE;IACD,KAAK,CAAC,OAAO,CAAC,IAAI;QAChB,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC;YAClC,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAkE;YAC9E,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;CACF,CAAC,CAAC"}
|
package/dist/tools/note-read.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin/tool";
|
|
2
|
-
import { readNote } from "../vault/note-read.js";
|
|
3
|
-
export const byoao_note_read = tool({
|
|
4
|
-
description: "Read a specific note from an Obsidian vault by name. Uses Obsidian CLI to resolve and read the note. Preferred over cat/Read for Obsidian vault notes.",
|
|
5
|
-
args: {
|
|
6
|
-
vaultPath: tool.schema.string().describe("Absolute path to the Obsidian vault"),
|
|
7
|
-
file: tool.schema
|
|
8
|
-
.string()
|
|
9
|
-
.describe("Note name to read (without .md extension, e.g. 'Refund Automation')"),
|
|
10
|
-
},
|
|
11
|
-
async execute(args) {
|
|
12
|
-
const result = await readNote({
|
|
13
|
-
vaultPath: args.vaultPath,
|
|
14
|
-
file: args.file,
|
|
15
|
-
});
|
|
16
|
-
return JSON.stringify(result, null, 2);
|
|
17
|
-
},
|
|
18
|
-
});
|
|
19
|
-
//# sourceMappingURL=note-read.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"note-read.js","sourceRoot":"","sources":["../../src/tools/note-read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;IAClC,WAAW,EACT,wJAAwJ;IAC1J,IAAI,EAAE;QACJ,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;QAC/E,IAAI,EAAE,IAAI,CAAC,MAAM;aACd,MAAM,EAAE;aACR,QAAQ,CAAC,qEAAqE,CAAC;KACnF;IACD,KAAK,CAAC,OAAO,CAAC,IAAI;QAChB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC;YAC5B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;SAChB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { tool } from "@opencode-ai/plugin/tool";
|
|
2
|
-
import { searchVault } from "../vault/search-vault.js";
|
|
3
|
-
export const byoao_search_vault = tool({
|
|
4
|
-
description: "Search an Obsidian vault for notes matching a text query. Uses Obsidian CLI search:context for vault-aware results. Preferred over grep/rg for Obsidian vault knowledge queries about notes, tags, and content.",
|
|
5
|
-
args: {
|
|
6
|
-
vaultPath: tool.schema.string().describe("Absolute path to the Obsidian vault"),
|
|
7
|
-
query: tool.schema.string().describe("Text query to search for in vault notes"),
|
|
8
|
-
limit: tool.schema
|
|
9
|
-
.number()
|
|
10
|
-
.optional()
|
|
11
|
-
.describe("Maximum number of results to return (default: 20)"),
|
|
12
|
-
},
|
|
13
|
-
async execute(args) {
|
|
14
|
-
const result = await searchVault({
|
|
15
|
-
vaultPath: args.vaultPath,
|
|
16
|
-
query: args.query,
|
|
17
|
-
limit: args.limit,
|
|
18
|
-
});
|
|
19
|
-
return JSON.stringify(result, null, 2);
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
//# sourceMappingURL=search-vault.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"search-vault.js","sourceRoot":"","sources":["../../src/tools/search-vault.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAEvD,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;IACrC,WAAW,EACT,iNAAiN;IACnN,IAAI,EAAE;QACJ,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qCAAqC,CAAC;QAC/E,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;QAC/E,KAAK,EAAE,IAAI,CAAC,MAAM;aACf,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CAAC,mDAAmD,CAAC;KACjE;IACD,KAAK,CAAC,OAAO,CAAC,IAAI;QAChB,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC;YAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import fs from "fs-extra";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import os from "node:os";
|
|
5
|
-
import { addGlossaryTerm } from "../glossary.js";
|
|
6
|
-
let tmpDir;
|
|
7
|
-
beforeEach(async () => {
|
|
8
|
-
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "byoao-glossary-"));
|
|
9
|
-
await fs.ensureDir(path.join(tmpDir, "Knowledge"));
|
|
10
|
-
});
|
|
11
|
-
afterEach(async () => {
|
|
12
|
-
await fs.remove(tmpDir);
|
|
13
|
-
});
|
|
14
|
-
const GLOSSARY_TEMPLATE = `---
|
|
15
|
-
title: Glossary
|
|
16
|
-
type: reference
|
|
17
|
-
status: active
|
|
18
|
-
tags: [glossary, reference]
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
# Glossary
|
|
22
|
-
|
|
23
|
-
Domain terms and key concepts in this knowledge base.
|
|
24
|
-
Maintained by /weave — run it to discover and add new terms.
|
|
25
|
-
|
|
26
|
-
| Term | Definition | Domain |
|
|
27
|
-
|------|-----------|--------|
|
|
28
|
-
| **API** | Application programming interface | engineering |
|
|
29
|
-
`;
|
|
30
|
-
describe("addGlossaryTerm", () => {
|
|
31
|
-
it("appends term to existing glossary table", async () => {
|
|
32
|
-
await fs.writeFile(path.join(tmpDir, "Knowledge/Glossary.md"), GLOSSARY_TEMPLATE);
|
|
33
|
-
const result = await addGlossaryTerm({
|
|
34
|
-
vaultPath: tmpDir,
|
|
35
|
-
term: "SDK",
|
|
36
|
-
definition: "Software development kit",
|
|
37
|
-
domain: "engineering",
|
|
38
|
-
});
|
|
39
|
-
expect(result.termAdded).toBe("SDK");
|
|
40
|
-
const content = await fs.readFile(path.join(tmpDir, "Knowledge/Glossary.md"), "utf-8");
|
|
41
|
-
expect(content).toContain("**SDK**");
|
|
42
|
-
expect(content).toContain("Software development kit");
|
|
43
|
-
expect(content).toContain("| engineering |");
|
|
44
|
-
// Original term still present
|
|
45
|
-
expect(content).toContain("**API**");
|
|
46
|
-
});
|
|
47
|
-
it("appends term with empty domain when domain not provided", async () => {
|
|
48
|
-
await fs.writeFile(path.join(tmpDir, "Knowledge/Glossary.md"), GLOSSARY_TEMPLATE);
|
|
49
|
-
const result = await addGlossaryTerm({
|
|
50
|
-
vaultPath: tmpDir,
|
|
51
|
-
term: "TDD",
|
|
52
|
-
definition: "Test-driven development",
|
|
53
|
-
domain: "",
|
|
54
|
-
});
|
|
55
|
-
expect(result.termAdded).toBe("TDD");
|
|
56
|
-
const content = await fs.readFile(path.join(tmpDir, "Knowledge/Glossary.md"), "utf-8");
|
|
57
|
-
expect(content).toContain("**TDD**");
|
|
58
|
-
});
|
|
59
|
-
it("throws when Glossary.md does not exist", async () => {
|
|
60
|
-
await expect(addGlossaryTerm({
|
|
61
|
-
vaultPath: tmpDir,
|
|
62
|
-
term: "Test",
|
|
63
|
-
definition: "A test",
|
|
64
|
-
domain: "",
|
|
65
|
-
})).rejects.toThrow("Glossary not found");
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
//# sourceMappingURL=glossary.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"glossary.test.js","sourceRoot":"","sources":["../../../src/vault/__tests__/glossary.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
vi.mock("../obsidian-cli.js", () => ({
|
|
3
|
-
isObsidianCliAvailable: vi.fn(),
|
|
4
|
-
execObsidianCmd: vi.fn(),
|
|
5
|
-
}));
|
|
6
|
-
import { getGraphHealth } from "../graph-health.js";
|
|
7
|
-
import { isObsidianCliAvailable, execObsidianCmd } from "../obsidian-cli.js";
|
|
8
|
-
const mockIsAvailable = vi.mocked(isObsidianCliAvailable);
|
|
9
|
-
const mockExecCmd = vi.mocked(execObsidianCmd);
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
vi.resetAllMocks();
|
|
12
|
-
});
|
|
13
|
-
describe("getGraphHealth", () => {
|
|
14
|
-
it("returns runtime_unavailable when CLI is not available", async () => {
|
|
15
|
-
mockIsAvailable.mockReturnValue(false);
|
|
16
|
-
const result = await getGraphHealth({ vaultPath: "/vault" });
|
|
17
|
-
expect(result.status).toBe("runtime_unavailable");
|
|
18
|
-
expect(result.mode).toBe("graph-health");
|
|
19
|
-
expect(result.diagnostics).toContain("Obsidian CLI not available");
|
|
20
|
-
});
|
|
21
|
-
it("runs all three checks by default", async () => {
|
|
22
|
-
mockIsAvailable.mockReturnValue(true);
|
|
23
|
-
mockExecCmd.mockReturnValue({ success: true, output: "" });
|
|
24
|
-
await getGraphHealth({ vaultPath: "/vault" });
|
|
25
|
-
expect(mockExecCmd).toHaveBeenCalledTimes(3);
|
|
26
|
-
expect(mockExecCmd).toHaveBeenCalledWith(["orphans", "--vault", "/vault"]);
|
|
27
|
-
expect(mockExecCmd).toHaveBeenCalledWith(["unresolved", "--vault", "/vault"]);
|
|
28
|
-
expect(mockExecCmd).toHaveBeenCalledWith(["deadends", "--vault", "/vault"]);
|
|
29
|
-
});
|
|
30
|
-
it("runs only the specified check", async () => {
|
|
31
|
-
mockIsAvailable.mockReturnValue(true);
|
|
32
|
-
mockExecCmd.mockReturnValue({ success: true, output: "" });
|
|
33
|
-
await getGraphHealth({ vaultPath: "/vault", check: "orphans" });
|
|
34
|
-
expect(mockExecCmd).toHaveBeenCalledTimes(1);
|
|
35
|
-
expect(mockExecCmd).toHaveBeenCalledWith(["orphans", "--vault", "/vault"]);
|
|
36
|
-
});
|
|
37
|
-
it("returns no_results when vault is healthy", async () => {
|
|
38
|
-
mockIsAvailable.mockReturnValue(true);
|
|
39
|
-
mockExecCmd.mockReturnValue({ success: true, output: "" });
|
|
40
|
-
const result = await getGraphHealth({ vaultPath: "/vault" });
|
|
41
|
-
expect(result.status).toBe("no_results");
|
|
42
|
-
expect(result.results).toHaveLength(0);
|
|
43
|
-
expect(result.summary).toContain("No issues");
|
|
44
|
-
});
|
|
45
|
-
it("parses orphan results into structured items", async () => {
|
|
46
|
-
mockIsAvailable.mockReturnValue(true);
|
|
47
|
-
mockExecCmd.mockImplementation((args) => {
|
|
48
|
-
if (args[0] === "orphans") {
|
|
49
|
-
return {
|
|
50
|
-
success: true,
|
|
51
|
-
output: "Projects/Abandoned.md\nInbox/Random.md",
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
return { success: true, output: "" };
|
|
55
|
-
});
|
|
56
|
-
const result = await getGraphHealth({ vaultPath: "/vault" });
|
|
57
|
-
expect(result.status).toBe("ok");
|
|
58
|
-
const orphans = result.results.filter((r) => r.metadata?.check === "orphans");
|
|
59
|
-
expect(orphans).toHaveLength(2);
|
|
60
|
-
expect(orphans[0].title).toBe("Abandoned");
|
|
61
|
-
expect(orphans[0].path).toBe("Projects/Abandoned.md");
|
|
62
|
-
});
|
|
63
|
-
it("parses unresolved links into structured items", async () => {
|
|
64
|
-
mockIsAvailable.mockReturnValue(true);
|
|
65
|
-
mockExecCmd.mockImplementation((args) => {
|
|
66
|
-
if (args[0] === "unresolved") {
|
|
67
|
-
return { success: true, output: "Missing Note\nAnother Missing" };
|
|
68
|
-
}
|
|
69
|
-
return { success: true, output: "" };
|
|
70
|
-
});
|
|
71
|
-
const result = await getGraphHealth({ vaultPath: "/vault" });
|
|
72
|
-
const unresolved = result.results.filter((r) => r.metadata?.check === "unresolved");
|
|
73
|
-
expect(unresolved).toHaveLength(2);
|
|
74
|
-
expect(unresolved[0].title).toBe("Missing Note");
|
|
75
|
-
});
|
|
76
|
-
it("continues when one check fails but others succeed", async () => {
|
|
77
|
-
mockIsAvailable.mockReturnValue(true);
|
|
78
|
-
mockExecCmd.mockImplementation((args) => {
|
|
79
|
-
if (args[0] === "orphans") {
|
|
80
|
-
return { success: false, output: "", error: "timeout" };
|
|
81
|
-
}
|
|
82
|
-
return { success: true, output: "Inbox/note.md" };
|
|
83
|
-
});
|
|
84
|
-
const result = await getGraphHealth({ vaultPath: "/vault" });
|
|
85
|
-
// Should still return results from successful checks
|
|
86
|
-
expect(result.results.length).toBeGreaterThan(0);
|
|
87
|
-
expect(result.diagnostics).toContain("orphans check failed: timeout");
|
|
88
|
-
});
|
|
89
|
-
it("respects the limit parameter", async () => {
|
|
90
|
-
mockIsAvailable.mockReturnValue(true);
|
|
91
|
-
const lines = Array.from({ length: 30 }, (_, i) => `Notes/note${i}.md`);
|
|
92
|
-
mockExecCmd.mockReturnValue({ success: true, output: lines.join("\n") });
|
|
93
|
-
const result = await getGraphHealth({
|
|
94
|
-
vaultPath: "/vault",
|
|
95
|
-
check: "orphans",
|
|
96
|
-
limit: 5,
|
|
97
|
-
});
|
|
98
|
-
expect(result.results).toHaveLength(5);
|
|
99
|
-
expect(result.truncated).toBe(true);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
//# sourceMappingURL=graph-health.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"graph-health.test.js","sourceRoot":"","sources":["../../../src/vault/__tests__/graph-health.test.ts"],"names":[],"mappings":"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"}
|