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,8 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { initializeGraph } from '@roam-research/roam-api-sdk';
|
|
3
|
-
import { API_TOKEN, GRAPH_NAME } from '../../config/environment.js';
|
|
4
2
|
import { SearchOperations } from '../../tools/operations/search/index.js';
|
|
5
3
|
import { printDebug, exitWithError } from '../utils/output.js';
|
|
4
|
+
import { resolveGraph } from '../utils/graph.js';
|
|
5
|
+
import { readStdin } from '../utils/input.js';
|
|
6
6
|
/**
|
|
7
7
|
* Format results grouped by page (default output)
|
|
8
8
|
*/
|
|
@@ -72,46 +72,77 @@ function parseIdentifier(identifier) {
|
|
|
72
72
|
}
|
|
73
73
|
export function createRefsCommand() {
|
|
74
74
|
return new Command('refs')
|
|
75
|
-
.description('Find blocks
|
|
76
|
-
.argument('
|
|
75
|
+
.description('Find all blocks that reference a page, tag, or block')
|
|
76
|
+
.argument('[identifier]', 'Page title, #tag, [[Page]], or ((block-uid)). Reads from stdin if "-" or omitted.')
|
|
77
77
|
.option('-n, --limit <n>', 'Limit number of results', '50')
|
|
78
78
|
.option('--json', 'Output as JSON array')
|
|
79
79
|
.option('--raw', 'Output raw UID + content lines (no grouping)')
|
|
80
80
|
.option('--debug', 'Show query metadata')
|
|
81
|
+
.option('-g, --graph <name>', 'Target graph key (for multi-graph mode)')
|
|
82
|
+
.addHelpText('after', `
|
|
83
|
+
Examples:
|
|
84
|
+
# Page references
|
|
85
|
+
roam refs "Project Alpha" # Blocks linking to page
|
|
86
|
+
roam refs "#TODO" # Blocks with #TODO tag
|
|
87
|
+
|
|
88
|
+
# Stdin / Batch references
|
|
89
|
+
echo "Project A" | roam refs # Pipe page title
|
|
90
|
+
cat uids.txt | roam refs --json # Find refs for multiple UIDs
|
|
91
|
+
|
|
92
|
+
# Block references
|
|
93
|
+
roam refs "((abc123def))" # Blocks embedding this block
|
|
94
|
+
`)
|
|
81
95
|
.action(async (identifier, options) => {
|
|
82
96
|
try {
|
|
83
|
-
const graph =
|
|
84
|
-
token: API_TOKEN,
|
|
85
|
-
graph: GRAPH_NAME
|
|
86
|
-
});
|
|
97
|
+
const graph = resolveGraph(options, false);
|
|
87
98
|
const limit = parseInt(options.limit || '50', 10);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
printDebug('Options', options);
|
|
99
|
+
// Determine identifiers
|
|
100
|
+
let identifiers = [];
|
|
101
|
+
if (identifier && identifier !== '-') {
|
|
102
|
+
identifiers = [identifier];
|
|
93
103
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (options.json) {
|
|
103
|
-
const jsonOutput = limitedMatches.map(m => ({
|
|
104
|
-
uid: m.block_uid,
|
|
105
|
-
content: m.content,
|
|
106
|
-
page: m.page_title
|
|
107
|
-
}));
|
|
108
|
-
console.log(JSON.stringify(jsonOutput, null, 2));
|
|
104
|
+
else {
|
|
105
|
+
if (process.stdin.isTTY && identifier !== '-') {
|
|
106
|
+
exitWithError('Identifier is required. Use: roam refs <title> or pipe identifiers via stdin');
|
|
107
|
+
}
|
|
108
|
+
const input = await readStdin();
|
|
109
|
+
if (input) {
|
|
110
|
+
identifiers = input.split('\n').map(t => t.trim()).filter(Boolean);
|
|
111
|
+
}
|
|
109
112
|
}
|
|
110
|
-
|
|
111
|
-
|
|
113
|
+
if (identifiers.length === 0) {
|
|
114
|
+
exitWithError('No identifiers provided');
|
|
112
115
|
}
|
|
113
|
-
|
|
114
|
-
|
|
116
|
+
const searchOps = new SearchOperations(graph);
|
|
117
|
+
// Helper to process a single identifier
|
|
118
|
+
const processIdentifier = async (id) => {
|
|
119
|
+
const { block_uid, title } = parseIdentifier(id);
|
|
120
|
+
if (options.debug) {
|
|
121
|
+
printDebug('Identifier', id);
|
|
122
|
+
printDebug('Parsed', { block_uid, title });
|
|
123
|
+
}
|
|
124
|
+
const result = await searchOps.searchBlockRefs({ block_uid, title });
|
|
125
|
+
const limitedMatches = result.matches.slice(0, limit);
|
|
126
|
+
if (options.json) {
|
|
127
|
+
return JSON.stringify(limitedMatches.map(m => ({
|
|
128
|
+
uid: m.block_uid,
|
|
129
|
+
content: m.content,
|
|
130
|
+
page: m.page_title
|
|
131
|
+
})));
|
|
132
|
+
}
|
|
133
|
+
else if (options.raw) {
|
|
134
|
+
return formatRaw(limitedMatches);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
return formatGrouped(limitedMatches);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
// Execute
|
|
141
|
+
for (const id of identifiers) {
|
|
142
|
+
const output = await processIdentifier(id);
|
|
143
|
+
console.log(output);
|
|
144
|
+
if (identifiers.length > 1 && !options.json)
|
|
145
|
+
console.log('\n---\n');
|
|
115
146
|
}
|
|
116
147
|
}
|
|
117
148
|
catch (error) {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { updatePage } from '@roam-research/roam-api-sdk';
|
|
3
|
+
import { printDebug, exitWithError } from '../utils/output.js';
|
|
4
|
+
import { resolveGraph } from '../utils/graph.js';
|
|
5
|
+
export function createRenameCommand() {
|
|
6
|
+
return new Command('rename')
|
|
7
|
+
.description('Rename a page')
|
|
8
|
+
.argument('<old-title>', 'Current page title (or use --uid for UID)')
|
|
9
|
+
.argument('<new-title>', 'New page title')
|
|
10
|
+
.option('-u, --uid <uid>', 'Use page UID instead of title')
|
|
11
|
+
.option('-g, --graph <name>', 'Target graph key (multi-graph mode)')
|
|
12
|
+
.option('--write-key <key>', 'Write confirmation key (non-default graphs)')
|
|
13
|
+
.option('--debug', 'Show debug information')
|
|
14
|
+
.addHelpText('after', `
|
|
15
|
+
Examples:
|
|
16
|
+
# Rename by title
|
|
17
|
+
roam rename "Old Page Name" "New Page Name"
|
|
18
|
+
|
|
19
|
+
# Rename by UID
|
|
20
|
+
roam rename --uid abc123def "New Page Name"
|
|
21
|
+
|
|
22
|
+
# Multi-graph
|
|
23
|
+
roam rename "Draft" "Published" -g work --write-key confirm
|
|
24
|
+
`)
|
|
25
|
+
.action(async (oldTitle, newTitle, options) => {
|
|
26
|
+
try {
|
|
27
|
+
if (options.debug) {
|
|
28
|
+
printDebug('Old title', oldTitle);
|
|
29
|
+
printDebug('New title', newTitle);
|
|
30
|
+
printDebug('UID', options.uid || 'none (using title)');
|
|
31
|
+
printDebug('Graph', options.graph || 'default');
|
|
32
|
+
}
|
|
33
|
+
const graph = resolveGraph(options, true); // Write operation
|
|
34
|
+
// Build the page identifier
|
|
35
|
+
const pageIdentifier = options.uid
|
|
36
|
+
? { uid: options.uid }
|
|
37
|
+
: { title: oldTitle };
|
|
38
|
+
if (options.debug) {
|
|
39
|
+
printDebug('Page identifier', pageIdentifier);
|
|
40
|
+
}
|
|
41
|
+
const success = await updatePage(graph, {
|
|
42
|
+
page: pageIdentifier,
|
|
43
|
+
title: newTitle
|
|
44
|
+
});
|
|
45
|
+
if (success) {
|
|
46
|
+
const identifier = options.uid ? `((${options.uid}))` : `"${oldTitle}"`;
|
|
47
|
+
console.log(`Renamed ${identifier} → "${newTitle}"`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
exitWithError('Failed to rename page (API returned false)');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
55
|
+
exitWithError(message);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|