All skills
launchdarkly avatar

/projects

@ef54971 official

Guide for setting up LaunchDarkly projects in your codebase. Helps you assess your stack, choose the right approach, and integrate project management that makes sense for your architecture.

Use this Skill: https://skilld.dev/gh/launchdarkly/agent-skills/projects

This session only. Nothing lands on disk.

referencesgo-setup.md

≈3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Go Project Setup

Implementation patterns for Go applications using the LaunchDarkly API.

Prerequisites

go get github.com/joho/godotenv

Basic Project Manager

Create a package for project operations:

// pkg/launchdarkly/projects.go
package launchdarkly

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
)

const BaseURL = "https://app.launchdarkly.com/api/v2"

type ProjectManager struct {
    apiToken string
    client   *http.Client
}

type Project struct {
    Name         string        `json:"name"`
    Key          string        `json:"key"`
    Tags         []string      `json:"tags,omitempty"`
    Environments *Environments `json:"environments,omitempty"`
}

type Environments struct {
    Items []Environment `json:"items"`
}

type Environment struct {
    Name   string `json:"name"`
    Key    string `json:"key"`
    APIKey string `json:"apiKey"`
}

func NewProjectManager(apiToken string) *ProjectManager {
    if apiToken == "" {
        apiToken = os.Getenv("LAUNCHDARKLY_API_TOKEN")
    }
    
    return &ProjectManager{
        apiToken: apiToken,
        client:   &http.Client{},
    }
}

func (pm *ProjectManager) CreateProject(name, key string, tags []string) (*Project, error) {
    payload := map[string]interface{}{
        "name": name,
        "key":  key,
    }
    if tags != nil {
        payload["tags"] = tags
    }
    
    body, err := json.Marshal(payload)
    if err != nil {
        return nil, fmt.Errorf("marshal payload: %w", err)
    }
    
    req, err := http.NewRequest("POST", fmt.Sprintf("%s/projects", BaseURL), bytes.NewReader(body))
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }
    
    req.Header.Set("Authorization", pm.apiToken)
    req.Header.Set("Content-Type", "application/json")
    
    resp, err := pm.client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("do request: %w", err)
    }
    defer resp.Body.Close()
    
    if resp.StatusCode == http.StatusConflict {
        // Project exists, fetch it
        return pm.GetProject(key)
    }
    
    if resp.StatusCode != http.StatusCreated {
        body, _ := io.ReadAll(resp.Body)
        return nil, fmt.Errorf("unexpected status %d: %s", resp.StatusCode, body)
    }
    
    var project Project
    if err := json.NewDecoder(resp.Body).Decode(&project); err != nil {
        return nil, fmt.Errorf("decode response: %w", err)
    }
    
    return &project, nil
}

func (pm *ProjectManager) GetProject(projectKey string) (*Project, error) {
    req, err := http.NewRequest("GET", fmt.Sprintf("%s/projects/%s?expand=environments", BaseURL, projectKey), nil)
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }
    
    req.Header.Set("Authorization", pm.apiToken)
    
    resp, err := pm.client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("do request: %w", err)
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("project not found: %d", resp.StatusCode)
    }
    
    var project Project
    if err := json.NewDecoder(resp.Body).Decode(&project); err != nil {
        return nil, fmt.Errorf("decode response: %w", err)
    }
    
    return &project, nil
}

func (pm *ProjectManager) GetSDKKey(projectKey, environment string) (string, error) {
    project, err := pm.GetProject(projectKey)
    if err != nil {
        return "", err
    }
    
    if project.Environments == nil {
        return "", fmt.Errorf("no environments found")
    }
    
    for _, env := range project.Environments.Items {
        if env.Key == environment {
            return env.APIKey, nil
        }
    }
    
    return "", fmt.Errorf("environment '%s' not found", environment)
}

func (pm *ProjectManager) ListProjects() ([]Project, error) {
    req, err := http.NewRequest("GET", fmt.Sprintf("%s/projects", BaseURL), nil)
    if err != nil {
        return nil, fmt.Errorf("create request: %w", err)
    }
    
    req.Header.Set("Authorization", pm.apiToken)
    
    resp, err := pm.client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("do request: %w", err)
    }
    defer resp.Body.Close()
    
    if resp.StatusCode != http.StatusOK {
        return nil, fmt.Errorf("failed to list projects: %d", resp.StatusCode)
    }
    
    var result struct {
        Items []Project `json:"items"`
    }
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        return nil, fmt.Errorf("decode response: %w", err)
    }
    
    return result.Items, nil
}

Usage Examples

Create a Project

package main

import (
    "fmt"
    "log"
    
    "yourmodule/pkg/launchdarkly"
)

func main() {
    pm := launchdarkly.NewProjectManager("")
    
    project, err := pm.CreateProject(
        "Customer Agent Service",
        "customer-ai",
        []string{"ai-configs", "production"},
    )
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("✓ Created project: %s (%s)\n", project.Name, project.Key)
}

Get SDK Key

func main() {
    pm := launchdarkly.NewProjectManager("")
    
    // Get production SDK key
    sdkKey, err := pm.GetSDKKey("customer-ai", "production")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Production SDK Key: %s\n", sdkKey)
}

List Projects

func main() {
    pm := launchdarkly.NewProjectManager("")
    
    projects, err := pm.ListProjects()
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("Projects:")
    for _, project := range projects {
        fmt.Printf("  - %s (%s)\n", project.Name, project.Key)
    }
}

HTTP Server Integration

Integrate into an HTTP server:

// cmd/server/main.go
package main

import (
    "log"
    "net/http"
    "os"
    
    "yourmodule/pkg/launchdarkly"
)

func main() {
    // Initialize LaunchDarkly project
    pm := launchdarkly.NewProjectManager(os.Getenv("LAUNCHDARKLY_API_TOKEN"))
    
    project, err := pm.CreateProject(
        "Go API Service",
        "go-api-service",
        []string{"api", "ai-configs"},
    )
    if err != nil {
        log.Fatalf("Failed to initialize LaunchDarkly: %v", err)
    }
    
    // Get SDK key
    sdkKey, err := pm.GetSDKKey("go-api-service", "production")
    if err != nil {
        log.Fatalf("Failed to get SDK key: %v", err)
    }
    
    // Store SDK key for SDK initialization
    os.Setenv("LAUNCHDARKLY_SDK_KEY", sdkKey)
    
    log.Printf("✓ LaunchDarkly project ready: %s\n", project.Key)
    
    // Start server
    http.HandleFunc("/health", healthHandler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("OK"))
}

CLI Tool

Create a CLI for project management:

// cmd/ldprojects/main.go
package main

import (
    "flag"
    "fmt"
    "log"
    "os"
    "strings"
    
    "yourmodule/pkg/launchdarkly"
)

func main() {
    createCmd := flag.NewFlagSet("create", flag.ExitOnError)
    createName := createCmd.String("name", "", "Project name")
    createKey := createCmd.String("key", "", "Project key")
    createTags := createCmd.String("tags", "", "Comma-separated tags")
    
    listCmd := flag.NewFlagSet("list", flag.ExitOnError)
    
    getKeyCmd := flag.NewFlagSet("get-key", flag.ExitOnError)
    getKeyProject := getKeyCmd.String("project", "", "Project key")
    getKeyEnv := getKeyCmd.String("env", "production", "Environment")
    
    if len(os.Args) < 2 {
        fmt.Println("Usage: ldprojects [create|list|get-key] [options]")
        os.Exit(1)
    }
    
    pm := launchdarkly.NewProjectManager("")
    
    switch os.Args[1] {
    case "create":
        createCmd.Parse(os.Args[2:])
        if *createName == "" || *createKey == "" {
            log.Fatal("name and key are required")
        }
        
        var tags []string
        if *createTags != "" {
            tags = strings.Split(*createTags, ",")
        }
        
        project, err := pm.CreateProject(*createName, *createKey, tags)
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Printf("✓ Created: %s (%s)\n", project.Name, project.Key)
        
    case "list":
        listCmd.Parse(os.Args[2:])
        
        projects, err := pm.ListProjects()
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Println("Projects:")
        for _, project := range projects {
            fmt.Printf("  - %s (%s)\n", project.Name, project.Key)
        }
        
    case "get-key":
        getKeyCmd.Parse(os.Args[2:])
        if *getKeyProject == "" {
            log.Fatal("project is required")
        }
        
        sdkKey, err := pm.GetSDKKey(*getKeyProject, *getKeyEnv)
        if err != nil {
            log.Fatal(err)
        }
        
        fmt.Println(sdkKey)
        
    default:
        fmt.Println("Unknown command:", os.Args[1])
        os.Exit(1)
    }
}

Usage:

go run cmd/ldprojects/main.go create -name "My Agent" -key my-ai -tags ai-configs,production
go run cmd/ldprojects/main.go list
go run cmd/ldprojects/main.go get-key -project my-ai -env production

Error Handling

Add comprehensive error handling:

type LaunchDarklyError struct {
    StatusCode int
    Message    string
}

func (e *LaunchDarklyError) Error() string {
    return fmt.Sprintf("LaunchDarkly API error (%d): %s", e.StatusCode, e.Message)
}

func (pm *ProjectManager) CreateProject(name, key string, tags []string) (*Project, error) {
    // ... request setup ...
    
    resp, err := pm.client.Do(req)
    if err != nil {
        return nil, fmt.Errorf("request failed: %w", err)
    }
    defer resp.Body.Close()
    
    switch resp.StatusCode {
    case http.StatusCreated:
        var project Project
        if err := json.NewDecoder(resp.Body).Decode(&project); err != nil {
            return nil, fmt.Errorf("decode response: %w", err)
        }
        return &project, nil
        
    case http.StatusConflict:
        return pm.GetProject(key)
        
    case http.StatusUnauthorized:
        return nil, &LaunchDarklyError{resp.StatusCode, "Invalid API token"}
        
    case http.StatusForbidden:
        return nil, &LaunchDarklyError{resp.StatusCode, "Insufficient permissions (need projects:write)"}
        
    default:
        body, _ := io.ReadAll(resp.Body)
        return nil, &LaunchDarklyError{resp.StatusCode, string(body)}
    }
}

Testing

Mock the HTTP client for testing:

// pkg/launchdarkly/projects_test.go
package launchdarkly

import (
    "net/http"
    "net/http/httptest"
    "testing"
)

func TestCreateProject(t *testing.T) {
    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.Method != "POST" {
            t.Errorf("Expected POST, got %s", r.Method)
        }
        
        w.WriteHeader(http.StatusCreated)
        w.Write([]byte(`{"name":"Test","key":"test","tags":[]}`))
    }))
    defer server.Close()
    
    pm := &ProjectManager{
        apiToken: "test-token",
        client:   server.Client(),
    }
    
    // Override BaseURL for test
    oldBaseURL := BaseURL
    BaseURL = server.URL
    defer func() { BaseURL = oldBaseURL }()
    
    project, err := pm.CreateProject("Test", "test", nil)
    if err != nil {
        t.Fatalf("CreateProject failed: %v", err)
    }
    
    if project.Key != "test" {
        t.Errorf("Expected key 'test', got '%s'", project.Key)
    }
}

Next Steps

Source: SKILL.md on GitHub

2 alerts2d3 checks · Risk HIGH
  • Gen Agent Trust Hub2d

    The skill facilitates LaunchDarkly project setup but performs high-risk operations, including instructions to automatically extract credentials from sensitive agent configuration files (~/.claude/config.json) and system environment variables. It also generates and executes local scripts to verify integrations, creating a potential path for local code execution.

  • Socket2d

    1 alert: gptAnomaly

  • Snyk2d

    Risk: HIGH · 1 issue

Signed by skilld at ef54971. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 3 days ago
metadata
{
  "author": "launchdarkly",
  "version": "0.4.0"
}
Other metadata
compatibility
Requires LaunchDarkly API access token with projects:write permission or LaunchDarkly MCP server.
  • Python
  • Go
  • Infrastructure
  • launchdarkly
  • project-setup
  • api-integration
  • nodejs
  • configuration-management
  • feature-flags
  • secrets-management

README badge

README badge for launchdarkly/agent-skills/projects

Guides setup of LaunchDarkly projects in a codebase by exploring the stack, assessing architecture, and choosing the right implementation path. Supports Python, Node.js, Go, and polyglot setups, with reference guides for quick setup, environment configuration, project cloning, and infrastructure automation.

Generated from the current SKILL.md.

What permissions do I need to set up LaunchDarkly projects?
Your API access token must have the projects:write permission, or you can use a LaunchDarkly MCP server configured in your environment instead.
Can the skill automatically detect my LaunchDarkly API key?
Yes. The skill checks for LAUNCHDARKLY_API_KEY, LAUNCHDARKLY_API_TOKEN, or LD_API_KEY environment variables, and also checks the Claude MCP config at ~/.claude/config.json before prompting you for the key.
What project key format is required?
Project keys must start with a letter, use only lowercase letters, numbers, and hyphens, and cannot contain uppercase letters, underscores, or dots.
Does this skill create the projects, or just guide me?
The skill guides you through the setup process and follows reference implementations for your tech stack (Python, Node.js, Go, etc.), but you choose which reference path to execute based on your architecture.
What should I do after projects are created?
After setup, verify the project exists and SDK keys are valid, then proceed to the configs-create skill to create feature flags or the sdk skill to integrate into your application.

Generated from the current SKILL.md. These answers refresh after source changes.