wolfpack-mcp 1.0.60 → 1.0.62

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/client.js CHANGED
@@ -60,6 +60,17 @@ export class WolfpackClient {
60
60
  const response = await this.api.get('/teams');
61
61
  return response.teams;
62
62
  }
63
+ // Search methods
64
+ async search(options) {
65
+ const params = new URLSearchParams({ q: options.query });
66
+ if (options.teamSlug)
67
+ params.append('teamSlug', options.teamSlug);
68
+ if (options.includeInactive)
69
+ params.append('includeInactive', 'true');
70
+ if (options.exactOnly)
71
+ params.append('exactOnly', 'true');
72
+ return this.api.get(`/search?${params.toString()}`);
73
+ }
63
74
  // Team Member methods
64
75
  async listTeamMembers(teamSlug) {
65
76
  const params = new URLSearchParams();
@@ -469,6 +480,14 @@ export class WolfpackClient {
469
480
  const query = params.toString();
470
481
  return this.api.get(`/categories${query ? `?${query}` : ''}`);
471
482
  }
483
+ // Tag methods
484
+ async listTags(teamSlug) {
485
+ const params = new URLSearchParams();
486
+ if (teamSlug)
487
+ params.append('teamSlug', teamSlug);
488
+ const query = params.toString();
489
+ return this.api.get(`/tags${query ? `?${query}` : ''}`);
490
+ }
472
491
  /**
473
492
  * Upload an image and get back a URL that can be used in markdown content.
474
493
  */
@@ -483,6 +502,14 @@ export class WolfpackClient {
483
502
  const { buffer, contentType } = await this.api.getBuffer(`/images/${team}/${filename}`);
484
503
  return { base64: buffer.toString('base64'), mimeType: contentType };
485
504
  }
505
+ /**
506
+ * Download a stored file (new /api/files/{fileId}/{filename} URL shape) and
507
+ * return raw base64 + mimeType.
508
+ */
509
+ async downloadFile(fileId) {
510
+ const { buffer, contentType } = await this.api.getBuffer(`/files/${fileId}`);
511
+ return { base64: buffer.toString('base64'), mimeType: contentType };
512
+ }
486
513
  /**
487
514
  * Download an issue attachment image and return raw base64 + mimeType.
488
515
  */
package/dist/index.js CHANGED
@@ -166,7 +166,10 @@ const UpdateWorkItemSchema = z.object({
166
166
  .nullable()
167
167
  .optional()
168
168
  .describe('User ID for third assisting user, or null to remove'),
169
- tag_ids: z.array(z.string()).optional().describe('Tag IDs to apply (replaces existing tags)'),
169
+ tags: z
170
+ .array(z.string())
171
+ .optional()
172
+ .describe('Tag names to apply (replaces existing tags). Use list_tags to see available tags.'),
170
173
  closing_comment: z
171
174
  .string()
172
175
  .nullable()
@@ -217,7 +220,14 @@ const CreateRadarItemSchema = z.object({
217
220
  .optional()
218
221
  .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
219
222
  title: z.string().describe('Title of the radar/initiative item'),
220
- description: z.string().optional().describe('Description of the initiative (markdown)'),
223
+ description: z
224
+ .string()
225
+ .optional()
226
+ .describe('Short summary shown on the roadmap card (1-2 sentences). Put extended detail in notes, not here.'),
227
+ notes: z
228
+ .string()
229
+ .optional()
230
+ .describe("Extended details shown on the initiative's Notes page (markdown). Use this for long-form content."),
221
231
  stage: z
222
232
  .enum(['pending', 'now', 'next', 'later', 'completed'])
223
233
  .describe('Stage: "pending" (not started), "now" (current sprint), "next" (upcoming), "later" (future), "completed"'),
@@ -225,8 +235,11 @@ const CreateRadarItemSchema = z.object({
225
235
  const UpdateRadarItemSchema = z.object({
226
236
  item_id: z.string().describe('The refId (number) of the radar item to update'),
227
237
  title: z.string().optional().describe('Updated title'),
228
- description: z.string().optional().describe('Updated description (markdown)'),
229
- notes: z.string().optional().describe('Updated internal notes (markdown)'),
238
+ description: z
239
+ .string()
240
+ .optional()
241
+ .describe('Updated short summary (1-2 sentences; extended detail belongs in notes)'),
242
+ notes: z.string().optional().describe('Updated Notes page content (markdown)'),
230
243
  stage: z
231
244
  .enum(['pending', 'now', 'next', 'later', 'completed'])
232
245
  .optional()
@@ -343,6 +356,22 @@ const UpdateIssueSchema = z.object({
343
356
  .optional()
344
357
  .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
345
358
  });
359
+ // Search schema
360
+ const SearchSchema = z.object({
361
+ query: z.string().describe('Search query (websearch syntax: quoted phrases, -exclusions)'),
362
+ project_slug: z
363
+ .string()
364
+ .optional()
365
+ .describe('Project slug; omit to search all accessible projects'),
366
+ include_inactive: z
367
+ .boolean()
368
+ .optional()
369
+ .describe('Include finished/archived items (default false)'),
370
+ exact_only: z
371
+ .boolean()
372
+ .optional()
373
+ .describe('Only exact full-text matches, skipping semantic results (default false)'),
374
+ });
346
375
  // Wiki Page schemas
347
376
  const ListWikiPagesSchema = z.object({
348
377
  project_slug: z.string().optional().describe('Project slug to filter wiki pages'),
@@ -488,7 +517,7 @@ const UploadImageSchema = z.object({
488
517
  const DownloadImageSchema = z.object({
489
518
  image_url: z
490
519
  .string()
491
- .describe('The image URL path found in content fields (e.g. "/api/files/images/{team}/{filename}").'),
520
+ .describe('The image URL path found in content fields (e.g. "/api/files/{fileId}/{filename}" or legacy "/api/files/images/{team}/{filename}").'),
492
521
  });
493
522
  // Skill schemas
494
523
  const GetSkillSchema = z.object({
@@ -505,6 +534,13 @@ const ListCategoriesSchema = z.object({
505
534
  .optional()
506
535
  .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
507
536
  });
537
+ // Tag schemas
538
+ const ListTagsSchema = z.object({
539
+ project_slug: z
540
+ .string()
541
+ .optional()
542
+ .describe('Project slug (required for multi-project users, use list_projects to get slugs)'),
543
+ });
508
544
  // Team member schemas
509
545
  const ListTeamMembersSchema = z.object({
510
546
  project_slug: z
@@ -671,6 +707,36 @@ class WolfpackMCPServer {
671
707
  properties: {},
672
708
  },
673
709
  },
710
+ // Search tool
711
+ {
712
+ name: 'search',
713
+ description: 'Ranked full-text search across project content: wiki pages, work items, cases, journal entries and issues. ' +
714
+ 'Title matches outrank body matches; results include a snippet, entity type, and refId/slug for follow-up calls ' +
715
+ '(get_work_item, get_issue, get_wiki_page, ...). ' +
716
+ 'Use this to find existing content before creating new items, or when the user asks to "find" or "look up" something without saying where it lives.',
717
+ inputSchema: {
718
+ type: 'object',
719
+ properties: {
720
+ query: {
721
+ type: 'string',
722
+ description: 'Search query (websearch syntax: quoted phrases, -exclusions)',
723
+ },
724
+ project_slug: {
725
+ type: 'string',
726
+ description: 'Project slug; omit to search all accessible projects',
727
+ },
728
+ include_inactive: {
729
+ type: 'boolean',
730
+ description: 'Include finished/archived items (default false)',
731
+ },
732
+ exact_only: {
733
+ type: 'boolean',
734
+ description: 'Only exact full-text matches, skipping semantic results (default false)',
735
+ },
736
+ },
737
+ required: ['query'],
738
+ },
739
+ },
674
740
  // Work Item tools
675
741
  {
676
742
  name: 'list_work_items',
@@ -748,7 +814,7 @@ class WolfpackMCPServer {
748
814
  '(pending→pull first, new→doing, blocked/ready/completed/closed→new→doing, then review when done). ' +
749
815
  'PLANNING: Check if the description contains a plan (markdown checklist). If not, APPEND one using update_work_progress - preserve all original description text and add your plan below a "---" separator. ' +
750
816
  'FORMS: Procedure-created work items may include formDefinition (field definitions with name, label, type, required, options) and formValues (current values). Use submit_work_item_form to fill in form values. ' +
751
- 'IMAGES: Image references in the description (e.g. `![alt](/api/files/images/...)`) can be viewed using the download_image tool.',
817
+ 'IMAGES: Image references in the description (e.g. `![alt](/api/files/...)`) can be viewed using the download_image tool.',
752
818
  inputSchema: {
753
819
  type: 'object',
754
820
  properties: {
@@ -790,7 +856,7 @@ class WolfpackMCPServer {
790
856
  name: 'update_work_item',
791
857
  description: 'Update one or more fields on a work item. Only provide the fields you want to change. ' +
792
858
  'Supports: title, description, status, leading_user_id (assignee), assisting_user1/2/3_id, ' +
793
- 'radar_item_id (initiative), category_id, priority (0-4), size (S/M/L), tag_ids, and closing_comment. ' +
859
+ 'radar_item_id (initiative), category_id, priority (0-4), size (S/M/L), tags, and closing_comment. ' +
794
860
  'STATUS WORKFLOW: "pending" (backlog) → "new" (to do) → "doing" (in progress) → "review" (work done) → "ready" (awaiting deployment) → "completed" (deployed). ' +
795
861
  'Use "blocked" when work cannot proceed. For "pending" items use pull_work_item first. ' +
796
862
  'When moving to "review", add a completion comment. When moving to "blocked", add a comment explaining the blocker.',
@@ -857,10 +923,10 @@ class WolfpackMCPServer {
857
923
  type: ['string', 'null'],
858
924
  description: 'User ID for third assisting user, or null to remove',
859
925
  },
860
- tag_ids: {
926
+ tags: {
861
927
  type: 'array',
862
928
  items: { type: 'string' },
863
- description: 'Tag IDs to apply (replaces existing tags)',
929
+ description: 'Tag names to apply (replaces existing tags). Use list_tags to see available tags.',
864
930
  },
865
931
  closing_comment: {
866
932
  type: ['string', 'null'],
@@ -953,7 +1019,7 @@ class WolfpackMCPServer {
953
1019
  {
954
1020
  name: 'get_radar_item',
955
1021
  description: 'Get a specific radar item (initiative) by reference number, including its work items and participants. ' +
956
- 'IMAGES: Image references in the description (e.g. `![alt](/api/files/images/...)`) can be viewed using the download_image tool.',
1022
+ 'IMAGES: Image references in the description (e.g. `![alt](/api/files/...)`) can be viewed using the download_image tool.',
957
1023
  inputSchema: {
958
1024
  type: 'object',
959
1025
  properties: {
@@ -972,6 +1038,7 @@ class WolfpackMCPServer {
972
1038
  {
973
1039
  name: 'create_radar_item',
974
1040
  description: 'Create a new radar/initiative item in your current project. Requires mcp:radar:create permission. ' +
1041
+ 'Keep "description" to a short summary; put extended detail in "notes". ' +
975
1042
  CONTENT_LINKING_HELP,
976
1043
  inputSchema: {
977
1044
  type: 'object',
@@ -983,7 +1050,11 @@ class WolfpackMCPServer {
983
1050
  title: { type: 'string', description: 'Title of the radar/initiative item' },
984
1051
  description: {
985
1052
  type: 'string',
986
- description: 'Description of the initiative (markdown)',
1053
+ description: 'Short summary shown on the roadmap card (1-2 sentences). Put extended detail in notes, not here.',
1054
+ },
1055
+ notes: {
1056
+ type: 'string',
1057
+ description: "Extended details shown on the initiative's Notes page (markdown). Use this for long-form content.",
987
1058
  },
988
1059
  stage: {
989
1060
  type: 'string',
@@ -1006,8 +1077,11 @@ class WolfpackMCPServer {
1006
1077
  description: 'The refId (number) of the radar item to update',
1007
1078
  },
1008
1079
  title: { type: 'string', description: 'Updated title' },
1009
- description: { type: 'string', description: 'Updated description (markdown)' },
1010
- notes: { type: 'string', description: 'Updated internal notes (markdown)' },
1080
+ description: {
1081
+ type: 'string',
1082
+ description: 'Updated short summary (1-2 sentences; extended detail belongs in notes)',
1083
+ },
1084
+ notes: { type: 'string', description: 'Updated Notes page content (markdown)' },
1011
1085
  stage: {
1012
1086
  type: 'string',
1013
1087
  enum: ['pending', 'now', 'next', 'later', 'completed'],
@@ -1086,7 +1160,7 @@ class WolfpackMCPServer {
1086
1160
  description: 'Get a specific issue by its PREFIXED reference #iN (e.g. #i5). ' +
1087
1161
  'IMPORTANT: A bare #N (without prefix) is ALWAYS a work item, NOT an issue. Only use this tool when the user explicitly says "issue" or uses the #i prefix. ' +
1088
1162
  'Returns full details including comment and attachment counts. ' +
1089
- 'IMAGES: Image references in the description (e.g. `![alt](/api/files/images/...)`) can be viewed using the download_image tool.',
1163
+ 'IMAGES: Image references in the description (e.g. `![alt](/api/files/...)`) can be viewed using the download_image tool.',
1090
1164
  inputSchema: {
1091
1165
  type: 'object',
1092
1166
  properties: {
@@ -1271,7 +1345,7 @@ class WolfpackMCPServer {
1271
1345
  {
1272
1346
  name: 'get_wiki_page',
1273
1347
  description: 'Get a specific wiki/documentation page by slug (URL path). Returns full content in markdown. ' +
1274
- 'IMAGES: Image references in the content (e.g. `![alt](/api/files/images/...)`) can be viewed using the download_image tool.',
1348
+ 'IMAGES: Image references in the content (e.g. `![alt](/api/files/...)`) can be viewed using the download_image tool.',
1275
1349
  inputSchema: {
1276
1350
  type: 'object',
1277
1351
  properties: {
@@ -1323,7 +1397,7 @@ class WolfpackMCPServer {
1323
1397
  tag_ids: {
1324
1398
  type: 'array',
1325
1399
  items: { type: 'string' },
1326
- description: 'Tag IDs to apply to the page (replaces existing tags). Use list tags endpoint to get available tag IDs.',
1400
+ description: 'Tag IDs to apply to the page (replaces existing tags)',
1327
1401
  },
1328
1402
  project_slug: {
1329
1403
  type: 'string',
@@ -1364,7 +1438,7 @@ class WolfpackMCPServer {
1364
1438
  {
1365
1439
  name: 'get_journal_entry',
1366
1440
  description: 'Get a specific journal/log entry by reference number. Returns full content. ' +
1367
- 'IMAGES: Image references in the content (e.g. `![alt](/api/files/images/...)`) can be viewed using the download_image tool.',
1441
+ 'IMAGES: Image references in the content (e.g. `![alt](/api/files/...)`) can be viewed using the download_image tool.',
1368
1442
  inputSchema: {
1369
1443
  type: 'object',
1370
1444
  properties: {
@@ -1427,7 +1501,7 @@ class WolfpackMCPServer {
1427
1501
  {
1428
1502
  name: 'list_work_item_comments',
1429
1503
  description: 'List all comments on a work item. Returns comments in chronological order. Requires mcp:comments:read permission. ' +
1430
- 'IMAGES: Image references in comments (e.g. `![alt](/api/files/images/...)`) can be viewed using the download_image tool.',
1504
+ 'IMAGES: Image references in comments (e.g. `![alt](/api/files/...)`) can be viewed using the download_image tool.',
1431
1505
  inputSchema: {
1432
1506
  type: 'object',
1433
1507
  properties: {
@@ -1443,7 +1517,7 @@ class WolfpackMCPServer {
1443
1517
  {
1444
1518
  name: 'list_issue_comments',
1445
1519
  description: 'List all comments on an issue. Returns comments in chronological order. Requires mcp:comments:read permission. ' +
1446
- 'IMAGES: Image references in comments (e.g. `![alt](/api/files/images/...)`) can be viewed using the download_image tool.',
1520
+ 'IMAGES: Image references in comments (e.g. `![alt](/api/files/...)`) can be viewed using the download_image tool.',
1447
1521
  inputSchema: {
1448
1522
  type: 'object',
1449
1523
  properties: {
@@ -1563,7 +1637,7 @@ class WolfpackMCPServer {
1563
1637
  {
1564
1638
  name: 'download_image',
1565
1639
  description: 'Download and view an image referenced in content fields. ' +
1566
- 'Content from work items, issues, wiki pages, journal entries, and comments may contain image references like `![alt](/api/files/images/...)`. ' +
1640
+ 'Content from work items, issues, wiki pages, journal entries, and comments may contain image references like `![alt](/api/files/{fileId}/...)` or the legacy `![alt](/api/files/images/...)`. ' +
1567
1641
  'Issues may also have attachment images with URLs like `/api/teams/{team}/issues/{refId}/attachments/{id}`. ' +
1568
1642
  'Use this tool with that URL path to download and view the image. ' +
1569
1643
  'Requires mcp:images:read permission.',
@@ -1572,7 +1646,7 @@ class WolfpackMCPServer {
1572
1646
  properties: {
1573
1647
  image_url: {
1574
1648
  type: 'string',
1575
- description: 'The image URL path found in content fields (e.g. "/api/files/images/{team}/{filename}" or "/api/teams/{team}/issues/{refId}/attachments/{id}").',
1649
+ description: 'The image URL path found in content fields (e.g. "/api/files/{fileId}/{filename}", legacy "/api/files/images/{team}/{filename}", or "/api/teams/{team}/issues/{refId}/attachments/{id}").',
1576
1650
  },
1577
1651
  },
1578
1652
  required: ['image_url'],
@@ -1593,6 +1667,21 @@ class WolfpackMCPServer {
1593
1667
  },
1594
1668
  },
1595
1669
  },
1670
+ // Tag tools
1671
+ {
1672
+ name: 'list_tags',
1673
+ description: 'List all tags available in a project. Returns tag names and colours. ' +
1674
+ 'Work items are tagged by name via update_work_item.',
1675
+ inputSchema: {
1676
+ type: 'object',
1677
+ properties: {
1678
+ project_slug: {
1679
+ type: 'string',
1680
+ description: 'Project slug (required for multi-project users, use list_projects to get slugs)',
1681
+ },
1682
+ },
1683
+ },
1684
+ },
1596
1685
  // Team member tools
1597
1686
  {
1598
1687
  name: 'list_team_members',
@@ -1929,6 +2018,22 @@ class WolfpackMCPServer {
1929
2018
  ],
1930
2019
  };
1931
2020
  }
2021
+ // Search handler
2022
+ case 'search': {
2023
+ const parsed = SearchSchema.parse(args);
2024
+ const results = await this.client.search({
2025
+ query: parsed.query,
2026
+ teamSlug: parsed.project_slug || this.client.getProjectSlug() || undefined,
2027
+ includeInactive: parsed.include_inactive,
2028
+ exactOnly: parsed.exact_only,
2029
+ });
2030
+ const text = results.length === 0
2031
+ ? 'No results found.'
2032
+ : JSON.stringify(stripUuids(results), null, 2);
2033
+ return {
2034
+ content: [{ type: 'text', text }],
2035
+ };
2036
+ }
1932
2037
  // Work Item handlers
1933
2038
  case 'list_work_items': {
1934
2039
  const parsed = ListWorkItemsSchema.parse(args);
@@ -2029,8 +2134,8 @@ class WolfpackMCPServer {
2029
2134
  fields.assistingUser2Id = parsed.assisting_user2_id;
2030
2135
  if (parsed.assisting_user3_id !== undefined)
2031
2136
  fields.assistingUser3Id = parsed.assisting_user3_id;
2032
- if (parsed.tag_ids !== undefined)
2033
- fields.tagIds = parsed.tag_ids;
2137
+ if (parsed.tags !== undefined)
2138
+ fields.tags = parsed.tags;
2034
2139
  if (parsed.closing_comment !== undefined)
2035
2140
  fields.closingComment = parsed.closing_comment;
2036
2141
  const workItem = await this.client.updateWorkItem(parsed.work_item_id, fields, teamSlug);
@@ -2077,7 +2182,7 @@ class WolfpackMCPServer {
2077
2182
  changes.push(parsed.assisting_user3_id
2078
2183
  ? `assisting user 3 updated`
2079
2184
  : 'assisting user 3 removed');
2080
- if (parsed.tag_ids !== undefined)
2185
+ if (parsed.tags !== undefined)
2081
2186
  changes.push('tags updated');
2082
2187
  if (parsed.closing_comment !== undefined)
2083
2188
  changes.push('closing comment set');
@@ -2168,6 +2273,7 @@ class WolfpackMCPServer {
2168
2273
  const radarItem = await this.client.createRadarItem({
2169
2274
  title: parsed.title,
2170
2275
  description: parsed.description,
2276
+ notes: parsed.notes,
2171
2277
  stage: parsed.stage,
2172
2278
  teamSlug: parsed.project_slug || this.client.getProjectSlug() || undefined,
2173
2279
  });
@@ -2557,13 +2663,18 @@ class WolfpackMCPServer {
2557
2663
  }
2558
2664
  case 'download_image': {
2559
2665
  const parsed = DownloadImageSchema.parse(args);
2560
- // Try S3-stored image pattern: /api/files/images/{team}/{filename}
2666
+ // Try stored-file pattern (#1439): /api/files/{fileId}/{filename}
2667
+ const storedFileMatch = parsed.image_url.match(/\/api\/files\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?:\/|$)/i);
2668
+ // Try legacy S3-stored image pattern: /api/files/images/{team}/{filename}
2561
2669
  const s3Match = parsed.image_url.match(/\/api\/files\/images\/([^/]+)\/(.+)$/);
2562
2670
  // Try issue attachment pattern: /api/teams/{team}/issues/{refId}/attachments/{id}
2563
2671
  const attachmentMatch = parsed.image_url.match(/\/api\/teams\/([^/]+)\/issues\/(\d+)\/attachments\/([^/]+)$/);
2564
2672
  let base64;
2565
2673
  let mimeType;
2566
- if (s3Match) {
2674
+ if (storedFileMatch) {
2675
+ ({ base64, mimeType } = await this.client.downloadFile(storedFileMatch[1]));
2676
+ }
2677
+ else if (s3Match) {
2567
2678
  const [, team, filename] = s3Match;
2568
2679
  ({ base64, mimeType } = await this.client.downloadImage(team, filename));
2569
2680
  }
@@ -2572,7 +2683,7 @@ class WolfpackMCPServer {
2572
2683
  ({ base64, mimeType } = await this.client.downloadIssueAttachment(team, refId, attachmentId));
2573
2684
  }
2574
2685
  else {
2575
- throw new Error('Invalid image URL. Expected format: /api/files/images/{team}/{filename} or /api/teams/{team}/issues/{refId}/attachments/{id}');
2686
+ throw new Error('Invalid image URL. Expected format: /api/files/{fileId}/{filename}, /api/files/images/{team}/{filename} or /api/teams/{team}/issues/{refId}/attachments/{id}');
2576
2687
  }
2577
2688
  return {
2578
2689
  content: [
@@ -2592,6 +2703,14 @@ class WolfpackMCPServer {
2592
2703
  content: [{ type: 'text', text: JSON.stringify(categories, null, 2) }],
2593
2704
  };
2594
2705
  }
2706
+ // Tag handlers
2707
+ case 'list_tags': {
2708
+ const parsed = ListTagsSchema.parse(args);
2709
+ const tags = await this.client.listTags(parsed.project_slug || this.client.getProjectSlug() || undefined);
2710
+ return {
2711
+ content: [{ type: 'text', text: JSON.stringify(tags, null, 2) }],
2712
+ };
2713
+ }
2595
2714
  // Team member handlers
2596
2715
  case 'list_team_members': {
2597
2716
  const parsed = ListTeamMembersSchema.parse(args);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.60",
3
+ "version": "1.0.62",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -1,21 +0,0 @@
1
- const IMAGE_URL_REGEX = /!\[([^\]]*)\]\(\/api\/files\/images\/([^/]+)\/([^)]+)\)/g;
2
- /**
3
- * Processes markdown content to replace image URLs with base64 data URIs.
4
- * Failed fetches or oversized images leave the original URL intact.
5
- */
6
- export async function processContentImages(content, client) {
7
- if (!content)
8
- return content;
9
- const matches = [...content.matchAll(IMAGE_URL_REGEX)];
10
- if (matches.length === 0)
11
- return content;
12
- let result = content;
13
- for (const match of matches) {
14
- const [fullMatch, alt, team, filename] = match;
15
- const dataUri = await client.fetchImageAsBase64(team, filename);
16
- if (dataUri) {
17
- result = result.replace(fullMatch, `![${alt}](${dataUri})`);
18
- }
19
- }
20
- return result;
21
- }