@parall/cli 1.32.1 → 1.34.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/commands/chats.d.ts.map +1 -1
- package/dist/commands/chats.js +5 -2
- package/dist/commands/clip.js +2 -2
- package/dist/commands/mcp.d.ts +5 -0
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +31 -482
- package/dist/lib/wiki-tools.d.ts +33 -0
- package/dist/lib/wiki-tools.d.ts.map +1 -0
- package/dist/lib/wiki-tools.js +540 -0
- package/package.json +6 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chats.d.ts","sourceRoot":"","sources":["../../src/commands/chats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,
|
|
1
|
+
{"version":3,"file":"chats.d.ts","sourceRoot":"","sources":["../../src/commands/chats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QA+IpD"}
|
package/dist/commands/chats.js
CHANGED
|
@@ -116,11 +116,14 @@ export function registerChatCommands(program) {
|
|
|
116
116
|
.description('Add a member to a chat')
|
|
117
117
|
.argument('<chatId>', 'Chat ID')
|
|
118
118
|
.requiredOption('--user-id <userId>', 'User ID to add')
|
|
119
|
-
.option('--role <role>', '
|
|
119
|
+
.option('--role <role>', 'Deprecated. Only "member" is supported')
|
|
120
120
|
.action(async (chatId, opts) => {
|
|
121
121
|
try {
|
|
122
|
+
if (opts.role !== undefined && opts.role !== 'member') {
|
|
123
|
+
throw new Error('--role only supports "member"; update the role after adding the member');
|
|
124
|
+
}
|
|
122
125
|
const { client, orgId } = resolveCredentials();
|
|
123
|
-
await client.addChatMember(orgId, stripPrllScheme(chatId), stripPrllScheme(opts.userId)
|
|
126
|
+
await client.addChatMember(orgId, stripPrllScheme(chatId), stripPrllScheme(opts.userId));
|
|
124
127
|
printJson({ ok: true });
|
|
125
128
|
}
|
|
126
129
|
catch (err) {
|
package/dist/commands/clip.js
CHANGED
|
@@ -58,6 +58,7 @@ export function registerClipCommands(program) {
|
|
|
58
58
|
.description('Install a clip from a registry source')
|
|
59
59
|
.argument('<source>', 'Registry source identifier (used as name and alias)')
|
|
60
60
|
.option('--alias <alias>', 'Override the alias (default: derived from source)')
|
|
61
|
+
.requiredOption('-m, --machine-id <machineId>', 'Machine ID that runs this clip')
|
|
61
62
|
.action(async (source, opts) => {
|
|
62
63
|
try {
|
|
63
64
|
const { client, orgId } = resolveCredentials();
|
|
@@ -65,10 +66,9 @@ export function registerClipCommands(program) {
|
|
|
65
66
|
const alias = opts.alias ?? deriveAlias(source);
|
|
66
67
|
const req = {
|
|
67
68
|
alias,
|
|
68
|
-
name: source,
|
|
69
|
-
manifest: {},
|
|
70
69
|
source_type: 'registry',
|
|
71
70
|
source_ref: source,
|
|
71
|
+
machine_id: opts.machineId,
|
|
72
72
|
};
|
|
73
73
|
const clip = await client.createClip(orgId, req);
|
|
74
74
|
printJson(clip);
|
package/dist/commands/mcp.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
/**
|
|
3
|
+
* Adds the `mcp` subcommand to the CLI, which runs a Parall MCP server over stdio.
|
|
4
|
+
* On invocation it resolves credentials, registers all wiki tools, and connects a
|
|
5
|
+
* stdio transport (server lifecycle log goes to stderr to keep stdout clean for the protocol).
|
|
6
|
+
*/
|
|
2
7
|
export declare function registerMcpCommands(program: Command): void;
|
|
3
8
|
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgCpC;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QAmBnD"}
|
package/dist/commands/mcp.js
CHANGED
|
@@ -1,8 +1,33 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import { resolveCredentials } from '../lib/client.js';
|
|
4
|
+
import { buildWikiTools } from '../lib/wiki-tools.js';
|
|
5
|
+
/**
|
|
6
|
+
* Registers a single wiki tool on the MCP server, wiring its schema/annotations and
|
|
7
|
+
* adapting the tool's `{ text, structuredContent }` result into MCP's content envelope.
|
|
8
|
+
*/
|
|
9
|
+
function registerWikiTool(server, tool) {
|
|
10
|
+
server.registerTool(tool.name, {
|
|
11
|
+
title: tool.title,
|
|
12
|
+
description: tool.description,
|
|
13
|
+
annotations: {
|
|
14
|
+
readOnlyHint: tool.readOnly,
|
|
15
|
+
},
|
|
16
|
+
inputSchema: tool.inputSchema,
|
|
17
|
+
outputSchema: tool.outputSchema,
|
|
18
|
+
}, async (args) => {
|
|
19
|
+
const result = await tool.handler(args);
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: 'text', text: result.text }],
|
|
22
|
+
structuredContent: result.structuredContent,
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Adds the `mcp` subcommand to the CLI, which runs a Parall MCP server over stdio.
|
|
28
|
+
* On invocation it resolves credentials, registers all wiki tools, and connects a
|
|
29
|
+
* stdio transport (server lifecycle log goes to stderr to keep stdout clean for the protocol).
|
|
30
|
+
*/
|
|
6
31
|
export function registerMcpCommands(program) {
|
|
7
32
|
program
|
|
8
33
|
.command('mcp')
|
|
@@ -13,485 +38,9 @@ export function registerMcpCommands(program) {
|
|
|
13
38
|
name: 'parall-mcp',
|
|
14
39
|
version: '1.0.0',
|
|
15
40
|
});
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
annotations: {
|
|
20
|
-
readOnlyHint: true,
|
|
21
|
-
},
|
|
22
|
-
outputSchema: {
|
|
23
|
-
wikis: z.array(z.object({
|
|
24
|
-
id: z.string(),
|
|
25
|
-
slug: z.string(),
|
|
26
|
-
name: z.string(),
|
|
27
|
-
default_branch: z.string(),
|
|
28
|
-
})),
|
|
29
|
-
},
|
|
30
|
-
}, async () => {
|
|
31
|
-
const wikis = await listWikis(ctx);
|
|
32
|
-
const structuredContent = {
|
|
33
|
-
wikis: wikis.map((wiki) => ({
|
|
34
|
-
id: wiki.id,
|
|
35
|
-
slug: wiki.slug,
|
|
36
|
-
name: wiki.name,
|
|
37
|
-
default_branch: wiki.default_branch,
|
|
38
|
-
})),
|
|
39
|
-
};
|
|
40
|
-
return {
|
|
41
|
-
content: [{ type: 'text', text: JSON.stringify(structuredContent, null, 2) }],
|
|
42
|
-
structuredContent,
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
server.registerTool('wiki_tree', {
|
|
46
|
-
title: 'Browse Wiki Tree',
|
|
47
|
-
description: 'List files and folders under a Parall wiki path.',
|
|
48
|
-
annotations: {
|
|
49
|
-
readOnlyHint: true,
|
|
50
|
-
},
|
|
51
|
-
inputSchema: {
|
|
52
|
-
wiki: z.string().describe('Wiki slug or ID'),
|
|
53
|
-
path: z.string().optional().describe('Optional tree path prefix inside the wiki'),
|
|
54
|
-
},
|
|
55
|
-
outputSchema: {
|
|
56
|
-
wiki_id: z.string(),
|
|
57
|
-
wiki_slug: z.string(),
|
|
58
|
-
wiki_name: z.string(),
|
|
59
|
-
path: z.string(),
|
|
60
|
-
entries: z.array(z.object({
|
|
61
|
-
name: z.string(),
|
|
62
|
-
path: z.string(),
|
|
63
|
-
type: z.enum(['tree', 'blob']),
|
|
64
|
-
size: z.number().optional(),
|
|
65
|
-
})),
|
|
66
|
-
},
|
|
67
|
-
}, async ({ wiki, path }) => {
|
|
68
|
-
const result = await getWikiTree(ctx, wiki, { path });
|
|
69
|
-
const structuredContent = {
|
|
70
|
-
wiki_id: result.wiki.id,
|
|
71
|
-
wiki_slug: result.wiki.slug,
|
|
72
|
-
wiki_name: result.wiki.name,
|
|
73
|
-
path: result.path,
|
|
74
|
-
entries: result.entries,
|
|
75
|
-
};
|
|
76
|
-
return {
|
|
77
|
-
content: [{ type: 'text', text: JSON.stringify(structuredContent, null, 2) }],
|
|
78
|
-
structuredContent,
|
|
79
|
-
};
|
|
80
|
-
});
|
|
81
|
-
server.registerTool('wiki_status', {
|
|
82
|
-
title: 'Inspect Wiki Workspace Status',
|
|
83
|
-
description: 'Inspect the current local mounted wiki workspace before proposing changes.',
|
|
84
|
-
annotations: {
|
|
85
|
-
readOnlyHint: true,
|
|
86
|
-
},
|
|
87
|
-
inputSchema: {
|
|
88
|
-
wiki: z.string().describe('Wiki slug or ID'),
|
|
89
|
-
},
|
|
90
|
-
outputSchema: {
|
|
91
|
-
wiki_id: z.string(),
|
|
92
|
-
wiki_slug: z.string(),
|
|
93
|
-
wiki_name: z.string(),
|
|
94
|
-
mount_path: z.string(),
|
|
95
|
-
mode: z.enum(['read_only', 'read_write']),
|
|
96
|
-
default_ref: z.string(),
|
|
97
|
-
changed_paths: z.array(z.string()),
|
|
98
|
-
diff_files: z.array(z.object({
|
|
99
|
-
path: z.string(),
|
|
100
|
-
additions: z.number(),
|
|
101
|
-
deletions: z.number(),
|
|
102
|
-
})),
|
|
103
|
-
dirty: z.boolean(),
|
|
104
|
-
source: z.literal('local_mount'),
|
|
105
|
-
},
|
|
106
|
-
}, async ({ wiki }) => {
|
|
107
|
-
const result = await getWikiWorkspaceStatus(ctx, wiki);
|
|
108
|
-
return {
|
|
109
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
110
|
-
structuredContent: result,
|
|
111
|
-
};
|
|
112
|
-
});
|
|
113
|
-
server.registerTool('wiki_diff', {
|
|
114
|
-
title: 'Read Wiki Workspace Diff',
|
|
115
|
-
description: 'Read the current unified diff for a local mounted wiki workspace.',
|
|
116
|
-
annotations: {
|
|
117
|
-
readOnlyHint: true,
|
|
118
|
-
},
|
|
119
|
-
inputSchema: {
|
|
120
|
-
wiki: z.string().describe('Wiki slug or ID'),
|
|
121
|
-
},
|
|
122
|
-
outputSchema: {
|
|
123
|
-
wiki_id: z.string(),
|
|
124
|
-
wiki_slug: z.string(),
|
|
125
|
-
wiki_name: z.string(),
|
|
126
|
-
mount_path: z.string(),
|
|
127
|
-
changed_paths: z.array(z.string()),
|
|
128
|
-
diff_files: z.array(z.object({
|
|
129
|
-
path: z.string(),
|
|
130
|
-
additions: z.number(),
|
|
131
|
-
deletions: z.number(),
|
|
132
|
-
})),
|
|
133
|
-
patch: z.string(),
|
|
134
|
-
source: z.literal('local_mount'),
|
|
135
|
-
},
|
|
136
|
-
}, async ({ wiki }) => {
|
|
137
|
-
const result = await getWikiWorkspaceDiff(ctx, wiki);
|
|
138
|
-
return {
|
|
139
|
-
content: [{ type: 'text', text: result.patch || JSON.stringify(result, null, 2) }],
|
|
140
|
-
structuredContent: result,
|
|
141
|
-
};
|
|
142
|
-
});
|
|
143
|
-
server.registerTool('wiki_blob', {
|
|
144
|
-
title: 'Read Wiki File',
|
|
145
|
-
description: 'Read a file from a Parall wiki and return its text content.',
|
|
146
|
-
annotations: {
|
|
147
|
-
readOnlyHint: true,
|
|
148
|
-
},
|
|
149
|
-
inputSchema: {
|
|
150
|
-
wiki: z.string().describe('Wiki slug or ID'),
|
|
151
|
-
path: z.string().describe('File path inside the wiki'),
|
|
152
|
-
},
|
|
153
|
-
outputSchema: {
|
|
154
|
-
wiki_id: z.string(),
|
|
155
|
-
wiki_slug: z.string(),
|
|
156
|
-
wiki_name: z.string(),
|
|
157
|
-
content: z.string(),
|
|
158
|
-
sha256: z.string(),
|
|
159
|
-
size: z.number(),
|
|
160
|
-
},
|
|
161
|
-
}, async ({ wiki, path }) => {
|
|
162
|
-
const result = await getWikiBlob(ctx, wiki, { path });
|
|
163
|
-
const structuredContent = {
|
|
164
|
-
wiki_id: result.wiki.id,
|
|
165
|
-
wiki_slug: result.wiki.slug,
|
|
166
|
-
wiki_name: result.wiki.name,
|
|
167
|
-
content: result.content,
|
|
168
|
-
size: result.size,
|
|
169
|
-
};
|
|
170
|
-
return {
|
|
171
|
-
content: [
|
|
172
|
-
{ type: 'text', text: result.content || JSON.stringify(structuredContent, null, 2) },
|
|
173
|
-
],
|
|
174
|
-
structuredContent,
|
|
175
|
-
};
|
|
176
|
-
});
|
|
177
|
-
server.registerTool('wiki_search', {
|
|
178
|
-
title: 'Search Wiki Markdown',
|
|
179
|
-
description: 'Search Markdown headings and section bodies inside a Parall wiki. Prefers local mounts when no ref is supplied and agent env is available; otherwise falls back to the Parall API.',
|
|
180
|
-
annotations: {
|
|
181
|
-
readOnlyHint: true,
|
|
182
|
-
},
|
|
183
|
-
inputSchema: {
|
|
184
|
-
wiki: z.string().describe('Wiki slug or ID'),
|
|
185
|
-
query: z.string().describe('Search query'),
|
|
186
|
-
ref: z.string().optional().describe('Optional git ref for API-backed search'),
|
|
187
|
-
path_prefix: z
|
|
188
|
-
.string()
|
|
189
|
-
.optional()
|
|
190
|
-
.describe('Restrict search to a file or directory path prefix'),
|
|
191
|
-
limit: z
|
|
192
|
-
.number()
|
|
193
|
-
.int()
|
|
194
|
-
.positive()
|
|
195
|
-
.max(20)
|
|
196
|
-
.optional()
|
|
197
|
-
.describe('Maximum number of results; default 5'),
|
|
198
|
-
include_content: z
|
|
199
|
-
.boolean()
|
|
200
|
-
.optional()
|
|
201
|
-
.describe('Include full matched section content in each result'),
|
|
202
|
-
},
|
|
203
|
-
outputSchema: {
|
|
204
|
-
results: z.array(z.object({
|
|
205
|
-
wiki_id: z.string(),
|
|
206
|
-
wiki_slug: z.string(),
|
|
207
|
-
wiki_name: z.string(),
|
|
208
|
-
node_id: z.string(),
|
|
209
|
-
path: z.string(),
|
|
210
|
-
title: z.string(),
|
|
211
|
-
heading_path: z.array(z.string()).optional(),
|
|
212
|
-
section_path: z.string(),
|
|
213
|
-
level: z.number(),
|
|
214
|
-
start_line: z.number(),
|
|
215
|
-
end_line: z.number(),
|
|
216
|
-
score: z.number(),
|
|
217
|
-
snippet: z.string(),
|
|
218
|
-
content: z.string().optional(),
|
|
219
|
-
source: z.enum(['local_mount', 'api']),
|
|
220
|
-
})),
|
|
221
|
-
},
|
|
222
|
-
}, async ({ wiki, query, ref, path_prefix: pathPrefix, limit, include_content: includeContent, }) => {
|
|
223
|
-
const results = await searchWiki(ctx, wiki, query, {
|
|
224
|
-
ref,
|
|
225
|
-
pathPrefix,
|
|
226
|
-
limit,
|
|
227
|
-
includeContent,
|
|
228
|
-
});
|
|
229
|
-
const structuredContent = { results };
|
|
230
|
-
return {
|
|
231
|
-
content: [{ type: 'text', text: JSON.stringify(structuredContent, null, 2) }],
|
|
232
|
-
structuredContent,
|
|
233
|
-
};
|
|
234
|
-
});
|
|
235
|
-
server.registerTool('wiki_query', {
|
|
236
|
-
title: 'Query Wiki Structurally',
|
|
237
|
-
description: 'Run a tree-aware wiki query that first narrows candidate documents and then selects the best sections.',
|
|
238
|
-
annotations: {
|
|
239
|
-
readOnlyHint: true,
|
|
240
|
-
},
|
|
241
|
-
inputSchema: {
|
|
242
|
-
wiki: z.string().describe('Wiki slug or ID'),
|
|
243
|
-
query: z.string().describe('Natural-language query'),
|
|
244
|
-
ref: z.string().optional().describe('Optional git ref for API-backed queries'),
|
|
245
|
-
path_prefix: z
|
|
246
|
-
.string()
|
|
247
|
-
.optional()
|
|
248
|
-
.describe('Restrict the query to a file or directory path prefix'),
|
|
249
|
-
limit: z
|
|
250
|
-
.number()
|
|
251
|
-
.int()
|
|
252
|
-
.positive()
|
|
253
|
-
.max(20)
|
|
254
|
-
.optional()
|
|
255
|
-
.describe('Maximum number of section results; default 5'),
|
|
256
|
-
include_content: z
|
|
257
|
-
.boolean()
|
|
258
|
-
.optional()
|
|
259
|
-
.describe('Include full matched section content in each result'),
|
|
260
|
-
},
|
|
261
|
-
outputSchema: {
|
|
262
|
-
wiki_id: z.string(),
|
|
263
|
-
wiki_slug: z.string(),
|
|
264
|
-
wiki_name: z.string(),
|
|
265
|
-
query: z.string(),
|
|
266
|
-
ref: z.string().optional(),
|
|
267
|
-
path_prefix: z.string().optional(),
|
|
268
|
-
generated_at: z.string(),
|
|
269
|
-
file_count: z.number(),
|
|
270
|
-
source: z.enum(['local_mount', 'api']),
|
|
271
|
-
documents: z.array(z.object({
|
|
272
|
-
path: z.string(),
|
|
273
|
-
title: z.string(),
|
|
274
|
-
score: z.number(),
|
|
275
|
-
matched_headings: z.array(z.string()),
|
|
276
|
-
node_count: z.number(),
|
|
277
|
-
})),
|
|
278
|
-
sections: z.array(z.object({
|
|
279
|
-
wiki_id: z.string(),
|
|
280
|
-
wiki_slug: z.string(),
|
|
281
|
-
wiki_name: z.string(),
|
|
282
|
-
node_id: z.string(),
|
|
283
|
-
path: z.string(),
|
|
284
|
-
title: z.string(),
|
|
285
|
-
heading_path: z.array(z.string()).optional(),
|
|
286
|
-
section_path: z.string(),
|
|
287
|
-
level: z.number(),
|
|
288
|
-
start_line: z.number(),
|
|
289
|
-
end_line: z.number(),
|
|
290
|
-
score: z.number(),
|
|
291
|
-
document_score: z.number(),
|
|
292
|
-
snippet: z.string(),
|
|
293
|
-
content: z.string().optional(),
|
|
294
|
-
source: z.enum(['local_mount', 'api']),
|
|
295
|
-
reasoning: z.array(z.string()),
|
|
296
|
-
})),
|
|
297
|
-
},
|
|
298
|
-
}, async ({ wiki, query, ref, path_prefix: pathPrefix, limit, include_content: includeContent, }) => {
|
|
299
|
-
const result = await queryWiki(ctx, wiki, query, {
|
|
300
|
-
ref,
|
|
301
|
-
pathPrefix,
|
|
302
|
-
limit,
|
|
303
|
-
includeContent,
|
|
304
|
-
});
|
|
305
|
-
return {
|
|
306
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
307
|
-
structuredContent: result,
|
|
308
|
-
};
|
|
309
|
-
});
|
|
310
|
-
server.registerTool('wiki_outline', {
|
|
311
|
-
title: 'Read Wiki Outline',
|
|
312
|
-
description: 'Return the structural node outline for a Parall wiki or a path prefix within it.',
|
|
313
|
-
annotations: {
|
|
314
|
-
readOnlyHint: true,
|
|
315
|
-
},
|
|
316
|
-
inputSchema: {
|
|
317
|
-
wiki: z.string().describe('Wiki slug or ID'),
|
|
318
|
-
ref: z.string().optional().describe('Optional git ref for API-backed outline'),
|
|
319
|
-
path_prefix: z
|
|
320
|
-
.string()
|
|
321
|
-
.optional()
|
|
322
|
-
.describe('Restrict outline to a file or directory path prefix'),
|
|
323
|
-
},
|
|
324
|
-
outputSchema: {
|
|
325
|
-
wiki_id: z.string(),
|
|
326
|
-
wiki_slug: z.string(),
|
|
327
|
-
wiki_name: z.string(),
|
|
328
|
-
ref: z.string().optional(),
|
|
329
|
-
path_prefix: z.string().optional(),
|
|
330
|
-
generated_at: z.string(),
|
|
331
|
-
file_count: z.number(),
|
|
332
|
-
source: z.enum(['local_mount', 'api']),
|
|
333
|
-
nodes: z.array(z.object({
|
|
334
|
-
wiki_id: z.string(),
|
|
335
|
-
wiki_slug: z.string(),
|
|
336
|
-
wiki_name: z.string(),
|
|
337
|
-
node_id: z.string(),
|
|
338
|
-
path: z.string(),
|
|
339
|
-
title: z.string(),
|
|
340
|
-
heading_path: z.array(z.string()).optional(),
|
|
341
|
-
section_path: z.string(),
|
|
342
|
-
level: z.number(),
|
|
343
|
-
start_line: z.number(),
|
|
344
|
-
end_line: z.number(),
|
|
345
|
-
})),
|
|
346
|
-
},
|
|
347
|
-
}, async ({ wiki, ref, path_prefix: pathPrefix }) => {
|
|
348
|
-
const outline = await getWikiOutline(ctx, wiki, { ref, pathPrefix });
|
|
349
|
-
return {
|
|
350
|
-
content: [{ type: 'text', text: JSON.stringify(outline, null, 2) }],
|
|
351
|
-
structuredContent: outline,
|
|
352
|
-
};
|
|
353
|
-
});
|
|
354
|
-
server.registerTool('wiki_section', {
|
|
355
|
-
title: 'Read Wiki Section',
|
|
356
|
-
description: 'Read a specific Markdown section from a Parall wiki by node ID. Node IDs come from `wiki_search` results.',
|
|
357
|
-
annotations: {
|
|
358
|
-
readOnlyHint: true,
|
|
359
|
-
},
|
|
360
|
-
inputSchema: {
|
|
361
|
-
wiki: z.string().describe('Wiki slug or ID'),
|
|
362
|
-
node_id: z.string().describe('Section node ID returned by wiki_search'),
|
|
363
|
-
ref: z
|
|
364
|
-
.string()
|
|
365
|
-
.optional()
|
|
366
|
-
.describe('Optional git ref; when set, reads from the Parall API instead of a local mount'),
|
|
367
|
-
},
|
|
368
|
-
outputSchema: {
|
|
369
|
-
wiki_id: z.string(),
|
|
370
|
-
wiki_slug: z.string(),
|
|
371
|
-
wiki_name: z.string(),
|
|
372
|
-
node_id: z.string(),
|
|
373
|
-
path: z.string(),
|
|
374
|
-
title: z.string(),
|
|
375
|
-
heading_path: z.array(z.string()).optional(),
|
|
376
|
-
section_path: z.string(),
|
|
377
|
-
level: z.number(),
|
|
378
|
-
start_line: z.number(),
|
|
379
|
-
end_line: z.number(),
|
|
380
|
-
content: z.string(),
|
|
381
|
-
source: z.enum(['local_mount', 'api']),
|
|
382
|
-
},
|
|
383
|
-
}, async ({ wiki, node_id: nodeId, ref }) => {
|
|
384
|
-
const section = await getWikiSection(ctx, wiki, { nodeId, ref });
|
|
385
|
-
return {
|
|
386
|
-
content: [{ type: 'text', text: section.content || JSON.stringify(section, null, 2) }],
|
|
387
|
-
structuredContent: section,
|
|
388
|
-
};
|
|
389
|
-
});
|
|
390
|
-
server.registerTool('wiki_changeset_diff', {
|
|
391
|
-
title: 'Read Wiki Changeset Diff',
|
|
392
|
-
description: 'Fetch the unified diff and changed file summary for a Parall wiki changeset.',
|
|
393
|
-
annotations: {
|
|
394
|
-
readOnlyHint: true,
|
|
395
|
-
},
|
|
396
|
-
inputSchema: {
|
|
397
|
-
wiki: z.string().describe('Wiki slug or ID'),
|
|
398
|
-
changeset_id: z.string().describe('Wiki changeset ID'),
|
|
399
|
-
},
|
|
400
|
-
outputSchema: {
|
|
401
|
-
wiki_id: z.string(),
|
|
402
|
-
wiki_slug: z.string(),
|
|
403
|
-
wiki_name: z.string(),
|
|
404
|
-
changeset_id: z.string(),
|
|
405
|
-
diff: z.object({
|
|
406
|
-
patch: z.string(),
|
|
407
|
-
files: z.array(z.object({
|
|
408
|
-
path: z.string(),
|
|
409
|
-
additions: z.number(),
|
|
410
|
-
deletions: z.number(),
|
|
411
|
-
})),
|
|
412
|
-
}),
|
|
413
|
-
},
|
|
414
|
-
}, async ({ wiki, changeset_id: changesetId }) => {
|
|
415
|
-
const diff = await getWikiChangesetDiff(ctx, wiki, changesetId);
|
|
416
|
-
return {
|
|
417
|
-
content: [{ type: 'text', text: diff.diff.patch || JSON.stringify(diff, null, 2) }],
|
|
418
|
-
structuredContent: diff,
|
|
419
|
-
};
|
|
420
|
-
});
|
|
421
|
-
server.registerTool('wiki_propose', {
|
|
422
|
-
title: 'Propose Wiki Changeset',
|
|
423
|
-
description: 'Create or update a wiki changeset from the current local mounted wiki workspace. Unprotected changesets may auto-merge immediately.',
|
|
424
|
-
inputSchema: {
|
|
425
|
-
wiki: z.string().describe('Wiki slug or ID'),
|
|
426
|
-
title: z.string().min(1).describe('Changeset title'),
|
|
427
|
-
message: z
|
|
428
|
-
.string()
|
|
429
|
-
.optional()
|
|
430
|
-
.describe('Changeset commit message (supports prll:// refs)'),
|
|
431
|
-
changeset_id: z.string().optional().describe('Existing changeset ID to update'),
|
|
432
|
-
source_chat_id: z
|
|
433
|
-
.string()
|
|
434
|
-
.optional()
|
|
435
|
-
.describe('Optional source chat ID for approval workflow linkage'),
|
|
436
|
-
source_message_id: z
|
|
437
|
-
.string()
|
|
438
|
-
.optional()
|
|
439
|
-
.describe('Optional source message ID for approval workflow linkage'),
|
|
440
|
-
source_run_id: z
|
|
441
|
-
.string()
|
|
442
|
-
.optional()
|
|
443
|
-
.describe('Optional source run ID for approval workflow linkage'),
|
|
444
|
-
},
|
|
445
|
-
outputSchema: {
|
|
446
|
-
wiki_id: z.string(),
|
|
447
|
-
wiki_slug: z.string(),
|
|
448
|
-
wiki_name: z.string(),
|
|
449
|
-
mount_path: z.string(),
|
|
450
|
-
changed_paths: z.array(z.string()),
|
|
451
|
-
status: z.string(),
|
|
452
|
-
auto_merged: z.boolean(),
|
|
453
|
-
requires_review: z.boolean(),
|
|
454
|
-
next_action: z.string(),
|
|
455
|
-
post_merge_sync: z
|
|
456
|
-
.object({
|
|
457
|
-
applied: z.number(),
|
|
458
|
-
fastForwarded: z.number(),
|
|
459
|
-
conflicts: z.array(z.unknown()),
|
|
460
|
-
failed: z.array(z.unknown()),
|
|
461
|
-
})
|
|
462
|
-
.nullable(),
|
|
463
|
-
post_merge_sync_error: z.string().nullable(),
|
|
464
|
-
diff_files: z.array(z.object({
|
|
465
|
-
path: z.string(),
|
|
466
|
-
additions: z.number(),
|
|
467
|
-
deletions: z.number(),
|
|
468
|
-
})),
|
|
469
|
-
source: z.literal('local_mount'),
|
|
470
|
-
changeset: z
|
|
471
|
-
.object({
|
|
472
|
-
id: z.string(),
|
|
473
|
-
wiki_id: z.string(),
|
|
474
|
-
title: z.string(),
|
|
475
|
-
status: z.string(),
|
|
476
|
-
changed_paths: z.array(z.string()).optional(),
|
|
477
|
-
})
|
|
478
|
-
.passthrough(),
|
|
479
|
-
},
|
|
480
|
-
}, async ({ wiki, title, message, changeset_id: changesetId, source_chat_id: sourceChatId, source_message_id: sourceMessageId, source_run_id: sourceRunId, }) => {
|
|
481
|
-
const runtime = resolveRuntimeContext();
|
|
482
|
-
const result = await proposeWikiChangeset(ctx, wiki, {
|
|
483
|
-
title,
|
|
484
|
-
message,
|
|
485
|
-
changesetId,
|
|
486
|
-
sourceChatId: sourceChatId ?? runtime.chatId,
|
|
487
|
-
sourceMessageId: sourceMessageId ?? runtime.triggerMessageId,
|
|
488
|
-
sourceRunId: sourceRunId ?? runtime.sessionId,
|
|
489
|
-
});
|
|
490
|
-
return {
|
|
491
|
-
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
492
|
-
structuredContent: result,
|
|
493
|
-
};
|
|
494
|
-
});
|
|
41
|
+
for (const tool of buildWikiTools(ctx)) {
|
|
42
|
+
registerWikiTool(server, tool);
|
|
43
|
+
}
|
|
495
44
|
const transport = new StdioServerTransport();
|
|
496
45
|
await server.connect(transport);
|
|
497
46
|
console.error('parall MCP server running on stdio');
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
|
+
import { type ParallContext } from './wiki.js';
|
|
3
|
+
/**
|
|
4
|
+
* Shared Parall wiki native tool registry.
|
|
5
|
+
*
|
|
6
|
+
* Single source of truth for the wiki tool surface consumed by:
|
|
7
|
+
* - the MCP server (`commands/mcp.ts`), which registers each definition
|
|
8
|
+
* - the eval harness (`@parall/eval`), which benchmarks the real tools
|
|
9
|
+
*
|
|
10
|
+
* Keep the schemas and handlers here behavior-identical to what the MCP
|
|
11
|
+
* server used to define inline. Do not fork an eval-only clone.
|
|
12
|
+
*/
|
|
13
|
+
export type WikiToolExecutionResult = {
|
|
14
|
+
structuredContent: Record<string, unknown>;
|
|
15
|
+
text: string;
|
|
16
|
+
};
|
|
17
|
+
export type WikiToolDefinition = {
|
|
18
|
+
name: string;
|
|
19
|
+
title: string;
|
|
20
|
+
description: string;
|
|
21
|
+
readOnly: boolean;
|
|
22
|
+
inputSchema: z.ZodRawShape;
|
|
23
|
+
outputSchema: z.ZodRawShape;
|
|
24
|
+
handler: (args: Record<string, unknown>) => Promise<WikiToolExecutionResult>;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Build the full wiki native tool registry bound to a Parall context (credentials,
|
|
28
|
+
* agent env, local mount). Returns one definition per wiki capability (list, tree,
|
|
29
|
+
* status, diff, blob, search, query, outline, section, changeset diff, propose),
|
|
30
|
+
* each finalized with input validation. Shared by the MCP server and the eval harness.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildWikiTools(ctx: ParallContext): WikiToolDefinition[];
|
|
33
|
+
//# sourceMappingURL=wiki-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiki-tools.d.ts","sourceRoot":"","sources":["../../src/lib/wiki-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,QAAQ,CAAC;AAE5B,OAAO,EASL,KAAK,aAAa,EAInB,MAAM,WAAW,CAAC;AAEnB;;;;;;;;;GASG;AAEH,MAAM,MAAM,uBAAuB,GAAG;IACpC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC;IAC3B,YAAY,EAAE,CAAC,CAAC,WAAW,CAAC;IAC5B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,uBAAuB,CAAC,CAAC;CAC9E,CAAC;AAoBF;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,kBAAkB,EAAE,CAiiBvE"}
|
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import * as z from 'zod/v4';
|
|
2
|
+
import { resolveRuntimeContext } from './client.js';
|
|
3
|
+
import { getWikiBlob, getWikiChangesetDiff, getWikiOutline, getWikiSection, getWikiTree, getWikiWorkspaceDiff, getWikiWorkspaceStatus, listWikis, proposeWikiChangeset, queryWiki, searchWiki, } from './wiki.js';
|
|
4
|
+
/** Pretty-print a value as 2-space-indented JSON for a tool's human-readable `text` field. */
|
|
5
|
+
function jsonText(value) {
|
|
6
|
+
return JSON.stringify(value, null, 2);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Wrap a tool definition so its handler validates raw args against the tool's
|
|
10
|
+
* `inputSchema` before running. Returns a copy with the handler replaced; the
|
|
11
|
+
* wrapped handler throws a Zod parse error on invalid/missing input.
|
|
12
|
+
*/
|
|
13
|
+
function finalizeWikiTool(tool) {
|
|
14
|
+
const inputParser = z.object(tool.inputSchema);
|
|
15
|
+
return {
|
|
16
|
+
...tool,
|
|
17
|
+
handler: async (args) => tool.handler(inputParser.parse(args ?? {})),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build the full wiki native tool registry bound to a Parall context (credentials,
|
|
22
|
+
* agent env, local mount). Returns one definition per wiki capability (list, tree,
|
|
23
|
+
* status, diff, blob, search, query, outline, section, changeset diff, propose),
|
|
24
|
+
* each finalized with input validation. Shared by the MCP server and the eval harness.
|
|
25
|
+
*/
|
|
26
|
+
export function buildWikiTools(ctx) {
|
|
27
|
+
const tools = [
|
|
28
|
+
{
|
|
29
|
+
name: 'wiki_list',
|
|
30
|
+
title: 'List Parall Wikis',
|
|
31
|
+
description: 'List Parall wiki repositories that the current credentials can access.',
|
|
32
|
+
readOnly: true,
|
|
33
|
+
inputSchema: {},
|
|
34
|
+
outputSchema: {
|
|
35
|
+
wikis: z.array(z.object({
|
|
36
|
+
id: z.string(),
|
|
37
|
+
slug: z.string(),
|
|
38
|
+
name: z.string(),
|
|
39
|
+
default_branch: z.string(),
|
|
40
|
+
})),
|
|
41
|
+
},
|
|
42
|
+
handler: async () => {
|
|
43
|
+
const wikis = await listWikis(ctx);
|
|
44
|
+
const structuredContent = {
|
|
45
|
+
wikis: wikis.map((wiki) => ({
|
|
46
|
+
id: wiki.id,
|
|
47
|
+
slug: wiki.slug,
|
|
48
|
+
name: wiki.name,
|
|
49
|
+
default_branch: wiki.default_branch,
|
|
50
|
+
})),
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
text: jsonText(structuredContent),
|
|
54
|
+
structuredContent,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'wiki_tree',
|
|
60
|
+
title: 'Browse Wiki Tree',
|
|
61
|
+
description: 'List files and folders under a Parall wiki path.',
|
|
62
|
+
readOnly: true,
|
|
63
|
+
inputSchema: {
|
|
64
|
+
wiki: z.string().describe('Wiki slug or ID'),
|
|
65
|
+
path: z.string().optional().describe('Optional tree path prefix inside the wiki'),
|
|
66
|
+
},
|
|
67
|
+
outputSchema: {
|
|
68
|
+
wiki_id: z.string(),
|
|
69
|
+
wiki_slug: z.string(),
|
|
70
|
+
wiki_name: z.string(),
|
|
71
|
+
path: z.string(),
|
|
72
|
+
entries: z.array(z.object({
|
|
73
|
+
name: z.string(),
|
|
74
|
+
path: z.string(),
|
|
75
|
+
type: z.enum(['tree', 'blob']),
|
|
76
|
+
size: z.number().optional(),
|
|
77
|
+
})),
|
|
78
|
+
},
|
|
79
|
+
handler: async (args) => {
|
|
80
|
+
const wiki = typeof args.wiki === 'string' ? args.wiki : '';
|
|
81
|
+
const path = typeof args.path === 'string' ? args.path : undefined;
|
|
82
|
+
const result = await getWikiTree(ctx, wiki, { path });
|
|
83
|
+
const structuredContent = {
|
|
84
|
+
wiki_id: result.wiki.id,
|
|
85
|
+
wiki_slug: result.wiki.slug,
|
|
86
|
+
wiki_name: result.wiki.name,
|
|
87
|
+
path: result.path,
|
|
88
|
+
entries: result.entries,
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
text: jsonText(structuredContent),
|
|
92
|
+
structuredContent,
|
|
93
|
+
};
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'wiki_status',
|
|
98
|
+
title: 'Inspect Wiki Workspace Status',
|
|
99
|
+
description: 'Inspect the current local mounted wiki workspace before proposing changes.',
|
|
100
|
+
readOnly: true,
|
|
101
|
+
inputSchema: {
|
|
102
|
+
wiki: z.string().describe('Wiki slug or ID'),
|
|
103
|
+
},
|
|
104
|
+
outputSchema: {
|
|
105
|
+
wiki_id: z.string(),
|
|
106
|
+
wiki_slug: z.string(),
|
|
107
|
+
wiki_name: z.string(),
|
|
108
|
+
mount_path: z.string(),
|
|
109
|
+
mode: z.enum(['read_only', 'read_write']),
|
|
110
|
+
default_ref: z.string(),
|
|
111
|
+
changed_paths: z.array(z.string()),
|
|
112
|
+
diff_files: z.array(z.object({
|
|
113
|
+
path: z.string(),
|
|
114
|
+
additions: z.number(),
|
|
115
|
+
deletions: z.number(),
|
|
116
|
+
})),
|
|
117
|
+
dirty: z.boolean(),
|
|
118
|
+
source: z.literal('local_mount'),
|
|
119
|
+
},
|
|
120
|
+
handler: async (args) => {
|
|
121
|
+
const wiki = typeof args.wiki === 'string' ? args.wiki : '';
|
|
122
|
+
const result = await getWikiWorkspaceStatus(ctx, wiki);
|
|
123
|
+
return {
|
|
124
|
+
text: jsonText(result),
|
|
125
|
+
structuredContent: result,
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'wiki_diff',
|
|
131
|
+
title: 'Read Wiki Workspace Diff',
|
|
132
|
+
description: 'Read the current unified diff for a local mounted wiki workspace.',
|
|
133
|
+
readOnly: true,
|
|
134
|
+
inputSchema: {
|
|
135
|
+
wiki: z.string().describe('Wiki slug or ID'),
|
|
136
|
+
},
|
|
137
|
+
outputSchema: {
|
|
138
|
+
wiki_id: z.string(),
|
|
139
|
+
wiki_slug: z.string(),
|
|
140
|
+
wiki_name: z.string(),
|
|
141
|
+
mount_path: z.string(),
|
|
142
|
+
changed_paths: z.array(z.string()),
|
|
143
|
+
diff_files: z.array(z.object({
|
|
144
|
+
path: z.string(),
|
|
145
|
+
additions: z.number(),
|
|
146
|
+
deletions: z.number(),
|
|
147
|
+
})),
|
|
148
|
+
patch: z.string(),
|
|
149
|
+
source: z.literal('local_mount'),
|
|
150
|
+
},
|
|
151
|
+
handler: async (args) => {
|
|
152
|
+
const wiki = typeof args.wiki === 'string' ? args.wiki : '';
|
|
153
|
+
const result = await getWikiWorkspaceDiff(ctx, wiki);
|
|
154
|
+
return {
|
|
155
|
+
text: result.patch || jsonText(result),
|
|
156
|
+
structuredContent: result,
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'wiki_blob',
|
|
162
|
+
title: 'Read Wiki File',
|
|
163
|
+
description: 'Read a file from a Parall wiki and return its text content.',
|
|
164
|
+
readOnly: true,
|
|
165
|
+
inputSchema: {
|
|
166
|
+
wiki: z.string().describe('Wiki slug or ID'),
|
|
167
|
+
path: z.string().describe('File path inside the wiki'),
|
|
168
|
+
},
|
|
169
|
+
outputSchema: {
|
|
170
|
+
wiki_id: z.string(),
|
|
171
|
+
wiki_slug: z.string(),
|
|
172
|
+
wiki_name: z.string(),
|
|
173
|
+
content: z.string(),
|
|
174
|
+
size: z.number(),
|
|
175
|
+
},
|
|
176
|
+
handler: async (args) => {
|
|
177
|
+
const wiki = typeof args.wiki === 'string' ? args.wiki : '';
|
|
178
|
+
const path = typeof args.path === 'string' ? args.path : '';
|
|
179
|
+
const result = await getWikiBlob(ctx, wiki, { path });
|
|
180
|
+
const structuredContent = {
|
|
181
|
+
wiki_id: result.wiki.id,
|
|
182
|
+
wiki_slug: result.wiki.slug,
|
|
183
|
+
wiki_name: result.wiki.name,
|
|
184
|
+
content: result.content,
|
|
185
|
+
size: result.size,
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
text: result.content || jsonText(structuredContent),
|
|
189
|
+
structuredContent,
|
|
190
|
+
};
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: 'wiki_search',
|
|
195
|
+
title: 'Search Wiki Markdown',
|
|
196
|
+
description: 'Search Markdown headings and section bodies inside a Parall wiki. Prefers local mounts when no ref is supplied and agent env is available; otherwise falls back to the Parall API.',
|
|
197
|
+
readOnly: true,
|
|
198
|
+
inputSchema: {
|
|
199
|
+
wiki: z.string().describe('Wiki slug or ID'),
|
|
200
|
+
query: z.string().describe('Search query'),
|
|
201
|
+
ref: z.string().optional().describe('Optional git ref for API-backed search'),
|
|
202
|
+
path_prefix: z
|
|
203
|
+
.string()
|
|
204
|
+
.optional()
|
|
205
|
+
.describe('Restrict search to a file or directory path prefix'),
|
|
206
|
+
limit: z
|
|
207
|
+
.number()
|
|
208
|
+
.int()
|
|
209
|
+
.positive()
|
|
210
|
+
.max(20)
|
|
211
|
+
.optional()
|
|
212
|
+
.describe('Maximum number of results; default 5'),
|
|
213
|
+
include_content: z
|
|
214
|
+
.boolean()
|
|
215
|
+
.optional()
|
|
216
|
+
.describe('Include full matched section content in each result'),
|
|
217
|
+
},
|
|
218
|
+
outputSchema: {
|
|
219
|
+
results: z.array(z.object({
|
|
220
|
+
wiki_id: z.string(),
|
|
221
|
+
wiki_slug: z.string(),
|
|
222
|
+
wiki_name: z.string(),
|
|
223
|
+
node_id: z.string(),
|
|
224
|
+
path: z.string(),
|
|
225
|
+
title: z.string(),
|
|
226
|
+
heading_path: z.array(z.string()).optional(),
|
|
227
|
+
section_path: z.string(),
|
|
228
|
+
level: z.number(),
|
|
229
|
+
start_line: z.number(),
|
|
230
|
+
end_line: z.number(),
|
|
231
|
+
score: z.number(),
|
|
232
|
+
snippet: z.string(),
|
|
233
|
+
content: z.string().optional(),
|
|
234
|
+
source: z.enum(['local_mount', 'api']),
|
|
235
|
+
})),
|
|
236
|
+
},
|
|
237
|
+
handler: async (args) => {
|
|
238
|
+
const wiki = typeof args.wiki === 'string' ? args.wiki : '';
|
|
239
|
+
const query = typeof args.query === 'string' ? args.query : '';
|
|
240
|
+
const ref = typeof args.ref === 'string' ? args.ref : undefined;
|
|
241
|
+
const pathPrefix = typeof args.path_prefix === 'string' ? args.path_prefix : undefined;
|
|
242
|
+
const limit = typeof args.limit === 'number' ? args.limit : undefined;
|
|
243
|
+
const includeContent = typeof args.include_content === 'boolean' ? args.include_content : undefined;
|
|
244
|
+
const results = await searchWiki(ctx, wiki, query, {
|
|
245
|
+
ref,
|
|
246
|
+
pathPrefix,
|
|
247
|
+
limit,
|
|
248
|
+
includeContent,
|
|
249
|
+
});
|
|
250
|
+
const structuredContent = { results };
|
|
251
|
+
return {
|
|
252
|
+
text: jsonText(structuredContent),
|
|
253
|
+
structuredContent,
|
|
254
|
+
};
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: 'wiki_query',
|
|
259
|
+
title: 'Query Wiki Structurally',
|
|
260
|
+
description: 'Run a tree-aware wiki query that first narrows candidate documents and then selects the best sections.',
|
|
261
|
+
readOnly: true,
|
|
262
|
+
inputSchema: {
|
|
263
|
+
wiki: z.string().describe('Wiki slug or ID'),
|
|
264
|
+
query: z.string().describe('Natural-language query'),
|
|
265
|
+
ref: z.string().optional().describe('Optional git ref for API-backed queries'),
|
|
266
|
+
path_prefix: z
|
|
267
|
+
.string()
|
|
268
|
+
.optional()
|
|
269
|
+
.describe('Restrict the query to a file or directory path prefix'),
|
|
270
|
+
limit: z
|
|
271
|
+
.number()
|
|
272
|
+
.int()
|
|
273
|
+
.positive()
|
|
274
|
+
.max(20)
|
|
275
|
+
.optional()
|
|
276
|
+
.describe('Maximum number of section results; default 5'),
|
|
277
|
+
include_content: z
|
|
278
|
+
.boolean()
|
|
279
|
+
.optional()
|
|
280
|
+
.describe('Include full matched section content in each result'),
|
|
281
|
+
},
|
|
282
|
+
outputSchema: {
|
|
283
|
+
wiki_id: z.string(),
|
|
284
|
+
wiki_slug: z.string(),
|
|
285
|
+
wiki_name: z.string(),
|
|
286
|
+
query: z.string(),
|
|
287
|
+
ref: z.string().optional(),
|
|
288
|
+
path_prefix: z.string().optional(),
|
|
289
|
+
generated_at: z.string(),
|
|
290
|
+
file_count: z.number(),
|
|
291
|
+
source: z.enum(['local_mount', 'api']),
|
|
292
|
+
documents: z.array(z.object({
|
|
293
|
+
path: z.string(),
|
|
294
|
+
title: z.string(),
|
|
295
|
+
score: z.number(),
|
|
296
|
+
matched_headings: z.array(z.string()),
|
|
297
|
+
node_count: z.number(),
|
|
298
|
+
})),
|
|
299
|
+
sections: z.array(z.object({
|
|
300
|
+
wiki_id: z.string(),
|
|
301
|
+
wiki_slug: z.string(),
|
|
302
|
+
wiki_name: z.string(),
|
|
303
|
+
node_id: z.string(),
|
|
304
|
+
path: z.string(),
|
|
305
|
+
title: z.string(),
|
|
306
|
+
heading_path: z.array(z.string()).optional(),
|
|
307
|
+
section_path: z.string(),
|
|
308
|
+
level: z.number(),
|
|
309
|
+
start_line: z.number(),
|
|
310
|
+
end_line: z.number(),
|
|
311
|
+
score: z.number(),
|
|
312
|
+
document_score: z.number(),
|
|
313
|
+
snippet: z.string(),
|
|
314
|
+
content: z.string().optional(),
|
|
315
|
+
source: z.enum(['local_mount', 'api']),
|
|
316
|
+
reasoning: z.array(z.string()),
|
|
317
|
+
})),
|
|
318
|
+
},
|
|
319
|
+
handler: async (args) => {
|
|
320
|
+
const wiki = typeof args.wiki === 'string' ? args.wiki : '';
|
|
321
|
+
const query = typeof args.query === 'string' ? args.query : '';
|
|
322
|
+
const ref = typeof args.ref === 'string' ? args.ref : undefined;
|
|
323
|
+
const pathPrefix = typeof args.path_prefix === 'string' ? args.path_prefix : undefined;
|
|
324
|
+
const limit = typeof args.limit === 'number' ? args.limit : undefined;
|
|
325
|
+
const includeContent = typeof args.include_content === 'boolean' ? args.include_content : undefined;
|
|
326
|
+
const result = await queryWiki(ctx, wiki, query, {
|
|
327
|
+
ref,
|
|
328
|
+
pathPrefix,
|
|
329
|
+
limit,
|
|
330
|
+
includeContent,
|
|
331
|
+
});
|
|
332
|
+
return {
|
|
333
|
+
text: jsonText(result),
|
|
334
|
+
structuredContent: result,
|
|
335
|
+
};
|
|
336
|
+
},
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
name: 'wiki_outline',
|
|
340
|
+
title: 'Read Wiki Outline',
|
|
341
|
+
description: 'Return the structural node outline for a Parall wiki or a path prefix within it.',
|
|
342
|
+
readOnly: true,
|
|
343
|
+
inputSchema: {
|
|
344
|
+
wiki: z.string().describe('Wiki slug or ID'),
|
|
345
|
+
ref: z.string().optional().describe('Optional git ref for API-backed outline'),
|
|
346
|
+
path_prefix: z
|
|
347
|
+
.string()
|
|
348
|
+
.optional()
|
|
349
|
+
.describe('Restrict outline to a file or directory path prefix'),
|
|
350
|
+
},
|
|
351
|
+
outputSchema: {
|
|
352
|
+
wiki_id: z.string(),
|
|
353
|
+
wiki_slug: z.string(),
|
|
354
|
+
wiki_name: z.string(),
|
|
355
|
+
ref: z.string().optional(),
|
|
356
|
+
path_prefix: z.string().optional(),
|
|
357
|
+
generated_at: z.string(),
|
|
358
|
+
file_count: z.number(),
|
|
359
|
+
source: z.enum(['local_mount', 'api']),
|
|
360
|
+
nodes: z.array(z.object({
|
|
361
|
+
wiki_id: z.string(),
|
|
362
|
+
wiki_slug: z.string(),
|
|
363
|
+
wiki_name: z.string(),
|
|
364
|
+
node_id: z.string(),
|
|
365
|
+
path: z.string(),
|
|
366
|
+
title: z.string(),
|
|
367
|
+
heading_path: z.array(z.string()).optional(),
|
|
368
|
+
section_path: z.string(),
|
|
369
|
+
level: z.number(),
|
|
370
|
+
start_line: z.number(),
|
|
371
|
+
end_line: z.number(),
|
|
372
|
+
})),
|
|
373
|
+
},
|
|
374
|
+
handler: async (args) => {
|
|
375
|
+
const wiki = typeof args.wiki === 'string' ? args.wiki : '';
|
|
376
|
+
const ref = typeof args.ref === 'string' ? args.ref : undefined;
|
|
377
|
+
const pathPrefix = typeof args.path_prefix === 'string' ? args.path_prefix : undefined;
|
|
378
|
+
const outline = await getWikiOutline(ctx, wiki, { ref, pathPrefix });
|
|
379
|
+
return {
|
|
380
|
+
text: jsonText(outline),
|
|
381
|
+
structuredContent: outline,
|
|
382
|
+
};
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
name: 'wiki_section',
|
|
387
|
+
title: 'Read Wiki Section',
|
|
388
|
+
description: 'Read a specific Markdown section from a Parall wiki by node ID. Node IDs come from `wiki_search` results.',
|
|
389
|
+
readOnly: true,
|
|
390
|
+
inputSchema: {
|
|
391
|
+
wiki: z.string().describe('Wiki slug or ID'),
|
|
392
|
+
node_id: z.string().describe('Section node ID returned by wiki_search'),
|
|
393
|
+
ref: z
|
|
394
|
+
.string()
|
|
395
|
+
.optional()
|
|
396
|
+
.describe('Optional git ref; when set, reads from the Parall API instead of a local mount'),
|
|
397
|
+
},
|
|
398
|
+
outputSchema: {
|
|
399
|
+
wiki_id: z.string(),
|
|
400
|
+
wiki_slug: z.string(),
|
|
401
|
+
wiki_name: z.string(),
|
|
402
|
+
node_id: z.string(),
|
|
403
|
+
path: z.string(),
|
|
404
|
+
title: z.string(),
|
|
405
|
+
heading_path: z.array(z.string()).optional(),
|
|
406
|
+
section_path: z.string(),
|
|
407
|
+
level: z.number(),
|
|
408
|
+
start_line: z.number(),
|
|
409
|
+
end_line: z.number(),
|
|
410
|
+
content: z.string(),
|
|
411
|
+
source: z.enum(['local_mount', 'api']),
|
|
412
|
+
},
|
|
413
|
+
handler: async (args) => {
|
|
414
|
+
const wiki = typeof args.wiki === 'string' ? args.wiki : '';
|
|
415
|
+
const nodeId = typeof args.node_id === 'string' ? args.node_id : '';
|
|
416
|
+
const ref = typeof args.ref === 'string' ? args.ref : undefined;
|
|
417
|
+
const section = await getWikiSection(ctx, wiki, { nodeId, ref });
|
|
418
|
+
return {
|
|
419
|
+
text: section.content || jsonText(section),
|
|
420
|
+
structuredContent: section,
|
|
421
|
+
};
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
name: 'wiki_changeset_diff',
|
|
426
|
+
title: 'Read Wiki Changeset Diff',
|
|
427
|
+
description: 'Fetch the unified diff and changed file summary for a Parall wiki changeset.',
|
|
428
|
+
readOnly: true,
|
|
429
|
+
inputSchema: {
|
|
430
|
+
wiki: z.string().describe('Wiki slug or ID'),
|
|
431
|
+
changeset_id: z.string().describe('Wiki changeset ID'),
|
|
432
|
+
},
|
|
433
|
+
outputSchema: {
|
|
434
|
+
wiki_id: z.string(),
|
|
435
|
+
wiki_slug: z.string(),
|
|
436
|
+
wiki_name: z.string(),
|
|
437
|
+
changeset_id: z.string(),
|
|
438
|
+
diff: z.object({
|
|
439
|
+
patch: z.string(),
|
|
440
|
+
files: z.array(z.object({
|
|
441
|
+
path: z.string(),
|
|
442
|
+
additions: z.number(),
|
|
443
|
+
deletions: z.number(),
|
|
444
|
+
})),
|
|
445
|
+
}),
|
|
446
|
+
},
|
|
447
|
+
handler: async (args) => {
|
|
448
|
+
const wiki = typeof args.wiki === 'string' ? args.wiki : '';
|
|
449
|
+
const changesetId = typeof args.changeset_id === 'string' ? args.changeset_id : '';
|
|
450
|
+
const diff = await getWikiChangesetDiff(ctx, wiki, changesetId);
|
|
451
|
+
return {
|
|
452
|
+
text: diff.diff.patch || jsonText(diff),
|
|
453
|
+
structuredContent: diff,
|
|
454
|
+
};
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
name: 'wiki_propose',
|
|
459
|
+
title: 'Propose Wiki Changeset',
|
|
460
|
+
description: 'Create or update a wiki changeset from the current local mounted wiki workspace. Unprotected changesets may auto-merge immediately.',
|
|
461
|
+
readOnly: false,
|
|
462
|
+
inputSchema: {
|
|
463
|
+
wiki: z.string().describe('Wiki slug or ID'),
|
|
464
|
+
title: z.string().min(1).describe('Changeset title'),
|
|
465
|
+
message: z.string().optional().describe('Changeset commit message (supports prll:// refs)'),
|
|
466
|
+
changeset_id: z.string().optional().describe('Existing changeset ID to update'),
|
|
467
|
+
source_chat_id: z
|
|
468
|
+
.string()
|
|
469
|
+
.optional()
|
|
470
|
+
.describe('Optional source chat ID for approval workflow linkage'),
|
|
471
|
+
source_message_id: z
|
|
472
|
+
.string()
|
|
473
|
+
.optional()
|
|
474
|
+
.describe('Optional source message ID for approval workflow linkage'),
|
|
475
|
+
source_run_id: z
|
|
476
|
+
.string()
|
|
477
|
+
.optional()
|
|
478
|
+
.describe('Optional source run ID for approval workflow linkage'),
|
|
479
|
+
},
|
|
480
|
+
outputSchema: {
|
|
481
|
+
wiki_id: z.string(),
|
|
482
|
+
wiki_slug: z.string(),
|
|
483
|
+
wiki_name: z.string(),
|
|
484
|
+
mount_path: z.string(),
|
|
485
|
+
changed_paths: z.array(z.string()),
|
|
486
|
+
status: z.string(),
|
|
487
|
+
auto_merged: z.boolean(),
|
|
488
|
+
requires_review: z.boolean(),
|
|
489
|
+
next_action: z.string(),
|
|
490
|
+
post_merge_sync: z
|
|
491
|
+
.object({
|
|
492
|
+
applied: z.number(),
|
|
493
|
+
fastForwarded: z.number(),
|
|
494
|
+
conflicts: z.array(z.unknown()),
|
|
495
|
+
failed: z.array(z.unknown()),
|
|
496
|
+
})
|
|
497
|
+
.nullable(),
|
|
498
|
+
post_merge_sync_error: z.string().nullable(),
|
|
499
|
+
diff_files: z.array(z.object({
|
|
500
|
+
path: z.string(),
|
|
501
|
+
additions: z.number(),
|
|
502
|
+
deletions: z.number(),
|
|
503
|
+
})),
|
|
504
|
+
source: z.literal('local_mount'),
|
|
505
|
+
changeset: z
|
|
506
|
+
.object({
|
|
507
|
+
id: z.string(),
|
|
508
|
+
wiki_id: z.string(),
|
|
509
|
+
title: z.string(),
|
|
510
|
+
status: z.string(),
|
|
511
|
+
changed_paths: z.array(z.string()).optional(),
|
|
512
|
+
})
|
|
513
|
+
.passthrough(),
|
|
514
|
+
},
|
|
515
|
+
handler: async (args) => {
|
|
516
|
+
const wiki = typeof args.wiki === 'string' ? args.wiki : '';
|
|
517
|
+
const title = typeof args.title === 'string' ? args.title : '';
|
|
518
|
+
const message = typeof args.message === 'string' ? args.message : undefined;
|
|
519
|
+
const changesetId = typeof args.changeset_id === 'string' ? args.changeset_id : undefined;
|
|
520
|
+
const sourceChatId = typeof args.source_chat_id === 'string' ? args.source_chat_id : undefined;
|
|
521
|
+
const sourceMessageId = typeof args.source_message_id === 'string' ? args.source_message_id : undefined;
|
|
522
|
+
const sourceRunId = typeof args.source_run_id === 'string' ? args.source_run_id : undefined;
|
|
523
|
+
const runtime = resolveRuntimeContext();
|
|
524
|
+
const result = await proposeWikiChangeset(ctx, wiki, {
|
|
525
|
+
title,
|
|
526
|
+
message,
|
|
527
|
+
changesetId,
|
|
528
|
+
sourceChatId: sourceChatId ?? runtime.chatId,
|
|
529
|
+
sourceMessageId: sourceMessageId ?? runtime.triggerMessageId,
|
|
530
|
+
sourceRunId: sourceRunId ?? runtime.sessionId,
|
|
531
|
+
});
|
|
532
|
+
return {
|
|
533
|
+
text: jsonText(result),
|
|
534
|
+
structuredContent: result,
|
|
535
|
+
};
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
];
|
|
539
|
+
return tools.map(finalizeWikiTool);
|
|
540
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.0",
|
|
4
4
|
"description": "CLI client for Parall — universal agent & human access to Parall API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"./wiki": {
|
|
18
18
|
"types": "./dist/lib/wiki.d.ts",
|
|
19
19
|
"default": "./dist/lib/wiki.js"
|
|
20
|
+
},
|
|
21
|
+
"./wiki-tools": {
|
|
22
|
+
"types": "./dist/lib/wiki-tools.d.ts",
|
|
23
|
+
"default": "./dist/lib/wiki-tools.js"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"main": "./dist/index.js",
|
|
@@ -30,7 +34,7 @@
|
|
|
30
34
|
"@modelcontextprotocol/sdk": "1.27.1",
|
|
31
35
|
"commander": "^13.0.0",
|
|
32
36
|
"zod": "^4.3.6",
|
|
33
|
-
"@parall/sdk": "1.
|
|
37
|
+
"@parall/sdk": "1.34.0"
|
|
34
38
|
},
|
|
35
39
|
"devDependencies": {
|
|
36
40
|
"@types/node": "^22.0.0",
|