diff --git a/ai/TOOLS.md b/ai/TOOLS.md index 06f9c42e3..0dd27cd7a 100644 --- a/ai/TOOLS.md +++ b/ai/TOOLS.md @@ -1,1977 +1,83 @@ # AlphaHuman Tools -This document lists all available tools that AlphaHuman can use to interact with external services and perform actions. Tools are organized by integration and automatically updated when the app loads. +This document lists all available tools that AlphaHuman can use to interact with external services and perform actions. Tools are organized by integration and automatically updated during build time. ## Overview -AlphaHuman has access to **104 tools** across **3 integrations**. +AlphaHuman has access to **4 tools** across **3 integrations** organized into **6 categories**. **Quick Statistics:** -- **Github**: 72 tools -- **Gmail**: 7 tools -- **Notion**: 25 tools +- **Telegram**: 2 tools +- **Notion**: 1 tools +- **Gmail**: 1 tools + +## Environment Configuration + +Tools are available in different environments with varying capabilities: + +### Development Environment + +Local development environment with full access + +- **Access Level**: Full access to all tools +- **Rate Limits**: Relaxed for testing +- **Authentication**: Development credentials +- **Logging**: Verbose logging enabled + +### Production Environment + +Production environment with security restrictions + +- **Access Level**: Production-safe tools only +- **Rate Limits**: Standard API limits enforced +- **Authentication**: Production credentials required +- **Logging**: Essential logs only + +### Testing Environment + +Testing environment for automated validation + +- **Access Level**: Safe tools for automated testing +- **Rate Limits**: Testing-specific limits +- **Authentication**: Test credentials +- **Logging**: Test execution logs + +## Tool Categories + +### Communication + +Tools for messaging, email, and social interaction + +- **Skills**: 2 +- **Tools**: 3 +- **Available Skills**: Telegram, Gmail + +### Productivity + +Tools for task management, note-taking, and organization + +- **Skills**: 1 +- **Tools**: 1 +- **Available Skills**: Notion ## Available Tools -### Github Tools - -This skill provides 72 tools for github integration. - -#### list-repos - -**Description**: List repositories for the authenticated user or a specific owner - -**Parameters**: - -- **limit** (number): Maximum number of repositories to return -- **owner** (string): Repository owner (user or org). Defaults to the authenticated user -- **sort** (string): Sort field -- **visibility** (string): Filter by visibility - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-repos", - "parameters": { - "limit": 10, - "owner": "example_owner", - "sort": "example_sort", - "visibility": "example_visibility" - } -} -``` - ---- - -#### get-repo - -**Description**: Get detailed information about a specific repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-repo", "parameters": { "owner": "example_owner", "repo": "example_repo" } } -``` - ---- - -#### create-repo - -**Description**: Create a new repository for the authenticated user - -**Parameters**: - -- **auto_init** (boolean): Initialize with a README -- **description** (string): Repository description -- **name** (string) **(required)**: Repository name -- **visibility** (string): Repository visibility - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-repo", - "parameters": { - "auto_init": true, - "description": "example_description", - "name": "example_name", - "visibility": "example_visibility" - } -} -``` - ---- - -#### fork-repo - -**Description**: Fork a repository to the authenticated user's account - -**Parameters**: - -- **fork_name** (string): Custom name for the forked repository -- **owner** (string) **(required)**: Owner of the repository to fork -- **repo** (string) **(required)**: Repository name to fork - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "fork-repo", - "parameters": { - "fork_name": "example_fork_name", - "owner": "example_owner", - "repo": "example_repo" - } -} -``` - ---- - -#### delete-repo - -**Description**: Permanently delete a repository. This action cannot be undone - -**Parameters**: - -- **confirm** (boolean) **(required)**: Must be true to confirm deletion -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "delete-repo", - "parameters": { "confirm": true, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### clone-repo - -**Description**: Get clone URLs for a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "clone-repo", "parameters": { "owner": "example_owner", "repo": "example_repo" } } -``` - ---- - -#### list-collaborators - -**Description**: List collaborators on a repository - -**Parameters**: - -- **limit** (number): Maximum number of collaborators to return -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-collaborators", - "parameters": { "limit": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### add-collaborator - -**Description**: Add a collaborator to a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **permission** (string): Permission level to grant -- **repo** (string) **(required)**: Repository name -- **username** (string) **(required)**: GitHub username of the collaborator to add - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "add-collaborator", - "parameters": { - "owner": "example_owner", - "permission": "example_permission", - "repo": "example_repo", - "username": "example_username" - } -} -``` - ---- - -#### remove-collaborator - -**Description**: Remove a collaborator from a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **username** (string) **(required)**: GitHub username of the collaborator to remove - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "remove-collaborator", - "parameters": { "owner": "example_owner", "repo": "example_repo", "username": "example_username" } -} -``` - ---- - -#### list-topics - -**Description**: List topics (tags) on a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "list-topics", "parameters": { "owner": "example_owner", "repo": "example_repo" } } -``` - ---- - -#### set-topics - -**Description**: Replace all topics on a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **topics** (array) **(required)**: List of topic names to set - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "set-topics", - "parameters": { "owner": "example_owner", "repo": "example_repo", "topics": [] } -} -``` - ---- - -#### list-languages - -**Description**: List programming languages detected in a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "list-languages", "parameters": { "owner": "example_owner", "repo": "example_repo" } } -``` - ---- - -#### list-issues - -**Description**: List issues in a repository with optional filters - -**Parameters**: - -- **assignee** (string): Filter by assignee username -- **label** (string): Filter by label name -- **limit** (number): Maximum number of issues to return -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **state** (string): Filter by state - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-issues", - "parameters": { - "assignee": "example_assignee", - "label": "example_label", - "limit": 10, - "owner": "example_owner", - "repo": "example_repo", - "state": "example_state" - } -} -``` - ---- - -#### get-issue - -**Description**: Get detailed information about a specific issue - -**Parameters**: - -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-issue", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### create-issue - -**Description**: Create a new issue in a repository - -**Parameters**: - -- **assignees** (array): Usernames to assign -- **body** (string): Issue body (Markdown supported) -- **labels** (array): Labels to apply -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **title** (string) **(required)**: Issue title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-issue", - "parameters": { - "assignees": [], - "body": "example_body", - "labels": [], - "owner": "example_owner", - "repo": "example_repo", - "title": "example_title" - } -} -``` - ---- - -#### close-issue - -**Description**: Close an issue - -**Parameters**: - -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **reason** (string): Reason for closing -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "close-issue", - "parameters": { - "number": 10, - "owner": "example_owner", - "reason": "example_reason", - "repo": "example_repo" - } -} -``` - ---- - -#### reopen-issue - -**Description**: Reopen a closed issue - -**Parameters**: - -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "reopen-issue", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### edit-issue - -**Description**: Edit an existing issue's title or body - -**Parameters**: - -- **body** (string): New issue body -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **title** (string): New issue title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "edit-issue", - "parameters": { - "body": "example_body", - "number": 10, - "owner": "example_owner", - "repo": "example_repo", - "title": "example_title" - } -} -``` - ---- - -#### comment-on-issue - -**Description**: Add a comment to an issue - -**Parameters**: - -- **body** (string) **(required)**: Comment body (Markdown supported) -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "comment-on-issue", - "parameters": { - "body": "example_body", - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } -} -``` - ---- - -#### list-issue-comments - -**Description**: List comments on an issue - -**Parameters**: - -- **limit** (number): Maximum number of comments to return -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-issue-comments", - "parameters": { "limit": 10, "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### add-issue-labels - -**Description**: Add labels to an issue - -**Parameters**: - -- **labels** (array) **(required)**: Labels to add -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "add-issue-labels", - "parameters": { "labels": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### remove-issue-labels - -**Description**: Remove labels from an issue - -**Parameters**: - -- **labels** (array) **(required)**: Labels to remove -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "remove-issue-labels", - "parameters": { "labels": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### add-issue-assignees - -**Description**: Add assignees to an issue - -**Parameters**: - -- **assignees** (array) **(required)**: Usernames to assign -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "add-issue-assignees", - "parameters": { "assignees": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### remove-issue-assignees - -**Description**: Remove assignees from an issue - -**Parameters**: - -- **assignees** (array) **(required)**: Usernames to remove -- **number** (number) **(required)**: Issue number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "remove-issue-assignees", - "parameters": { "assignees": [], "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### list-prs - -**Description**: List pull requests in a repository with optional filters - -**Parameters**: - -- **base** (string): Filter by base branch name -- **limit** (number): Maximum number of pull requests to return -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **state** (string): Filter by state - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-prs", - "parameters": { - "base": "example_base", - "limit": 10, - "owner": "example_owner", - "repo": "example_repo", - "state": "example_state" - } -} -``` - ---- - -#### get-pr - -**Description**: Get detailed information about a specific pull request - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-pr", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### create-pr - -**Description**: Create a new pull request - -**Parameters**: - -- **base** (string): The branch to merge into (defaults to repo default branch) -- **body** (string): Pull request body (Markdown supported) -- **draft** (boolean): Create as a draft pull request -- **head** (string) **(required)**: The branch containing the changes (e.g. 'feature-branch' or 'user:feature-branch') -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **title** (string) **(required)**: Pull request title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-pr", - "parameters": { - "base": "example_base", - "body": "example_body", - "draft": true, - "head": "example_head", - "owner": "example_owner", - "repo": "example_repo", - "title": "example_title" - } -} -``` - ---- - -#### close-pr - -**Description**: Close a pull request without merging - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "close-pr", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### reopen-pr - -**Description**: Reopen a closed pull request - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "reopen-pr", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### merge-pr - -**Description**: Merge a pull request - -**Parameters**: - -- **commit_message** (string): Custom merge commit message -- **delete_branch** (boolean): Delete the head branch after merging -- **method** (string): Merge method to use -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "merge-pr", - "parameters": { - "commit_message": "example_commit_message", - "delete_branch": true, - "method": "example_method", - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } -} -``` - ---- - -#### edit-pr - -**Description**: Edit a pull request's title, body, or base branch - -**Parameters**: - -- **base** (string): New base branch -- **body** (string): New pull request body -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **title** (string): New pull request title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "edit-pr", - "parameters": { - "base": "example_base", - "body": "example_body", - "number": 10, - "owner": "example_owner", - "repo": "example_repo", - "title": "example_title" - } -} -``` - ---- - -#### comment-on-pr - -**Description**: Add a comment to a pull request - -**Parameters**: - -- **body** (string) **(required)**: Comment body (Markdown supported) -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "comment-on-pr", - "parameters": { - "body": "example_body", - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } -} -``` - ---- - -#### list-pr-comments - -**Description**: List comments on a pull request - -**Parameters**: - -- **limit** (number): Maximum number of comments to return -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-pr-comments", - "parameters": { "limit": 10, "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### list-pr-reviews - -**Description**: List reviews on a pull request - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-pr-reviews", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### create-pr-review - -**Description**: Submit a review on a pull request - -**Parameters**: - -- **body** (string): Review comment body -- **event** (string) **(required)**: Review action to perform -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-pr-review", - "parameters": { - "body": "example_body", - "event": "example_event", - "number": 10, - "owner": "example_owner", - "repo": "example_repo" - } -} -``` - ---- - -#### list-pr-files - -**Description**: List files changed in a pull request - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-pr-files", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### get-pr-diff - -**Description**: Get the unified diff for a pull request - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-pr-diff", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### get-pr-checks - -**Description**: Get CI/CD check runs and status for a pull request - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-pr-checks", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### request-pr-reviewers - -**Description**: Request reviews from specific users on a pull request - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **reviewers** (array) **(required)**: Usernames to request review from - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "request-pr-reviewers", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo", "reviewers": [] } -} -``` - ---- - -#### mark-pr-ready - -**Description**: Mark a draft pull request as ready for review - -**Parameters**: - -- **number** (number) **(required)**: Pull request number -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "mark-pr-ready", - "parameters": { "number": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### search-repos - -**Description**: Search GitHub repositories by query - -**Parameters**: - -- **limit** (number): Maximum number of results to return -- **order** (string): Sort order -- **query** (string) **(required)**: Search query (supports GitHub search syntax) -- **sort** (string): Sort field - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search-repos", - "parameters": { - "limit": 10, - "order": "example_order", - "query": "example_query", - "sort": "example_sort" - } -} -``` - ---- - -#### search-issues - -**Description**: Search issues and pull requests across GitHub - -**Parameters**: - -- **limit** (number): Maximum number of results to return -- **query** (string) **(required)**: Search query (supports GitHub search syntax, e.g. 'is:issue is:open label:bug') -- **sort** (string): Sort field - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search-issues", - "parameters": { "limit": 10, "query": "example_query", "sort": "example_sort" } -} -``` - ---- - -#### search-code - -**Description**: Search code across GitHub repositories - -**Parameters**: - -- **language** (string): Filter by programming language -- **limit** (number): Maximum number of results to return -- **query** (string) **(required)**: Search query (supports GitHub code search syntax) -- **repo** (string): Restrict search to a specific repo (owner/name format) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search-code", - "parameters": { - "language": "example_language", - "limit": 10, - "query": "example_query", - "repo": "example_repo" - } -} -``` - ---- - -#### search-commits - -**Description**: Search commits across GitHub repositories - -**Parameters**: - -- **limit** (number): Maximum number of results to return -- **query** (string) **(required)**: Search query (supports GitHub commit search syntax) -- **repo** (string): Restrict search to a specific repo (owner/name format) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search-commits", - "parameters": { "limit": 10, "query": "example_query", "repo": "example_repo" } -} -``` - ---- - -#### view-file - -**Description**: View the contents of a file in a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **path** (string) **(required)**: File path within the repository -- **ref** (string): Git ref (branch, tag, or commit SHA). Defaults to the default branch -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "view-file", - "parameters": { - "owner": "example_owner", - "path": "example_path", - "ref": "example_ref", - "repo": "example_repo" - } -} -``` - ---- - -#### list-directory - -**Description**: List the contents of a directory in a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **path** (string): Directory path within the repository. Defaults to the root -- **ref** (string): Git ref (branch, tag, or commit SHA). Defaults to the default branch -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-directory", - "parameters": { - "owner": "example_owner", - "path": "example_path", - "ref": "example_ref", - "repo": "example_repo" - } -} -``` - ---- - -#### get-readme - -**Description**: Get the README file for a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-readme", "parameters": { "owner": "example_owner", "repo": "example_repo" } } -``` - ---- - -#### list-releases - -**Description**: List releases for a repository - -**Parameters**: - -- **limit** (number): Maximum number of releases to return -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-releases", - "parameters": { "limit": 10, "owner": "example_owner", "repo": "example_repo" } -} -``` - ---- - -#### get-release - -**Description**: Get a specific release by tag name - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **tag** (string) **(required)**: Release tag name (e.g. 'v1.0.0') - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-release", - "parameters": { "owner": "example_owner", "repo": "example_repo", "tag": "example_tag" } -} -``` - ---- - -#### create-release - -**Description**: Create a new release for a repository - -**Parameters**: - -- **draft** (boolean): Create as a draft release -- **generate_notes** (boolean): Auto-generate release notes from commits -- **notes** (string): Release notes body (Markdown supported) -- **owner** (string) **(required)**: Repository owner -- **prerelease** (boolean): Mark as a pre-release -- **repo** (string) **(required)**: Repository name -- **tag** (string) **(required)**: Tag name for the release (e.g. 'v1.0.0') -- **target** (string): Target commitish (branch or commit SHA) for the tag -- **title** (string): Release title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-release", - "parameters": { - "draft": true, - "generate_notes": true, - "notes": "example_notes", - "owner": "example_owner", - "prerelease": true, - "repo": "example_repo", - "tag": "example_tag", - "target": "example_target", - "title": "example_title" - } -} -``` - ---- - -#### delete-release - -**Description**: Delete a release by tag name - -**Parameters**: - -- **cleanup_tag** (boolean): Also delete the associated git tag -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **tag** (string) **(required)**: Release tag name to delete - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "delete-release", - "parameters": { - "cleanup_tag": true, - "owner": "example_owner", - "repo": "example_repo", - "tag": "example_tag" - } -} -``` - ---- - -#### list-release-assets - -**Description**: List assets (downloadable files) attached to a release - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **tag** (string) **(required)**: Release tag name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-release-assets", - "parameters": { "owner": "example_owner", "repo": "example_repo", "tag": "example_tag" } -} -``` - ---- - -#### get-latest-release - -**Description**: Get the latest published release for a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-latest-release", "parameters": { "owner": "example_owner", "repo": "example_repo" } } -``` - ---- - -#### list-gists - -**Description**: List gists for the authenticated user or a specific user - -**Parameters**: - -- **limit** (number): Maximum number of gists to return -- **username** (string): GitHub username. Defaults to the authenticated user - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "list-gists", "parameters": { "limit": 10, "username": "example_username" } } -``` - ---- - -#### get-gist - -**Description**: Get a specific gist by ID, including its files and content - -**Parameters**: - -- **gist_id** (string) **(required)**: The gist ID - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-gist", "parameters": { "gist_id": "example_gist_id" } } -``` - ---- - -#### create-gist - -**Description**: Create a new gist with one or more files - -**Parameters**: - -- **description** (string): Gist description -- **files** (object) **(required)**: Map of filename to file content, e.g. {"hello.py": {"content": "print('hello')"}} -- **public** (boolean): Whether the gist is public - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-gist", - "parameters": { "description": "example_description", "files": {}, "public": true } -} -``` - ---- - -#### edit-gist - -**Description**: Edit an existing gist's description or files - -**Parameters**: - -- **description** (string): New gist description -- **files** (object): Map of filename to new content. Set content to null to delete a file -- **gist_id** (string) **(required)**: The gist ID - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "edit-gist", - "parameters": { "description": "example_description", "files": {}, "gist_id": "example_gist_id" } -} -``` - ---- - -#### delete-gist - -**Description**: Permanently delete a gist - -**Parameters**: - -- **gist_id** (string) **(required)**: The gist ID to delete - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "delete-gist", "parameters": { "gist_id": "example_gist_id" } } -``` - ---- - -#### clone-gist - -**Description**: Get the clone URL for a gist - -**Parameters**: - -- **gist_id** (string) **(required)**: The gist ID to clone - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "clone-gist", "parameters": { "gist_id": "example_gist_id" } } -``` - ---- - -#### list-workflows - -**Description**: List GitHub Actions workflows defined in a repository - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "list-workflows", "parameters": { "owner": "example_owner", "repo": "example_repo" } } -``` - ---- - -#### list-workflow-runs - -**Description**: List recent workflow runs for a repository - -**Parameters**: - -- **branch** (string): Filter by branch name -- **limit** (number): Maximum number of runs to return -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **status** (string): Filter by status -- **workflow_id** (string): Filter by workflow ID or filename (e.g. 'ci.yml') - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-workflow-runs", - "parameters": { - "branch": "example_branch", - "limit": 10, - "owner": "example_owner", - "repo": "example_repo", - "status": "example_status", - "workflow_id": "example_workflow_id" - } -} -``` - ---- - -#### get-workflow-run - -**Description**: Get detailed information about a specific workflow run - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **run_id** (number) **(required)**: Workflow run ID - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-workflow-run", - "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } -} -``` - ---- - -#### list-run-jobs - -**Description**: List jobs for a specific workflow run - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **run_id** (number) **(required)**: Workflow run ID - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "list-run-jobs", - "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } -} -``` - ---- - -#### get-run-logs - -**Description**: Get the logs URL for a specific workflow run - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **run_id** (number) **(required)**: Workflow run ID - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-run-logs", - "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } -} -``` - ---- - -#### rerun-workflow - -**Description**: Re-run an entire workflow run - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **run_id** (number) **(required)**: Workflow run ID to re-run - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "rerun-workflow", - "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } -} -``` - ---- - -#### cancel-workflow-run - -**Description**: Cancel a workflow run that is in progress - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **run_id** (number) **(required)**: Workflow run ID to cancel - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "cancel-workflow-run", - "parameters": { "owner": "example_owner", "repo": "example_repo", "run_id": 10 } -} -``` - ---- - -#### trigger-workflow - -**Description**: Manually trigger a workflow dispatch event - -**Parameters**: - -- **inputs** (object): Input key-value pairs for the workflow_dispatch event -- **owner** (string) **(required)**: Repository owner -- **ref** (string): Git ref (branch or tag) to run the workflow on -- **repo** (string) **(required)**: Repository name -- **workflow_id** (string) **(required)**: Workflow ID or filename (e.g. 'deploy.yml') - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "trigger-workflow", - "parameters": { - "inputs": {}, - "owner": "example_owner", - "ref": "example_ref", - "repo": "example_repo", - "workflow_id": "example_workflow_id" - } -} -``` - ---- - -#### view-workflow-yaml - -**Description**: View the YAML source of a workflow definition - -**Parameters**: - -- **owner** (string) **(required)**: Repository owner -- **repo** (string) **(required)**: Repository name -- **workflow_id** (string) **(required)**: Workflow ID or filename (e.g. 'ci.yml') - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "view-workflow-yaml", - "parameters": { - "owner": "example_owner", - "repo": "example_repo", - "workflow_id": "example_workflow_id" - } -} -``` - ---- - -#### list-notifications - -**Description**: List GitHub notifications for the authenticated user - -**Parameters**: - -- **all** (boolean): Include read notifications -- **limit** (number): Maximum number of notifications to return - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "list-notifications", "parameters": { "all": true, "limit": 10 } } -``` - ---- - -#### mark-notification-read - -**Description**: Mark a specific notification thread as read - -**Parameters**: - -- **thread_id** (string) **(required)**: Notification thread ID - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "mark-notification-read", "parameters": { "thread_id": "example_thread_id" } } -``` - ---- - -#### mark-all-notifications-read - -**Description**: Mark all notifications as read - -**Parameters**: _None_ - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "mark-all-notifications-read", "parameters": {} } -``` - ---- - -#### gh-api - -**Description**: Make a raw GitHub REST API request. Use this for any endpoint not covered by the other tools - -**Parameters**: - -- **body** (object): Request body (for POST/PUT/PATCH) -- **endpoint** (string) **(required)**: API endpoint path (e.g. '/repos/owner/repo/branches') -- **method** (string): HTTP method - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "gh-api", - "parameters": { "body": {}, "endpoint": "example_endpoint", "method": "example_method" } -} -``` - ---- - ### Gmail Tools -This skill provides 7 tools for gmail integration. +**Category**: Communication -#### get-email +This skill provides 1 tool for gmail integration. -**Description**: Get full details of a specific email by its ID, including headers, body content, and attachments. +#### send_email + +**Description**: Send an email via Gmail **Parameters**: -- **format** (string): Message format level (default: full) -- **include_body** (boolean): Include email body content (default: true) -- **message_id** (string) **(required)**: The Gmail message ID to retrieve - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-email", - "parameters": { - "format": "example_format", - "include_body": true, - "message_id": "example_message_id" - } -} -``` - ---- - -#### get-emails - -**Description**: Get emails from Gmail with optional filtering by query, labels, and pagination. Supports Gmail search syntax. When accessToken is provided (e.g. from frontend after OAuth), uses it directly; otherwise uses the skill OAuth credential. - -**Parameters**: - -- **accessToken** (string): Optional OAuth access token. When provided (e.g. by frontend after OAuth), Gmail API is called directly with this token instead of the skill credential. -- **include_spam_trash** (boolean): Include emails from spam and trash (default: false) -- **label_ids** (array): Filter by specific label IDs (e.g., ["INBOX", "IMPORTANT"]) -- **maxResults** (number): Alias for max_results (e.g. for frontend calls) -- **max_results** (number): Maximum number of emails to return (default: 20, max: 100) -- **page_token** (string): Token for pagination (returned from previous request) -- **query** (string): Search query using Gmail search syntax (e.g., "from:example@gmail.com", "subject:meeting", "is:unread") - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-emails", - "parameters": { - "accessToken": "example_accessToken", - "include_spam_trash": true, - "label_ids": [], - "maxResults": 10, - "max_results": 10, - "page_token": "example_page_token", - "query": "example_query" - } -} -``` - ---- - -#### get-labels - -**Description**: Get all Gmail labels including system and user-created labels with message counts and details. - -**Parameters**: - -- **include_hidden** (boolean): Include hidden labels (default: false) -- **type** (string): Filter labels by type (default: all) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-labels", "parameters": { "include_hidden": true, "type": "example_type" } } -``` - ---- - -#### get-profile - -**Description**: Get Gmail user profile information including email address, total message counts, and account details. Optional accessToken for frontend calls. - -**Parameters**: - -- **accessToken** (string): Optional OAuth access token (e.g. from frontend). - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-profile", "parameters": { "accessToken": "example_accessToken" } } -``` - ---- - -#### mark-email - -**Description**: Mark emails with specific status (read/unread, important, starred) or add/remove labels. - -**Parameters**: - -- **action** (string) **(required)**: Action to perform on the messages -- **label_ids** (array): Label IDs to add or remove (required for add_labels/remove_labels actions) -- **message_ids** (array) **(required)**: Array of message IDs to modify - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "mark-email", - "parameters": { "action": "example_action", "label_ids": [], "message_ids": [] } -} -``` - ---- - -#### search-emails - -**Description**: Search emails using advanced Gmail query syntax. Supports complex queries with operators like from:, to:, subject:, has:attachment, is:unread, etc. - -**Parameters**: - -- **include_spam_trash** (boolean): Include results from spam and trash folders (default: false) -- **max_results** (number): Maximum number of results to return (default: 20, max: 100) -- **page_token** (string): Token for pagination (from previous search) -- **query** (string) **(required)**: Gmail search query (e.g., "from:john@example.com subject:meeting is:unread", "has:attachment after:2023/01/01") - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search-emails", - "parameters": { - "include_spam_trash": true, - "max_results": 10, - "page_token": "example_page_token", - "query": "example_query" - } -} -``` - ---- - -#### send-email - -**Description**: Send an email through Gmail with support for HTML/text content, attachments, CC/BCC recipients, and reply threading. - -**Parameters**: - -- **attachments** (array): File attachments (optional) -- **bcc** (array): BCC recipients (optional) -- **body_html** (string): HTML email body (optional if body_text provided) -- **body_text** (string): Plain text email body (optional if body_html provided) -- **cc** (array): CC recipients (optional) -- **reply_to_message_id** (string): Message ID being replied to (optional) +- **body** (string) **(required)**: Email body content - **subject** (string) **(required)**: Email subject line -- **thread_id** (string): Thread ID for replies (optional) -- **to** (array) **(required)**: Primary recipients +- **to** (string) **(required)**: Recipient email address **Usage Context**: Available in all environments @@ -1979,18 +85,8 @@ This skill provides 7 tools for gmail integration. ```json { - "tool": "send-email", - "parameters": { - "attachments": [], - "bcc": [], - "body_html": "example_body_html", - "body_text": "example_body_text", - "cc": [], - "reply_to_message_id": "example_reply_to_message_id", - "subject": "example_subject", - "thread_id": "example_thread_id", - "to": [] - } + "tool": "send_email", + "parameters": { "body": "example_body", "subject": "example_subject", "to": "example_to" } } ``` @@ -1998,113 +94,18 @@ This skill provides 7 tools for gmail integration. ### Notion Tools -This skill provides 25 tools for notion integration. +**Category**: Productivity -#### append-blocks +This skill provides 1 tool for notion integration. -**Description**: Append child blocks to a page or block. Supports various block types. +#### create_page + +**Description**: Create a new page in Notion workspace **Parameters**: -- **block_id** (string) **(required)**: The parent page or block ID -- **blocks** (string) **(required)**: JSON string of blocks array. Example: [{"type":"paragraph","paragraph":{"rich_text":[{"text":{"content":"Hello"}}]}}] - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "append-blocks", - "parameters": { "block_id": "example_block_id", "blocks": "example_blocks" } -} -``` - ---- - -#### append-text - -**Description**: Append text content to a page or block. Creates paragraph blocks with the given text. - -**Parameters**: - -- **block_id** (string) **(required)**: The page or block ID to append to -- **text** (string) **(required)**: Text content to append - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "append-text", "parameters": { "block_id": "example_block_id", "text": "example_text" } } -``` - ---- - -#### create-comment - -**Description**: Create a comment on a page or in a discussion thread. Must specify either page_id (for new discussion) or discussion_id (to reply). - -**Parameters**: - -- **discussion_id** (string): Discussion ID to reply to an existing thread -- **page_id** (string): Page ID to start a new discussion on -- **text** (string) **(required)**: Comment text content - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-comment", - "parameters": { - "discussion_id": "example_discussion_id", - "page_id": "example_page_id", - "text": "example_text" - } -} -``` - ---- - -#### create-database - -**Description**: Create a new database in Notion. Must specify parent page and property schema. - -**Parameters**: - -- **parent_page_id** (string) **(required)**: Parent page ID where the database will be created -- **properties** (string): JSON string of properties schema. Example: {"Name":{"title":{}},"Status":{"select":{"options":[{"name":"Todo"},{"name":"Done"}]}}} -- **title** (string) **(required)**: Database title - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "create-database", - "parameters": { - "parent_page_id": "example_parent_page_id", - "properties": "example_properties", - "title": "example_title" - } -} -``` - ---- - -#### create-page - -**Description**: Create a new page in Notion. Parent can be another page or a database. For database parents, properties must match the database schema. - -**Parameters**: - -- **content** (string): Initial text content (creates a paragraph block) -- **parent_id** (string) **(required)**: Parent page ID or database ID -- **parent_type** (string): Type of parent (default: page_id) -- **properties** (string): JSON string of additional properties (for database pages) +- **content** (array): Page content blocks +- **parent_id** (string) **(required)**: Parent database or page ID - **title** (string) **(required)**: Page title **Usage Context**: Available in all environments @@ -2113,421 +114,61 @@ This skill provides 25 tools for notion integration. ```json { - "tool": "create-page", + "tool": "create_page", + "parameters": { "content": [], "parent_id": "example_parent_id", "title": "example_title" } +} +``` + +--- + +### Telegram Tools + +**Category**: Communication + +This skill provides 2 tools for telegram integration. + +#### send_message + +**Description**: Send a message to a Telegram chat or user + +**Parameters**: + +- **chat_id** (string) **(required)**: Telegram chat ID or username +- **message** (string) **(required)**: Message text to send +- **parse_mode** (string): Message formatting mode + +**Usage Context**: Available in all environments + +**Example**: + +```json +{ + "tool": "send_message", "parameters": { - "content": "example_content", - "parent_id": "example_parent_id", - "parent_type": "example_parent_type", - "properties": "example_properties", - "title": "example_title" + "chat_id": "example_chat_id", + "message": "example_message", + "parse_mode": "example_parse_mode" } } ``` --- -#### delete-block +#### get_chat_history -**Description**: Delete a block. This permanently removes the block from Notion. +**Description**: Retrieve message history from a Telegram chat **Parameters**: -- **block_id** (string) **(required)**: The block ID to delete +- **chat_id** (string) **(required)**: Telegram chat ID or username +- **limit** (number): Number of messages to retrieve **Usage Context**: Available in all environments **Example**: ```json -{ "tool": "delete-block", "parameters": { "block_id": "example_block_id" } } -``` - ---- - -#### delete-page - -**Description**: Delete (archive) a page. Archived pages can be restored from Notion's trash. - -**Parameters**: - -- **page_id** (string) **(required)**: The page ID to delete/archive - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "delete-page", "parameters": { "page_id": "example_page_id" } } -``` - ---- - -#### get-block - -**Description**: Get a block by its ID. Returns the block's type and content. - -**Parameters**: - -- **block_id** (string) **(required)**: The block ID - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-block", "parameters": { "block_id": "example_block_id" } } -``` - ---- - -#### get-block-children - -**Description**: Get the children blocks of a block or page. - -**Parameters**: - -- **block_id** (string) **(required)**: The parent block or page ID -- **page_size** (number): Number of blocks (default 50, max 100) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-block-children", "parameters": { "block_id": "example_block_id", "page_size": 10 } } -``` - ---- - -#### get-database - -**Description**: Get a database's schema and metadata. Shows all properties and their types. - -**Parameters**: - -- **database_id** (string) **(required)**: The database ID - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-database", "parameters": { "database_id": "example_database_id" } } -``` - ---- - -#### get-page - -**Description**: Get a page's metadata and properties by its ID. Use notion-get-page-content to get the actual content/blocks. - -**Parameters**: - -- **page_id** (string) **(required)**: The page ID (UUID format, with or without dashes) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-page", "parameters": { "page_id": "example_page_id" } } -``` - ---- - -#### get-page-content - -**Description**: Get the content blocks of a page. Returns the text and structure of the page. Use recursive=true to also get nested blocks. - -**Parameters**: - -- **page_id** (string) **(required)**: The page ID to get content from -- **page_size** (number): Number of blocks to return (default 50, max 100) -- **recursive** (string): Whether to fetch nested blocks (default: false) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "get-page-content", - "parameters": { "page_id": "example_page_id", "page_size": 10, "recursive": "example_recursive" } -} -``` - ---- - -#### get-user - -**Description**: Get a user by their ID. - -**Parameters**: - -- **user_id** (string) **(required)**: The user ID - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "get-user", "parameters": { "user_id": "example_user_id" } } -``` - ---- - -#### list-all-databases - -**Description**: List all databases in the workspace that the integration has access to. - -**Parameters**: - -- **page_size** (number): Number of results (default 20, max 100) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "list-all-databases", "parameters": { "page_size": 10 } } -``` - ---- - -#### list-all-pages - -**Description**: List all pages in the workspace that the integration has access to. - -**Parameters**: - -- **page_size** (number): Number of results to return (default 20, max 100) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "list-all-pages", "parameters": { "page_size": 10 } } -``` - ---- - -#### list-comments - -**Description**: List comments on a block or page. - -**Parameters**: - -- **block_id** (string) **(required)**: Block or page ID to get comments for -- **page_size** (number): Number of results (default 20, max 100) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "list-comments", "parameters": { "block_id": "example_block_id", "page_size": 10 } } -``` - ---- - -#### list-users - -**Description**: List all users in the workspace that the integration can see. - -**Parameters**: - -- **page_size** (number): Number of results (default 20, max 100) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "list-users", "parameters": { "page_size": 10 } } -``` - ---- - -#### query-database - -**Description**: Query a database with optional filters and sorts. Returns database rows/pages. - -**Parameters**: - -- **database_id** (string) **(required)**: The database ID to query -- **filter** (string): JSON string of filter object (Notion filter syntax) -- **page_size** (number): Number of results (default 20, max 100) -- **sorts** (string): JSON string of sorts array (Notion sort syntax) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "query-database", - "parameters": { - "database_id": "example_database_id", - "filter": "example_filter", - "page_size": 10, - "sorts": "example_sorts" - } -} -``` - ---- - -#### search - -**Description**: Search for pages and databases in your Notion workspace. Can filter by type (page or database) and returns matching results. - -**Parameters**: - -- **filter** (string): Filter results by type -- **page_size** (number): Number of results to return (default 20, max 100) -- **query** (string): Search query (optional, returns recent if empty) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "search", - "parameters": { "filter": "example_filter", "page_size": 10, "query": "example_query" } -} -``` - ---- - -#### summarize-pages - -**Description**: AI summarization of Notion pages is now handled by the backend server. Synced page content is submitted to the server which runs summarization. - -**Parameters**: _None_ - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "summarize-pages", "parameters": {} } -``` - ---- - -#### sync-now - -**Description**: Trigger an immediate Notion sync to refresh local data. Returns sync results including counts of synced pages and databases. - -**Parameters**: _None_ - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "sync-now", "parameters": {} } -``` - ---- - -#### sync-status - -**Description**: Get the current Notion sync status including last sync time, total synced pages/databases, sync progress, and any errors. - -**Parameters**: _None_ - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ "tool": "sync-status", "parameters": {} } -``` - ---- - -#### update-block - -**Description**: Update a block's content. The structure depends on the block type. - -**Parameters**: - -- **archived** (string): Set to true to archive the block -- **block_id** (string) **(required)**: The block ID to update -- **content** (string): JSON string of the block type content. Example for paragraph: {"paragraph":{"rich_text":[{"text":{"content":"Updated text"}}]}} - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "update-block", - "parameters": { - "archived": "example_archived", - "block_id": "example_block_id", - "content": "example_content" - } -} -``` - ---- - -#### update-database - -**Description**: Update a database's title or properties schema. - -**Parameters**: - -- **database_id** (string) **(required)**: The database ID to update -- **properties** (string): JSON string of properties to add or update -- **title** (string): New title (optional) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "update-database", - "parameters": { - "database_id": "example_database_id", - "properties": "example_properties", - "title": "example_title" - } -} -``` - ---- - -#### update-page - -**Description**: Update a page's properties. Can update title and other properties. Use notion-append-text to add content blocks. - -**Parameters**: - -- **archived** (string): Set to true to archive the page -- **page_id** (string) **(required)**: The page ID to update -- **properties** (string): JSON string of properties to update -- **title** (string): New title (optional) - -**Usage Context**: Available in all environments - -**Example**: - -```json -{ - "tool": "update-page", - "parameters": { - "archived": "example_archived", - "page_id": "example_page_id", - "properties": "example_properties", - "title": "example_title" - } -} +{ "tool": "get_chat_history", "parameters": { "chat_id": "example_chat_id", "limit": 10 } } ``` --- @@ -2539,24 +180,108 @@ This skill provides 25 tools for notion integration. - All tools require proper authentication setup through the Skills system - OAuth credentials are managed securely and refreshed automatically - API keys are stored encrypted in the application keychain +- Test credentials are available for development and testing environments ### Rate Limiting - Tools automatically respect API rate limits of external services - Intelligent retry logic handles temporary failures with exponential backoff +- Bulk operations are automatically chunked to avoid hitting limits +- Rate limit status is monitored and reported in real-time ### Error Handling - All tools return structured error responses with detailed information - Network failures trigger automatic retry with configurable attempts +- Invalid parameters return clear validation messages with examples +- Tool execution timeouts are handled gracefully with partial results + +### Security & Privacy + +- Input validation is performed on all parameters using JSON Schema +- Output sanitization prevents injection attacks and data leakage +- Sensitive data is never logged or exposed in error messages +- All API communications use secure protocols (HTTPS/TLS) + +### Performance Optimization + +- Tool results are cached when appropriate to reduce API calls +- Parallel execution is used for independent operations +- Connection pooling minimizes overhead for repeated API calls +- Background sync keeps data fresh without blocking operations + +### Monitoring & Observability + +- Tool execution metrics are collected for performance analysis +- Error rates and response times are monitored continuously +- Debug logging is available in development environments +- Tool usage analytics help optimize integration performance + +## Skill Management + +Tools are provided by Skills, which are JavaScript modules running in a secure V8 runtime: + +- **Discovery**: Tools are automatically discovered at build time from running skills +- **Lifecycle**: Skills can be enabled/disabled independently without affecting others +- **Configuration**: Each skill has its own configuration panel with setup wizards +- **Updates**: Skills are updated through Git submodules and the Skills management system +- **Security**: Skills run in sandboxed environments with limited system access + +## Integration Architecture + +### V8 Runtime + +- Skills execute in isolated V8 JavaScript contexts on desktop platforms +- Mobile platforms use lightweight alternatives with server-side execution +- Memory limits and execution timeouts prevent resource exhaustion +- Inter-skill communication is managed through secure message passing + +### API Bridge + +- Tools communicate with external services through standardized API bridges +- Rate limiting, retry logic, and error handling are implemented at the bridge level +- Authentication tokens are managed centrally and shared across tools +- Response caching and optimization are handled transparently + +### Data Flow + +1. Tool request received from AI agent +2. Input validation and parameter processing +3. Skill execution in secure V8 context +4. API calls through standardized bridges +5. Response processing and formatting +6. Result delivery to AI agent + +## Support & Troubleshooting + +### Common Issues + +1. **Tool Not Available**: Check if the associated skill is enabled in Settings → Skills +2. **Authentication Errors**: Verify credentials in the skill's configuration panel +3. **Rate Limit Exceeded**: Wait for the limit to reset or upgrade your API plan +4. **Invalid Parameters**: Review the parameter documentation and examples above + +### Getting Help + +- **Skill Documentation**: Each skill has detailed setup and usage instructions +- **Debug Logs**: Enable verbose logging in development mode for detailed error information +- **Community Support**: Join our Discord community for help from other users +- **Technical Support**: Contact our support team for critical issues + +### Contributing + +- **New Tools**: Submit tool requests through our GitHub repository +- **Bug Reports**: Report issues with specific tools and include error logs +- **Improvements**: Suggest enhancements to existing tools and their documentation --- **Tool Statistics** -- Total Tools: 104 +- Total Tools: 4 - Active Skills: 3 -- Last Updated: 2026-03-06T10:39:36.767Z +- Categories: 6 +- Last Updated: 2026-03-11T23:23:47.633Z -_This file was automatically generated when the app loaded._ -_Tools are discovered from the running V8 skills runtime._ +_This file was automatically generated at build time from the V8 skills runtime._ +_For the most up-to-date information, regenerate this file by running `yarn tools:generate`._ diff --git a/package.json b/package.json index 4a6c16f2f..bf986b8f7 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "compile": "tsc --noEmit", "preview": "vite preview", "tauri": "tauri", - "tauri:dev": "source scripts/load-dotenv.sh 2>/dev/null; (cd src-tauri && bash scripts/download-tdlib.sh) && LOCAL_TDLIB_PATH=$(pwd)/src-tauri/tdlib-cache/macos-$(uname -m | sed 's/arm64/aarch64/; s/x86_64/x86_64/') tauri dev", + "tauri:dev": "RUST_LOG=debug source scripts/load-dotenv.sh 2>/dev/null; (cd src-tauri && bash scripts/download-tdlib.sh) && LOCAL_TDLIB_PATH=$(pwd)/src-tauri/tdlib-cache/macos-$(uname -m | sed 's/arm64/aarch64/; s/x86_64/x86_64/') tauri dev", "macos:build:debug": "yarn macos:build:release --debug", "macos:build:release": "source scripts/load-dotenv.sh 2>/dev/null; (cd src-tauri && bash scripts/download-tdlib.sh) && LOCAL_TDLIB_PATH=$(pwd)/src-tauri/tdlib-cache/macos-$(uname -m | sed 's/arm64/aarch64/; s/x86_64/x86_64/') tauri build --bundles app dmg", "macos:build:sign:release": "source scripts/load-env.sh && tauri build --bundles app dmg", @@ -22,7 +22,7 @@ "android:build": "tauri android build", "test": "vitest run", "test:unit": "vitest run", - "test:unit:watch": "vitest", + "test:unit:watch": "vi13:17:20.609 ERROR [skill:gmail] start() failed: start() timed out after 30s\ntest", "test:watch": "vitest", "test:coverage": "vitest run --coverage", "test:rust": "source $HOME/.cargo/env 2>/dev/null; cargo test --manifest-path src-tauri/Cargo.toml", diff --git a/skills b/skills index 88c9c8c8d..8168045b0 160000 --- a/skills +++ b/skills @@ -1 +1 @@ -Subproject commit 88c9c8c8d1b567661e1172b0af8c2134608d2ec7 +Subproject commit 8168045b0bc2ac16b24160f920d19c13d25e8e99 diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore index ef21e7a7a..b871c6237 100644 --- a/src-tauri/.gitignore +++ b/src-tauri/.gitignore @@ -14,3 +14,4 @@ /tdlib-build/ # TDLib downloaded by scripts/download-tdlib.sh (avoids build timeout) /tdlib-cache/ +/tdlib-prebuilt diff --git a/src-tauri/src/alphahuman/daemon/mod.rs b/src-tauri/src/alphahuman/daemon/mod.rs index 47e12daf7..687bc9dc5 100644 --- a/src-tauri/src/alphahuman/daemon/mod.rs +++ b/src-tauri/src/alphahuman/daemon/mod.rs @@ -338,7 +338,7 @@ async fn spawn_state_writer( _ = interval.tick() => { event_count += 1; if event_count % 12 == 1 { // Log every minute (12 * 5s = 60s) - log::info!("[alphahuman] Health monitoring active (event #{})", event_count); +// log::info!("[alphahuman] Health monitoring active (event #{})", event_count); } }, _ = cancel.cancelled() => { diff --git a/src-tauri/src/services/quickjs-libs/qjs_ops/ops_net.rs b/src-tauri/src/services/quickjs-libs/qjs_ops/ops_net.rs index 80e451092..a8f038210 100644 --- a/src-tauri/src/services/quickjs-libs/qjs_ops/ops_net.rs +++ b/src-tauri/src/services/quickjs-libs/qjs_ops/ops_net.rs @@ -3,84 +3,123 @@ use parking_lot::RwLock; use rquickjs::{function::Async, Ctx, Function, Object}; use std::collections::HashMap; -use std::sync::Arc; +use std::sync::{Arc, OnceLock}; use super::types::{js_err, WebSocketConnection, WebSocketState}; -pub fn register<'js>(ctx: &Ctx<'js>, ops: &Object<'js>, ws_state: Arc>) -> rquickjs::Result<()> { +/// Shared HTTP client — built once, reused across all fetch calls. +/// Using a shared client enables connection pooling, persistent TLS sessions, +/// and prevents per-request TLS handshake overhead that can cause hangs. +static HTTP_CLIENT: OnceLock = OnceLock::new(); + +fn get_http_client() -> &'static reqwest::Client { + HTTP_CLIENT.get_or_init(|| { + reqwest::Client::builder() + .use_rustls_tls() + .connect_timeout(std::time::Duration::from_secs(10)) + .pool_idle_timeout(std::time::Duration::from_secs(90)) + .pool_max_idle_per_host(10) + .build() + .expect("failed to build shared HTTP client") + }) +} + +pub fn register<'js>( + ctx: &Ctx<'js>, + ops: &Object<'js>, + ws_state: Arc>, +) -> rquickjs::Result<()> { // ======================================================================== // Fetch (1) - ASYNC // ======================================================================== - ops.set("fetch", Function::new(ctx.clone(), - Async(move |url: String, options: String| async move { - let opts: serde_json::Value = - serde_json::from_str(&options).map_err(|e| js_err(e.to_string()))?; + ops.set( + "fetch", + Function::new( + ctx.clone(), + Async(move |url: String, options: String| async move { + let opts: serde_json::Value = + serde_json::from_str(&options).map_err(|e| js_err(e.to_string()))?; - let method = opts["method"].as_str().unwrap_or("GET"); - let headers_obj = opts["headers"].as_object(); - let body = opts["body"].as_str(); - let timeout_secs = opts["timeout"] - .as_u64() - .or_else(|| opts["timeout"].as_f64().map(|f| f as u64)) - .unwrap_or(30); + let method = opts["method"].as_str().unwrap_or("GET"); + let headers_obj = opts["headers"].as_object(); + let body = opts["body"].as_str(); + let timeout_secs = opts["timeout"] + .as_u64() + .or_else(|| opts["timeout"].as_f64().map(|f| f as u64)) + .unwrap_or(30); - let client = reqwest::Client::builder() - .use_rustls_tls() - .build() - .map_err(|e| js_err(e.to_string()))?; - let mut req = match method { - "GET" => client.get(&url), - "POST" => client.post(&url), - "PUT" => client.put(&url), - "PATCH" => client.patch(&url), - "DELETE" => client.delete(&url), - _ => client.get(&url), - }; + let client = get_http_client(); + let mut req = match method { + "GET" => client.get(&url), + "POST" => client.post(&url), + "PUT" => client.put(&url), + "PATCH" => client.patch(&url), + "DELETE" => client.delete(&url), + _ => client.get(&url), + }; - req = req.timeout(std::time::Duration::from_secs(timeout_secs)); + req = req.timeout(std::time::Duration::from_secs(timeout_secs)); - if let Some(h) = headers_obj { - for (k, v) in h { - if let Some(val_str) = v.as_str() { - req = req.header(k, val_str); + if let Some(h) = headers_obj { + for (k, v) in h { + if let Some(val_str) = v.as_str() { + req = req.header(k, val_str); + } } } - } - if let Some(b) = body { - req = req.body(b.to_string()); - } - - let response = req.send().await.map_err(|e| { - let mut msg = e.to_string(); - let mut source = std::error::Error::source(&e); - while let Some(cause) = source { - msg.push_str(&format!(" | caused by: {cause}")); - source = std::error::Error::source(cause); + if let Some(b) = body { + req = req.body(b.to_string()); } - js_err(msg) - })?; - let status = response.status().as_u16(); - let status_text = response.status().canonical_reason().unwrap_or("").to_string(); - let headers: HashMap = response - .headers() - .iter() - .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) - .collect(); - let body_text = response.text().await.map_err(|e| js_err(e.to_string()))?; + // Hard safety net: tokio timeout wraps send+body read so a stalled + // connection cannot block the QuickJS event loop indefinitely even + // if the per-request timeout on the reqwest builder fails to fire. + let total_deadline = std::time::Duration::from_secs(timeout_secs + 5); + let response = tokio::time::timeout(total_deadline, req.send()) + .await + .map_err(|_| js_err(format!("request timed out after {}s", timeout_secs + 5)))? + .map_err(|e| { + let mut msg = e.to_string(); + let mut source = std::error::Error::source(&e); + while let Some(cause) = source { + msg.push_str(&format!(" | caused by: {cause}")); + source = std::error::Error::source(cause); + } + js_err(msg) + })?; - let result = serde_json::json!({ - "status": status, - "statusText": status_text, - "headers": headers, - "body": body_text, - }); + let status = response.status().as_u16(); + let status_text = response + .status() + .canonical_reason() + .unwrap_or("") + .to_string(); + let headers: HashMap = response + .headers() + .iter() + .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) + .collect(); + let body_text = tokio::time::timeout( + std::time::Duration::from_secs(timeout_secs + 5), + response.text(), + ) + .await + .map_err(|_| js_err(format!("body read timed out after {}s", timeout_secs + 5)))? + .map_err(|e| js_err(e.to_string()))?; - Ok::(result.to_string()) - }), - ))?; + let result = serde_json::json!({ + "status": status, + "statusText": status_text, + "headers": headers, + "body": body_text, + }); + + Ok::(result.to_string()) + }), + ), + )?; // ======================================================================== // WebSocket (4) - placeholders @@ -88,43 +127,57 @@ pub fn register<'js>(ctx: &Ctx<'js>, ops: &Object<'js>, ws_state: Arc(id) - } - }), - ))?; + ops.set( + "ws_connect", + Function::new( + ctx.clone(), + Async(move |url: String| { + let ws = ws.clone(); + async move { + let mut state = ws.write(); + let id = state.next_id; + state.next_id += 1; + state.connections.insert(id, WebSocketConnection { url }); + Ok::(id) + } + }), + ), + )?; } { let ws = ws_state.clone(); - ops.set("ws_send", Function::new(ctx.clone(), move |_id: u32, _data: String| { - let _state = ws.read(); - }))?; + ops.set( + "ws_send", + Function::new(ctx.clone(), move |_id: u32, _data: String| { + let _state = ws.read(); + }), + )?; } { let ws = ws_state.clone(); - ops.set("ws_recv", Function::new(ctx.clone(), - Async(move |_id: u32| { - let _ws = ws.clone(); - async move { Ok::, rquickjs::Error>(None) } - }), - ))?; + ops.set( + "ws_recv", + Function::new( + ctx.clone(), + Async(move |_id: u32| { + let _ws = ws.clone(); + async move { Ok::, rquickjs::Error>(None) } + }), + ), + )?; } { let ws = ws_state; - ops.set("ws_close", Function::new(ctx.clone(), move |id: u32, _code: u16, _reason: String| { - let mut state = ws.write(); - state.connections.remove(&id); - }))?; + ops.set( + "ws_close", + Function::new(ctx.clone(), move |id: u32, _code: u16, _reason: String| { + let mut state = ws.write(); + state.connections.remove(&id); + }), + )?; } Ok(()) } diff --git a/src/components/oauth/providerConfigs.tsx b/src/components/oauth/providerConfigs.tsx index 34227e34f..601395fbd 100644 --- a/src/components/oauth/providerConfigs.tsx +++ b/src/components/oauth/providerConfigs.tsx @@ -52,7 +52,7 @@ export const oauthProviderConfigs: OAuthProviderConfig[] = [ color: 'bg-white border border-gray-200', hoverColor: 'hover:bg-gray-50 hover:border-gray-300', textColor: 'text-gray-900', - loginUrl: `${BACKEND_URL}/auth/google/login${IS_DEV ? '?debug=true' : ''}`, + loginUrl: `${BACKEND_URL}/auth/google/login?${IS_DEV ? 'responseType=json' : ''}`, }, { id: 'github', @@ -61,7 +61,7 @@ export const oauthProviderConfigs: OAuthProviderConfig[] = [ color: 'bg-gray-900 border border-gray-800', hoverColor: 'hover:bg-gray-800 hover:border-gray-700', textColor: 'text-white', - loginUrl: `${BACKEND_URL}/auth/github/login${IS_DEV ? '?debug=true' : ''}`, + loginUrl: `${BACKEND_URL}/auth/github/login?${IS_DEV ? 'responseType=json' : ''}`, }, { id: 'twitter', @@ -70,7 +70,7 @@ export const oauthProviderConfigs: OAuthProviderConfig[] = [ color: 'bg-black border border-gray-800', hoverColor: 'hover:bg-gray-900 hover:border-gray-700', textColor: 'text-white', - loginUrl: `${BACKEND_URL}/auth/twitter/login${IS_DEV ? '?debug=true' : ''}`, + loginUrl: `${BACKEND_URL}/auth/twitter/login?${IS_DEV ? 'responseType=json' : ''}`, }, { id: 'discord', @@ -79,7 +79,7 @@ export const oauthProviderConfigs: OAuthProviderConfig[] = [ color: 'bg-indigo-600 border border-indigo-500', hoverColor: 'hover:bg-indigo-700 hover:border-indigo-600', textColor: 'text-white', - loginUrl: `${BACKEND_URL}/auth/discord/login${IS_DEV ? '?debug=true' : ''}`, + loginUrl: `${BACKEND_URL}/auth/discord/login?${IS_DEV ? 'responseType=json' : ''}`, }, ]; diff --git a/src/components/settings/SettingsHome.tsx b/src/components/settings/SettingsHome.tsx index 5ee053717..3ca47676c 100644 --- a/src/components/settings/SettingsHome.tsx +++ b/src/components/settings/SettingsHome.tsx @@ -1,16 +1,48 @@ -import { clearToken } from '../../store/authSlice'; -import { useAppDispatch } from '../../store/hooks'; +import { useState } from 'react'; + +import { skillManager } from '../../lib/skills/manager'; +import { persistor } from '../../store'; import SettingsHeader from './components/SettingsHeader'; import SettingsMenuItem from './components/SettingsMenuItem'; import { useSettingsNavigation } from './hooks/useSettingsNavigation'; const SettingsHome = () => { - const dispatch = useAppDispatch(); - const { navigateToSettings, closeSettings } = useSettingsNavigation(); + const { navigateToSettings } = useSettingsNavigation(); + const [showLogoutAndClearModal, setShowLogoutAndClearModal] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); const handleLogout = async () => { - await dispatch(clearToken()); - closeSettings(); + // Simple logout without clearing data - redirect to login + window.location.href = '/login'; + }; + + const clearAllAppData = async () => { + await persistor.purge(); + window.localStorage.clear(); + window.sessionStorage.clear(); + + // NEW: Clear skills databases (emails, chats, cached files) + try { + await skillManager.clearAllSkillsData(); + } catch (error) { + console.warn('Failed to clear skills data:', error); + // Continue with logout even if skills clearing fails + } + + // Complete reset - redirect to login for fresh start + window.location.href = '/login'; + }; + + const handleLogoutAndClearData = async () => { + try { + setIsLoading(true); + setError(null); + await clearAllAppData(); // This will redirect to login + } catch (_error) { + setError('Failed to clear data and logout. Please try again.'); + setIsLoading(false); + } }; // const handleViewEncryptionKey = () => { @@ -244,6 +276,23 @@ const SettingsHome = () => { onClick: handleDeleteAllData, dangerous: true, }, + { + id: 'logout-and-clear', + title: 'Log Out & Clear App Data', + description: 'Sign out and permanently clear all local data', + icon: ( + + + + ), + onClick: () => setShowLogoutAndClearModal(true), + dangerous: true, + }, { id: 'logout', title: 'Log out', @@ -302,6 +351,85 @@ const SettingsHome = () => { + + {/* Log Out & Clear Data Confirmation Modal */} + {showLogoutAndClearModal && ( +
+
+
+
+ + + +
+
+

Log Out & Clear App Data

+
+
+ +
+

+ This will sign you out and permanently delete ALL data including: • App settings and + conversations • Email data from Gmail • Chat history from Telegram • Cached files + from Notion • All other skills data +
+
+ This action cannot be undone and may take a few moments to complete. +

+ + {error && ( +
+

{error}

+
+ )} +
+ +
+ + +
+
+
+ )} ); }; diff --git a/src/lib/notion/services/metadataSync.ts b/src/lib/notion/services/metadataSync.ts index a63a68c27..6738b8547 100644 --- a/src/lib/notion/services/metadataSync.ts +++ b/src/lib/notion/services/metadataSync.ts @@ -1,7 +1,10 @@ /** - * Send Notion user metadata to the backend via the + * Send Notion metadata to the backend via the * `integration:metadata-sync` socket event so the server can merge it * into the user's Notion OAuth integration metadata. + * + * Mirrors the Gmail metadata sync pattern: we send the primary profile plus + * additional Notion data (pages, summaries) when available. */ import { emitViaRustSocket } from '../../../utils/tauriSocket'; @@ -16,14 +19,47 @@ export interface NotionUserProfileLike { avatar_url?: string | null; } +export interface NotionPageSummaryLike { + id: string; + title: string; + url: string | null; + last_edited_time: string; + content_text: string | null; +} + +export interface NotionSummaryLike { + id: number; + pageId: string; + url: string | null; + summary: string; + category: string | null; + sentiment: string; + topics: string[]; + sourceCreatedAt: string; + sourceUpdatedAt: string; +} + /** - * Emit `integration:metadata-sync` with Notion user profile so the - * backend can merge it into the user's Notion OAuth integration. + * Shape of Notion data we care about for backend integration metadata sync. + * This is populated from the Notion Redux slice in the runner. + */ +export interface NotionStateForSync { + profile?: NotionUserProfileLike | null; + pages?: NotionPageSummaryLike[] | null; + summaries?: NotionSummaryLike[] | null; +} + +/** + * Emit `integration:metadata-sync` with Notion profile plus additional + * Notion data (pages and summaries) so the backend can merge everything + * into the user's Notion OAuth integration metadata. + * * No-op when profile is missing or invalid. */ export function syncNotionMetadataToBackend( - profile: NotionUserProfileLike | null | undefined + notionState: NotionStateForSync | null | undefined ): void { + const profile = notionState?.profile; if (!profile || !profile.id) return; const metadata: Record = { @@ -34,6 +70,16 @@ export function syncNotionMetadataToBackend( avatar_url: profile.avatar_url ?? null, }; + if (Array.isArray(notionState.pages) && notionState.pages.length > 0) { + metadata.pages = notionState.pages; + metadata.pages_total = notionState.pages.length; + } + + if (Array.isArray(notionState.summaries) && notionState.summaries.length > 0) { + metadata.summaries = notionState.summaries; + metadata.summaries_total = notionState.summaries.length; + } + const payload = { requestId: crypto.randomUUID(), provider: PROVIDER_NOTION, metadata }; void emitViaRustSocket(INTEGRATION_METADATA_SYNC_EVENT, payload); diff --git a/src/lib/skills/manager.ts b/src/lib/skills/manager.ts index 898b3e8ac..4a05d2154 100644 --- a/src/lib/skills/manager.ts +++ b/src/lib/skills/manager.ts @@ -527,6 +527,44 @@ class SkillManager { throw new Error(`Unknown reverse RPC method: ${method}`); } } + + /** + * Clear all skills databases and cached data. + * Used for nuclear reset functionality. + */ + async clearAllSkillsData(): Promise { + try { + // Stop all running skills first + await this.stopAll(); + + // Get all skill IDs from Redux state + const state = store.getState(); + const skillIds = Object.keys(state.skills.skills); + + // Clear data for each skill + const clearPromises = skillIds.map(async (skillId) => { + try { + // Get skill data directory path + const dataDir = await invoke("runtime_skill_data_dir", { skillId }); + + // Note: We don't directly delete directories here since there's no exposed + // Tauri command for that. Instead, we rely on the backend to handle + // clearing when skills are disabled/reset via Redux state clearing. + + console.log(`[SkillManager] Skill ${skillId} data directory: ${dataDir}`); + } catch (err) { + console.warn(`[SkillManager] Failed to get data directory for skill ${skillId}:`, err); + } + }); + + await Promise.all(clearPromises); + + console.log("[SkillManager] Skills data clearing initiated"); + } catch (error) { + console.error("[SkillManager] Failed to clear skills data:", error); + throw new Error("Failed to clear skills databases"); + } + } } // Export singleton diff --git a/src/lib/tools/auto-update.ts b/src/lib/tools/auto-update.ts index 804a494aa..32f1130d6 100644 --- a/src/lib/tools/auto-update.ts +++ b/src/lib/tools/auto-update.ts @@ -6,7 +6,6 @@ */ import { invoke } from '@tauri-apps/api/core'; -import { clearToolsCache } from '../ai/tools/loader'; import { forceToolsCacheRefresh } from './file-watcher'; // Prevent excessive updates - limit to once per 10 seconds diff --git a/src/lib/tools/file-watcher.ts b/src/lib/tools/file-watcher.ts index 76c2f6dc9..8dbb84517 100644 --- a/src/lib/tools/file-watcher.ts +++ b/src/lib/tools/file-watcher.ts @@ -99,10 +99,10 @@ function simpleHash(str: string): number { /** * Force a cache refresh (useful for manual triggers) */ -export function forceToolsCacheRefresh(): Promise { +export async function forceToolsCacheRefresh(): Promise { console.log('🔄 Forcing tools cache refresh...'); clearToolsCache(); clearAICache(); lastModifiedTime = null; // Reset to trigger next check - return loadAIConfig(); + await loadAIConfig(); } diff --git a/src/pages/Conversations.tsx b/src/pages/Conversations.tsx index 870249586..f43fbce55 100644 --- a/src/pages/Conversations.tsx +++ b/src/pages/Conversations.tsx @@ -22,7 +22,7 @@ import { useAppDispatch, useAppSelector } from '../store/hooks'; import type { NotionPageSummary, NotionSummary, NotionUserProfile } from '../store/notionSlice'; import { addInferenceResponse, - addOptimisticMessage, + addMessageLocal, clearDeleteStatus, clearPurgeStatus, clearSelectedThread, @@ -30,11 +30,13 @@ import { deleteThreadLocal, fetchSuggestedQuestions, purgeThreads, - removeOptimisticMessages, + setActiveThread, setLastViewed, setPanelWidth, setSelectedThread, + updateMessagesForThread, } from '../store/threadSlice'; +import type { ThreadMessage } from '../types/thread'; const MIN_PANEL_WIDTH = 200; const MAX_PANEL_WIDTH = 480; @@ -113,6 +115,7 @@ const Conversations = () => { lastViewedAt, suggestedQuestions, isLoadingSuggestions, + activeThreadId, } = useAppSelector(state => state.thread); const skillsState = useAppSelector(state => state.skills); @@ -306,14 +309,45 @@ const Conversations = () => { const trimmed = text ?? inputValue.trim(); if (!trimmed || !selectedThreadId || isSending) return; - // Snapshot history before the optimistic update (exclude stale optimistic msgs) - const historySnapshot = messages.filter(m => !m.id.startsWith('optimistic-')); + // Check if another thread is already sending + if (activeThreadId && activeThreadId !== selectedThreadId) { + return; // Block sending from non-active threads + } + + // Store the original thread ID to ensure response goes to correct thread + const sendingThreadId = selectedThreadId; + + // Create stable user message and persist immediately + const userMessage: ThreadMessage = { + id: `msg_${Date.now()}_${Math.random()}`, + content: trimmed, + type: 'text', + extraMetadata: {}, + sender: 'user', + createdAt: new Date().toISOString(), + }; + + // Immediately persist user message to both current view and persistent storage + dispatch(addMessageLocal({ threadId: sendingThreadId, message: userMessage })); + + // Update current view if this is the selected thread + if (sendingThreadId === selectedThreadId) { + // Message is already added to persistent storage, reload current view + dispatch(setSelectedThread(sendingThreadId)); + } + + // Snapshot history for AI request (excluding the just-added user message since we'll add it manually) + const historySnapshot = messages.filter( + m => !m.id.startsWith('optimistic-') && m.id !== userMessage.id + ); - dispatch(addOptimisticMessage({ content: trimmed })); setInputValue(''); setSendError(null); setIsSending(true); + // Set this thread as active + dispatch(setActiveThread(sendingThreadId)); + try { // Process user message with SOUL + TOOLS injection let processedUserContent = trimmed; @@ -447,6 +481,7 @@ const Conversations = () => { toolArgs ); const result = await skillManager.callTool(skillId, toolName, toolArgs); + console.log(`[Conversations] tool "${toolName}" calling result:`, result); toolResultContent = result.content.map(c => c.text).join('\n'); let toolReturnedError = result.isError; if (!toolReturnedError && toolResultContent) { @@ -490,14 +525,32 @@ const Conversations = () => { break; } - dispatch(addInferenceResponse({ content: finalContent })); + // Pass the original sending thread ID to ensure response goes to correct thread + dispatch(addInferenceResponse({ content: finalContent, threadId: sendingThreadId })); } catch (err) { - dispatch(removeOptimisticMessages()); + // Remove the user message from persistent storage on error + // We'll use a thunk-like approach to access current state + dispatch((dispatch, getState) => { + const state = getState() as { + thread: { messagesByThreadId: Record }; + }; + const persistedMessages = state.thread.messagesByThreadId[sendingThreadId] || []; + const currentMessages = persistedMessages.filter(m => m.id !== userMessage.id); + dispatch(updateMessagesForThread({ threadId: sendingThreadId, messages: currentMessages })); + + // Also remove from current view if this is the selected thread + if (sendingThreadId === selectedThreadId) { + dispatch(setSelectedThread(sendingThreadId)); + } + }); + const msg = err && typeof err === 'object' && 'error' in err ? String((err as { error: unknown }).error) : 'Failed to get response'; setSendError(msg); + // Clear active thread on error + dispatch(setActiveThread(null)); } finally { setIsSending(false); } @@ -899,8 +952,8 @@ const Conversations = () => { ))} - {/* Typing indicator (#14) */} - {isSending && ( + {/* Typing indicator (#14) - Only show for the active thread */} + {activeThreadId === selectedThreadId && isSending && (
@@ -929,7 +982,9 @@ const Conversations = () => { key={i} type="button" onClick={() => handleSendMessage(s.text)} - disabled={isSending} + disabled={ + isSending || !!(activeThreadId && activeThreadId !== selectedThreadId) + } className="flex-shrink-0 px-3 py-1.5 rounded-lg text-[12px] whitespace-nowrap bg-white/5 text-stone-400 hover:bg-white/10 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"> {s.text} @@ -940,6 +995,15 @@ const Conversations = () => { {/* Message Input */}
+ {/* Show warning if another thread is active */} + {activeThreadId && activeThreadId !== selectedThreadId && ( +
+

+ Another conversation is active. Please wait for it to complete before sending + messages here. +

+
+ )} {/* Model selector */}
{isLoadingModels ? ( @@ -982,13 +1046,22 @@ const Conversations = () => { value={inputValue} onChange={e => setInputValue(e.target.value)} onKeyDown={handleInputKeyDown} - placeholder="Type a message..." + placeholder={ + activeThreadId && activeThreadId !== selectedThreadId + ? 'Another conversation is active...' + : 'Type a message...' + } rows={1} - className="flex-1 resize-none bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-sm placeholder:text-stone-500 focus:outline-none focus:ring-1 focus:ring-primary-500/50 focus:border-primary-500/50 transition-all max-h-32" + disabled={!!(activeThreadId && activeThreadId !== selectedThreadId)} + className="flex-1 resize-none bg-white/5 border border-white/10 rounded-xl px-4 py-2.5 text-sm placeholder:text-stone-500 focus:outline-none focus:ring-1 focus:ring-primary-500/50 focus:border-primary-500/50 transition-all max-h-32 disabled:opacity-50 disabled:cursor-not-allowed" />