roam-research-mcp 2.4.0 → 2.13.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.
Files changed (53) hide show
  1. package/README.md +175 -667
  2. package/build/Roam_Markdown_Cheatsheet.md +138 -289
  3. package/build/cache/page-uid-cache.js +40 -2
  4. package/build/cli/batch/translator.js +1 -1
  5. package/build/cli/commands/batch.js +3 -8
  6. package/build/cli/commands/get.js +478 -60
  7. package/build/cli/commands/refs.js +51 -31
  8. package/build/cli/commands/save.js +61 -10
  9. package/build/cli/commands/search.js +63 -58
  10. package/build/cli/commands/status.js +3 -4
  11. package/build/cli/commands/update.js +71 -28
  12. package/build/cli/utils/graph.js +6 -2
  13. package/build/cli/utils/input.js +10 -0
  14. package/build/cli/utils/output.js +28 -5
  15. package/build/cli/utils/sort-group.js +110 -0
  16. package/build/config/graph-registry.js +31 -13
  17. package/build/config/graph-registry.test.js +42 -5
  18. package/build/markdown-utils.js +114 -4
  19. package/build/markdown-utils.test.js +125 -0
  20. package/build/query/generator.js +330 -0
  21. package/build/query/index.js +149 -0
  22. package/build/query/parser.js +319 -0
  23. package/build/query/parser.test.js +389 -0
  24. package/build/query/types.js +4 -0
  25. package/build/search/ancestor-rule.js +14 -0
  26. package/build/search/block-ref-search.js +1 -5
  27. package/build/search/hierarchy-search.js +5 -12
  28. package/build/search/index.js +1 -0
  29. package/build/search/status-search.js +10 -9
  30. package/build/search/tag-search.js +8 -24
  31. package/build/search/text-search.js +70 -27
  32. package/build/search/types.js +13 -0
  33. package/build/search/utils.js +71 -2
  34. package/build/server/roam-server.js +4 -3
  35. package/build/shared/index.js +2 -0
  36. package/build/shared/page-validator.js +233 -0
  37. package/build/shared/page-validator.test.js +128 -0
  38. package/build/shared/staged-batch.js +144 -0
  39. package/build/tools/helpers/batch-utils.js +57 -0
  40. package/build/tools/helpers/page-resolution.js +136 -0
  41. package/build/tools/helpers/refs.js +68 -0
  42. package/build/tools/operations/batch.js +75 -3
  43. package/build/tools/operations/block-retrieval.js +15 -4
  44. package/build/tools/operations/block-retrieval.test.js +87 -0
  45. package/build/tools/operations/blocks.js +1 -288
  46. package/build/tools/operations/memory.js +32 -90
  47. package/build/tools/operations/outline.js +38 -156
  48. package/build/tools/operations/pages.js +169 -122
  49. package/build/tools/operations/todos.js +5 -37
  50. package/build/tools/schemas.js +20 -9
  51. package/build/tools/tool-handlers.js +4 -4
  52. package/build/utils/helpers.js +27 -0
  53. package/package.json +1 -1
@@ -1,128 +1,85 @@
1
- # Roam Markdown Cheatsheet — Generic Foundation v2.0.1
1
+ # Roam Markdown Cheatsheet v2.1.0
2
2
 
3
- > ⚠️ **MODEL DIRECTIVE**: Always consult this cheatsheet BEFORE making any Roam tool calls. Syntax errors in Roam are unforgiving.
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
- | Type | Syntax | Notes |
21
- |------|--------|-------|
22
- | Page reference | `[[Page Name]]` | Creates/links to page |
23
- | Block reference | `((block-uid))` | Embeds block content inline |
24
- | Block embed | `{{[[embed]]: ((block-uid))}}` | Full block embed with children |
25
- | External link | `[text](URL)` | Standard markdown |
26
- | Aliased page ref | `[display text]([[Actual Page]])` | Shows custom text, links to page |
27
- | Aliased block ref | `[display text](<((block-uid))>)` | Links to specific block |
28
- | Image embed | `![alt text](URL)` | Inline image |
29
-
30
- ### Tags & Hashtags
31
- | Type | Syntax | When to Use |
32
- |------|--------|-------------|
33
- | Single word | `#tag` | Simple categorization |
34
- | Multi-word | `#[[multiple words]]` | Phrases, compound concepts |
35
- | Hyphenated | `#self-esteem` | Naturally hyphenated terms |
36
-
37
- ⚠️ **CRITICAL**: Never concatenate multi-word tags. `#knowledgemanagement` ≠ `#[[knowledge management]]`
38
-
39
- ⚠️ **CRITICAL**: Never use `#` to mean "number" (e.g., `#1`, `#2`). In Roam, `#` **always** creates a hashtag. Write `Step 1`, `No. 1`, or just spell out the number instead.
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:** `![alt](URL)`
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`
40
24
 
41
25
  ### Dates
42
- - **Always use ordinal format**: `[[January 1st, 2025]]`, `[[December 23rd, 2024]]`
43
- - Ordinals: 1st, 2nd, 3rd, 4th–20th, 21st, 22nd, 23rd, 24th–30th, 31st
26
+ Always ordinal format: `[[January 1st, 2025]]`, `[[December 23rd, 2024]]`
44
27
 
45
- ### Task Management
46
- | Status | Syntax |
47
- |--------|--------|
48
- | Todo | `{{[[TODO]]}} task description` |
49
- | Done | `{{[[DONE]]}} task description` |
28
+ ### Tasks
29
+ - Todo: `{{[[TODO]]}} task`
30
+ - Done: `{{[[DONE]]}} task`
50
31
 
51
- ### Attributes (Properties)
32
+ ### Attributes
52
33
  ```
53
34
  Type:: Book
54
35
  Author:: [[Person Name]]
55
36
  Rating:: 4/5
56
- Source:: https://example.com
57
37
  ```
58
38
 
59
- **Purpose**: Attributes create structured metadata that is **queryable across your entire graph**. The attribute name becomes a page reference, so only use `::` when the attribute is a reusable property that applies to multiple pages or concepts.
60
-
61
- **When to USE attributes:**
62
- | Attribute | Why It's Good |
63
- |-----------|---------------|
64
- | `Type:: Book` | Reusable across all media you consume |
65
- | `Author:: [[Person]]` | Links to author page, queryable |
66
- | `Status:: In Progress` | Standard project states, queryable |
67
- | `Source:: URL` | Consistent sourcing across notes |
68
- | `Date:: [[January 1st, 2025]]` | Enables date-based queries |
69
-
70
- **When NOT to use attributes:**
71
- | ❌ Wrong | ✅ Use Instead | Why |
72
- |----------|----------------|-----|
73
- | `Step 1:: Do this thing` | `**Step 1:** Do this thing` | Step numbers are page-specific, not queryable concepts |
74
- | `Note:: Some observation` | Just write the text, or use `#note` | One-off labels don't need attribute syntax |
75
- | `Summary:: The main point` | `**Summary:** The main point` | Section headers are formatting, not metadata |
76
- | `Definition:: Some text` | `Term:: Definition` | Only use for actual definitions you want to query |
77
- | `Implementation Tier 3 (Societal Restructuring):: Some text` | `**Implementation Tier 3 (Societal Restructuring):** Some text` | Label is specific to current concept |
78
-
79
- ⚠️ **The Test**: Ask yourself: "Will I ever query for all blocks with this attribute across my graph?" If no, use **bold formatting** (`**Label:**`) instead of `::` syntax.
39
+ **Use `::` when:** queryable across graph (Type, Author, Status, Source, Date)
40
+ **Use bold instead when:** page-specific labels (Step 1, Summary, Note)
80
41
 
81
- NOTE: Never combine bold markdown formatting with `::`. Roam formats attributes in bold by default. ✅ `<attribute>::` ❌ `**<attribute>**::`
82
-
83
- ---
42
+ ⚠️ Test: "Will I query all blocks with this attribute?" If no use `**Label:**` instead
43
+ ⚠️ Never `**Attr**::` — Roam auto-bolds attributes
84
44
 
85
45
  ## Block Structures
86
46
 
87
- ### Bullet Points
88
- - Use `-` (dash) followed by space
89
- - Nested bullets: indent with tab or spaces
47
+ ### Bullets
90
48
  ```
91
- - Parent item
92
- - Child item
93
- - Grandchild item
49
+ - Parent
50
+ - Child
51
+ - Grandchild
94
52
  ```
95
53
 
96
54
  ### Code Blocks
97
55
  ````
98
56
  ```javascript
99
- const example = () => {
100
- return "syntax highlighted";
101
- }
57
+ const x = 1;
102
58
  ```
103
59
  ````
104
60
 
105
61
  ### Queries
106
62
  ```
107
63
  {{[[query]]: {and: [[tag1]] [[tag2]]}}}
108
- {{[[query]]: {or: [[optionA]] [[optionB]]}}}
109
- {{[[query]]: {not: [[exclude-this]]}}}
64
+ {{[[query]]: {or: [[A]] [[B]]}}}
65
+ {{[[query]]: {not: [[exclude]]}}}
110
66
  {{[[query]]: {between: [[January 1st, 2025]] [[January 31st, 2025]]}}}
111
67
  ```
112
68
 
113
69
  ### Calculator
114
- ```
115
- {{[[calc]]: 2 + 2}}
116
- {{[[calc]]: 100 * 0.15}}
70
+ `{{[[calc]]: 2 + 2}}`
71
+
72
+ ### Codeblock
73
+ Roam uses "shell" not "bash". Other common languages okay, including "plain text".
74
+ ```shell
75
+ echo "Hello world!"
117
76
  ```
118
77
 
119
- ---
120
78
 
121
79
  ## Complex Structures
122
80
 
123
81
  ### Tables
124
- Tables use nested indentation. Each column header/cell nests ONE LEVEL DEEPER than previous.
125
-
82
+ Each column nests ONE LEVEL DEEPER than previous:
126
83
  ```
127
84
  {{[[table]]}}
128
85
  - Header 1
@@ -131,235 +88,114 @@ Tables use nested indentation. Each column header/cell nests ONE LEVEL DEEPER th
131
88
  - Row 1 Label
132
89
  - Cell 1.1
133
90
  - Cell 1.2
134
- - Cell 1.3
135
91
  - Row 2 Label
136
92
  - Cell 2.1
137
93
  - Cell 2.2
138
- - Cell 2.3
139
94
  ```
95
+ Keep tables ≤5 columns.
140
96
 
141
- **Rules:**
142
- - `{{[[table]]}}` is level 1
143
- - First header/row-label at level 2
144
- - Each subsequent column nests +1 level deeper
145
- - Keep tables ≤5 columns for readability
146
-
147
- ### Kanban Boards
97
+ ### Kanban
148
98
  ```
149
99
  {{[[kanban]]}}
150
- - Column 1 Title
151
- - Card 1 content
152
- - Card 2 content
153
- - Column 2 Title
154
- - Card 3 content
100
+ - Column 1
101
+ - Card 1
102
+ - Card 2
103
+ - Column 2
104
+ - Card 3
155
105
  ```
156
106
 
157
- ### Mermaid Diagrams
107
+ ### Mermaid
158
108
  ```
159
109
  {{[[mermaid]]}}
160
110
  - graph TD
161
111
  - A[Start] --> B{Decision}
162
112
  - B -->|Yes| C[Action]
163
- - B -->|No| D[Alternative]
164
- ```
165
-
166
- ### Hiccup (Custom HTML)
167
- `:hiccup [:iframe {:width "600" :height "400" :src "https://example.com"}]`
168
-
169
- `:hiccup [:div {:style {:color "red"}} "Custom styled content"]`
170
-
171
- ---
172
-
173
- ## Anti-Patterns — DON'T DO THIS
174
-
175
- | ❌ Wrong | ✅ Correct | Why |
176
- |----------|-----------|-----|
177
- | `Step 1:: Do this` | `**Step 1:** Do this` | `::` creates queryable attributes; use bold for page-specific labels |
178
- | `#multiplewords` | `#[[multiple words]]` | Concatenated tags create dead references |
179
- | `#1`, `#2`, `#3` | `Step 1`, `No. 1`, or spell out | `#` always creates hashtags, never means "number" |
180
- | `[[january 1, 2025]]` | `[[January 1st, 2025]]` | Must use ordinal format with proper capitalization |
181
- | `[text](((block-uid)))` | `[text](<((block-uid))>)` | Block ref links need angle bracket wrapper |
182
- | `{{embed: ((uid))}}` | `{{[[embed]]: ((uid))}}` | Embed requires double brackets around keyword |
183
- | Deeply nested tables (6+ cols) | Max 4-5 columns | Becomes unreadable/unmanageable |
184
- | `- *bullet` | `- bullet` | Use dash, not asterisk for bullets |
185
- | `[[TODO]] task` | `{{[[TODO]]}} task` | TODO needs double curly braces |
186
-
187
- ---
188
-
189
- ## Tool Selection Decision Tree
190
-
191
113
  ```
192
- CREATING CONTENT IN ROAM:
193
-
194
- ┌─ Is this a NEW standalone page with structure?
195
- │ └─ YES → roam_create_page (with content array)
196
-
197
- ├─ Adding content to EXISTING page/block?
198
- │ ├─ Simple outline structure → roam_create_outline
199
- │ │ (provide page_title_uid and/or block_text_uid)
200
- │ │
201
- │ └─ Complex/nested markdown → roam_import_markdown
202
- │ (for deeply nested content, tables, etc.)
203
-
204
- ├─ Replacing/revising ENTIRE page content?
205
- │ └─ roam_update_page_markdown
206
- │ (fetches page internally, computes smart diff, preserves UIDs)
207
-
208
- ├─ Need to CREATE, UPDATE, MOVE, or DELETE individual blocks?
209
- │ └─ roam_process_batch_actions
210
- │ (fine-grained control, UID placeholders for parent refs)
211
-
212
- ├─ Creating a TABLE?
213
- │ └─ roam_create_table
214
- │ (handles complex nested structure automatically)
215
-
216
- ├─ Adding a memory/note to remember?
217
- │ └─ roam_remember (auto-tags with MEMORIES_TAG)
218
-
219
- ├─ Adding TODO items to today?
220
- │ └─ roam_add_todo (creates individual TODO blocks)
221
-
222
- └─ SEARCHING/READING:
223
- ├─ Find by tag → roam_search_for_tag
224
- ├─ Find by text → roam_search_by_text
225
- ├─ Find by date range → roam_search_by_date
226
- ├─ Find by status → roam_search_by_status
227
- ├─ Find block/page references → roam_search_block_refs
228
- ├─ Find pages modified today → roam_find_pages_modified_today
229
- ├─ Get page content → roam_fetch_page_by_title
230
- ├─ Get block + children → roam_fetch_block_with_children
231
- ├─ Recall memories → roam_recall
232
- └─ Complex queries → roam_datomic_query
233
- ```
234
-
235
- ---
236
-
237
- ## API Efficiency Guidelines (Rate Limit Avoidance)
238
-
239
- The Roam API has rate limits. Follow these guidelines to minimize API calls:
240
-
241
- ### Tool Efficiency Ranking (Best to Worst)
242
- 1. **`roam_update_page_markdown`** - Single call: fetches, diffs, and updates (MOST EFFICIENT for revisions)
243
- 2. **`roam_process_batch_actions`** - Single API call for multiple operations
244
- 3. **`roam_create_page`** - Batches content with page creation
245
- 4. **`roam_create_outline` / `roam_import_markdown`** - Include verification queries (use for smaller operations)
246
- 5. **Multiple sequential tool calls** - Each call = multiple API requests (AVOID)
247
-
248
- ### Best Practices for Intensive Operations
249
-
250
- #### When Updating/Revising a Page:
251
- 1. **Preferred**: Use `roam_update_page_markdown` — it fetches, diffs, and updates in one call
252
- 2. **Alternative** (for fine-grained control): Fetch once with `roam_fetch_page_by_title`, then execute ALL changes in a SINGLE `roam_process_batch_actions` call
253
- 3. Do NOT fetch-modify-fetch-modify in a loop
254
-
255
- #### When Creating Large Content:
256
- - For 10+ blocks: Use `roam_process_batch_actions` with nested structure
257
- - For simple outlines (<10 blocks): `roam_create_outline` is fine
258
-
259
- #### UID Caching:
260
- - Save UIDs from previous operations - don't re-query for them
261
- - Use `page_uid` instead of `page_title` when available (avoids lookup query)
262
- - Use `block_uid` instead of `block_text_uid` when you have it
263
114
 
264
- #### UID Placeholders for Nested Blocks:
265
- When using `roam_process_batch_actions` to create nested blocks, use **placeholder tokens** instead of generating UIDs yourself. The server generates proper random UIDs and returns a mapping.
115
+ ### Hiccup
116
+ `:hiccup [:iframe {:width "600" :height "400" :src "URL"}]`
266
117
 
267
- **Syntax:** `{{uid:name}}` where `name` is any identifier you choose.
118
+ ## Anti-Patterns
268
119
 
269
- **Example:**
120
+ | ❌ Wrong | ✅ Correct |
121
+ |----------|-----------|
122
+ | `#multiplewords` | `#[[multiple words]]` |
123
+ | `#1`, `#2` | `Step 1`, `No. 1` |
124
+ | `[[january 1, 2025]]` | `[[January 1st, 2025]]` |
125
+ | `[text](((uid)))` | `[text](<((uid))>)` |
126
+ | `{{embed: ((uid))}}` | `{{[[embed]]: ((uid))}}` |
127
+ | `[[TODO]] task` | `{{[[TODO]]}} task` |
128
+ | `- *bullet` | `- bullet` |
129
+ | `* bullet` | `- bullet` |
130
+ | `**Attr**:: val` | `Attr:: val` |
131
+
132
+ ## Tool Selection
133
+
134
+ ```
135
+ CREATING:
136
+ ├─ New page + structure → roam_create_page
137
+ ├─ Add to existing page/block:
138
+ │ ├─ Simple outline → roam_create_outline
139
+ │ └─ Complex markdown → roam_import_markdown
140
+ ├─ Revise entire page → roam_update_page_markdown
141
+ ├─ Fine-grained CRUD → roam_process_batch_actions
142
+ ├─ Table → roam_create_table
143
+ ├─ Memory → roam_remember
144
+ └─ Todos → roam_add_todo
145
+
146
+ SEARCHING:
147
+ ├─ By tag → roam_search_for_tag
148
+ ├─ By text → roam_search_by_text
149
+ ├─ By date → roam_search_by_date
150
+ ├─ By status → roam_search_by_status
151
+ ├─ Block refs → roam_search_block_refs
152
+ ├─ Modified today → roam_find_pages_modified_today
153
+ ├─ Page content → roam_fetch_page_by_title
154
+ ├─ Block + children → roam_fetch_block_with_children
155
+ ├─ Memories → roam_recall
156
+ └─ Complex → roam_datomic_query
157
+ ```
158
+
159
+ ## API Efficiency
160
+
161
+ **Ranking (best → worst):**
162
+ 1. `roam_update_page_markdown` — single call: fetch + diff + update
163
+ 2. `roam_process_batch_actions` — batch multiple ops
164
+ 3. `roam_create_page` — batches content with creation
165
+ 4. `roam_create_outline` / `roam_import_markdown` — includes verification
166
+ 5. Multiple sequential calls — avoid
167
+
168
+ **Best practices:**
169
+ - Use `roam_update_page_markdown` for revisions (handles everything)
170
+ - For 10+ blocks: `roam_process_batch_actions`
171
+ - Cache UIDs — use `page_uid` over `page_title` when available
172
+ - Never fetch-modify-fetch-modify in loops
173
+
174
+ ### UID Placeholders
175
+ Use `{{uid:name}}` for parent refs in batch actions:
270
176
  ```json
271
177
  [
272
- { "action": "create-block", "uid": "{{uid:parent}}", "string": "Parent Block", "location": { "parent-uid": "pageUid123", "order": 0 } },
273
- { "action": "create-block", "string": "Child Block", "location": { "parent-uid": "{{uid:parent}}", "order": 0 } }
178
+ {"action": "create-block", "uid": "{{uid:parent}}", "string": "Parent", "location": {"parent-uid": "pageUid", "order": 0}},
179
+ {"action": "create-block", "string": "Child", "location": {"parent-uid": "{{uid:parent}}", "order": 0}}
274
180
  ]
275
181
  ```
276
-
277
- **Response includes UID mapping:**
278
- ```json
279
- {
280
- "success": true,
281
- "uid_map": {
282
- "parent": "Xk7mN2pQ9"
283
- }
284
- }
285
- ```
286
-
287
- **Why placeholders?** LLMs are not reliable random generators. The server uses cryptographically secure randomness to generate proper 9-character Roam UIDs.
288
-
289
- ### Example: Efficient Page Revision
290
-
291
- **Instead of:**
292
- ```
293
- 1. roam_fetch_page_by_title → get content
294
- 2. roam_create_outline → add section 1
295
- 3. roam_create_outline → add section 2
296
- 4. roam_import_markdown → add more content
297
- ```
298
-
299
- **Do this:**
300
- ```
301
- 1. roam_update_page_markdown → single call handles fetch, diff, and updates
302
- ```
303
-
304
- **Alternative** (when you need fine-grained control):
305
- ```
306
- 1. roam_fetch_page_by_title → get page UID and content
307
- 2. roam_process_batch_actions → ALL creates/updates in one call
308
- ```
309
-
310
- ---
182
+ Server returns `{"uid_map": {"parent": "Xk7mN2pQ9"}}`.
311
183
 
312
184
  ## Structural Defaults
313
185
 
314
- When unsure how to structure output:
315
-
316
- 1. **Hierarchy depth**: Prefer 2-4 levels; rarely exceed 5
317
- 2. **Block length**: Keep blocks atomic one idea per block
318
- 3. **Page refs vs hashtags**:
319
- - `[[Page]]` for concepts you'll expand into full pages
320
- - `#tag` for categorization/filtering
321
- 4. **When to embed vs reference**:
322
- - `((uid))` — inline reference (shows content)
323
- - `{{[[embed]]: ((uid))}}` — full block with children
324
- - `[text](<((uid))>)` — clickable link only
325
-
326
- ---
327
- ## Visual Separation — Hierarchy First, Separators Never
328
-
329
- Empty blocks and decorative dividers create clutter. Roam's outliner structure provides all the visual separation you need.
186
+ - **Hierarchy:** 2-4 levels preferred, rarely exceed 5
187
+ - **Blocks:** One idea per block
188
+ - **Page refs vs tags:** `[[Page]]` for expandable concepts, `#tag` for filtering
189
+ - **Embed vs ref:** `((uid))` inline, `{{[[embed]]: ((uid))}}` with children, `[text](<((uid))>)` link only
190
+ - **No empty blocks or `---` dividers** — use hierarchy for visual separation
330
191
 
331
- | Avoid | ✅ Instead |
332
- |----------|-----------|
333
- | Blank blocks | Let hierarchy create space — child blocks are visually indented |
334
- | `---` dividers | Use a **heading block** to signal section breaks |
335
- | Decorative lines `───` | Nest content under a parent block as a structural container |
336
-
337
- **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.
192
+ ## Output Conventions
338
193
 
339
- **The test**: If you'd delete it during cleanup, don't create it.
194
+ **Quote:** `<text> —[[Author]] #quote`
195
+ **Definition:** `Term:: definition #definition`
196
+ **Open question:** `{{[[TODO]]}} Research: <question> #[[open questions]]`
340
197
 
341
198
  ---
342
-
343
- ## Output Format Conventions
344
-
345
- ### Quotes
346
- ```
347
- <quote text> —[[Author Name]] #quote #[[relevant topic]]
348
- ```
349
-
350
- ### Definitions
351
- ```
352
- Term:: Definition text #definition #[[domain]]
353
- ```
354
-
355
- ### Questions for Future
356
- ```
357
- {{[[TODO]]}} Research: <question> #[[open questions]]
358
- ```
359
-
360
- ---
361
-
362
- *End of Generic Foundation — Personalization section follows during user setup.*
363
199
  # Roam Preferences — Personalization Layer
364
200
 
365
201
  > This section contains YOUR specific conventions, tagging philosophy, and graph-specific rules. Customize to match your workflow.
@@ -421,6 +257,11 @@ ASK YOURSELF:
421
257
  | `#single-word` | Simple, unambiguous category |
422
258
  | Attribute `Type::` | Structured metadata for queries |
423
259
 
260
+ ### WHEN creating Endnotes/Footnotes:
261
+ - Find/Create the block with heading "Footnotes::" and nest footnote item below. (Footnotes do not need to be on the same page as the block to which it references. Typically on the same page unless instructed otherwise.)
262
+ - If not known, retrieve the block_uid reference for this footnote item.
263
+ - In the block referencing the footnote, append the reference with footnote-item-block_id, example: "- <block_text> #ref ((block_uid))"
264
+
424
265
  ### Structural Tagging (Beyond Content)
425
266
 
426
267
  Tag by **patterns and mechanisms**, not just subjects:
@@ -466,6 +307,9 @@ Always include 2-3 relevant hashtags after quotes.
466
307
  ```
467
308
 
468
309
  ### Scheduled Reviews
310
+
311
+ - Any block tagged with a date will show on that respective daily page.
312
+
469
313
  ```
470
314
  [[For review]]: [[Date in ordinal format]]
471
315
  ```
@@ -492,6 +336,7 @@ When a tag would awkwardly affect sentence capitalization:
492
336
  - **Use inconsistent capitalization** — Tags are lowercase unless proper nouns
493
337
  - **Create orphan tags** — Check if existing page/tag serves the purpose
494
338
  - **Bold Attributes** - ❌ `**Attribute**::`, ✅ `Attribute::` (Roam auto-formats)
339
+ - **Separators** - `---` Don't use them.
495
340
 
496
341
  ### DO
497
342
  - **Think retrieval-first** — How will you search for this later?
@@ -516,7 +361,7 @@ CUSTOMIZE THIS SECTION with your specific conventions:
516
361
 
517
362
  **Books:**
518
363
  ```
519
- [[Book: Title by Author]]
364
+ [[Book/<title> | <author>]]
520
365
  Type:: Book
521
366
  Author:: [[Author Name]]
522
367
  Status:: Reading | Completed | Abandoned
@@ -529,14 +374,18 @@ Rating:: X/5
529
374
  Type:: Person
530
375
  Context:: How I know them
531
376
  ```
532
-
377
+ - When linking bibliographic references —>
378
+ Example: `McAdams, D.P. (2001) [The Psychology of Life Stories](https://journals.sagepub.com/doi/10.1037/1089-2680.5.2.100) — foundational paper`
379
+ - [McAdams, D.P.]([[Dan McAdams]]) - author's name in the graph
380
+ - If source URL, link to source: [The Psychology of Life Stories](https://journals.sagepub.com/doi/10.1037/1089-2680.5.2.100)
381
+ - If notes page exists or will exist in Roam: append ` | [Notes]([[Article/The Psychology of Life Stories]]), if not, just leave it without link.
382
+
533
383
  **Projects:**
534
384
  ```
535
- [[Project: Name]]
385
+ [[Project/<project anme>]]
536
386
  Status:: Active | Paused | Completed
537
387
  Start:: [[Date]]
538
388
  ```
539
-
540
389
  ---
541
390
 
542
391
  ## Integration Notes
@@ -1,11 +1,16 @@
1
1
  /**
2
- * Simple in-memory cache for page title -> UID mappings.
2
+ * Simple in-memory cache for page title -> UID mappings and UID existence tracking.
3
3
  * Pages are stable entities that rarely get deleted, making them safe to cache.
4
4
  * This reduces redundant API queries when looking up the same page multiple times.
5
+ *
6
+ * The cache tracks two things:
7
+ * 1. Title -> UID mappings (for getPageUid lookups)
8
+ * 2. Known existing UIDs (for existence validation before batch operations)
5
9
  */
6
10
  class PageUidCache {
7
11
  constructor() {
8
12
  this.cache = new Map(); // title (lowercase) -> UID
13
+ this.knownUids = new Set(); // UIDs confirmed to exist
9
14
  }
10
15
  /**
11
16
  * Get a cached page UID by title.
@@ -17,11 +22,13 @@ class PageUidCache {
17
22
  }
18
23
  /**
19
24
  * Cache a page title -> UID mapping.
25
+ * Also marks the UID as known to exist.
20
26
  * @param title - Page title (will be stored lowercase)
21
27
  * @param uid - Page UID
22
28
  */
23
29
  set(title, uid) {
24
30
  this.cache.set(title.toLowerCase(), uid);
31
+ this.knownUids.add(uid);
25
32
  }
26
33
  /**
27
34
  * Check if a page title is cached.
@@ -30,6 +37,30 @@ class PageUidCache {
30
37
  has(title) {
31
38
  return this.cache.has(title.toLowerCase());
32
39
  }
40
+ /**
41
+ * Check if a UID is known to exist.
42
+ * @param uid - Page or block UID
43
+ */
44
+ hasUid(uid) {
45
+ return this.knownUids.has(uid);
46
+ }
47
+ /**
48
+ * Mark a UID as known to exist (without title mapping).
49
+ * Use this when you've verified a UID exists but don't know/need its title.
50
+ * @param uid - Page or block UID
51
+ */
52
+ addUid(uid) {
53
+ this.knownUids.add(uid);
54
+ }
55
+ /**
56
+ * Mark multiple UIDs as known to exist.
57
+ * @param uids - Array of page or block UIDs
58
+ */
59
+ addUids(uids) {
60
+ for (const uid of uids) {
61
+ this.knownUids.add(uid);
62
+ }
63
+ }
33
64
  /**
34
65
  * Called when a page is created - immediately add to cache.
35
66
  * @param title - Page title
@@ -43,13 +74,20 @@ class PageUidCache {
43
74
  */
44
75
  clear() {
45
76
  this.cache.clear();
77
+ this.knownUids.clear();
46
78
  }
47
79
  /**
48
- * Get the current cache size.
80
+ * Get the current title cache size.
49
81
  */
50
82
  get size() {
51
83
  return this.cache.size;
52
84
  }
85
+ /**
86
+ * Get the current known UIDs cache size.
87
+ */
88
+ get uidCacheSize() {
89
+ return this.knownUids.size;
90
+ }
53
91
  }
54
92
  // Singleton instance - shared across all operations
55
93
  export const pageUidCache = new PageUidCache();
@@ -296,7 +296,7 @@ function translateRemember(cmd, context) {
296
296
  const tags = params.categories.map(cat => `#[[${cat}]]`).join(' ');
297
297
  memoryText = `${params.text} ${tags}`;
298
298
  }
299
- // Add MEMORIES_TAG if configured (we'll handle this in the CLI command)
299
+ // Add ROAM_MEMORIES_TAG if configured (we'll handle this in the CLI command)
300
300
  // For now, just create the block
301
301
  let parentUid;
302
302
  // If heading is specified, we'd need to look it up or create it
@@ -6,14 +6,7 @@ import { printDebug, exitWithError } from '../utils/output.js';
6
6
  import { resolveGraph } from '../utils/graph.js';
7
7
  import { collectPageTitles, resolveAllPages, resolveDailyPageUid, needsDailyPage, createResolutionContext, getDailyPageTitle } from '../batch/resolver.js';
8
8
  import { translateAllCommands } from '../batch/translator.js';
9
- /** Read all input from stdin */
10
- async function readStdin() {
11
- const chunks = [];
12
- for await (const chunk of process.stdin) {
13
- chunks.push(chunk);
14
- }
15
- return Buffer.concat(chunks).toString('utf-8');
16
- }
9
+ import { readStdin } from '../utils/input.js';
17
10
  // Required params per command type
18
11
  const REQUIRED_PARAMS = {
19
12
  todo: ['text'],
@@ -167,6 +160,8 @@ Example:
167
160
  {"command": "outline", "params": {"parent": "{{overview}}", "items": ["Goal 1", "Goal 2"]}},
168
161
  {"command": "todo", "params": {"text": "Review project"}}
169
162
  ]
163
+
164
+ Output (JSON): { success, pages_created, actions_executed, uid_map? }
170
165
  `)
171
166
  .action(async (file, options) => {
172
167
  try {