# 16 - Skill Publishing System How agents create, register, and manage skills programmatically through the `publish_skill` builtin tool, working in tandem with the `skill-creator` core skill. --- ## 1. Overview The skill publishing system bridges the gap between **skill creation** (filesystem) and **skill management** (database). It consists of two components: | Component | Type | Purpose | |-----------|------|---------| | `skill-creator` | Core skill (bundled) | Guides agents through skill design, implementation, testing, and optimization | | `publish_skill` | Builtin tool | Registers a skill directory in the database, copies files to managed store, auto-grants to creating agent | Without `publish_skill`, skills created by agents exist only on the filesystem and are invisible to the database-backed skill management system (no search, no grants, no UI visibility). --- ## 2. End-to-End Flow ``` Agent receives request to create a skill │ ▼ ┌─────────────────────────────────────┐ │ 1. skill-creator skill activated │ │ Agent reads SKILL.md guidance │ │ Creates files via write_file: │ │ skills/my-skill/SKILL.md │ │ skills/my-skill/scripts/ │ │ skills/my-skill/references/ │ └──────────────┬──────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ 2. publish_skill tool called │ │ publish_skill(path: "skills/ │ │ my-skill") │ └──────────────┬──────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────┐ │ 3. Tool executes: │ │ a. Validate SKILL.md + parse frontmatter │ │ b. Derive slug, validate format │ │ c. Check system skill conflict │ │ d. Compute SHA-256 hash │ │ e. Copy dir → skills-store/{slug}/{version}/ │ │ f. INSERT/UPSERT into skills table │ │ g. Auto-grant to calling agent │ │ h. Scan + report missing dependencies │ │ i. Bump loader cache version │ │ j. Generate embedding (async) │ └──────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ 4. Result returned to agent: │ │ - Skill ID, slug, version │ │ - Grant confirmation │ │ - Dep warnings (if any) │ └─────────────────────────────────────┘ ``` --- ## 3. publish_skill Tool ### Parameters | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `path` | string | yes | - | Path to skill directory containing SKILL.md (absolute or relative to workspace) | ### Activation Conditions The tool is registered at gateway startup when: 1. `pgStores.Skills` is available (PostgreSQL skill store initialized) 2. `PGSkillStore` has at least one managed directory (`skills-store/`) 3. Skills loader is initialized The tool appears in every agent's tool set — no per-agent configuration needed. Can be toggled via the builtin tools admin UI. ### Context Values Used | Context Key | Source | Purpose | |-------------|--------|---------| | `store.UserIDFromContext(ctx)` | WS connect / HTTP header | Skill owner + grant source | | `store.AgentIDFromContext(ctx)` | Agent loop | Agent to auto-grant access to | | `ToolWorkspaceFromCtx(ctx)` | Tool registry | Resolve relative paths | ### SKILL.md Frontmatter Requirements ```yaml --- name: my-skill-name # REQUIRED — display name description: What it does # Recommended — used for search + auto-activation slug: my-skill-name # Optional — derived from name if absent --- ``` - `name` is mandatory; tool returns error if missing - `slug` auto-derived via `Slugify(name)` if not specified - Slug must match `^[a-z0-9][a-z0-9-]*[a-z0-9]$` --- ## 4. Core Logic Details ### 4.1 Slug Validation ``` name: "My Awesome Skill" → Slugify → "my-awesome-skill" → SlugRegexp check → ✓ valid ``` Rejects: leading/trailing hyphens, uppercase, special chars, spaces. ### 4.2 System Skill Conflict Check Prevents overwriting bundled skills (pdf, xlsx, docx, pptx, skill-creator, etc.): ```go if t.skills.IsSystemSkill(slug) { return ErrorResult("slug conflicts with a system skill") } ``` ### 4.3 Versioned Storage Skills are stored in versioned directories. Re-publishing the same slug increments the version: ``` skills-store/ ├── my-skill/ │ ├── 1/ │ │ ├── SKILL.md │ │ └── scripts/ │ └── 2/ ← re-publish creates new version │ ├── SKILL.md │ └── scripts/ ``` `GetNextVersion(slug)` queries `MAX(version)` from the skills table (includes archived skills). ### 4.4 Database Upsert Uses `CreateSkillManaged()` with `ON CONFLICT(slug) DO UPDATE`: - New slug → INSERT with `visibility = 'private'` - Existing slug → UPDATE name, description, version, file_path, file_hash - Archived skill re-published → status reset to `'active'` - Embedding generated asynchronously after insert/update ### 4.5 Auto-Grant When the calling agent has a valid `AgentID` in context: ```go GrantToAgent(ctx, skillID, agentID, version, userID) ``` This also **auto-promotes** skill visibility from `private` → `internal`, making it accessible via `ListAccessible()` for the granted agent. ### 4.6 Dependency Scanning After publishing, the tool runs static analysis on the skill's `scripts/` directory: 1. **ScanSkillDeps** — detects required binaries, Python imports, Node packages 2. **CheckSkillDeps** — verifies each dependency is available on the system 3. If missing deps found: - Stored in `deps` JSONB column via `StoreMissingDeps()` - Warning returned to agent with specific missing packages - Agent is guided to install via `exec` (pip/npm) or inform the user Unlike the HTTP upload handler, the tool does **not** archive the skill on missing deps — it warns and lets the agent decide. ### 4.7 Directory Copy Security | Check | Action | |-------|--------| | `..` in relative path | Skip (prevent traversal) | | Symlinks | Skip (prevent escape) | | System artifacts | Skip (`.DS_Store`, `__MACOSX`, `Thumbs.db`, etc.) | | Total dir size > 20 MB | Reject with error | --- ## 5. skill-creator Skill ### Activation Triggers The skill-creator is a bundled system skill with a "pushy" description that triggers on: - Creating new skills or extending agent capabilities - Skill scripts, references, benchmark optimization - Description optimization and eval testing ### Creation Workflow 1. **Capture Intent** — what, when, output 2. **Research** — best practices via docs-seeker 3. **Plan** — identify scripts, references, assets 4. **Initialize** — `scripts/init_skill.py --path ` 5. **Write** — implement SKILL.md + resources 6. **Test & Evaluate** — eval suite with parallel runs 7. **Optimize Description** — AI-powered trigger optimization 8. **Publish** — `publish_skill(path: "skills/")` 9. **Package** (optional) — ZIP for external distribution 10. **Iterate** — refine from feedback ### Skill File Structure ``` skills// ├── SKILL.md (required, <300 lines) ├── scripts/ (optional: executable code) ├── references/ (optional: docs loaded as-needed) ├── agents/ (optional: eval agent templates) └── assets/ (optional: output resources) ``` ### Key Constraints | Resource | Limit | |----------|-------| | Description | ≤1024 chars | | SKILL.md | <300 lines | | Each reference | <300 lines | | Scripts | No limit (executed, not loaded into context) | --- ## 6. Database Schema ### skills table (relevant columns) | Column | Type | Purpose | |--------|------|---------| | `id` | UUID | Primary key | | `slug` | VARCHAR(255) UNIQUE | Canonical identifier | | `name` | VARCHAR(255) | Display name | | `description` | TEXT | Auto-activation trigger text | | `owner_id` | VARCHAR(255) | User who created (or "system") | | `visibility` | VARCHAR(10) | `private` → `internal` (on grant) → `public` | | `version` | INT | Increments on re-publish | | `status` | VARCHAR(20) | `active` or `archived` | | `is_system` | BOOLEAN | True for bundled skills | | `enabled` | BOOLEAN | Admin toggle | | `file_path` | TEXT | Filesystem path to versioned dir | | `file_hash` | VARCHAR(64) | SHA-256 of SKILL.md | | `deps` | JSONB | `{"missing": ["pip:opencv", "python3"]}` | | `frontmatter` | JSONB | Parsed YAML metadata | | `embedding` | vector(1536) | pgvector for similarity search | ### skill_agent_grants table | Column | Type | Purpose | |--------|------|---------| | `skill_id` | UUID FK | References skills | | `agent_id` | UUID FK | References agents | | `pinned_version` | INT | Stored but not used — agent always uses latest | | `granted_by` | VARCHAR | User who granted | --- ## 7. Visibility & Access Model ``` publish_skill creates with visibility = "private" │ ▼ GrantToAgent auto-promotes → "internal" │ ▼ ListAccessible query includes: - is_system = true (all system skills) - visibility = 'public' (anyone) - visibility = 'private' (owner only) - visibility = 'internal' (agents/users with grants) ``` Revoking the last grant auto-demotes `internal` → `private` (atomic SQL). --- ## 8. Cache Invalidation After publishing, two caches are bumped: 1. **PGSkillStore cache** — `BumpVersion()` sets `version = time.Now().UnixMilli()`, invalidating the `ListSkills()` cache (TTL 5min + version check) 2. **Skills Loader cache** — `loader.BumpVersion()` invalidates the filesystem-based skill index used for system prompt injection Next agent turn picks up the new skill in its tool set. --- ## 9. Related Files | File | Purpose | |------|---------| | `internal/tools/publish_skill.go` | Tool implementation | | `internal/skills/helpers.go` | Shared helpers: ParseSkillFrontmatter, Slugify, IsSystemArtifact, SlugRegexp | | `internal/store/pg/skills_crud.go` | DB operations: CreateSkillManaged, GetNextVersion, StoreMissingDeps | | `internal/store/pg/skills_admin.go` | Admin operations: IsSystemSkill | | `internal/store/pg/skills_grants.go` | GrantToAgent, RevokeFromAgent, ListAccessible | | `internal/skills/loader.go` | Filesystem skill loader with priority hierarchy | | `internal/skills/seeder.go` | System skill seeder (bundled → DB) | | `internal/skills/dep_scanner.go` | Static analysis for skill dependencies | | `internal/skills/dep_checker.go` | Runtime dependency verification | | `internal/http/skills_upload.go` | HTTP ZIP upload handler (alternative to publish_skill) | | `cmd/gateway.go` | Tool registration and gateway initialization | | `cmd/gateway_builtin_tools.go` | Builtin tool seed data | | `skills/skill-creator/SKILL.md` | Core skill instructions |