Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions go/api/config/crd/bases/kagent.dev_agents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10216,8 +10216,10 @@ spec:
skills from.
properties:
name:
description: Name for the skill directory under /skills.
Defaults to the repo name.
description: |-
Name for the skill directory under /skills. If omitted, defaults to the last
segment of Path when Path is set; otherwise defaults to the repo name (last
URL path segment, without .git).
type: string
path:
description: Subdirectory within the repo to use as the
Expand Down
6 changes: 4 additions & 2 deletions go/api/config/crd/bases/kagent.dev_sandboxagents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7866,8 +7866,10 @@ spec:
skills from.
properties:
name:
description: Name for the skill directory under /skills.
Defaults to the repo name.
description: |-
Name for the skill directory under /skills. If omitted, defaults to the last
segment of Path when Path is set; otherwise defaults to the repo name (last
URL path segment, without .git).
type: string
path:
description: Subdirectory within the repo to use as the
Expand Down
4 changes: 3 additions & 1 deletion go/api/v1alpha2/agent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ type GitRepo struct {
// +optional
Path string `json:"path,omitempty"`

// Name for the skill directory under /skills. Defaults to the repo name.
// Name for the skill directory under /skills. If omitted, defaults to the last
// segment of Path when Path is set; otherwise defaults to the repo name (last
// URL path segment, without .git).
// +optional
Name string `json:"name,omitempty"`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1080,12 +1080,16 @@ func isCommitSHA(ref string) bool {
}

// gitSkillName returns the directory name for a git skill ref.
// If Name is set, it is used; otherwise the last path segment of the repo URL
// (with any .git suffix stripped) is used.
// Query parameters and fragments are stripped before extracting the base name.
// If Name is set, it is used. Otherwise, if Path (in-repo directory) is set, the
// last path segment of Path is used. If Path is empty, the last path segment of
// the repo URL (with any .git suffix stripped) is used.
// Query parameters and fragments are stripped before extracting the base name from the URL.
func gitSkillName(ref v1alpha2.GitRepo) string {
if ref.Name != "" {
return ref.Name
if n := strings.TrimSpace(ref.Name); n != "" {
return n
}
if p := strings.Trim(strings.TrimSpace(ref.Path), "/"); p != "" {
return path.Base(p)
}
// Parse the URL to strip query params and fragments
u := ref.URL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,36 @@ func Test_gitSkillName(t *testing.T) {
ref: v1alpha2.GitRepo{URL: "git@github.com:org/repo.git"},
want: "repo",
},
{
name: "path last segment when name empty (monorepo)",
ref: v1alpha2.GitRepo{
URL: "https://github.com/reponame/myskills.git",
Path: "someskills/skill1",
},
want: "skill1",
},
{
name: "path with leading and trailing slash",
ref: v1alpha2.GitRepo{
URL: "https://github.com/reponame/myskills.git",
Path: "/someskills/skill1/",
},
want: "skill1",
},
{
name: "explicit name still wins over path",
ref: v1alpha2.GitRepo{
URL: "https://github.com/reponame/myskills.git",
Path: "someskills/skill1",
Name: "custom",
},
want: "custom",
},
{
name: "no path uses repo name",
ref: v1alpha2.GitRepo{URL: "https://github.com/reponame/myskills"},
want: "myskills",
},
}

for _, tt := range tests {
Expand Down
6 changes: 4 additions & 2 deletions helm/kagent-crds/templates/kagent.dev_agents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10216,8 +10216,10 @@ spec:
skills from.
properties:
name:
description: Name for the skill directory under /skills.
Defaults to the repo name.
description: |-
Name for the skill directory under /skills. If omitted, defaults to the last
segment of Path when Path is set; otherwise defaults to the repo name (last
URL path segment, without .git).
type: string
path:
description: Subdirectory within the repo to use as the
Expand Down
6 changes: 4 additions & 2 deletions helm/kagent-crds/templates/kagent.dev_sandboxagents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7866,8 +7866,10 @@ spec:
skills from.
properties:
name:
description: Name for the skill directory under /skills.
Defaults to the repo name.
description: |-
Name for the skill directory under /skills. If omitted, defaults to the last
segment of Path when Path is set; otherwise defaults to the repo name (last
URL path segment, without .git).
type: string
path:
description: Subdirectory within the repo to use as the
Expand Down
56 changes: 46 additions & 10 deletions ui/src/app/actions/agents.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
"use server";

import { AgentSpec, BaseResponse, DeclarativeAgentSpec, PromptSource, SandboxAgent } from "@/types";
import { Agent, AgentResponse, Tool } from "@/types";
import {
Agent,
AgentResponse,
AgentSpec,
BaseResponse,
DeclarativeAgentSpec,
PromptSource,
SandboxAgent,
SkillForAgent,
Tool,
} from "@/types";
import { revalidatePath } from "next/cache";
import { fetchApi, createErrorResponse } from "./utils";
import { AgentFormData } from "@/components/AgentsProvider";
import { isMcpTool } from "@/lib/toolUtils";
import { k8sRefUtils } from "@/lib/k8sUtils";
import { formRowsToGitRepos, type GitSkillFormRow } from "@/lib/agentSkillsForm";

function attachPromptTemplateToDeclarative(decl: DeclarativeAgentSpec, agentFormData: AgentFormData) {
if (!agentFormData.promptSources?.some((s) => s.name.trim())) {
Expand All @@ -31,6 +41,34 @@ function attachPromptTemplateToDeclarative(decl: DeclarativeAgentSpec, agentForm
}
}

function buildSkillsForAgentSpec(agentFormData: AgentFormData): SkillForAgent | undefined {
const refs = (agentFormData.skillRefs || []).map((r) => r.trim()).filter(Boolean);
const rows: GitSkillFormRow[] = (agentFormData.skillGitRepos || []).map((g) => ({
url: g.url ?? "",
ref: g.ref ?? "",
path: g.path ?? "",
name: g.name ?? "",
}));
const gitRefs = formRowsToGitRepos(rows);

if (refs.length === 0 && gitRefs.length === 0) {
return undefined;
}

const skills: SkillForAgent = {};
if (refs.length > 0) {
skills.refs = refs;
}
if (gitRefs.length > 0) {
skills.gitRefs = gitRefs;
const secretName = agentFormData.skillsGitAuthSecretName?.trim();
if (secretName) {
skills.gitAuthSecretRef = { name: secretName };
}
}
return skills;
}

/**
* Converts AgentFormData to Agent format
* @param agentFormData The form data to convert
Expand Down Expand Up @@ -138,10 +176,9 @@ function fromAgentFormDataToAgent(agentFormData: AgentFormData): Agent {
tools: convertTools(agentFormData.tools || []),
};

if (agentFormData.skillRefs && agentFormData.skillRefs.length > 0) {
base.spec!.skills = {
refs: agentFormData.skillRefs,
};
const skills = buildSkillsForAgentSpec(agentFormData);
if (skills) {
base.spec!.skills = skills;
}

if (agentFormData.memory?.modelConfig) {
Expand Down Expand Up @@ -337,10 +374,9 @@ function fromAgentFormDataToSandboxAgent(agentFormData: AgentFormData): SandboxA
description: agentFormData.description,
};

if (agentFormData.skillRefs && agentFormData.skillRefs.length > 0) {
spec.skills = {
refs: agentFormData.skillRefs,
};
const skills = buildSkillsForAgentSpec(agentFormData);
if (skills) {
spec.skills = skills;
}

return {
Expand Down
Loading
Loading