fix(skills): make built-in skill bundle launch-ready

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Naiyuan Qing
2026-06-02 13:43:21 +08:00
parent 47d716a939
commit fa7437dff0
6 changed files with 106 additions and 282 deletions

View File

@@ -71,9 +71,9 @@ behave as described, check the source rather than guessing:
- `server/internal/util/mention.go:16` — the mention regex. The id must be a
hex UUID (or the literal `all`); a name silently fails to parse.
- `server/internal/handler/comment.go:884``enqueueMentionedAgentTasks`:
- `server/internal/handler/comment.go:948``enqueueMentionedAgentTasks`:
how `@agent` enqueues a run and `@squad` enqueues the leader, plus the
guards (already-pending dedup, archived, private) that make a valid mention
no-op.
- `server/internal/handler/comment.go:768``@all` is a broadcast that
- `server/internal/handler/comment.go:966``@all` is a broadcast that
suppresses the assignee's auto-trigger.

View File

@@ -1,136 +0,0 @@
---
name: multica-skill-authoring
description: Use when a user asks to create, edit, or maintain a Multica workspace skill. Teaches the current CLI workflow for SKILL.md content, metadata, supporting files, and verification without treating one-off notes as durable skills.
user-invocable: false
allowed-tools: Bash(multica *)
---
# Authoring Multica skills
Use this skill when the user asks to create, update, or maintain a Multica
workspace skill. This is different from finding an existing skill
(`multica-skill-discovery`) or importing a known URL (`multica-skill-importing`).
## The invariant
A Multica skill is durable workspace behavior. It should capture a reusable method,
platform rule, or tool workflow that future agents should apply on demand.
Do not create a skill for one-run progress, temporary TODOs, PR numbers, issue
numbers, session summaries, transient decisions, credentials, API keys, or other
secrets. Those belong in issue comments, issue metadata, project docs, or not at
all.
## Authoring standard
Every `SKILL.md` must have clear frontmatter and body guidance:
```md
---
name: short-slug
description: Use when ...
---
# Skill title
Use this skill when ...
```
The `description` is the trigger contract. Write it as a concise "Use when ..."
sentence so the agent can decide whether to load the skill before seeing the
body.
The body should cover:
- when to use the skill and when not to use it;
- the exact commands or APIs to run;
- verification steps that prove the work succeeded;
- failure modes and recovery rules;
- source of truth links to code, API, CLI, docs, or product behavior.
Keep the main body focused. Put large examples, templates, or reference material
in supporting files instead of bloating `SKILL.md`.
## Current create flow
Create the workspace skill from explicit current CLI fields:
```bash
multica skill create --name <name> --description <description> --content <path-or-text> --output json
```
Read the JSON response and keep the returned `id`. Do not claim the skill exists
until the create command succeeds.
If the content lives in a local `SKILL.md`, read the file first and pass its full
content as the `--content` value. The current CLI does not have `--content-file`,
so large content may require a wrapper script or shell-safe command construction.
## Current update flow
Update an existing skill with the skill id or supported identifier:
```bash
multica skill update <skill-id> --content <path-or-text> --output json
```
Use `--name`, `--description`, or `--config` only when those fields actually need
to change. Avoid rewriting unrelated fields.
After update, verify by reading it back:
```bash
multica skill get <skill-id> --output json
```
Compare the returned `name`, `description`, `content`, `config`, and `files`
against what you intended.
## Supporting files
Use supporting files for reusable references, templates, scripts, and assets that
are too large or too specific for the main `SKILL.md`.
Current workaround for supporting files:
```bash
multica skill files upsert <skill-id> --path <relative-path> --content <path-or-text>
multica skill files delete <skill-id> <file-id>
multica skill get <skill-id> --output json
```
Recommended relative paths are stable, portable paths such as:
```text
references/<topic>.md
templates/<name>.md
scripts/<name>.sh
assets/<name>.<ext>
```
Do not store secrets in supporting files. Do not store one-off PR numbers, issue
numbers, run timestamps, or temporary session state. If the fact will be stale in
a week, it is not skill content.
## Quality gate
Before creating or updating a skill, check:
1. Is this reusable across future runs?
2. Is the trigger condition clear from the description alone?
3. Does it cite a real source of truth instead of relying on vibes?
4. Does it include verification steps?
5. Does it avoid secrets, temporary progress, PR numbers, issue numbers, and stale
session notes?
6. Are large examples moved into supporting files?
If the answer is no, do not create the skill yet. Write an issue comment or a doc
instead.
## Source of truth
- `server/cmd/multica/cmd_skill.go` implements `multica skill create`,
`multica skill update`, `multica skill get`, and `multica skill files upsert`.
- `server/internal/handler/skill.go` implements the workspace skill API.
- `docs/agent-skills/skill-necessity.md` records why built-in platform skills
exist and how to evaluate whether they work.

View File

@@ -1,6 +1,6 @@
---
name: multica-skill-discovery
description: Use when the user describes a capability but does not know which skill URL to import. Teaches how to search for candidate skills, verify fit against the user's need, choose an importable URL, and then install through Multica's import path.
description: Use when the user describes a capability but does not know which skill URL to import. Teaches metadata-only candidate search, fit selection from available search fields, and then installation through Multica's import path.
user-invocable: false
allowed-tools: Bash(multica *)
---
@@ -8,8 +8,8 @@ allowed-tools: Bash(multica *)
# Discovering skills before import
Use this skill when the user wants a capability but does not provide a specific
skill URL. Your job is to find candidates, verify the best fit, and then hand off
to the Multica import path.
skill URL. Your job is to find candidates from search metadata, select the best
fit, and then hand off to the Multica import path.
discovery is not installation. The final installation step is still:
@@ -58,21 +58,33 @@ say that clearly instead of inventing a URL.
Do not stop at the first result. Search output is a candidate list, not a product
decision.
## Verify before import
## Select using metadata-only before import
You must verify before import. Compare candidates with the user's actual need.
Use these signals:
Selection is metadata-only before import. Current search does not expose a remote
content preview. Compare candidates with the user's actual need using only the
fields available in the search result:
- content match in `SKILL.md`, not only the title;
- `name`;
- `url`;
- `source`;
- `repo`;
- `install_count`;
- `github_stars` / `repo` when present;
- `github_stars`;
- `description`;
- source reputation and owner/repo credibility;
- whether the skill is general enough or too project-specific;
- whether the URL is importable by `multica skill import`;
- whether the skill duplicates an existing workspace skill.
- whether the candidate appears too project-specific from its metadata.
If a candidate looks good from the search result but the `SKILL.md` does not
match the user's intent, reject it and explain why.
Do not claim you inspected remote skill content during search. The limitation is
explicit: full content verification happens after import by reading the imported
workspace skill, for example:
```bash
multica skill get <skill-id> --output json
```
If metadata is too weak to choose safely, say that and ask for a URL or a more
specific requirement instead of importing a weak match.
## Import after choosing
@@ -85,15 +97,15 @@ multica skill import --url <selected-url> --output json
Use `multica-skill-importing` for duplicate handling, returned fields, and agent
binding.
Do not use `npx skills add` as the final step; this is not `npx skills add`. That installs outside Multica and
will not create a managed workspace skill.
Do not use `npx skills add` as the final step; this is not `npx skills add`. That
installs outside Multica and will not create a managed workspace skill.
## Output to the user
Report the decision, not the whole search dump:
- selected skill name and URL;
- why it matched the user's request;
- why the metadata matched the user's request;
- any strong rejected alternatives if relevant;
- import result: `id`, `name`, `config.origin`, files count;
- whether it still needs to be bound to an agent.
@@ -106,21 +118,23 @@ something.
Incorrect:
```text
I found the first result on skills.sh and installed it with npx skills add.
I opened the first remote skill file during search, verified its full content,
and installed it with npx skills add.
```
Correct:
```text
I searched for `frontend design`, compared the top candidates by install count,
source reputation, and SKILL.md content, selected the matching skills.sh URL, and
imported it with `multica skill import --url <selected-url> --output json`.
github_stars, repo, source reputation, URL, and description, selected the matching
skills.sh URL, and imported it with `multica skill import --url <selected-url> --output json`.
```
## Source of truth
- `multica skill search <query> --output json` / `GET /api/skills/search?q=...`
are the supported structured discovery surfaces.
- Search returns candidate metadata only; it is not a remote content preview.
- `multica-skill-importing` defines the final Multica workspace import path.
- `POST /api/skills/import` and `multica skill import --url` are the supported
Multica installation surfaces.

View File

@@ -1,6 +1,6 @@
---
name: multica-skill-importing
description: Use when a user provides a skill URL or asks to import/install a skill into Multica. Teaches the Multica workspace import API/CLI path, returned data, duplicate handling, and agent binding; never treats external local installers as the final Multica install.
description: Use when a user provides a skill URL or asks to import/install a skill into Multica. Teaches the Multica workspace import API/CLI path, returned data, duplicate handling, and safe agent binding; never treats external local installers as the final Multica install.
user-invocable: false
allowed-tools: Bash(multica *)
---
@@ -64,14 +64,22 @@ multica skill import --url <url> --output json
- `files` / files count
- `created_at` / `updated_at`
3. If the user wants an agent to use the skill, bind the returned skill id:
3. If the user wants an agent to use the skill, bind the returned skill id safely.
`multica agent skills set` is replace-all: it replaces every current assignment
with the ids you pass. Never use `set` with only the new id unless the user
explicitly wants to remove all other skills.
Safe read-modify-write binding:
```bash
multica agent skills set <agent-id> --skill-ids <skill-id>
multica agent skills list <agent-id> --output json
# merge the new skill id with the existing ids
multica agent skills set <agent-id> --skill-ids <existing-id-1>,<existing-id-2>,<skill-id> --output json
multica agent skills list <agent-id> --output json
```
Do not claim the skill is available to an agent until you have bound it or
verified that it is already bound.
After the final `list`, verify the target skill id is present before claiming the
skill is available to that agent.
## Duplicate imports
@@ -119,7 +127,15 @@ npx skills add https://skills.sh/owner/repo/skill
That bypasses Multica. The skill may exist locally, but Multica cannot manage it
as a workspace skill.
Correct:
Incorrect agent binding:
```bash
multica agent skills set <agent-id> --skill-ids <new-skill-id>
```
That replaces all existing assignments with just the new skill.
Correct import:
```bash
multica skill import --url https://skills.sh/owner/repo/skill --output json
@@ -128,12 +144,19 @@ multica skill import --url https://skills.sh/owner/repo/skill --output json
Correct follow-up when the skill must be available to an agent:
```bash
multica agent skills set <agent-id> --skill-ids <skill-id>
multica agent skills list <agent-id> --output json
# merge the new skill id with the existing ids
multica agent skills set <agent-id> --skill-ids <existing-id-1>,<existing-id-2>,<skill-id> --output json
multica agent skills list <agent-id> --output json
```
## Source of truth
- `server/internal/handler/skill.go` implements `ImportSkill` for `/api/skills/import`.
- `server/cmd/multica/cmd_skill.go` implements `multica skill import --url`.
- `server/cmd/multica/cmd_agent.go` documents `agent skills set` as replacing
all current assignments.
- `server/internal/handler/skill.go` implements `SetAgentSkills` by clearing
then re-adding assignments.
- The import response is a workspace `SkillResponse`, so agents can read returned
fields instead of guessing whether the import succeeded.

View File

@@ -1,72 +1,19 @@
---
name: multica-working-on-issues
description: Use when working on a Multica issue or issue comment — read the triggering context, perform and verify the work, report results, link PRs to issues, use metadata/status carefully, and create sub-issues without accidentally starting the wrong work.
description: Use when working on a Multica issue after the runtime has provided the trigger context link PRs to issues, use metadata/status carefully, create sub-issues without accidentally starting work, and understand platform side effects.
user-invocable: false
allowed-tools: Bash(multica *), Bash(git *), Bash(gh *)
---
# Working on Multica issues
This skill covers the Multica issue execution loop: understand the trigger, do
real work, verify it, and leave the issue in a state the next person or agent can
trust.
This skill covers product contracts that the runtime brief does not fully encode:
PR linking, close intent, metadata semantics, status side effects, and sub-issue
creation behavior.
Do not use this skill to learn how to build mention links. For mentions, load
`multica-mentioning`.
## The invariant
If you perform actual work for an issue, the result must be visible on the issue.
Terminal output, local files, and agent logs are not delivery. Post a concise
issue comment after the work is done.
If the triggering comment is only an acknowledgement, thanks, or sign-off and
you did no work, stay silent. Do not post "no action needed".
## Start from the trigger, not from memory
1. Read the issue:
```bash
multica issue get <issue-id> --output json
```
2. Read pinned metadata:
```bash
multica issue metadata list <issue-id> --output json
```
3. If this run came from a comment, read that conversation thread first:
```bash
multica issue comment list <issue-id> --thread <trigger-comment-id> --tail 30 --output json
```
4. Use recent threads only when the current thread does not contain enough
context:
```bash
multica issue comment list <issue-id> --recent 20 --output json
```
Do not answer an old comment just because it appears in the history. Focus on
the trigger that started this run.
## Reply only after doing the requested work
A result comment should state outcome and evidence, not narrate every command.
Good comments answer: what changed, what was verified, and what remains blocked.
Use the trigger comment as the parent when replying to a triggered comment:
```bash
multica issue comment add <issue-id> --parent <trigger-comment-id> --content "..."
```
For multiline comments, use `--content-stdin` or `--content-file` so quotes and
code blocks survive intact.
## PR linking and close intent
Multica links GitHub PRs to issues when the PR title, body, or branch contains a
@@ -115,8 +62,8 @@ that the issue is linked.
## Metadata is a high-signal scratchpad
Read metadata on entry. Write it only when the value will likely be re-read by a
future run on the same issue.
Read metadata on entry when the runtime asks for issue context. Write it only
when the value will likely be re-read by a future run on the same issue.
Usually valid keys:
@@ -187,19 +134,6 @@ need them, inspect the attachment CLI help and use the authenticated CLI path.
## Incorrect → correct
Incorrect:
```text
I fixed it locally.
```
Correct:
```text
Fixed the login redirect and opened PR: https://github.com/org/repo/pull/123
Verified with `go test ./internal/service -run TestLoginRedirect`.
```
Incorrect PR title:
```text

View File

@@ -171,10 +171,6 @@ func TestWorkingOnIssuesSkillCoversIssueLoopContracts(t *testing.T) {
}
mustContain := []string{
"multica issue get <issue-id> --output json",
"multica issue metadata list <issue-id> --output json",
"multica issue comment list <issue-id> --thread <trigger-comment-id>",
"multica issue comment add <issue-id> --parent <trigger-comment-id>",
"multica issue pull-requests <issue-id> --output json",
"Closes MUL-2759",
"--status backlog",
@@ -188,6 +184,19 @@ func TestWorkingOnIssuesSkillCoversIssueLoopContracts(t *testing.T) {
t.Errorf("working-on-issues skill missing %q", want)
}
}
mustNotContain := []string{
"Start from the trigger, not from memory",
"multica issue get <issue-id> --output json",
"multica issue metadata list <issue-id> --output json",
"multica issue comment list <issue-id> --thread <trigger-comment-id>",
"multica issue comment add <issue-id> --parent <trigger-comment-id>",
}
for _, forbidden := range mustNotContain {
if strings.Contains(body, forbidden) {
t.Errorf("working-on-issues skill duplicates runtime prompt contract %q", forbidden)
}
}
}
func TestSkillImportingSkillCoversWorkspaceImportContracts(t *testing.T) {
@@ -218,16 +227,29 @@ func TestSkillImportingSkillCoversWorkspaceImportContracts(t *testing.T) {
"legacy",
"multica skill list --output json",
"npx skills add",
"multica agent skills set <agent-id> --skill-ids <skill-id>",
"multica agent skills list <agent-id> --output json",
"merge the new skill id with the existing ids",
"multica agent skills set <agent-id> --skill-ids <existing-id-1>,<existing-id-2>,<skill-id> --output json",
"replace-all",
}
for _, want := range mustContain {
if !strings.Contains(body, want) {
t.Errorf("skill-importing skill missing %q", want)
}
}
mustNotContain := []string{
"multica agent skills set <agent-id> --skill-ids <skill-id>",
"multica agent skills add",
}
for _, forbidden := range mustNotContain {
if strings.Contains(body, forbidden) {
t.Errorf("skill-importing skill should not teach unavailable or destructive binding command %q", forbidden)
}
}
}
func TestSkillDiscoverySkillCoversFindVerifyImportContracts(t *testing.T) {
func TestSkillDiscoverySkillCoversMetadataOnlyPreImportContracts(t *testing.T) {
skill, ok := findSkill(t, "multica-skill-discovery")
if !ok {
return
@@ -246,12 +268,13 @@ func TestSkillDiscoverySkillCoversFindVerifyImportContracts(t *testing.T) {
"GET /api/skills/search?q=...",
"clawhub.ai",
"upstream_unavailable",
"verify before import",
"metadata-only before import",
"full content verification happens after import",
"install_count",
"github_stars",
"repo",
"source reputation",
"SKILL.md",
"description",
"multica skill import --url <selected-url> --output json",
"not `npx skills add`",
"discovery is not installation",
@@ -261,50 +284,16 @@ func TestSkillDiscoverySkillCoversFindVerifyImportContracts(t *testing.T) {
t.Errorf("skill-discovery skill missing %q", want)
}
}
}
func TestSkillAuthoringSkillCoversCreateUpdateMaintainContracts(t *testing.T) {
skill, ok := findSkill(t, "multica-skill-authoring")
if !ok {
return
}
fm, body, _ := splitFrontmatter(skill.Content)
if got := strings.TrimSpace(fm["user-invocable"]); got != "false" {
t.Errorf("user-invocable = %q, want false (skill authoring guidance triggers from context)", got)
}
if got := strings.TrimSpace(fm["allowed-tools"]); !strings.Contains(got, "Bash(multica *)") {
t.Errorf("allowed-tools = %q, want access to the Multica CLI", got)
}
mustContain := []string{
"multica skill create --name <name> --description <description> --content <path-or-text> --output json",
"multica skill update <skill-id> --content <path-or-text> --output json",
"multica skill files upsert <skill-id> --path <relative-path> --content <path-or-text>",
"multica skill files delete <skill-id> <file-id>",
"multica skill get <skill-id> --output json",
"SKILL.md",
"frontmatter",
"supporting files",
"secrets",
"PR numbers",
"current CLI",
"source of truth",
"verify by reading it back",
}
for _, want := range mustContain {
if !strings.Contains(body, want) {
t.Errorf("skill-authoring skill missing %q", want)
}
}
mustNotContain := []string{
"--bundle-dir",
"local bundle",
"content match in `SKILL.md`, not only the title",
"If a candidate looks good from the search result but the `SKILL.md` does not",
"source reputation, and SKILL.md content",
"verify before import",
}
for _, forbidden := range mustNotContain {
if strings.Contains(body, forbidden) {
t.Errorf("skill-authoring skill should not mention unsupported local import workflow %q", forbidden)
t.Errorf("skill-discovery skill should not imply remote content preview %q", forbidden)
}
}
}