feat: AI gets access to post and media connections

This commit is contained in:
2026-02-14 18:27:02 +01:00
parent 34eb0d3781
commit b0e9e39020
2 changed files with 124 additions and 0 deletions

View File

@@ -316,6 +316,10 @@ Available Tools:
- update_media_metadata: Update a media file's alt text, caption, or tags. - update_media_metadata: Update a media file's alt text, caption, or tags.
- list_tags: List all tags with post counts. - list_tags: List all tags with post counts.
- list_categories: List all categories with post counts. - list_categories: List all categories with post counts.
- get_post_backlinks: Get posts that link TO a given post (backlinks). Use to discover what references a post.
- get_post_outlinks: Get posts that a given post links TO. Use to traverse outbound links.
- get_post_media: Get media files linked to a post (featured images, galleries).
- get_media_posts: Get posts that use a specific media file.
When answering questions: When answering questions:
1. USE THE TOOLS to find information. Never make up data about posts or media. 1. USE THE TOOLS to find information. Never make up data about posts or media.

View File

@@ -15,6 +15,7 @@ import { BrowserWindow } from 'electron';
import { ChatEngine } from './ChatEngine'; import { ChatEngine } from './ChatEngine';
import { PostEngine } from './PostEngine'; import { PostEngine } from './PostEngine';
import { MediaEngine } from './MediaEngine'; import { MediaEngine } from './MediaEngine';
import { getPostMediaEngine } from './PostMediaEngine';
// OpenCode Zen API endpoints // OpenCode Zen API endpoints
const ZEN_ANTHROPIC_URL = 'https://opencode.ai/zen/v1/messages'; const ZEN_ANTHROPIC_URL = 'https://opencode.ai/zen/v1/messages';
@@ -782,6 +783,50 @@ export class OpenCodeManager {
required: ['mediaId'], required: ['mediaId'],
}, },
}, },
{
name: 'get_post_backlinks',
description: 'Get all posts that link TO a specific post (backlinks/inbound links). Use this to discover which other posts reference or cite a given post. Helpful for understanding how content is interconnected and finding related posts.',
input_schema: {
type: 'object',
properties: {
postId: { type: 'string', description: 'The ID of the post to find backlinks for' },
},
required: ['postId'],
},
},
{
name: 'get_post_outlinks',
description: 'Get all posts that a specific post links TO (outbound links). Use this to discover what other posts are referenced or cited by a given post. Helpful for understanding content relationships and traversing linked posts.',
input_schema: {
type: 'object',
properties: {
postId: { type: 'string', description: 'The ID of the post to find outbound links for' },
},
required: ['postId'],
},
},
{
name: 'get_post_media',
description: 'Get all media files linked to a specific post. Returns media that has been explicitly associated with the post (featured images, galleries, etc.). Use this to discover images and other media attached to a post.',
input_schema: {
type: 'object',
properties: {
postId: { type: 'string', description: 'The ID of the post to get linked media for' },
},
required: ['postId'],
},
},
{
name: 'get_media_posts',
description: 'Get all posts that a specific media file is linked to. Use this to find which posts use or reference a particular image or media file. Helpful for understanding media usage across posts.',
input_schema: {
type: 'object',
properties: {
mediaId: { type: 'string', description: 'The ID of the media file to find linked posts for' },
},
required: ['mediaId'],
},
},
]; ];
} }
@@ -995,6 +1040,81 @@ export class OpenCodeManager {
}; };
} }
case 'get_post_backlinks': {
const linkedBy = await this.postEngine.getLinkedBy(args.postId as string);
return {
success: true,
postId: args.postId,
count: linkedBy.length,
linkedBy: linkedBy.map(p => ({
id: p.id,
title: p.title,
slug: p.slug,
})),
};
}
case 'get_post_outlinks': {
const linksTo = await this.postEngine.getLinksTo(args.postId as string);
return {
success: true,
postId: args.postId,
count: linksTo.length,
linksTo: linksTo.map(p => ({
id: p.id,
title: p.title,
slug: p.slug,
})),
};
}
case 'get_post_media': {
const postMediaEngine = getPostMediaEngine();
const linkedMedia = await postMediaEngine.getLinkedMediaDataForPost(args.postId as string);
return {
success: true,
postId: args.postId,
count: linkedMedia.length,
media: linkedMedia.map(link => ({
id: link.media.id,
filename: link.media.filename,
originalName: link.media.originalName,
mimeType: link.media.mimeType,
alt: link.media.alt,
caption: link.media.caption,
width: link.media.width,
height: link.media.height,
sortOrder: link.sortOrder,
})),
};
}
case 'get_media_posts': {
const postMediaEngine = getPostMediaEngine();
const linkedPosts = await postMediaEngine.getLinkedPostsForMedia(args.mediaId as string);
// Fetch full post data for each linked post
const postsData = await Promise.all(
linkedPosts.map(async (link) => {
const post = await this.postEngine.getPost(link.postId);
return post ? {
id: post.id,
title: post.title,
slug: post.slug,
status: post.status,
} : null;
})
);
const validPosts = postsData.filter(p => p !== null);
return {
success: true,
mediaId: args.mediaId,
count: validPosts.length,
posts: validPosts,
};
}
default: default:
return { success: false, error: `Unknown tool: ${name}` }; return { success: false, error: `Unknown tool: ${name}` };
} }