roam-research-mcp 1.6.0 → 2.4.3
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/README.md +202 -13
- package/build/Roam_Markdown_Cheatsheet.md +116 -269
- package/build/cli/batch/resolver.js +138 -0
- package/build/cli/batch/translator.js +363 -0
- package/build/cli/batch/types.js +4 -0
- package/build/cli/commands/batch.js +345 -0
- package/build/cli/commands/get.js +156 -43
- package/build/cli/commands/refs.js +63 -32
- package/build/cli/commands/rename.js +58 -0
- package/build/cli/commands/save.js +436 -63
- package/build/cli/commands/search.js +152 -31
- package/build/cli/commands/status.js +91 -0
- package/build/cli/commands/update.js +194 -0
- package/build/cli/roam.js +18 -1
- package/build/cli/utils/graph.js +56 -0
- package/build/cli/utils/input.js +10 -0
- package/build/cli/utils/output.js +34 -0
- package/build/config/environment.js +70 -34
- package/build/config/graph-registry.js +221 -0
- package/build/config/graph-registry.test.js +30 -0
- package/build/search/status-search.js +5 -4
- package/build/server/roam-server.js +98 -53
- package/build/shared/validation.js +10 -5
- package/build/tools/helpers/refs.js +50 -31
- package/build/tools/operations/blocks.js +38 -1
- package/build/tools/operations/memory.js +59 -9
- package/build/tools/operations/pages.js +186 -111
- package/build/tools/operations/search/index.js +5 -1
- package/build/tools/operations/todos.js +1 -1
- package/build/tools/schemas.js +123 -42
- package/build/tools/tool-handlers.js +9 -2
- package/build/utils/helpers.js +22 -0
- package/package.json +8 -5
|
@@ -1,126 +1,78 @@
|
|
|
1
|
-
# Roam Markdown Cheatsheet
|
|
1
|
+
# Roam Markdown Cheatsheet v2.1.0
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Core Syntax
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Quick Reference: Core Syntax
|
|
8
|
-
|
|
9
|
-
### Text Formatting
|
|
10
|
-
| Style | Syntax | Example |
|
|
11
|
-
|-------|--------|---------|
|
|
12
|
-
| Bold | `**text**` | **bold text** |
|
|
13
|
-
| Italic | `__text__` | __italic text__ |
|
|
14
|
-
| Highlight | `^^text^^` | ^^highlighted^^ |
|
|
15
|
-
| Strikethrough | `~~text~~` | ~~struck~~ |
|
|
16
|
-
| Inline code | `` `code` `` | `code` |
|
|
17
|
-
| LaTeX | `$$E=mc^2$$` | rendered math |
|
|
5
|
+
### Formatting
|
|
6
|
+
`**bold**` · `__italic__` · `^^highlight^^` · `~~strike~~` · `` `code` `` · `$$LaTeX$$`
|
|
18
7
|
|
|
19
8
|
### Links & References
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
| Hyphenated | `#self-esteem` | Naturally hyphenated terms |
|
|
36
|
-
|
|
37
|
-
⚠️ **CRITICAL**: Never concatenate multi-word tags. `#knowledgemanagement` ≠ `#[[knowledge management]]`
|
|
9
|
+
- **Page ref:** `[[Page Name]]` — creates/links to page
|
|
10
|
+
- **Block ref:** `((block-uid))` — embeds block content inline
|
|
11
|
+
- **Block embed:** `{{[[embed]]: ((block-uid))}}` — full block with children
|
|
12
|
+
- **External:** `[text](URL)`
|
|
13
|
+
- **Aliased page:** `[display text]([[Actual Page]])`
|
|
14
|
+
- **Aliased block:** `[display text](<((block-uid))>)` — note the angle brackets
|
|
15
|
+
- **Image:** ``
|
|
16
|
+
|
|
17
|
+
### Tags
|
|
18
|
+
- Single word: `#tag`
|
|
19
|
+
- Multi-word: `#[[multiple words]]`
|
|
20
|
+
- Hyphenated: `#self-esteem`
|
|
21
|
+
|
|
22
|
+
⚠️ Never concatenate: `#knowledgemanagement` ≠ `#[[knowledge management]]`
|
|
23
|
+
⚠️ `#` always creates tags — write `Step 1` not `#1`
|
|
38
24
|
|
|
39
25
|
### Dates
|
|
40
|
-
|
|
41
|
-
- Ordinals: 1st, 2nd, 3rd, 4th–20th, 21st, 22nd, 23rd, 24th–30th, 31st
|
|
26
|
+
Always ordinal format: `[[January 1st, 2025]]`, `[[December 23rd, 2024]]`
|
|
42
27
|
|
|
43
|
-
###
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
| Todo | `{{[[TODO]]}} task description` |
|
|
47
|
-
| Done | `{{[[DONE]]}} task description` |
|
|
28
|
+
### Tasks
|
|
29
|
+
- Todo: `{{[[TODO]]}} task`
|
|
30
|
+
- Done: `{{[[DONE]]}} task`
|
|
48
31
|
|
|
49
|
-
### Attributes
|
|
32
|
+
### Attributes
|
|
50
33
|
```
|
|
51
34
|
Type:: Book
|
|
52
35
|
Author:: [[Person Name]]
|
|
53
36
|
Rating:: 4/5
|
|
54
|
-
Source:: https://example.com
|
|
55
37
|
```
|
|
56
38
|
|
|
57
|
-
**
|
|
58
|
-
|
|
59
|
-
**When to USE attributes:**
|
|
60
|
-
| Attribute | Why It's Good |
|
|
61
|
-
|-----------|---------------|
|
|
62
|
-
| `Type:: Book` | Reusable across all media you consume |
|
|
63
|
-
| `Author:: [[Person]]` | Links to author page, queryable |
|
|
64
|
-
| `Status:: In Progress` | Standard project states, queryable |
|
|
65
|
-
| `Source:: URL` | Consistent sourcing across notes |
|
|
66
|
-
| `Date:: [[January 1st, 2025]]` | Enables date-based queries |
|
|
67
|
-
|
|
68
|
-
**When NOT to use attributes:**
|
|
69
|
-
| ❌ Wrong | ✅ Use Instead | Why |
|
|
70
|
-
|----------|----------------|-----|
|
|
71
|
-
| `Step 1:: Do this thing` | `**Step 1:** Do this thing` | Step numbers are page-specific, not queryable concepts |
|
|
72
|
-
| `Note:: Some observation` | Just write the text, or use `#note` | One-off labels don't need attribute syntax |
|
|
73
|
-
| `Summary:: The main point` | `**Summary:** The main point` | Section headers are formatting, not metadata |
|
|
74
|
-
| `Definition:: Some text` | `Term:: Definition` | Only use for actual definitions you want to query |
|
|
75
|
-
| `Implementation Tier 3 (Societal Restructuring):: Some text` | `** Implementation Tier 3 (Societal Restructuring)**: Some text` | Label is specific to current concept |
|
|
39
|
+
**Use `::` when:** queryable across graph (Type, Author, Status, Source, Date)
|
|
40
|
+
**Use bold instead when:** page-specific labels (Step 1, Summary, Note)
|
|
76
41
|
|
|
77
|
-
⚠️
|
|
78
|
-
|
|
79
|
-
NOTE: Never combine bold markdown formatting with `::`. Roam formats attributes in bold by default. ✅ `<attribute>::` ❌ `**<attribute>**::`
|
|
80
|
-
|
|
81
|
-
---
|
|
42
|
+
⚠️ Test: "Will I query all blocks with this attribute?" If no → use `**Label:**` instead
|
|
43
|
+
⚠️ Never `**Attr**::` — Roam auto-bolds attributes
|
|
82
44
|
|
|
83
45
|
## Block Structures
|
|
84
46
|
|
|
85
|
-
###
|
|
86
|
-
- Use `-` (dash) followed by space
|
|
87
|
-
- Nested bullets: indent with tab or spaces
|
|
47
|
+
### Bullets
|
|
88
48
|
```
|
|
89
|
-
- Parent
|
|
90
|
-
- Child
|
|
91
|
-
- Grandchild
|
|
49
|
+
- Parent
|
|
50
|
+
- Child
|
|
51
|
+
- Grandchild
|
|
92
52
|
```
|
|
93
53
|
|
|
94
54
|
### Code Blocks
|
|
95
55
|
````
|
|
96
56
|
```javascript
|
|
97
|
-
const
|
|
98
|
-
return "syntax highlighted";
|
|
99
|
-
}
|
|
57
|
+
const x = 1;
|
|
100
58
|
```
|
|
101
59
|
````
|
|
102
60
|
|
|
103
61
|
### Queries
|
|
104
62
|
```
|
|
105
63
|
{{[[query]]: {and: [[tag1]] [[tag2]]}}}
|
|
106
|
-
{{[[query]]: {or: [[
|
|
107
|
-
{{[[query]]: {not: [[exclude
|
|
64
|
+
{{[[query]]: {or: [[A]] [[B]]}}}
|
|
65
|
+
{{[[query]]: {not: [[exclude]]}}}
|
|
108
66
|
{{[[query]]: {between: [[January 1st, 2025]] [[January 31st, 2025]]}}}
|
|
109
67
|
```
|
|
110
68
|
|
|
111
69
|
### Calculator
|
|
112
|
-
|
|
113
|
-
{{[[calc]]: 2 + 2}}
|
|
114
|
-
{{[[calc]]: 100 * 0.15}}
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
---
|
|
70
|
+
`{{[[calc]]: 2 + 2}}`
|
|
118
71
|
|
|
119
72
|
## Complex Structures
|
|
120
73
|
|
|
121
74
|
### Tables
|
|
122
|
-
|
|
123
|
-
|
|
75
|
+
Each column nests ONE LEVEL DEEPER than previous:
|
|
124
76
|
```
|
|
125
77
|
{{[[table]]}}
|
|
126
78
|
- Header 1
|
|
@@ -129,219 +81,114 @@ Tables use nested indentation. Each column header/cell nests ONE LEVEL DEEPER th
|
|
|
129
81
|
- Row 1 Label
|
|
130
82
|
- Cell 1.1
|
|
131
83
|
- Cell 1.2
|
|
132
|
-
- Cell 1.3
|
|
133
84
|
- Row 2 Label
|
|
134
85
|
- Cell 2.1
|
|
135
86
|
- Cell 2.2
|
|
136
|
-
- Cell 2.3
|
|
137
87
|
```
|
|
88
|
+
Keep tables ≤5 columns.
|
|
138
89
|
|
|
139
|
-
|
|
140
|
-
- `{{[[table]]}}` is level 1
|
|
141
|
-
- First header/row-label at level 2
|
|
142
|
-
- Each subsequent column nests +1 level deeper
|
|
143
|
-
- Keep tables ≤5 columns for readability
|
|
144
|
-
|
|
145
|
-
### Kanban Boards
|
|
90
|
+
### Kanban
|
|
146
91
|
```
|
|
147
92
|
{{[[kanban]]}}
|
|
148
|
-
- Column 1
|
|
149
|
-
- Card 1
|
|
150
|
-
- Card 2
|
|
151
|
-
- Column 2
|
|
152
|
-
- Card 3
|
|
93
|
+
- Column 1
|
|
94
|
+
- Card 1
|
|
95
|
+
- Card 2
|
|
96
|
+
- Column 2
|
|
97
|
+
- Card 3
|
|
153
98
|
```
|
|
154
99
|
|
|
155
|
-
### Mermaid
|
|
100
|
+
### Mermaid
|
|
156
101
|
```
|
|
157
102
|
{{[[mermaid]]}}
|
|
158
103
|
- graph TD
|
|
159
104
|
- A[Start] --> B{Decision}
|
|
160
105
|
- B -->|Yes| C[Action]
|
|
161
|
-
- B -->|No| D[Alternative]
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Hiccup (Custom HTML)
|
|
165
|
-
`:hiccup [:iframe {:width "600" :height "400" :src "https://example.com"}]`
|
|
166
|
-
|
|
167
|
-
`:hiccup [:div {:style {:color "red"}} "Custom styled content"]`
|
|
168
|
-
|
|
169
|
-
---
|
|
170
|
-
|
|
171
|
-
## Anti-Patterns — DON'T DO THIS
|
|
172
|
-
|
|
173
|
-
| ❌ Wrong | ✅ Correct | Why |
|
|
174
|
-
|----------|-----------|-----|
|
|
175
|
-
| `Step 1:: Do this` | `**Step 1:** Do this` | `::` creates queryable attributes; use bold for page-specific labels |
|
|
176
|
-
| `#multiplewords` | `#[[multiple words]]` | Concatenated tags create dead references |
|
|
177
|
-
| `[[january 1, 2025]]` | `[[January 1st, 2025]]` | Must use ordinal format with proper capitalization |
|
|
178
|
-
| `[text](((block-uid)))` | `[text](<((block-uid))>)` | Block ref links need angle bracket wrapper |
|
|
179
|
-
| `{{embed: ((uid))}}` | `{{[[embed]]: ((uid))}}` | Embed requires double brackets around keyword |
|
|
180
|
-
| Deeply nested tables (6+ cols) | Max 4-5 columns | Becomes unreadable/unmanageable |
|
|
181
|
-
| `- *bullet` | `- bullet` | Use dash, not asterisk for bullets |
|
|
182
|
-
| `[[TODO]] task` | `{{[[TODO]]}} task` | TODO needs double curly braces |
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
## Tool Selection Decision Tree
|
|
187
|
-
|
|
188
106
|
```
|
|
189
|
-
CREATING CONTENT IN ROAM:
|
|
190
|
-
|
|
191
|
-
┌─ Is this a NEW standalone page with structure?
|
|
192
|
-
│ └─ YES → roam_create_page (with content array)
|
|
193
|
-
│
|
|
194
|
-
├─ Adding content to EXISTING page/block?
|
|
195
|
-
│ ├─ Simple outline structure → roam_create_outline
|
|
196
|
-
│ │ (provide page_title_uid and/or block_text_uid)
|
|
197
|
-
│ │
|
|
198
|
-
│ └─ Complex/nested markdown → roam_import_markdown
|
|
199
|
-
│ (for deeply nested content, tables, etc.)
|
|
200
|
-
│
|
|
201
|
-
├─ Need to CREATE, UPDATE, MOVE, or DELETE individual blocks?
|
|
202
|
-
│ └─ roam_process_batch_actions
|
|
203
|
-
│ (fine-grained control, temporary UIDs for parent refs)
|
|
204
|
-
│
|
|
205
|
-
├─ Adding a memory/note to remember?
|
|
206
|
-
│ └─ roam_remember (auto-tags with MEMORIES_TAG)
|
|
207
|
-
│
|
|
208
|
-
├─ Adding TODO items to today?
|
|
209
|
-
│ └─ roam_add_todo (creates individual TODO blocks)
|
|
210
|
-
│
|
|
211
|
-
└─ SEARCHING/READING:
|
|
212
|
-
├─ Find by tag → roam_search_for_tag
|
|
213
|
-
├─ Find by text → roam_search_by_text
|
|
214
|
-
├─ Find by date range → roam_search_by_date
|
|
215
|
-
├─ Find by status → roam_search_by_status
|
|
216
|
-
├─ Get page content → roam_fetch_page_by_title
|
|
217
|
-
├─ Get block + children → roam_fetch_block_with_children
|
|
218
|
-
├─ Recall memories → roam_recall
|
|
219
|
-
└─ Complex queries → roam_datomic_query
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
---
|
|
223
|
-
|
|
224
|
-
## API Efficiency Guidelines (Rate Limit Avoidance)
|
|
225
|
-
|
|
226
|
-
The Roam API has rate limits. Follow these guidelines to minimize API calls:
|
|
227
|
-
|
|
228
|
-
### Tool Efficiency Ranking (Best to Worst)
|
|
229
|
-
1. **`roam_process_batch_actions`** - Single API call for multiple operations (MOST EFFICIENT)
|
|
230
|
-
2. **`roam_create_page`** - Batches content with page creation
|
|
231
|
-
3. **`roam_create_outline` / `roam_import_markdown`** - Include verification queries (use for smaller operations)
|
|
232
|
-
4. **Multiple sequential tool calls** - Each call = multiple API requests (AVOID)
|
|
233
|
-
|
|
234
|
-
### Best Practices for Intensive Operations
|
|
235
107
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
2. Plan ALL changes needed (creates, updates, deletes)
|
|
239
|
-
3. Execute ALL changes in a SINGLE `roam_process_batch_actions` call
|
|
240
|
-
4. Do NOT fetch-modify-fetch-modify in a loop
|
|
108
|
+
### Hiccup
|
|
109
|
+
`:hiccup [:iframe {:width "600" :height "400" :src "URL"}]`
|
|
241
110
|
|
|
242
|
-
|
|
243
|
-
- For 10+ blocks: Use `roam_process_batch_actions` with nested structure
|
|
244
|
-
- For simple outlines (<10 blocks): `roam_create_outline` is fine
|
|
111
|
+
## Anti-Patterns
|
|
245
112
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
113
|
+
| ❌ Wrong | ✅ Correct |
|
|
114
|
+
|----------|-----------|
|
|
115
|
+
| `#multiplewords` | `#[[multiple words]]` |
|
|
116
|
+
| `#1`, `#2` | `Step 1`, `No. 1` |
|
|
117
|
+
| `[[january 1, 2025]]` | `[[January 1st, 2025]]` |
|
|
118
|
+
| `[text](((uid)))` | `[text](<((uid))>)` |
|
|
119
|
+
| `{{embed: ((uid))}}` | `{{[[embed]]: ((uid))}}` |
|
|
120
|
+
| `[[TODO]] task` | `{{[[TODO]]}} task` |
|
|
121
|
+
| `- *bullet` | `- bullet` |
|
|
122
|
+
| `* bullet` | `- bullet` |
|
|
123
|
+
| `**Attr**:: val` | `Attr:: val` |
|
|
124
|
+
|
|
125
|
+
## Tool Selection
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
CREATING:
|
|
129
|
+
├─ New page + structure → roam_create_page
|
|
130
|
+
├─ Add to existing page/block:
|
|
131
|
+
│ ├─ Simple outline → roam_create_outline
|
|
132
|
+
│ └─ Complex markdown → roam_import_markdown
|
|
133
|
+
├─ Revise entire page → roam_update_page_markdown
|
|
134
|
+
├─ Fine-grained CRUD → roam_process_batch_actions
|
|
135
|
+
├─ Table → roam_create_table
|
|
136
|
+
├─ Memory → roam_remember
|
|
137
|
+
└─ Todos → roam_add_todo
|
|
138
|
+
|
|
139
|
+
SEARCHING:
|
|
140
|
+
├─ By tag → roam_search_for_tag
|
|
141
|
+
├─ By text → roam_search_by_text
|
|
142
|
+
├─ By date → roam_search_by_date
|
|
143
|
+
├─ By status → roam_search_by_status
|
|
144
|
+
├─ Block refs → roam_search_block_refs
|
|
145
|
+
├─ Modified today → roam_find_pages_modified_today
|
|
146
|
+
├─ Page content → roam_fetch_page_by_title
|
|
147
|
+
├─ Block + children → roam_fetch_block_with_children
|
|
148
|
+
├─ Memories → roam_recall
|
|
149
|
+
└─ Complex → roam_datomic_query
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## API Efficiency
|
|
153
|
+
|
|
154
|
+
**Ranking (best → worst):**
|
|
155
|
+
1. `roam_update_page_markdown` — single call: fetch + diff + update
|
|
156
|
+
2. `roam_process_batch_actions` — batch multiple ops
|
|
157
|
+
3. `roam_create_page` — batches content with creation
|
|
158
|
+
4. `roam_create_outline` / `roam_import_markdown` — includes verification
|
|
159
|
+
5. Multiple sequential calls — avoid
|
|
160
|
+
|
|
161
|
+
**Best practices:**
|
|
162
|
+
- Use `roam_update_page_markdown` for revisions (handles everything)
|
|
163
|
+
- For 10+ blocks: `roam_process_batch_actions`
|
|
164
|
+
- Cache UIDs — use `page_uid` over `page_title` when available
|
|
165
|
+
- Never fetch-modify-fetch-modify in loops
|
|
166
|
+
|
|
167
|
+
### UID Placeholders
|
|
168
|
+
Use `{{uid:name}}` for parent refs in batch actions:
|
|
257
169
|
```json
|
|
258
170
|
[
|
|
259
|
-
{
|
|
260
|
-
{
|
|
171
|
+
{"action": "create-block", "uid": "{{uid:parent}}", "string": "Parent", "location": {"parent-uid": "pageUid", "order": 0}},
|
|
172
|
+
{"action": "create-block", "string": "Child", "location": {"parent-uid": "{{uid:parent}}", "order": 0}}
|
|
261
173
|
]
|
|
262
174
|
```
|
|
263
|
-
|
|
264
|
-
**Response includes UID mapping:**
|
|
265
|
-
```json
|
|
266
|
-
{
|
|
267
|
-
"success": true,
|
|
268
|
-
"uid_map": {
|
|
269
|
-
"parent": "Xk7mN2pQ9"
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
**Why placeholders?** LLMs are not reliable random generators. The server uses cryptographically secure randomness to generate proper 9-character Roam UIDs.
|
|
275
|
-
|
|
276
|
-
### Example: Efficient Page Revision
|
|
277
|
-
|
|
278
|
-
**Instead of:**
|
|
279
|
-
```
|
|
280
|
-
1. roam_fetch_page_by_title → get content
|
|
281
|
-
2. roam_create_outline → add section 1
|
|
282
|
-
3. roam_create_outline → add section 2
|
|
283
|
-
4. roam_import_markdown → add more content
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
**Do this:**
|
|
287
|
-
```
|
|
288
|
-
1. roam_fetch_page_by_title → get page UID and content
|
|
289
|
-
2. roam_process_batch_actions → ALL creates/updates in one call
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
---
|
|
175
|
+
Server returns `{"uid_map": {"parent": "Xk7mN2pQ9"}}`.
|
|
293
176
|
|
|
294
177
|
## Structural Defaults
|
|
295
178
|
|
|
296
|
-
|
|
179
|
+
- **Hierarchy:** 2-4 levels preferred, rarely exceed 5
|
|
180
|
+
- **Blocks:** One idea per block
|
|
181
|
+
- **Page refs vs tags:** `[[Page]]` for expandable concepts, `#tag` for filtering
|
|
182
|
+
- **Embed vs ref:** `((uid))` inline, `{{[[embed]]: ((uid))}}` with children, `[text](<((uid))>)` link only
|
|
183
|
+
- **No empty blocks or `---` dividers** — use hierarchy for visual separation
|
|
297
184
|
|
|
298
|
-
|
|
299
|
-
2. **Block length**: Keep blocks atomic — one idea per block
|
|
300
|
-
3. **Page refs vs hashtags**:
|
|
301
|
-
- `[[Page]]` for concepts you'll expand into full pages
|
|
302
|
-
- `#tag` for categorization/filtering
|
|
303
|
-
4. **When to embed vs reference**:
|
|
304
|
-
- `((uid))` — inline reference (shows content)
|
|
305
|
-
- `{{[[embed]]: ((uid))}}` — full block with children
|
|
306
|
-
- `[text](<((uid))>)` — clickable link only
|
|
307
|
-
|
|
308
|
-
---
|
|
309
|
-
## Visual Separation — Hierarchy First, Separators Never
|
|
185
|
+
## Output Conventions
|
|
310
186
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|----------|-----------|
|
|
315
|
-
| Blank blocks | Let hierarchy create space — child blocks are visually indented |
|
|
316
|
-
| `---` dividers | Use a **heading block** to signal section breaks |
|
|
317
|
-
| Decorative lines `───` | Nest content under a parent block as a structural container |
|
|
318
|
-
|
|
319
|
-
**Principle**: If you feel the urge to add visual breathing room, you probably need *better structure*, not more blocks. Promote a block to a heading, or reorganize into parent/child relationships.
|
|
320
|
-
|
|
321
|
-
**The test**: If you'd delete it during cleanup, don't create it.
|
|
322
|
-
|
|
323
|
-
---
|
|
324
|
-
|
|
325
|
-
## Output Format Conventions
|
|
326
|
-
|
|
327
|
-
### Quotes
|
|
328
|
-
```
|
|
329
|
-
<quote text> —[[Author Name]] #quote #[[relevant topic]]
|
|
330
|
-
```
|
|
331
|
-
|
|
332
|
-
### Definitions
|
|
333
|
-
```
|
|
334
|
-
Term:: Definition text #definition #[[domain]]
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
### Questions for Future
|
|
338
|
-
```
|
|
339
|
-
{{[[TODO]]}} Research: <question> #[[open questions]]
|
|
340
|
-
```
|
|
187
|
+
**Quote:** `<text> —[[Author]] #quote`
|
|
188
|
+
**Definition:** `Term:: definition #definition`
|
|
189
|
+
**Open question:** `{{[[TODO]]}} Research: <question> #[[open questions]]`
|
|
341
190
|
|
|
342
191
|
---
|
|
343
|
-
|
|
344
|
-
*End of Generic Foundation — Personalization section follows during user setup.*
|
|
345
192
|
# Roam Preferences — Personalization Layer
|
|
346
193
|
|
|
347
194
|
> This section contains YOUR specific conventions, tagging philosophy, and graph-specific rules. Customize to match your workflow.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolver for page/block title lookups before batch execution
|
|
3
|
+
*/
|
|
4
|
+
import { q } from '@roam-research/roam-api-sdk';
|
|
5
|
+
import { capitalizeWords } from '../../tools/helpers/text.js';
|
|
6
|
+
import { formatRoamDate } from '../../utils/helpers.js';
|
|
7
|
+
import { pageUidCache } from '../../cache/page-uid-cache.js';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a page title to its UID, trying multiple case variations
|
|
10
|
+
*/
|
|
11
|
+
export async function resolvePageUid(graph, title) {
|
|
12
|
+
// Check cache first
|
|
13
|
+
const cachedUid = pageUidCache.get(title);
|
|
14
|
+
if (cachedUid) {
|
|
15
|
+
return cachedUid;
|
|
16
|
+
}
|
|
17
|
+
// Try different case variations
|
|
18
|
+
const variations = [
|
|
19
|
+
title,
|
|
20
|
+
capitalizeWords(title),
|
|
21
|
+
title.toLowerCase()
|
|
22
|
+
];
|
|
23
|
+
const orClause = variations.map(v => `[?e :node/title "${v}"]`).join(' ');
|
|
24
|
+
const searchQuery = `[:find ?uid .
|
|
25
|
+
:where [?e :block/uid ?uid]
|
|
26
|
+
(or ${orClause})]`;
|
|
27
|
+
const result = await q(graph, searchQuery, []);
|
|
28
|
+
const uid = (result === null || result === undefined) ? null : String(result);
|
|
29
|
+
// Cache the result
|
|
30
|
+
if (uid) {
|
|
31
|
+
pageUidCache.set(title, uid);
|
|
32
|
+
}
|
|
33
|
+
return uid;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Get today's daily page title in Roam format
|
|
37
|
+
*/
|
|
38
|
+
export function getDailyPageTitle() {
|
|
39
|
+
return formatRoamDate(new Date());
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve today's daily page UID
|
|
43
|
+
*/
|
|
44
|
+
export async function resolveDailyPageUid(graph) {
|
|
45
|
+
const dailyTitle = getDailyPageTitle();
|
|
46
|
+
return resolvePageUid(graph, dailyTitle);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Collect all unique page titles that need resolution from commands
|
|
50
|
+
*/
|
|
51
|
+
export function collectPageTitles(commands) {
|
|
52
|
+
const titles = new Set();
|
|
53
|
+
for (const cmd of commands) {
|
|
54
|
+
const params = cmd.params;
|
|
55
|
+
// Commands that can have 'page' param
|
|
56
|
+
if ('page' in params && typeof params.page === 'string') {
|
|
57
|
+
titles.add(params.page);
|
|
58
|
+
}
|
|
59
|
+
// Remember command can have heading that needs parent page resolution
|
|
60
|
+
// But heading lookup is handled separately
|
|
61
|
+
// Todo/remember without explicit page need daily page
|
|
62
|
+
if (cmd.command === 'todo' || cmd.command === 'remember') {
|
|
63
|
+
if (!('page' in params) && !('pageUid' in params) && !('parent' in params)) {
|
|
64
|
+
titles.add(getDailyPageTitle());
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return titles;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Check if any commands need daily page resolution
|
|
72
|
+
*/
|
|
73
|
+
export function needsDailyPage(commands) {
|
|
74
|
+
for (const cmd of commands) {
|
|
75
|
+
const params = cmd.params;
|
|
76
|
+
if (cmd.command === 'todo' || cmd.command === 'remember') {
|
|
77
|
+
if (!('page' in params) && !('pageUid' in params) && !('parent' in params)) {
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve all page titles to UIDs
|
|
86
|
+
* Returns a map of title -> uid
|
|
87
|
+
*/
|
|
88
|
+
export async function resolveAllPages(graph, titles) {
|
|
89
|
+
const resolved = new Map();
|
|
90
|
+
// Resolve in parallel for efficiency
|
|
91
|
+
const entries = Array.from(titles);
|
|
92
|
+
const results = await Promise.all(entries.map(async (title) => {
|
|
93
|
+
const uid = await resolvePageUid(graph, title);
|
|
94
|
+
return [title, uid];
|
|
95
|
+
}));
|
|
96
|
+
for (const [title, uid] of results) {
|
|
97
|
+
if (uid) {
|
|
98
|
+
resolved.set(title, uid);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return resolved;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Create initial resolution context
|
|
105
|
+
*/
|
|
106
|
+
export function createResolutionContext() {
|
|
107
|
+
return {
|
|
108
|
+
pageUids: new Map(),
|
|
109
|
+
placeholders: new Map(),
|
|
110
|
+
levelStack: [],
|
|
111
|
+
currentParent: null,
|
|
112
|
+
dailyPageUid: null
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Resolve a parent reference - could be a UID, placeholder, or page title
|
|
117
|
+
*/
|
|
118
|
+
export function resolveParentRef(ref, context) {
|
|
119
|
+
// Check if it's a placeholder reference {{name}}
|
|
120
|
+
const placeholderMatch = ref.match(/^\{\{([^}]+)\}\}$/);
|
|
121
|
+
if (placeholderMatch) {
|
|
122
|
+
const name = placeholderMatch[1];
|
|
123
|
+
return context.placeholders.get(name) || `{{uid:${name}}}`;
|
|
124
|
+
}
|
|
125
|
+
// Check if it's a resolved page title
|
|
126
|
+
if (context.pageUids.has(ref)) {
|
|
127
|
+
return context.pageUids.get(ref);
|
|
128
|
+
}
|
|
129
|
+
// Assume it's a direct UID
|
|
130
|
+
return ref;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Generate a placeholder UID for tracking
|
|
134
|
+
* Returns the placeholder in {{uid:name}} format for batch processing
|
|
135
|
+
*/
|
|
136
|
+
export function generatePlaceholder(name) {
|
|
137
|
+
return `{{uid:${name}}}`;
|
|
138
|
+
}
|