feat: add personal skill resolver (#25362)

> Mux updated this PR on behalf of Mike.

## Stack Context

This stack splits experimental personal skills into smaller reviewable
PRs. Personal skills are user-owned `SKILL.md` files stored by Coder and
injected into chatd alongside workspace skills.

Stack order:
1. #25362 personal skill resolver
2. #25363 storage, permissions, API, and SDK
3. #25365 API test coverage
4. #25366 chattool and chatd integration
5. #25066 settings UI and docs
6. #25386 personal skills slash menu

## What?

Adds the shared personal skill parser and resolver package, plus
reusable skill-name validation exported from `workspacesdk`.

The parser enforces the full personal skill contract: max raw size,
kebab-case name, max name length, and non-empty body.

## Why?

The rest of the stack needs one source-aware resolver for personal and
workspace skills, including collision handling and qualified aliases.
Keeping personal skill constraints in the parser prevents callers from
accidentally parsing invalid personal skills.

## Validation

- `go test ./coderd/x/skills ./codersdk/workspacesdk`
- pre-commit hooks on this branch
This commit is contained in:
Michael Suchacz
2026-05-16 15:33:43 +00:00
committed by GitHub
parent 191dd230ae
commit 792f0b4902
7 changed files with 615 additions and 11 deletions
+2 -7
View File
@@ -28,7 +28,7 @@ const (
const (
maxInstructionFileBytes = 64 * 1024
maxSkillMetaBytes = 64 * 1024
maxSkillMetaBytes = workspacesdk.MaxSkillMetaBytes
)
// markdownCommentPattern strips HTML comments from instruction
@@ -53,11 +53,6 @@ var invisibleRunePattern = regexp.MustCompile(
"\ufff0-\ufff8]",
)
// skillNamePattern validates kebab-case skill names.
var skillNamePattern = regexp.MustCompile(
`^[a-z0-9]+(-[a-z0-9]+)*$`,
)
// Default values for agent-internal configuration. These are
// used when the corresponding env vars are unset.
//
@@ -357,7 +352,7 @@ func discoverSkills(skillsDirs []string, metaFile string) []codersdk.ChatMessage
if name != entry.Name() {
continue
}
if !skillNamePattern.MatchString(name) {
if !workspacesdk.SkillNamePattern.MatchString(name) {
continue
}