diff --git a/plugins/kbagent/skills/kbagent/SKILL.md b/plugins/kbagent/skills/kbagent/SKILL.md index 29db2abb..520859da 100644 --- a/plugins/kbagent/skills/kbagent/SKILL.md +++ b/plugins/kbagent/skills/kbagent/SKILL.md @@ -1,80 +1,20 @@ --- name: kbagent description: > - Use when working with Keboola Connection projects via kbagent CLI. - Covers: exploring and searching configurations (extractors, writers, transformations), - browsing job history, analyzing cross-project data lineage, calling MCP tools - across multiple projects, managing development branches, debugging SQL in - temporary workspaces, bulk-onboarding organizations, syncing project configs - as local files (GitOps), git-branching with Keboola dev branch isolation, - sharing buckets across projects, linking shared data, - encrypting secrets for MCP tool call workflows, - uploading/downloading Storage Files with tag management, - and syncing storage metadata and job history. Triggers: kbagent, Keboola project, - keboola configs, keboola jobs, keboola lineage, keboola transformations, - keboola MCP tools, keboola workspace, SQL debugging, keboola branches, - keboola organization, keboola sharing, bucket sharing, link bucket, - keboola sync, keboola git, - keboola gitops, sync pull, sync push, sync diff, branch-link, - search configs, find in configurations, audit configurations, - input mapping migration, remove input mapping, Snowflake paths, - MULTI_STATEMENT_COUNT, statement count error, SQL transformation migration, - keboola encrypt, encrypt secrets, encrypt credentials, encrypt password, - keboola encryption API, #password, #api_token, KBC::ProjectSecure, - safe config write, dry-run preview, fresh fetch before edit, - stale local config file, config version overwrite, - default bucket, output bucket, default_bucket, storage.output, - raw mode bucket override, custom output bucket name, - data app, data apps, keboola data app, streamlit app, streamlit deployment, - flask app, fastapi app, node app, python-js, deploy data app, - data-app create, data-app deploy, data-app password, data-app start, - data-app logs, container logs, app logs, tail logs, build logs, - app stdout, app stderr, troubleshoot data app, debug data app, - app proxy, simpleAuth, app auto-suspend, configVersion, redeploy contract, - Data Science API, /apps endpoint, app password, KBC::Project ciphertext, - data-app secrets, app secrets, app runtime secrets, secrets-set, - secrets-list, secrets-get, secrets-remove, encrypt app secret, - app environment variable, validate repo, validate-repo, - data-app golden rule, pre-flight repo check, repo structure check, - managed git repo, use-managed-git-repo, Keboola-hosted repo, - git-credentials, git-credentials-create, git-repo, - deploy from git, managed repository, - data app runs, deploy attempts, deploy failure reason, why deploy stopped, - local workspace, project directory, kbagent init, - invite user, invite member, project invitation, manage members, - list members, remove member, change role, project role, - bulk invite, invite from CSV, project access, member management, - manage token prompt, --allow-env-manage-token, KBC_MANAGE_API_TOKEN, - feature flag, feature flags, list features, project features, user features, - enable feature, disable feature, set feature flag, add feature, remove feature, - early-adopter-preview, direct-access, pay-as-you-go, /manage/features, - super admin token, super-admin feature, stack feature catalogue, - data stream, data streams, keboola data streams, stream source, OTLP, - OpenTelemetry, otel, OTLP endpoint, OTEL_EXPORTER_OTLP_ENDPOINT, telemetry ingest, - logs metrics traces, stream create-source, stream detail, stream list, - stream delete, otlp source, http source, stream-in, ingest endpoint, - scoped token, scoped storage token, token create, token delete, token refresh, - mint token, revoke token, rotate token, short-lived token, expiring token, - bucket-write token, single-bucket write token, canManageTokens, device enrollment, - per-device credential, per-device token, per-device stream source, - create_scoped_token, create_stream_source, delete_token, refresh_token, - semantic-layer, semantic layer, semantic-layer model, metastore, - semantic-metric, semantic-dataset, semantic-relationship, - semantic-constraint, semantic-glossary, add metric, edit metric, - rename metric, remove metric, validate model, validate semantic layer, - promote model, semantic-layer build, semantic-layer export, - semantic-layer diff, semantic-layer import, semantic-layer token, - metric SQL, dataset FQN, constraint rule, threshold constraint, - 4-band health, _critical _warning _healthy _review, CODE_METRIC, - DIM_METRIC_THRESHOLD, dangling metric FK, orphaned constraint, - phantom field, AGG on STRING, SUM on PCT, deep validate, - sl, kbagent sl, semantic layer wizard, sl-build, sl-add, sl-edit, - reference data, semantic-layer reference-data, chart of accounts, COA, - dimension members, account list, dimension data, member list, - developer portal, dev-portal, apps-api, register component, vendor app, - portal property, ui-options, encryption portal, defaultBucket portal, - app icon, configurationSchema portal, publish component, deprecate component, - kbagent dev-portal, portal identity, vendor login, service account portal. + Use when working with Keboola Connection projects via the kbagent CLI. + Covers: exploring and searching component configurations, job history and + job runs, cross-project data lineage, Keboola MCP tools, development + branches, SQL debugging in temporary workspaces, GitOps sync of configs as + local files (pull/push/diff/clone), bucket sharing and linking, encrypting + secrets, Storage tables and files, data apps (create/deploy/logs/secrets), + conditional flows and schedules, project members and invitations, feature + flags, OTLP data streams, scoped Storage tokens, the semantic layer + (models, datasets, metrics, constraints, reference data), and the Keboola + Developer Portal. Triggers: kbagent, Keboola, keboola config, keboola job, + keboola lineage, keboola sync, gitops, dev branch, workspace SQL, data app, + streamlit deploy, semantic layer, sl, dev-portal, data stream, OTLP, + scoped token, bucket sharing, encrypt secrets, feature flag, flow schedule, + invite member. --- # kbagent -- Keboola Agent CLI diff --git a/tests/test_skill_frontmatter.py b/tests/test_skill_frontmatter.py new file mode 100644 index 00000000..dadaf1dd --- /dev/null +++ b/tests/test_skill_frontmatter.py @@ -0,0 +1,55 @@ +"""Static compliance tests for the kbagent skill frontmatter. + +Claude Desktop (and the Agent Skills spec) enforce a hard limit of 1024 +characters on the ``description`` field in SKILL.md frontmatter; a longer +description makes the whole skill fail to load with +"field 'description' in SKILL.md must be at most 1024 characters" +(issue #447). These tests keep the frontmatter within every consumer's +limits so the plugin stays loadable in Claude Desktop, Claude Code, and +claude.ai alike. +""" + +from __future__ import annotations + +import re +from pathlib import Path + +import pytest +import yaml + +REPO_ROOT = Path(__file__).parent.parent +SKILL_MD = REPO_ROOT / "plugins" / "kbagent" / "skills" / "kbagent" / "SKILL.md" + +# Hard limit from the Agent Skills spec, enforced by Claude Desktop at +# skill-load time (issue #447). +DESCRIPTION_CHAR_LIMIT = 1024 + + +@pytest.fixture(scope="module") +def frontmatter() -> dict: + text = SKILL_MD.read_text(encoding="utf-8") + match = re.match(r"^---\n(.*?)\n---\n", text, re.DOTALL) + assert match is not None, "SKILL.md is missing YAML frontmatter" + data = yaml.safe_load(match.group(1)) + assert isinstance(data, dict), "SKILL.md frontmatter must be a YAML mapping" + return data + + +class TestSkillFrontmatter: + def test_skill_file_exists(self) -> None: + assert SKILL_MD.is_file(), f"SKILL.md missing at {SKILL_MD}" + + def test_name_matches_directory(self, frontmatter: dict) -> None: + assert frontmatter.get("name") == "kbagent" + + def test_description_present(self, frontmatter: dict) -> None: + description = frontmatter.get("description", "") + assert description.strip(), "SKILL.md description must not be empty" + + def test_description_within_claude_desktop_limit(self, frontmatter: dict) -> None: + description = frontmatter["description"] + assert len(description) <= DESCRIPTION_CHAR_LIMIT, ( + f"SKILL.md description is {len(description)} characters; Claude " + f"Desktop rejects skills whose description exceeds " + f"{DESCRIPTION_CHAR_LIMIT} characters (issue #447). Trim it." + )