`complete` ships in both the homelab and the ai-agent repo, so /api/skills listed it twice with the same (name-keyed) usage, counted twice in the totals. merge_same_name keeps the repo's copy (else the newest) and lists the others under `copies` — shown as a +source badge on the card; their share refs stay pickable in Settings → Users. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
2.5 KiB
Python
72 lines
2.5 KiB
Python
"""The skills catalog (``backend/skills.py``)."""
|
||
from __future__ import annotations
|
||
|
||
from typing import Optional
|
||
|
||
from .base import Schema
|
||
|
||
__all__ = [
|
||
"SkillCopy",
|
||
"SkillSummary",
|
||
"SkillsTotals",
|
||
"SkillsResponse",
|
||
]
|
||
|
||
|
||
class SkillCopy(Schema):
|
||
"""Another on-disk copy of a skill that shares the card's name."""
|
||
source: str
|
||
sourceKind: str
|
||
dir: Optional[str] = None
|
||
path: Optional[str] = None
|
||
updatedAt: Optional[str] = None
|
||
shareRef: Optional[str] = None
|
||
sharedWithGuests: bool = False
|
||
|
||
|
||
class SkillSummary(Schema):
|
||
"""One skill card: what it is on disk + what its use has cost."""
|
||
name: str # the invocation name (its dir name)
|
||
title: str # frontmatter `name:`, falls back to `name`
|
||
description: str
|
||
# None for a built-in/plugin skill: nothing of it lives in this repo, so
|
||
# there is no file to link into the editor.
|
||
dir: Optional[str] = None
|
||
path: Optional[str] = None
|
||
source: str # "repo" | <project dir-name> | "builtin"
|
||
sourceKind: str # "repo" | "project" | "builtin"
|
||
# Frontmatter `trigger_words:` — the words that make the composer's
|
||
# auto-tag suggest this skill. Empty for a built-in (no SKILL.md here) and
|
||
# for a skill deliberately kept out of auto-tag.
|
||
triggerWords: list[str] = []
|
||
files: int
|
||
updatedAt: Optional[str] = None
|
||
count: int # total invocations across all transcripts
|
||
lastUsed: Optional[str] = None
|
||
conversations: int # distinct conversations that invoked it
|
||
contextTokens: Optional[int] = None
|
||
contextCost: Optional[float] = None
|
||
estSpend: Optional[float] = None # contextCost × count
|
||
sessionCost: float # total spend of the conversations using it
|
||
sessionTokens: int
|
||
# How the guest-sharing list names it: `name` (repo) or `<project>/<name>`.
|
||
# None for a built-in — there's no dir to hand a guest run.
|
||
shareRef: Optional[str] = None
|
||
sharedWithGuests: bool = False # in Settings → Users' shared-skills list
|
||
# The other dirs shipping a skill of the same name (one card per name —
|
||
# the repo's own copy leads). Empty for a skill with a single home.
|
||
copies: list[SkillCopy] = []
|
||
|
||
|
||
class SkillsTotals(Schema):
|
||
skills: int
|
||
used: int
|
||
invocations: int
|
||
contextCost: float
|
||
estSpend: float
|
||
|
||
|
||
class SkillsResponse(Schema):
|
||
skills: list[SkillSummary]
|
||
totals: SkillsTotals
|