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.

referencesnodejs-setup.md

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

Node.js/TypeScript Project Setup

Implementation patterns for Node.js and TypeScript applications.

Prerequisites

npm install axios dotenv
# or
yarn add axios dotenv

TypeScript Project Manager

Create a typed module for project operations:

// src/launchdarkly/projects.ts
import axios, { AxiosInstance } from 'axios';

interface Project {
  name: string;
  key: string;
  tags?: string[];
  environments?: {
    items: Environment[];
  };
}

interface Environment {
  name: string;
  key: string;
  apiKey: string;
}

interface CreateProjectParams {
  name: string;
  key: string;
  tags?: string[];
}

export class ProjectManager {
  private client: AxiosInstance;

  constructor(apiToken?: string) {
    const token = apiToken || process.env.LAUNCHDARKLY_API_TOKEN;
    if (!token) {
      throw new Error('LAUNCHDARKLY_API_TOKEN is required');
    }

    this.client = axios.create({
      baseURL: 'https://app.launchdarkly.com/api/v2',
      headers: {
        Authorization: token,
        'Content-Type': 'application/json',
      },
    });
  }

  async createProject(params: CreateProjectParams): Promise<Project> {
    try {
      const response = await this.client.post<Project>('/projects', params);
      return response.data;
    } catch (error: any) {
      if (error.response?.status === 409) {
        // Project exists, fetch and return it
        console.log(`Project '${params.key}' already exists`);
        return this.getProject(params.key);
      }
      throw error;
    }
  }

  async getProject(projectKey: string): Promise<Project> {
    const response = await this.client.get<Project>(`/projects/${projectKey}`, {
      params: { expand: 'environments' },
    });
    return response.data;
  }

  async getSdkKey(projectKey: string, environment: string = 'production'): Promise<string | null> {
    const project = await this.getProject(projectKey);
    const envItems = project.environments?.items || [];
    
    const env = envItems.find((e) => e.key === environment);
    return env?.apiKey || null;
  }

  async listProjects(): Promise<Project[]> {
    const response = await this.client.get<{ items: Project[] }>('/projects');
    return response.data.items;
  }
}

JavaScript (CommonJS)

For Node.js without TypeScript:

// src/launchdarkly/projects.js
const axios = require('axios');

class ProjectManager {
  constructor(apiToken) {
    const token = apiToken || process.env.LAUNCHDARKLY_API_TOKEN;
    if (!token) {
      throw new Error('LAUNCHDARKLY_API_TOKEN is required');
    }

    this.client = axios.create({
      baseURL: 'https://app.launchdarkly.com/api/v2',
      headers: {
        Authorization: token,
        'Content-Type': 'application/json',
      },
    });
  }

  async createProject({ name, key, tags = [] }) {
    try {
      const response = await this.client.post('/projects', { name, key, tags });
      return response.data;
    } catch (error) {
      if (error.response?.status === 409) {
        console.log(`Project '${key}' already exists`);
        return this.getProject(key);
      }
      throw error;
    }
  }

  async getProject(projectKey) {
    const response = await this.client.get(`/projects/${projectKey}`, {
      params: { expand: 'environments' },
    });
    return response.data;
  }

  async getSdkKey(projectKey, environment = 'production') {
    const project = await this.getProject(projectKey);
    const envItems = project.environments?.items || [];
    
    const env = envItems.find((e) => e.key === environment);
    return env?.apiKey || null;
  }

  async listProjects() {
    const response = await this.client.get('/projects');
    return response.data.items;
  }
}

module.exports = { ProjectManager };

Express.js Integration

Integrate project setup into Express app:

// src/app.ts
import express from 'express';
import dotenv from 'dotenv';
import { ProjectManager } from './launchdarkly/projects';

dotenv.config();

const app = express();
const pm = new ProjectManager();

// Ensure project exists on startup
async function initializeLaunchDarkly() {
  try {
    const project = await pm.createProject({
      name: 'Express API',
      key: 'express-api',
      tags: ['api', 'ai-configs'],
    });
    
    const sdkKey = await pm.getSdkKey('express-api', 'production');
    console.log(`✓ LaunchDarkly project ready: ${project.key}`);
    
    // Store SDK key for SDK initialization
    process.env.LAUNCHDARKLY_SDK_KEY = sdkKey || '';
  } catch (error) {
    console.error('Failed to initialize LaunchDarkly:', error);
    process.exit(1);
  }
}

// Initialize before starting server
initializeLaunchDarkly().then(() => {
  app.listen(3000, () => {
    console.log('Server running on port 3000');
  });
});

NestJS Integration

For NestJS applications:

// src/launchdarkly/launchdarkly.module.ts
import { Module, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { ProjectManager } from './projects';

@Module({
  providers: [ProjectManager],
  exports: [ProjectManager],
})
export class LaunchDarklyModule implements OnModuleInit {
  constructor(
    private readonly pm: ProjectManager,
    private readonly config: ConfigService,
  ) {}

  async onModuleInit() {
    const projectKey = this.config.get('LAUNCHDARKLY_PROJECT_KEY', 'nestjs-app');
    
    try {
      const project = await this.pm.createProject({
        name: 'NestJS Application',
        key: projectKey,
        tags: ['nestjs', 'ai-configs'],
      });
      
      console.log(`✓ LaunchDarkly project ready: ${project.key}`);
    } catch (error) {
      console.error('LaunchDarkly initialization failed:', error);
    }
  }
}


// src/launchdarkly/projects.ts (Injectable version)
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import axios, { AxiosInstance } from 'axios';

@Injectable()
export class ProjectManager {
  private client: AxiosInstance;

  constructor(private config: ConfigService) {
    const apiToken = this.config.get('LAUNCHDARKLY_API_TOKEN');
    this.client = axios.create({
      baseURL: 'https://app.launchdarkly.com/api/v2',
      headers: {
        Authorization: apiToken,
        'Content-Type': 'application/json',
      },
    });
  }

  // ... same methods as before
}

CLI Tool

Create a CLI for project management:

// cli/projects.ts
#!/usr/bin/env node
import { Command } from 'commander';
import { ProjectManager } from '../src/launchdarkly/projects';

const program = new Command();
const pm = new ProjectManager();

program
  .name('ld-projects')
  .description('LaunchDarkly project management CLI');

program
  .command('create <name> <key>')
  .description('Create a new project')
  .option('-t, --tags <tags...>', 'Project tags')
  .action(async (name: string, key: string, options: { tags?: string[] }) => {
    try {
      const project = await pm.createProject({ name, key, tags: options.tags });
      console.log(`✓ Created: ${project.name} (${project.key})`);
    } catch (error: any) {
      console.error('Error:', error.message);
      process.exit(1);
    }
  });

program
  .command('list')
  .description('List all projects')
  .action(async () => {
    try {
      const projects = await pm.listProjects();
      projects.forEach((p) => {
        console.log(`- ${p.name} (${p.key})`);
      });
    } catch (error: any) {
      console.error('Error:', error.message);
      process.exit(1);
    }
  });

program
  .command('get-key <projectKey>')
  .description('Get SDK key for a project')
  .option('-e, --env <environment>', 'Environment', 'production')
  .action(async (projectKey: string, options: { env: string }) => {
    try {
      const sdkKey = await pm.getSdkKey(projectKey, options.env);
      if (sdkKey) {
        console.log(sdkKey);
      } else {
        console.error(`Environment '${options.env}' not found`);
        process.exit(1);
      }
    } catch (error: any) {
      console.error('Error:', error.message);
      process.exit(1);
    }
  });

program.parse();

Usage:

npm run ld-projects create "My Agent" my-ai -t ai-configs production
npm run ld-projects list
npm run ld-projects get-key my-ai --env production

Error Handling

Add comprehensive error handling:

export class LaunchDarklyError extends Error {
  constructor(
    message: string,
    public statusCode?: number,
    public response?: any
  ) {
    super(message);
    this.name = 'LaunchDarklyError';
  }
}

export class ProjectManager {
  async createProject(params: CreateProjectParams): Promise<Project> {
    try {
      const response = await this.client.post<Project>('/projects', params);
      return response.data;
    } catch (error: any) {
      if (error.response) {
        const status = error.response.status;
        if (status === 409) {
          return this.getProject(params.key);
        }
        if (status === 401) {
          throw new LaunchDarklyError('Invalid API token', status);
        }
        if (status === 403) {
          throw new LaunchDarklyError(
            'Insufficient permissions (need projects:write)',
            status
          );
        }
        throw new LaunchDarklyError(
          `API error: ${error.response.data.message || 'Unknown error'}`,
          status,
          error.response.data
        );
      }
      throw new LaunchDarklyError(`Request failed: ${error.message}`);
    }
  }
}

Testing

Mock with Jest:

// __tests__/projects.test.ts
import axios from 'axios';
import { ProjectManager } from '../src/launchdarkly/projects';

jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

describe('ProjectManager', () => {
  let pm: ProjectManager;

  beforeEach(() => {
    mockedAxios.create.mockReturnValue(mockedAxios as any);
    pm = new ProjectManager('test-token');
  });

  it('should create a project', async () => {
    const mockProject = { name: 'Test', key: 'test', tags: [] };
    mockedAxios.post.mockResolvedValue({ data: mockProject });

    const project = await pm.createProject({ name: 'Test', key: 'test' });

    expect(project.key).toBe('test');
    expect(mockedAxios.post).toHaveBeenCalledWith('/projects', {
      name: 'Test',
      key: 'test',
    });
  });

  it('should handle existing project', async () => {
    const mockProject = { name: 'Test', key: 'test' };
    mockedAxios.post.mockRejectedValue({
      response: { status: 409 },
    });
    mockedAxios.get.mockResolvedValue({ data: mockProject });

    const project = await pm.createProject({ name: 'Test', key: 'test' });

    expect(project.key).toBe('test');
    expect(mockedAxios.get).toHaveBeenCalledWith('/projects/test', {
      params: { expand: 'environments' },
    });
  });
});

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.