readysite / website / internal / assist / tools.go
20.6 KB
tools.go
// Package assist provides AI tool definitions for the website CMS.
package assist

import (
	"github.com/readysite/readysite/pkg/assistant"
)

// =============================================================================
// Tool Registry
// =============================================================================

// All returns all available tools.
func All() []assistant.Tool {
	return []assistant.Tool{
		// Page tools
		createPageTool,
		updatePageTool,
		deletePageTool,
		deletePagesTool, // Bulk delete
		getPageTool,
		listPagesTool,

		// Collection tools
		createCollectionTool,
		updateCollectionTool,
		deleteCollectionTool,
		deleteCollectionsTool, // Bulk delete
		getCollectionTool,
		listCollectionsTool,

		// Document tools
		createDocumentTool,
		createDocumentsTool, // Batch creation
		updateDocumentTool,
		deleteDocumentTool,
		getDocumentTool,
		queryDocumentsTool,

		// Partial tools
		createPartialTool,
		updatePartialTool,
		deletePartialTool,
		getPartialTool,
		listPartialsTool,

		// File tools
		updateFileTool,
		getFileTool,
		listFilesTool,
		readFileTool,

		// Note tools
		createNoteTool,
		listNotesTool,
		getNoteTool,
		updateNoteTool,
		deleteNoteTool,

		// User tools
		createUserTool,
		updateUserTool,
		deleteUserTool,
		listUsersTool,

		// Utility tools
		validateTemplateTool,
		navigateUserTool,
		searchTool,
	}
}

// ToolNames returns all tool names.
func ToolNames() []string {
	tools := All()
	names := make([]string, len(tools))
	for i, t := range tools {
		names[i] = t.Name
	}
	return names
}

// GetTool returns a tool by name.
func GetTool(name string) *assistant.Tool {
	for _, t := range All() {
		if t.Name == name {
			return &t
		}
	}
	return nil
}

// =============================================================================
// Page Tools
// =============================================================================

var (
	createPageTool = assistant.NewTool("create_page", "Create a new page with the given title and HTML content").
			String("title", "Page title", true).
			String("html", "HTML content for the page", true).
			String("parent_id", "Parent page ID (empty for root page)", false).
			String("id", "URL-friendly ID/slug for the page (auto-generated if not provided)", false).
			String("description", "Meta description for SEO", false).
			Bool("published", "Whether the page is publicly visible", false).
			Build()

	updatePageTool = assistant.NewTool("update_page", "Update an existing page's properties").
			String("id", "Page ID to update", true).
			String("title", "New page title", false).
			String("html", "New HTML content", false).
			String("description", "New meta description", false).
			String("parent_id", "New parent page ID", false).
			Int("position", "New position within parent", false).
			Bool("published", "Whether the page is publicly visible", false).
			Build()

	deletePageTool = assistant.NewTool("delete_page", "Delete a page by ID").
			String("id", "Page ID to delete", true).
			Build()

	deletePagesTool = assistant.NewTool("delete_pages", "Delete multiple pages at once. Use this when you need to remove several pages.").
			String("ids", "JSON array of page IDs to delete, e.g. [\"page1\", \"page2\", \"page3\"]", true).
			Build()

	getPageTool = assistant.NewTool("get_page", "Get a page by ID").
			String("id", "Page ID to retrieve", true).
			Build()

	listPagesTool = assistant.NewTool("list_pages", "List pages, optionally filtered by parent").
			String("parent_id", "Parent page ID to filter by (empty for root pages)", false).
			Bool("include_unpublished", "Include unpublished pages in results", false).
			Build()
)

// CreatePageArgs holds arguments for create_page tool.
type CreatePageArgs struct {
	Title       string `json:"title"`
	HTML        string `json:"html"`
	ParentID    string `json:"parent_id"`
	ID          string `json:"id"`
	Description string `json:"description"`
	Published   bool   `json:"published"`
}

// UpdatePageArgs holds arguments for update_page tool.
type UpdatePageArgs struct {
	ID          string  `json:"id"`
	Title       *string `json:"title"`
	HTML        *string `json:"html"`
	Description *string `json:"description"`
	ParentID    *string `json:"parent_id"`
	Position    *int    `json:"position"`
	Published   *bool   `json:"published"`
}

// DeletePageArgs holds arguments for delete_page tool.
type DeletePageArgs struct {
	ID string `json:"id"`
}

// DeletePagesArgs holds arguments for delete_pages tool.
type DeletePagesArgs struct {
	IDs string `json:"ids"` // JSON array of IDs
}

// GetPageArgs holds arguments for get_page tool.
type GetPageArgs struct {
	ID string `json:"id"`
}

// ListPagesArgs holds arguments for list_pages tool.
type ListPagesArgs struct {
	ParentID           string `json:"parent_id"`
	IncludeUnpublished bool   `json:"include_unpublished"`
}

// =============================================================================
// Collection Tools
// =============================================================================

var (
	createCollectionTool = assistant.NewTool("create_collection", "Create a new collection with the given name and schema").
				String("name", "Collection display name", true).
				String("id", "URL-friendly ID/slug (auto-generated if not provided)", false).
				String("description", "Collection description", false).
				String("schema", `JSON array of field definitions. Each field: {"name": string, "type": string, "required": bool, "unique": bool, "options": object}.

Field types: text (maxLength, pattern), number (min, max, onlyInt), bool, date, email, url, select (values REQUIRED, multiple), relation (collection REQUIRED, multiple), file (maxSize, mimeTypes, multiple), json, geopoint, editor (maxLength), autodate (onCreate, onUpdate).

Example: [{"name":"title","type":"text","required":true},{"name":"status","type":"select","options":{"values":["draft","published"]}}]`, false).
				Build()

	updateCollectionTool = assistant.NewTool("update_collection", "Update a collection's properties").
				String("id", "Collection ID to update", true).
				String("name", "New display name", false).
				String("description", "New description", false).
				String("schema", `JSON array of field definitions. Each field: {"name": string, "type": string, "required": bool, "unique": bool, "options": object}.

Field types: text, number, bool, date, email, url, select (values REQUIRED), relation (collection REQUIRED), file, json, geopoint, editor, autodate.`, false).
				Build()

	deleteCollectionTool = assistant.NewTool("delete_collection", "Delete a collection and all its documents").
				String("id", "Collection ID to delete", true).
				Build()

	deleteCollectionsTool = assistant.NewTool("delete_collections", "Delete multiple collections and all their documents at once").
				String("ids", "JSON array of collection IDs to delete, e.g. [\"collection1\", \"collection2\"]", true).
				Build()

	getCollectionTool = assistant.NewTool("get_collection", "Get a collection by ID").
				String("id", "Collection ID to retrieve", true).
				Build()

	listCollectionsTool = assistant.NewTool("list_collections", "List all collections").
				Build()
)

// CreateCollectionArgs holds arguments for create_collection tool.
type CreateCollectionArgs struct {
	Name        string `json:"name"`
	ID          string `json:"id"`
	Description string `json:"description"`
	Schema      string `json:"schema"`
}

// UpdateCollectionArgs holds arguments for update_collection tool.
type UpdateCollectionArgs struct {
	ID          string  `json:"id"`
	Name        *string `json:"name"`
	Description *string `json:"description"`
	Schema      *string `json:"schema"`
}

// DeleteCollectionArgs holds arguments for delete_collection tool.
type DeleteCollectionArgs struct {
	ID string `json:"id"`
}

// DeleteCollectionsArgs holds arguments for delete_collections tool.
type DeleteCollectionsArgs struct {
	IDs string `json:"ids"` // JSON array of IDs
}

// GetCollectionArgs holds arguments for get_collection tool.
type GetCollectionArgs struct {
	ID string `json:"id"`
}

// =============================================================================
// Document Tools
// =============================================================================

var (
	createDocumentTool = assistant.NewTool("create_document", "Create a new document in a collection").
				String("collection_id", "Collection ID to add document to", true).
				String("data", "JSON object with field values", true).
				Build()

	createDocumentsTool = assistant.NewTool("create_documents", "Create multiple documents in a collection at once").
				String("collection_id", "Collection ID to add documents to", true).
				String("documents", "JSON array of document data objects", true).
				Build()

	updateDocumentTool = assistant.NewTool("update_document", "Update a document's data").
				String("id", "Document ID to update", true).
				String("data", "JSON object with field values to update", true).
				Build()

	deleteDocumentTool = assistant.NewTool("delete_document", "Delete a document by ID").
				String("id", "Document ID to delete", true).
				Build()

	getDocumentTool = assistant.NewTool("get_document", "Get a document by ID").
			String("id", "Document ID to retrieve", true).
			Build()

	queryDocumentsTool = assistant.NewTool("query_documents", "Query documents in a collection").
				String("collection_id", "Collection ID to query", true).
				String("filter", "SQL WHERE clause filter (without WHERE keyword)", false).
				String("order_by", "SQL ORDER BY clause (without ORDER BY keyword)", false).
				Int("limit", "Maximum number of documents to return", false).
				Build()
)

// CreateDocumentArgs holds arguments for create_document tool.
type CreateDocumentArgs struct {
	CollectionID string `json:"collection_id"`
	Data         string `json:"data"`
}

// CreateDocumentsArgs holds arguments for create_documents tool.
type CreateDocumentsArgs struct {
	CollectionID string `json:"collection_id"`
	Documents    string `json:"documents"`
}

// UpdateDocumentArgs holds arguments for update_document tool.
type UpdateDocumentArgs struct {
	ID   string `json:"id"`
	Data string `json:"data"`
}

// DeleteDocumentArgs holds arguments for delete_document tool.
type DeleteDocumentArgs struct {
	ID string `json:"id"`
}

// GetDocumentArgs holds arguments for get_document tool.
type GetDocumentArgs struct {
	ID string `json:"id"`
}

// QueryDocumentsArgs holds arguments for query_documents tool.
type QueryDocumentsArgs struct {
	CollectionID string `json:"collection_id"`
	Filter       string `json:"filter"`
	OrderBy      string `json:"order_by"`
	Limit        int    `json:"limit"`
}

// =============================================================================
// Partial Tools
// =============================================================================

var (
	createPartialTool = assistant.NewTool("create_partial", "Create a reusable HTML partial/component that can be included in pages using {{partial \"name\"}}").
				String("name", "Display name for the partial", true).
				String("html", "HTML content for the partial", true).
				String("id", "URL-friendly ID/slug for the partial (auto-generated from name if not provided)", false).
				String("description", "Description of what this partial is for (helps AI understand usage)", false).
				Bool("published", "Whether the partial is available for use in pages", false).
				Build()

	updatePartialTool = assistant.NewTool("update_partial", "Update an existing partial's properties").
				String("id", "Partial ID to update", true).
				String("name", "New display name", false).
				String("html", "New HTML content", false).
				String("description", "New description", false).
				Bool("published", "Whether the partial is available for use", false).
				Build()

	deletePartialTool = assistant.NewTool("delete_partial", "Delete a partial by ID").
				String("id", "Partial ID to delete", true).
				Build()

	getPartialTool = assistant.NewTool("get_partial", "Get a partial by ID").
			String("id", "Partial ID to retrieve", true).
			Build()

	listPartialsTool = assistant.NewTool("list_partials", "List all partials").
				Bool("include_unpublished", "Include unpublished partials in results", false).
				Build()
)

// CreatePartialArgs holds arguments for create_partial tool.
type CreatePartialArgs struct {
	Name        string `json:"name"`
	HTML        string `json:"html"`
	ID          string `json:"id"`
	Description string `json:"description"`
	Published   bool   `json:"published"`
}

// UpdatePartialArgs holds arguments for update_partial tool.
type UpdatePartialArgs struct {
	ID          string  `json:"id"`
	Name        *string `json:"name"`
	HTML        *string `json:"html"`
	Description *string `json:"description"`
	Published   *bool   `json:"published"`
}

// DeletePartialArgs holds arguments for delete_partial tool.
type DeletePartialArgs struct {
	ID string `json:"id"`
}

// GetPartialArgs holds arguments for get_partial tool.
type GetPartialArgs struct {
	ID string `json:"id"`
}

// ListPartialsArgs holds arguments for list_partials tool.
type ListPartialsArgs struct {
	IncludeUnpublished bool `json:"include_unpublished"`
}

// =============================================================================
// File Tools
// =============================================================================

var (
	updateFileTool = assistant.NewTool("update_file", "Update a file's publishing settings").
			String("id", "File ID to update", true).
			Bool("published", "Whether the file is publicly accessible at its path", false).
			String("path", "Public URL path (e.g., 'images/logo.png')", false).
			Build()

	getFileTool = assistant.NewTool("get_file", "Get a file's metadata by ID").
			String("id", "File ID to retrieve", true).
			Build()

	listFilesTool = assistant.NewTool("list_files", "List uploaded files").
			Bool("include_unpublished", "Include unpublished files", false).
			String("mime_type", "Filter by MIME type prefix (e.g., 'image/')", false).
			Build()

	readFileTool = assistant.NewTool("read_file", "Read the text content of a file. Only works for text-based files (HTML, CSS, JS, JSON, etc.) up to 100KB.").
			String("id", "File ID to read", true).
			Build()
)

// UpdateFileArgs holds arguments for update_file tool.
type UpdateFileArgs struct {
	ID        string  `json:"id"`
	Published *bool   `json:"published"`
	Path      *string `json:"path"`
}

// GetFileArgs holds arguments for get_file tool.
type GetFileArgs struct {
	ID string `json:"id"`
}

// ListFilesArgs holds arguments for list_files tool.
type ListFilesArgs struct {
	IncludeUnpublished bool   `json:"include_unpublished"`
	MimeType           string `json:"mime_type"`
}

// ReadFileArgs holds arguments for read_file tool.
type ReadFileArgs struct {
	ID string `json:"id"`
}

// =============================================================================
// Note Tools
// =============================================================================

var (
	createNoteTool = assistant.NewTool("create_note", "Create a persistent note to remember preferences, conventions, or learned patterns").
			String("type", "Note type: 'preference' (user preferences), 'convention' (coding/style conventions), 'learned' (patterns you've discovered)", true).
			String("category", "Note category: 'style', 'content', 'structure', or 'behavior'", true).
			String("title", "Short descriptive title for the note", true).
			String("content", "Full note content with details", true).
			Build()

	listNotesTool = assistant.NewTool("list_notes", "List all project notes").
			Bool("include_inactive", "Include inactive/disabled notes", false).
			String("type", "Filter by note type: 'preference', 'convention', or 'learned'", false).
			String("category", "Filter by category: 'style', 'content', 'structure', or 'behavior'", false).
			Build()

	getNoteTool = assistant.NewTool("get_note", "Get a specific note by ID").
			String("id", "Note ID to retrieve", true).
			Build()

	updateNoteTool = assistant.NewTool("update_note", "Update an existing note").
			String("id", "Note ID to update", true).
			String("title", "New title", false).
			String("content", "New content", false).
			Bool("active", "Enable or disable the note", false).
			Build()

	deleteNoteTool = assistant.NewTool("delete_note", "Delete a note").
			String("id", "Note ID to delete", true).
			Build()
)

// CreateNoteArgs holds arguments for create_note tool.
type CreateNoteArgs struct {
	Type     string `json:"type"`
	Category string `json:"category"`
	Title    string `json:"title"`
	Content  string `json:"content"`
}

// ListNotesArgs holds arguments for list_notes tool.
type ListNotesArgs struct {
	IncludeInactive bool   `json:"include_inactive"`
	Type            string `json:"type"`
	Category        string `json:"category"`
}

// GetNoteArgs holds arguments for get_note tool.
type GetNoteArgs struct {
	ID string `json:"id"`
}

// UpdateNoteArgs holds arguments for update_note tool.
type UpdateNoteArgs struct {
	ID      string  `json:"id"`
	Title   *string `json:"title"`
	Content *string `json:"content"`
	Active  *bool   `json:"active"`
}

// DeleteNoteArgs holds arguments for delete_note tool.
type DeleteNoteArgs struct {
	ID string `json:"id"`
}

// =============================================================================
// User Tools
// =============================================================================

var (
	createUserTool = assistant.NewTool("create_user",
		"Create a new user account. Use this when the admin asks to add a user.").
		String("email", "User's email address (required, must be unique)", true).
		String("password", "User's password (required, min 8 characters)", true).
		String("name", "User's display name", false).
		Enum("role", "User's role", []string{"user", "admin"}, false).
		Build()

	updateUserTool = assistant.NewTool("update_user",
		"Update an existing user's details.").
		String("id", "User ID", true).
		String("name", "New display name", false).
		String("email", "New email address", false).
		Enum("role", "New role", []string{"user", "admin"}, false).
		Bool("verified", "Set verified status", false).
		Build()

	deleteUserTool = assistant.NewTool("delete_user",
		"Delete a user account. This cannot be undone.").
		String("id", "User ID to delete", true).
		Build()

	listUsersTool = assistant.NewTool("list_users",
		"List all users in the system.").
		Build()
)

// CreateUserArgs holds arguments for create_user tool.
type CreateUserArgs struct {
	Email    string `json:"email"`
	Password string `json:"password"`
	Name     string `json:"name"`
	Role     string `json:"role"`
}

// UpdateUserArgs holds arguments for update_user tool.
type UpdateUserArgs struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Email    string `json:"email"`
	Role     string `json:"role"`
	Verified *bool  `json:"verified"`
}

// DeleteUserArgs holds arguments for delete_user tool.
type DeleteUserArgs struct {
	ID string `json:"id"`
}

// =============================================================================
// Utility Tools
// =============================================================================

var (
	validateTemplateTool = assistant.NewTool("validate_template",
		"Validate HTML template syntax before saving. Checks for valid Go template syntax and common errors.").
		String("html", "HTML template content to validate", true).
		Build()

	navigateUserTool = assistant.NewTool("navigate_user",
		"Navigate the user's preview panel to a specific admin page. Use this after creating or updating content to show the user the result.").
		String("url", "The admin URL to navigate to (e.g. /admin/page/about, /admin/collection/blog-posts)", true).
		Build()
)

// ValidateTemplateArgs holds arguments for validate_template tool.
type ValidateTemplateArgs struct {
	HTML string `json:"html"`
}

// NavigateUserArgs holds arguments for the navigate_user tool.
type NavigateUserArgs struct {
	URL string `json:"url"`
}

// =============================================================================
// Search Tools
// =============================================================================

var searchTool = assistant.NewTool("search",
	"Search across all website content (pages, collections, documents, files, partials, notes, users). Returns matching results ranked by relevance.").
	String("query", "Search query text", true).
	String("type", "Filter by entity type: page, collection, document, file, partial, note, user", false).
	String("collection_id", "Filter documents by collection ID", false).
	Int("limit", "Max results to return (default 20, max 100)", false).
	Build()

// SearchArgs holds arguments for the search tool.
type SearchArgs struct {
	Query        string `json:"query"`
	Type         string `json:"type"`
	CollectionID string `json:"collection_id"`
	Limit        int    `json:"limit"`
}
← Back