From cacf492d95412ecbbaf1ba7e7d8cb0413da90fcd Mon Sep 17 00:00:00 2001 From: Matt Faltyn Date: Fri, 11 Sep 2026 11:54:37 +0200 Subject: [PATCH] feat(agent): install compact shared skill --- README.md | 2 +- internal/cli/agent_prompt_budget_test.go | 11 ++-- internal/cli/agents.go | 68 ++++++++++++++++++++++-- internal/cli/agents_test.go | 9 ++-- 4 files changed, 80 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2e2458351..73ad21b7b 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ pad server info # How this client is connected to Pad ### For AI Agents -**Your agent becomes a project partner.** Install the `/pad` skill once, and your AI coding tool can read, create, and update project items through natural language. +**Your agent becomes a project partner.** Install the `/pad` skill once, and your AI coding tool can read, create, and update project items through natural language. Cursor, Codex, Windsurf, and OpenCode receive a compact dispatcher and load detailed guidance by topic with `pad agent guide`, keeping routine turns small. ```bash pad agent install # Auto-detects your tools and installs the skill diff --git a/internal/cli/agent_prompt_budget_test.go b/internal/cli/agent_prompt_budget_test.go index def6b4a99..15184a197 100644 --- a/internal/cli/agent_prompt_budget_test.go +++ b/internal/cli/agent_prompt_budget_test.go @@ -6,9 +6,9 @@ import ( "testing" ) -// This initially records the existing shared Cursor/Codex payload with modest -// headroom. Later prompt-reduction changes should lower this budget, not spend it. -const agentSkillPromptBudget = 40 * 1024 +// The compact dispatcher should stay small enough to load eagerly. Detailed +// reference material belongs behind `pad agent guide`, not in this payload. +const agentSkillPromptBudget = 5 * 1024 func TestAgentSkillPromptBudget(t *testing.T) { skill, err := os.ReadFile(filepath.Join("..", "..", "skills", "pad", "SKILL.md")) @@ -27,6 +27,11 @@ func TestAgentSkillPromptBudget(t *testing.T) { if len(payload) > agentSkillPromptBudget { t.Fatalf("%s installed skill payload is %d bytes; budget is %d", agent, len(payload), agentSkillPromptBudget) } + for _, required := range []string{"pad bootstrap", "issue IDs", "convention_index", "pad agent guide"} { + if !contains(string(payload), required) { + t.Errorf("%s installed skill is missing core guidance %q", agent, required) + } + } }) } } diff --git a/internal/cli/agents.go b/internal/cli/agents.go index 3242c97bd..9c16321e1 100644 --- a/internal/cli/agents.go +++ b/internal/cli/agents.go @@ -82,6 +82,68 @@ var SupportedTools = []AgentTool{ }, } +// agentsSkillBody is the eager dispatcher installed for tools that share the +// .agents skill format. The full, canonical guide stays embedded in Pad and is +// available by topic through `pad agent guide`; repeating it here would charge +// Cursor and Codex for reference material on every skill load. +const agentsSkillBody = `# Pad — Talk to Your Project + +Use Pad when the user discusses project work: issues, tasks, plans, ideas, +progress, dependencies, conventions, roles, standups, or retrospectives. + +## Start every invocation + +Run ` + "`pad bootstrap --format json`" + ` before acting. It returns the workspace, +user, collections and schemas, always-on conventions, convention index, roles, +playbooks, dashboard, and recent activity in one call. + +- If ` + "`pad`" + ` is missing, ask the user to install it or add it to PATH. +- If bootstrap fails, run ` + "`pad agent guide context-loading`" + ` and follow that + section. Never initialize authentication blindly. +- Follow every body in ` + "`conventions`" + `. Before meaningful work, inspect + ` + "`convention_index`" + ` and load bodies for the matching trigger. +- If ` + "`needs_onboarding`" + ` is true, offer setup and wait for consent. +- If roles exist and none was chosen in this conversation, ask once; do not block + if the user declines. + +## Act safely + +- Use issue IDs such as ` + "`TASK-5`" + `, never slugs. +- Read an item before updating it. Send only changed fields and include a comment + with status changes. +- Read collection schemas instead of guessing field names or terminal statuses. +- Confirm each mutation from the returned object before saying it succeeded. +- Use active playbooks when intent or an invocation slug matches; never run draft + or deprecated playbooks unless the user explicitly asks. +- Prefer summary reads and bounded lists. Fetch full bodies only when needed. + +## Common commands + +` + "```bash" + ` +pad project dashboard --format json +pad item show TASK-5 --format json +pad item list [collection] --format json +pad item create "Title" [flags] +pad item update TASK-5 [flags] +pad item comment TASK-5 -m "Message" +pad playbook list --format json +pad playbook show --format markdown +` + "```" + ` + +Use ` + "`pad --help`" + ` for exact flags. + +## Load details only when needed + +` + "```bash" + ` +pad agent guide # list available topics +pad agent guide items # item commands and contracts +pad agent guide before-performing-work # convention routing +pad agent guide role-awareness # role behavior +pad agent guide multi-step-workflows # planning, ideation, retro, onboarding +pad agent guide all # explicit full reference +` + "```" + ` +` + // ResolveTool finds an AgentTool by name or alias. Returns nil if not found. func ResolveTool(nameOrAlias string) *AgentTool { lower := strings.ToLower(nameOrAlias) @@ -206,15 +268,15 @@ func FormatForTool(tool AgentTool, embeddedContent []byte) []byte { return embeddedContent case "agents": - // Codex/Cursor/Windsurf/OpenCode use name + description frontmatter - body := StripFrontmatter(embeddedContent) + // Codex/Cursor/Windsurf/OpenCode use a compact eager dispatcher. + // Detailed guidance remains available through `pad agent guide`. fm := `--- name: pad description: "Talk to your project. Natural-language project management — create items, check status, create plans, brainstorm ideas, and more." --- ` - return append([]byte(fm), body...) + return []byte(fm + agentsSkillBody) case "copilot": // GitHub Copilot uses applyTo frontmatter diff --git a/internal/cli/agents_test.go b/internal/cli/agents_test.go index 03ff86f56..56077dbd2 100644 --- a/internal/cli/agents_test.go +++ b/internal/cli/agents_test.go @@ -101,7 +101,7 @@ Body content here. } }) - t.Run("agents has name+description frontmatter", func(t *testing.T) { + t.Run("agents has compact dispatcher", func(t *testing.T) { tool := *ResolveTool("agents") got := string(FormatForTool(tool, embedded)) if got[:4] != "---\n" { @@ -116,8 +116,11 @@ Body content here. if contains(got, "allowed-tools") { t.Error("Agents format should NOT contain allowed-tools") } - if !contains(got, "# Pad Skill") { - t.Error("Agents format should contain body") + if !contains(got, "pad agent guide") { + t.Error("Agents format should point to on-demand guidance") + } + if contains(got, "Body content here") { + t.Error("Agents format should not eagerly copy the full embedded guide") } })