package store import ( "context" "fmt" "strings" "github.com/google/uuid" 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{} } row := db.pool.QueryRow(ctx, ` insert into agent_skills (name, description, content, tags) values ($1, $2, $3, $4) returning id, created_at, updated_at `, skill.Name, skill.Description, skill.Content, skill.Tags) created := skill if err := row.Scan(&created.ID, &created.CreatedAt, &created.UpdatedAt); err != nil { return ext.AgentSkill{}, fmt.Errorf("insert agent skill: %w", err) } return created, nil } func (db *DB) RemoveSkill(ctx context.Context, id uuid.UUID) 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, 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)", 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() { var s ext.AgentSkill var desc *string if err := rows.Scan(&s.ID, &s.Name, &desc, &s.Content, &s.Tags, &s.CreatedAt, &s.UpdatedAt); err != nil { return nil, fmt.Errorf("scan agent skill: %w", err) } s.Description = strVal(desc) if s.Tags == nil { s.Tags = []string{} } skills = append(skills, s) } return skills, rows.Err() } func (db *DB) GetSkill(ctx context.Context, id uuid.UUID) (ext.AgentSkill, error) { row := db.pool.QueryRow(ctx, ` select id, name, description, content, tags, created_at, updated_at from agent_skills where id = $1 `, id) var s ext.AgentSkill var desc *string if err := row.Scan(&s.ID, &s.Name, &desc, &s.Content, &s.Tags, &s.CreatedAt, &s.UpdatedAt); err != nil { return ext.AgentSkill{}, fmt.Errorf("get agent skill: %w", err) } s.Description = strVal(desc) if s.Tags == nil { s.Tags = []string{} } return s, 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, created_at, updated_at `, g.Name, g.Description, g.Content, g.Severity, g.Tags) created := g if err := row.Scan(&created.ID, &created.CreatedAt, &created.UpdatedAt); err != nil { return ext.AgentGuardrail{}, fmt.Errorf("insert agent guardrail: %w", err) } return created, nil } func (db *DB) RemoveGuardrail(ctx context.Context, id uuid.UUID) 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, 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 g ext.AgentGuardrail var desc *string if err := rows.Scan(&g.ID, &g.Name, &desc, &g.Content, &g.Severity, &g.Tags, &g.CreatedAt, &g.UpdatedAt); err != nil { return nil, fmt.Errorf("scan agent guardrail: %w", err) } g.Description = strVal(desc) if g.Tags == nil { g.Tags = []string{} } guardrails = append(guardrails, g) } return guardrails, rows.Err() } func (db *DB) GetGuardrail(ctx context.Context, id uuid.UUID) (ext.AgentGuardrail, error) { row := db.pool.QueryRow(ctx, ` select id, name, description, content, severity, tags, created_at, updated_at from agent_guardrails where id = $1 `, id) var g ext.AgentGuardrail var desc *string if err := row.Scan(&g.ID, &g.Name, &desc, &g.Content, &g.Severity, &g.Tags, &g.CreatedAt, &g.UpdatedAt); err != nil { return ext.AgentGuardrail{}, fmt.Errorf("get agent guardrail: %w", err) } g.Description = strVal(desc) if g.Tags == nil { g.Tags = []string{} } return g, nil } // Project Skills func (db *DB) AddProjectSkill(ctx context.Context, projectID, skillID uuid.UUID) 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 uuid.UUID) 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 uuid.UUID) ([]ext.AgentSkill, error) { rows, err := db.pool.Query(ctx, ` select s.id, s.name, s.description, s.content, s.tags, s.created_at, s.updated_at 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() { var s ext.AgentSkill var desc *string if err := rows.Scan(&s.ID, &s.Name, &desc, &s.Content, &s.Tags, &s.CreatedAt, &s.UpdatedAt); err != nil { return nil, fmt.Errorf("scan project skill: %w", err) } s.Description = strVal(desc) if s.Tags == nil { s.Tags = []string{} } skills = append(skills, s) } return skills, rows.Err() } // Project Guardrails func (db *DB) AddProjectGuardrail(ctx context.Context, projectID, guardrailID uuid.UUID) 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 uuid.UUID) 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 uuid.UUID) ([]ext.AgentGuardrail, error) { rows, err := db.pool.Query(ctx, ` select g.id, g.name, g.description, g.content, g.severity, g.tags, 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 g ext.AgentGuardrail var desc *string if err := rows.Scan(&g.ID, &g.Name, &desc, &g.Content, &g.Severity, &g.Tags, &g.CreatedAt, &g.UpdatedAt); err != nil { return nil, fmt.Errorf("scan project guardrail: %w", err) } g.Description = strVal(desc) if g.Tags == nil { g.Tags = []string{} } guardrails = append(guardrails, g) } return guardrails, rows.Err() }