|
1 | 1 | import * as z from "zod"; |
2 | | -import { ListWikiPagesSchema, GetWikiPageSchema } from "./schema-readonly"; |
3 | | -import { CreateWikiPageSchema, UpdateWikiPageSchema, DeleteWikiPageSchema } from "./schema"; |
| 2 | +import { BrowseWikiSchema } from "./schema-readonly"; |
| 3 | +import { ManageWikiSchema } from "./schema"; |
4 | 4 | import { gitlab, toQuery } from "../../utils/gitlab-api"; |
5 | 5 | import { resolveNamespaceForAPI } from "../../utils/namespace"; |
6 | 6 | import { ToolRegistry, EnhancedToolDefinition } from "../../types"; |
| 7 | +import { assertDefined } from "../utils"; |
7 | 8 |
|
8 | 9 | /** |
9 | | - * Wiki tools registry - unified registry containing all wiki operation tools with their handlers |
| 10 | + * Wiki tools registry - 2 CQRS tools replacing 5 individual tools |
| 11 | + * |
| 12 | + * browse_wiki (Query): list, get |
| 13 | + * manage_wiki (Command): create, update, delete |
10 | 14 | */ |
11 | 15 | export const wikiToolRegistry: ToolRegistry = new Map<string, EnhancedToolDefinition>([ |
| 16 | + // ============================================================================ |
| 17 | + // browse_wiki - CQRS Query Tool |
| 18 | + // ============================================================================ |
12 | 19 | [ |
13 | | - "list_wiki_pages", |
| 20 | + "browse_wiki", |
14 | 21 | { |
15 | | - name: "list_wiki_pages", |
| 22 | + name: "browse_wiki", |
16 | 23 | description: |
17 | | - "BROWSE: Explore all wiki pages in project or group documentation. Use when: Discovering available guides and documentation, Understanding project knowledge base structure, Finding existing pages before creating new ones. Wiki provides collaborative documentation separate from code repository. Returns page titles, slugs, content formats, and creation dates.", |
18 | | - inputSchema: z.toJSONSchema(ListWikiPagesSchema), |
| 24 | + 'BROWSE wiki pages. Actions: "list" shows all wiki pages in project/group, "get" retrieves single wiki page content by slug.', |
| 25 | + inputSchema: z.toJSONSchema(BrowseWikiSchema), |
19 | 26 | handler: async (args: unknown) => { |
20 | | - const options = ListWikiPagesSchema.parse(args); |
21 | | - const { entityType, encodedPath } = await resolveNamespaceForAPI(options.namespace); |
| 27 | + const input = BrowseWikiSchema.parse(args); |
| 28 | + const { entityType, encodedPath } = await resolveNamespaceForAPI(input.namespace); |
22 | 29 |
|
23 | | - return gitlab.get(`${entityType}/${encodedPath}/wikis`, { |
24 | | - query: toQuery(options, ["namespace"]), |
25 | | - }); |
26 | | - }, |
27 | | - }, |
28 | | - ], |
29 | | - [ |
30 | | - "get_wiki_page", |
31 | | - { |
32 | | - name: "get_wiki_page", |
33 | | - description: |
34 | | - "READ: Get complete wiki page content and metadata by slug. Use when: Reading technical documentation and guides, Accessing project knowledge base content, Getting full markdown with formatting. Returns complete page content, metadata, edit history, and author information. Perfect for content analysis and documentation review.", |
35 | | - inputSchema: z.toJSONSchema(GetWikiPageSchema), |
36 | | - handler: async (args: unknown) => { |
37 | | - const options = GetWikiPageSchema.parse(args); |
38 | | - const { entityType, encodedPath } = await resolveNamespaceForAPI(options.namespace); |
| 30 | + switch (input.action) { |
| 31 | + case "list": { |
| 32 | + const { action: _action, namespace: _namespace, ...rest } = input; |
| 33 | + const query = toQuery(rest, []); |
39 | 34 |
|
40 | | - return gitlab.get(`${entityType}/${encodedPath}/wikis/${encodeURIComponent(options.slug)}`); |
41 | | - }, |
42 | | - }, |
43 | | - ], |
44 | | - [ |
45 | | - "create_wiki_page", |
46 | | - { |
47 | | - name: "create_wiki_page", |
48 | | - description: |
49 | | - "CREATE: Add new documentation page to project or group wiki. Use when: Adding technical documentation, user guides, or FAQs, Creating project knowledge base content, Establishing team documentation standards. Check list_wiki_pages FIRST to avoid duplicate topics. Supports GitLab Flavored Markdown with extensions. Creates version-controlled documentation.", |
50 | | - inputSchema: z.toJSONSchema(CreateWikiPageSchema), |
51 | | - handler: async (args: unknown) => { |
52 | | - const options = CreateWikiPageSchema.parse(args); |
53 | | - const { entityType, encodedPath } = await resolveNamespaceForAPI(options.namespace); |
54 | | - const { namespace: _namespace, ...body } = options; |
55 | | - |
56 | | - return gitlab.post(`${entityType}/${encodedPath}/wikis`, { |
57 | | - body, |
58 | | - contentType: "json", |
59 | | - }); |
60 | | - }, |
61 | | - }, |
62 | | - ], |
63 | | - [ |
64 | | - "update_wiki_page", |
65 | | - { |
66 | | - name: "update_wiki_page", |
67 | | - description: |
68 | | - "UPDATE: Modify existing wiki page content or properties. Use when: Updating documentation with new information, Fixing errors or improving clarity, Reorganizing content structure. Maintains complete version history with change tracking. Supports collaborative editing with author attribution and diff viewing.", |
69 | | - inputSchema: z.toJSONSchema(UpdateWikiPageSchema), |
70 | | - handler: async (args: unknown) => { |
71 | | - const options = UpdateWikiPageSchema.parse(args); |
72 | | - const { entityType, encodedPath } = await resolveNamespaceForAPI(options.namespace); |
73 | | - const { namespace: _namespace, slug, ...body } = options; |
74 | | - |
75 | | - return gitlab.put(`${entityType}/${encodedPath}/wikis/${encodeURIComponent(slug)}`, { |
76 | | - body, |
77 | | - contentType: "json", |
78 | | - }); |
| 35 | + return gitlab.get(`${entityType}/${encodedPath}/wikis`, { query }); |
| 36 | + } |
| 37 | + |
| 38 | + case "get": { |
| 39 | + // slug is required for get action (validated by .refine()) |
| 40 | + assertDefined(input.slug, "slug"); |
| 41 | + return gitlab.get( |
| 42 | + `${entityType}/${encodedPath}/wikis/${encodeURIComponent(input.slug)}` |
| 43 | + ); |
| 44 | + } |
| 45 | + |
| 46 | + /* istanbul ignore next -- unreachable with Zod validation */ |
| 47 | + default: |
| 48 | + throw new Error(`Unknown action: ${(input as { action: string }).action}`); |
| 49 | + } |
79 | 50 | }, |
80 | 51 | }, |
81 | 52 | ], |
| 53 | + |
| 54 | + // ============================================================================ |
| 55 | + // manage_wiki - CQRS Command Tool |
| 56 | + // ============================================================================ |
82 | 57 | [ |
83 | | - "delete_wiki_page", |
| 58 | + "manage_wiki", |
84 | 59 | { |
85 | | - name: "delete_wiki_page", |
| 60 | + name: "manage_wiki", |
86 | 61 | description: |
87 | | - "DELETE: Permanently remove wiki page from documentation. Use when: Cleaning up outdated or obsolete content, Removing duplicate or incorrect pages, Reorganizing wiki structure. WARNING: Deletes page and ALL version history permanently - cannot be undone. Consider archiving important content first.", |
88 | | - inputSchema: z.toJSONSchema(DeleteWikiPageSchema), |
| 62 | + 'MANAGE wiki pages. Actions: "create" adds new wiki page, "update" modifies existing page, "delete" removes wiki page permanently.', |
| 63 | + inputSchema: z.toJSONSchema(ManageWikiSchema), |
89 | 64 | handler: async (args: unknown) => { |
90 | | - const options = DeleteWikiPageSchema.parse(args); |
91 | | - const { entityType, encodedPath } = await resolveNamespaceForAPI(options.namespace); |
| 65 | + const input = ManageWikiSchema.parse(args); |
| 66 | + const { entityType, encodedPath } = await resolveNamespaceForAPI(input.namespace); |
| 67 | + |
| 68 | + switch (input.action) { |
| 69 | + case "create": { |
| 70 | + const { action: _action, namespace: _namespace, ...body } = input; |
| 71 | + |
| 72 | + return gitlab.post(`${entityType}/${encodedPath}/wikis`, { |
| 73 | + body, |
| 74 | + contentType: "json", |
| 75 | + }); |
| 76 | + } |
| 77 | + |
| 78 | + case "update": { |
| 79 | + // slug is required for update action (validated by .refine()) |
| 80 | + assertDefined(input.slug, "slug"); |
| 81 | + const { action: _action, namespace: _namespace, slug, ...body } = input; |
92 | 82 |
|
93 | | - await gitlab.delete( |
94 | | - `${entityType}/${encodedPath}/wikis/${encodeURIComponent(options.slug)}` |
95 | | - ); |
96 | | - return { deleted: true }; |
| 83 | + return gitlab.put(`${entityType}/${encodedPath}/wikis/${encodeURIComponent(slug)}`, { |
| 84 | + body, |
| 85 | + contentType: "json", |
| 86 | + }); |
| 87 | + } |
| 88 | + |
| 89 | + case "delete": { |
| 90 | + // slug is required for delete action (validated by .refine()) |
| 91 | + assertDefined(input.slug, "slug"); |
| 92 | + |
| 93 | + await gitlab.delete( |
| 94 | + `${entityType}/${encodedPath}/wikis/${encodeURIComponent(input.slug)}` |
| 95 | + ); |
| 96 | + return { deleted: true }; |
| 97 | + } |
| 98 | + |
| 99 | + /* istanbul ignore next -- unreachable with Zod validation */ |
| 100 | + default: |
| 101 | + throw new Error(`Unknown action: ${(input as { action: string }).action}`); |
| 102 | + } |
97 | 103 | }, |
98 | 104 | }, |
99 | 105 | ], |
100 | 106 | ]); |
101 | 107 |
|
| 108 | +/** |
| 109 | + * Get read-only tool names from the registry |
| 110 | + */ |
102 | 111 | export function getWikiReadOnlyToolNames(): string[] { |
103 | | - return ["list_wiki_pages", "get_wiki_page"]; |
| 112 | + return ["browse_wiki"]; |
104 | 113 | } |
105 | 114 |
|
| 115 | +/** |
| 116 | + * Get all tool definitions from the registry |
| 117 | + */ |
106 | 118 | export function getWikiToolDefinitions(): EnhancedToolDefinition[] { |
107 | 119 | return Array.from(wikiToolRegistry.values()); |
108 | 120 | } |
109 | 121 |
|
| 122 | +/** |
| 123 | + * Get filtered tools based on read-only mode |
| 124 | + */ |
110 | 125 | export function getFilteredWikiTools(readOnlyMode: boolean = false): EnhancedToolDefinition[] { |
111 | 126 | if (readOnlyMode) { |
112 | 127 | const readOnlyNames = getWikiReadOnlyToolNames(); |
|
0 commit comments