package store import ( "context" "fmt" "strings" "git.warky.dev/wdevs/amcs/internal/generatedmodels" ext "git.warky.dev/wdevs/amcs/internal/types" ) // Agent Skills func (db *DB) AddSkill(ctx context.Context, skill ext.AgentSkill) (ext.AgentSkill, error) { if skill.Tags == nil { skill.Tags = []string{} } if skill.LanguageTags == nil { skill.LanguageTags = []string{} } if skill.LibraryTags == nil { skill.LibraryTags = []string{} } if skill.FrameworkTags == nil { skill.FrameworkTags = []string{} } if skill.DomainTags == nil { skill.DomainTags = []string{} } row := db.pool.QueryRow(ctx, ` insert into agent_skills (name, description, content, tags, language_tags, library_tags, framework_tags, domain_tags) values ($1, $2, $3, $4, $5, $6, $7, $8) returning id, guid, created_at, updated_at `, skill.Name, skill.Description, skill.Content, skill.Tags, skill.LanguageTags, skill.LibraryTags, skill.FrameworkTags, skill.DomainTags) created := skill var model generatedmodels.ModelPublicAgentSkills if err := row.Scan(&model.ID, &model.GUID, &model.CreatedAt, &model.UpdatedAt); err != nil { return ext.AgentSkill{}, fmt.Errorf("insert agent skill: %w", err) } created.ID = model.ID.Int64() created.GUID = model.GUID.UUID() created.CreatedAt = model.CreatedAt.Time() created.UpdatedAt = model.UpdatedAt.Time() return created, nil } func (db *DB) RemoveSkill(ctx context.Context, id int64) error { tag, err := db.pool.Exec(ctx, `delete from agent_skills where id = $1`, id) if err != nil { return fmt.Errorf("delete agent skill: %w", err) } if tag.RowsAffected() == 0 { return fmt.Errorf("skill not found") } return nil } func (db *DB) ListSkills(ctx context.Context, tag string) ([]ext.AgentSkill, error) { q := `select id, name, description, content, tags::text[], language_tags::text[], library_tags::text[], framework_tags::text[], domain_tags::text[], created_at, updated_at from agent_skills` args := []any{} if t := strings.TrimSpace(tag); t != "" { args = append(args, t) q += fmt.Sprintf(" where $%d = any(tags) or $%d = any(language_tags) or $%d = any(library_tags) or $%d = any(framework_tags) or $%d = any(domain_tags)", len(args), len(args), len(args), len(args), len(args)) } q += " order by name" rows, err := db.pool.Query(ctx, q, args...) if err != nil { return nil, fmt.Errorf("list agent skills: %w", err) } defer rows.Close() var skills []ext.AgentSkill for rows.Next() { s, err := scanSkill(rows) if err != nil { return nil, fmt.Errorf("scan agent skill: %w", err) } skills = append(skills, s) } return skills, rows.Err() } const skillSelectCols = `id, name, description, content, tags::text[], language_tags::text[], library_tags::text[], framework_tags::text[], domain_tags::text[], created_at, updated_at` type skillScanner interface { Scan(dest ...any) error } func scanSkill(row skillScanner) (ext.AgentSkill, error) { var model generatedmodels.ModelPublicAgentSkills var tags, langTags, libTags, fwTags, domTags []string if err := row.Scan(&model.ID, &model.Name, &model.Description, &model.Content, &tags, &langTags, &libTags, &fwTags, &domTags, &model.CreatedAt, &model.UpdatedAt); err != nil { return ext.AgentSkill{}, err } nilToEmpty := func(s []string) []string { if s == nil { return []string{} } return s } return ext.AgentSkill{ ID: model.ID.Int64(), Name: model.Name.String(), Description: model.Description.String(), Content: model.Content.String(), Tags: nilToEmpty(tags), LanguageTags: nilToEmpty(langTags), LibraryTags: nilToEmpty(libTags), FrameworkTags: nilToEmpty(fwTags), DomainTags: nilToEmpty(domTags), CreatedAt: model.CreatedAt.Time(), UpdatedAt: model.UpdatedAt.Time(), }, nil } func (db *DB) GetSkill(ctx context.Context, id int64) (ext.AgentSkill, error) { row := db.pool.QueryRow(ctx, `select `+skillSelectCols+` from agent_skills where id = $1`, id) s, err := scanSkill(row) if err != nil { return ext.AgentSkill{}, fmt.Errorf("get agent skill: %w", err) } return s, nil } func (db *DB) GetSkillByName(ctx context.Context, name string) (ext.AgentSkill, error) { row := db.pool.QueryRow(ctx, `select `+skillSelectCols+` from agent_skills where name = $1`, name) s, err := scanSkill(row) if err != nil { return ext.AgentSkill{}, fmt.Errorf("get agent skill by name: %w", err) } return s, nil } func (db *DB) GetGuardrailByName(ctx context.Context, name string) (ext.AgentGuardrail, error) { row := db.pool.QueryRow(ctx, ` select id, name, description, content, severity, tags::text[], created_at, updated_at from agent_guardrails where name = $1 `, name) var model generatedmodels.ModelPublicAgentGuardrails var tags []string if err := row.Scan(&model.ID, &model.Name, &model.Description, &model.Content, &model.Severity, &tags, &model.CreatedAt, &model.UpdatedAt); err != nil { return ext.AgentGuardrail{}, fmt.Errorf("get agent guardrail by name: %w", err) } g := ext.AgentGuardrail{ ID: model.ID.Int64(), Name: model.Name.String(), Description: model.Description.String(), Content: model.Content.String(), Severity: model.Severity.String(), Tags: tags, CreatedAt: model.CreatedAt.Time(), UpdatedAt: model.UpdatedAt.Time(), } if g.Tags == nil { g.Tags = []string{} } return g, nil } // Agent Guardrails func (db *DB) AddGuardrail(ctx context.Context, g ext.AgentGuardrail) (ext.AgentGuardrail, error) { if g.Tags == nil { g.Tags = []string{} } if g.Severity == "" { g.Severity = "medium" } row := db.pool.QueryRow(ctx, ` insert into agent_guardrails (name, description, content, severity, tags) values ($1, $2, $3, $4, $5) returning id, guid, created_at, updated_at `, g.Name, g.Description, g.Content, g.Severity, g.Tags) created := g var model generatedmodels.ModelPublicAgentGuardrails if err := row.Scan(&model.ID, &model.GUID, &model.CreatedAt, &model.UpdatedAt); err != nil { return ext.AgentGuardrail{}, fmt.Errorf("insert agent guardrail: %w", err) } created.ID = model.ID.Int64() created.GUID = model.GUID.UUID() created.CreatedAt = model.CreatedAt.Time() created.UpdatedAt = model.UpdatedAt.Time() return created, nil } func (db *DB) RemoveGuardrail(ctx context.Context, id int64) error { tag, err := db.pool.Exec(ctx, `delete from agent_guardrails where id = $1`, id) if err != nil { return fmt.Errorf("delete agent guardrail: %w", err) } if tag.RowsAffected() == 0 { return fmt.Errorf("guardrail not found") } return nil } func (db *DB) ListGuardrails(ctx context.Context, tag, severity string) ([]ext.AgentGuardrail, error) { args := []any{} conditions := []string{} if t := strings.TrimSpace(tag); t != "" { args = append(args, t) conditions = append(conditions, fmt.Sprintf("$%d = any(tags)", len(args))) } if s := strings.TrimSpace(severity); s != "" { args = append(args, s) conditions = append(conditions, fmt.Sprintf("severity = $%d", len(args))) } q := `select id, name, description, content, severity, tags::text[], created_at, updated_at from agent_guardrails` if len(conditions) > 0 { q += " where " + strings.Join(conditions, " and ") } q += " order by name" rows, err := db.pool.Query(ctx, q, args...) if err != nil { return nil, fmt.Errorf("list agent guardrails: %w", err) } defer rows.Close() var guardrails []ext.AgentGuardrail for rows.Next() { var model generatedmodels.ModelPublicAgentGuardrails var tags []string if err := rows.Scan(&model.ID, &model.Name, &model.Description, &model.Content, &model.Severity, &tags, &model.CreatedAt, &model.UpdatedAt); err != nil { return nil, fmt.Errorf("scan agent guardrail: %w", err) } g := ext.AgentGuardrail{ ID: model.ID.Int64(), Name: model.Name.String(), Description: model.Description.String(), Content: model.Content.String(), Severity: model.Severity.String(), Tags: tags, CreatedAt: model.CreatedAt.Time(), UpdatedAt: model.UpdatedAt.Time(), } if g.Tags == nil { g.Tags = []string{} } guardrails = append(guardrails, g) } return guardrails, rows.Err() } func (db *DB) GetGuardrail(ctx context.Context, id int64) (ext.AgentGuardrail, error) { row := db.pool.QueryRow(ctx, ` select id, name, description, content, severity, tags::text[], created_at, updated_at from agent_guardrails where id = $1 `, id) var model generatedmodels.ModelPublicAgentGuardrails var tags []string if err := row.Scan(&model.ID, &model.Name, &model.Description, &model.Content, &model.Severity, &tags, &model.CreatedAt, &model.UpdatedAt); err != nil { return ext.AgentGuardrail{}, fmt.Errorf("get agent guardrail: %w", err) } g := ext.AgentGuardrail{ ID: model.ID.Int64(), Name: model.Name.String(), Description: model.Description.String(), Content: model.Content.String(), Severity: model.Severity.String(), Tags: tags, CreatedAt: model.CreatedAt.Time(), UpdatedAt: model.UpdatedAt.Time(), } if g.Tags == nil { g.Tags = []string{} } return g, nil } // Project Skills func (db *DB) AddProjectSkill(ctx context.Context, projectID, skillID int64) error { _, err := db.pool.Exec(ctx, ` insert into project_skills (project_id, skill_id) values ($1, $2) on conflict do nothing `, projectID, skillID) if err != nil { return fmt.Errorf("add project skill: %w", err) } return nil } func (db *DB) RemoveProjectSkill(ctx context.Context, projectID, skillID int64) error { tag, err := db.pool.Exec(ctx, ` delete from project_skills where project_id = $1 and skill_id = $2 `, projectID, skillID) if err != nil { return fmt.Errorf("remove project skill: %w", err) } if tag.RowsAffected() == 0 { return fmt.Errorf("project skill link not found") } return nil } func (db *DB) ListProjectSkills(ctx context.Context, projectID int64) ([]ext.AgentSkill, error) { rows, err := db.pool.Query(ctx, ` select s.`+skillSelectCols+` from agent_skills s join project_skills ps on ps.skill_id = s.id where ps.project_id = $1 order by s.name `, projectID) if err != nil { return nil, fmt.Errorf("list project skills: %w", err) } defer rows.Close() var skills []ext.AgentSkill for rows.Next() { s, err := scanSkill(rows) if err != nil { return nil, fmt.Errorf("scan project skill: %w", err) } skills = append(skills, s) } return skills, rows.Err() } // Project Guardrails func (db *DB) AddProjectGuardrail(ctx context.Context, projectID, guardrailID int64) error { _, err := db.pool.Exec(ctx, ` insert into project_guardrails (project_id, guardrail_id) values ($1, $2) on conflict do nothing `, projectID, guardrailID) if err != nil { return fmt.Errorf("add project guardrail: %w", err) } return nil } func (db *DB) RemoveProjectGuardrail(ctx context.Context, projectID, guardrailID int64) error { tag, err := db.pool.Exec(ctx, ` delete from project_guardrails where project_id = $1 and guardrail_id = $2 `, projectID, guardrailID) if err != nil { return fmt.Errorf("remove project guardrail: %w", err) } if tag.RowsAffected() == 0 { return fmt.Errorf("project guardrail link not found") } return nil } func (db *DB) ListProjectGuardrails(ctx context.Context, projectID int64) ([]ext.AgentGuardrail, error) { rows, err := db.pool.Query(ctx, ` select g.id, g.name, g.description, g.content, g.severity, g.tags::text[], g.created_at, g.updated_at from agent_guardrails g join project_guardrails pg on pg.guardrail_id = g.id where pg.project_id = $1 order by g.name `, projectID) if err != nil { return nil, fmt.Errorf("list project guardrails: %w", err) } defer rows.Close() var guardrails []ext.AgentGuardrail for rows.Next() { var model generatedmodels.ModelPublicAgentGuardrails var tags []string if err := rows.Scan(&model.ID, &model.Name, &model.Description, &model.Content, &model.Severity, &tags, &model.CreatedAt, &model.UpdatedAt); err != nil { return nil, fmt.Errorf("scan project guardrail: %w", err) } g := ext.AgentGuardrail{ ID: model.ID.Int64(), Name: model.Name.String(), Description: model.Description.String(), Content: model.Content.String(), Severity: model.Severity.String(), Tags: tags, CreatedAt: model.CreatedAt.Time(), UpdatedAt: model.UpdatedAt.Time(), } if g.Tags == nil { g.Tags = []string{} } guardrails = append(guardrails, g) } return guardrails, rows.Err() }