Restructure the codebase so the CLI app consumes the SDK rather than the SDK wrapping CLI internals. This eliminates the circular dependency (sdk -> cmd -> sdk) and establishes pkg/kit as the canonical API. Key changes: - Create pkg/kit/ with InitConfig, SetupAgent, BuildProviderConfig extracted from cmd/root.go and cmd/setup.go as parameterized functions - Move sdk/kit.go -> pkg/kit/kit.go (remove cmd import, use local calls) - Move sdk/types.go -> pkg/kit/types.go - Move main.go -> cmd/kit/main.go (standard Go project layout) - cmd/root.go and cmd/setup.go now delegate to pkg/kit, injecting CLI-specific state (quietFlag) via the Quiet field on AgentSetupOptions - Add setSDKDefaults() for cobra-free SDK usage (viper defaults) - Fix .gitignore: kit -> /kit (was blocking cmd/kit/ and pkg/kit/) - Update .goreleaser.yaml, Taskfile.yml, AGENTS.md, contribute/build.sh, README.md for new cmd/kit entrypoint and pkg/kit import paths - Add plans/ with 10 detailed SDK revamp plans and Taskfile.yml - Delete sdk/ directory entirely
4.8 KiB
Plan 08: Skills & Prompts System
Priority: P2 Effort: Medium Goal: Expose skills loading, prompt templates, and dynamic system prompt management. CLI and SDK share the same skills infrastructure.
Background
Pi exports loadSkills(), formatSkillsForPrompt(), PromptTemplate, expandPromptTemplate(). Kit has an extension system but no "skills" concept (markdown-based instruction files) or prompt template system. This plan introduces a skills layer designed SDK-first.
Prerequisites
- Plan 00 (Create
pkg/kit/) - Plan 02 (Richer type exports)
Step-by-Step
Step 1: Create internal/skills/ package
File: internal/skills/skills.go — Skill loading and parsing
type Skill struct {
Name string
Description string
Content string
Path string
Tags []string
When string // "always", "on-demand", "file:*.go"
}
func LoadSkill(path string) (*Skill, error) { ... } // Markdown with YAML frontmatter
func LoadSkillsFromDir(dir string) ([]*Skill, error) { ... } // .md/.txt files + SKILL.md subdirs
func LoadSkills(cwd string) ([]*Skill, error) { ... } // Auto-discover .kit/skills/ + ~/.config/kit/skills/
func FormatForPrompt(skills []*Skill) string { ... } // Format for system prompt
File: internal/skills/templates.go — Prompt templates
type PromptTemplate struct {
Name string
Content string
Variables []string
}
func NewPromptTemplate(name, content string) *PromptTemplate { ... }
func LoadPromptTemplate(path string) (*PromptTemplate, error) { ... }
func (t *PromptTemplate) Expand(values map[string]string) string { ... }
func (t *PromptTemplate) ExpandStrict(values map[string]string) (string, error) { ... }
File: internal/skills/prompt_builder.go — System prompt composition
type PromptBuilder struct { ... }
func NewPromptBuilder(basePrompt string) *PromptBuilder { ... }
func (pb *PromptBuilder) WithSkills(skills []*Skill) *PromptBuilder { ... }
func (pb *PromptBuilder) WithSection(name, content string) *PromptBuilder { ... }
func (pb *PromptBuilder) Build() string { ... }
Step 2: Export in SDK
File: pkg/kit/skills.go (new)
package kit
import "github.com/mark3labs/kit/internal/skills"
type Skill = skills.Skill
type PromptTemplate = skills.PromptTemplate
type PromptBuilder = skills.PromptBuilder
func LoadSkill(path string) (*Skill, error) { return skills.LoadSkill(path) }
func LoadSkillsFromDir(dir string) ([]*Skill, error) { return skills.LoadSkillsFromDir(dir) }
func LoadSkills(cwd string) ([]*Skill, error) { return skills.LoadSkills(cwd) }
func FormatSkillsForPrompt(s []*Skill) string { return skills.FormatForPrompt(s) }
func NewPromptTemplate(name, content string) *PromptTemplate { return skills.NewPromptTemplate(name, content) }
func LoadPromptTemplate(path string) (*PromptTemplate, error) { return skills.LoadPromptTemplate(path) }
func NewPromptBuilder(basePrompt string) *PromptBuilder { return skills.NewPromptBuilder(basePrompt) }
Step 3: Integrate skills into Kit Options
type Options struct {
// ... existing fields ...
Skills []string // Skill files/dirs to load (empty = auto-discover)
SkillsDir string // Override default skills directory
}
In New(), load skills and compose system prompt via PromptBuilder.
Step 4: App-as-Consumer — CLI uses SDK for skills
Currently Kit's extension loader (internal/extensions/loader.go) discovers extensions from .kit/extensions/ and ~/.config/kit/extensions/. The skills system follows the same pattern but for instruction files.
The CLI should:
- Use
kit.LoadSkills(cwd)to discover skills - Pass them via
kit.Options{Skills: ...}or let auto-discovery handle it - A
/skillsslash command in interactive mode could list loaded skills
The existing .agents/skills/ directory in the repo (used by btca) aligns with this convention. The SDK auto-discovers from .kit/skills/ to avoid conflict with the .agents/ convention used by other tools.
Step 5: Write tests and verify
go build -o output/kit ./cmd/kit
go test -race ./...
Files Changed Summary
| Action | File | Change |
|---|---|---|
| CREATE | internal/skills/skills.go |
Skill loading/parsing |
| CREATE | internal/skills/templates.go |
PromptTemplate |
| CREATE | internal/skills/prompt_builder.go |
System prompt composition |
| CREATE | pkg/kit/skills.go |
Public SDK exports |
| EDIT | pkg/kit/kit.go |
Skills option, auto-loading |
Verification Checklist
- Skills with YAML frontmatter parse correctly
- Skills without frontmatter load (name from filename)
- PromptTemplate expansion works
- PromptBuilder composes multi-section prompts
- Auto-discovery finds skills in standard directories
- CLI uses SDK for skill loading