Add a new project to CCPM configuration
Add a new project to your CCPM configuration with interactive prompts. Use this when setting up a new project to track tasks, repositories, and integrations.
/plugin marketplace add duongdev/ccpm/plugin install ccpm@duongdev-ccpm-marketplace<project-id> [--template TEMPLATE]Add a new project configuration to ~/.claude/ccpm-config.yaml.
const projectId = $1
if (!projectId) {
console.log("ā Error: Project ID required")
console.log("Usage: /ccpm:project:add <project-id> [--template TEMPLATE]")
exit(1)
}
// Validate format (lowercase, hyphens only)
if (!/^[a-z0-9-]+$/.test(projectId)) {
console.log("ā Error: Invalid project ID format")
console.log("Project ID must be lowercase with hyphens only (e.g., 'my-app')")
exit(1)
}
CONFIG_FILE="$HOME/.claude/ccpm-config.yaml"
if [[ ! -f "$CONFIG_FILE" ]]; then
echo "š No CCPM configuration found. Creating new one..."
# Create from example
cp "$HOME/.claude/plugins/ccpm/ccpm-config.example.yaml" "$CONFIG_FILE"
echo "ā
Created $CONFIG_FILE"
fi
# Use yq to check if project exists
if yq eval ".projects.$PROJECT_ID" "$CONFIG_FILE" > /dev/null 2>&1; then
EXISTING=$(yq eval ".projects.$PROJECT_ID.name" "$CONFIG_FILE")
if [[ "$EXISTING" != "null" ]]; then
echo "ā ļø Project '$PROJECT_ID' already exists: $EXISTING"
echo ""
echo "Options:"
echo " 1. Update existing project: /ccpm:project:update $PROJECT_ID"
echo " 2. Delete and recreate: /ccpm:project:delete $PROJECT_ID"
echo " 3. Choose different ID: /ccpm:project:add <different-id>"
exit 1
fi
fi
Use AskUserQuestion to gather project details:
{
questions: [
{
question: "What type of project is this?",
header: "Project Type",
multiSelect: false,
options: [
{
label: "Full-stack with Jira",
description: "Jira, Confluence, Slack integration (template: fullstack-with-jira)"
},
{
label: "Simple Linear-only",
description: "Linear tracking only, no external PM (template: simple-linear)"
},
{
label: "Open Source",
description: "GitHub-based open source project (template: open-source)"
},
{
label: "Custom",
description: "Configure from scratch"
}
]
},
{
question: "What's the project name (human-readable)?",
header: "Project Name",
multiSelect: false,
options: [
{
label: "Enter manually",
description: "Type the project name"
}
]
},
{
question: "Which Linear team should this project use?",
header: "Linear Team",
multiSelect: false,
options: [
{
label: "Work",
description: "Work-related projects"
},
{
label: "Personal",
description: "Personal projects"
},
{
label: "Other",
description: "Specify custom team"
}
]
},
{
question: "What's the code repository type?",
header: "Repository",
multiSelect: false,
options: [
{
label: "GitHub",
description: "GitHub repository"
},
{
label: "BitBucket",
description: "BitBucket repository"
},
{
label: "GitLab",
description: "GitLab repository"
}
]
}
]
}
Store answers:
projectType ā template to useprojectName ā human-readable namelinearTeam ā Linear teamrepoType ā repository type{
questions: [
{
question: "What's your Jira project key? (e.g., PROJ)",
header: "Jira Key",
multiSelect: false,
options: [
{
label: "Enter manually",
description: "Type the Jira project key"
}
]
},
{
question: "What's your Confluence space key?",
header: "Confluence",
multiSelect: false,
options: [
{
label: "Same as Jira",
description: "Use same key as Jira project"
},
{
label: "Enter manually",
description: "Type the Confluence space key"
}
]
},
{
question: "What's your primary Slack channel?",
header: "Slack Channel",
multiSelect: false,
options: [
{
label: "Enter manually",
description: "e.g., #project-dev"
}
]
}
]
}
{
questions: [
{
question: `What's your ${repoType} repository? (format: owner/repo)`,
header: "Repository",
multiSelect: false,
options: [
{
label: "Enter manually",
description: "e.g., company/project-name"
}
]
}
]
}
// Start with template or empty config
let projectConfig = {}
if (projectType !== "Custom") {
// Load template from global config
const template = await yq(".templates.${templateName}", CONFIG_FILE)
projectConfig = { ...template }
}
// Set basic fields
projectConfig.name = projectName
projectConfig.description = projectDescription || `${projectName} project`
projectConfig.owner = projectOwner || "Engineering Team"
// Repository
projectConfig.repository = {
url: repositoryUrl,
default_branch: "main"
}
// Linear configuration
projectConfig.linear = {
team: linearTeam,
project: projectName,
default_labels: [projectId, "planning"]
}
// External PM (if applicable)
if (jiraEnabled) {
projectConfig.external_pm = {
enabled: true,
type: "jira",
jira: {
enabled: true,
base_url: jiraBaseUrl || "https://jira.company.com",
project_key: jiraProjectKey
},
confluence: {
enabled: confluenceEnabled,
base_url: confluenceBaseUrl || "https://confluence.company.com",
space_key: confluenceSpaceKey || jiraProjectKey
},
slack: {
enabled: slackEnabled,
workspace: slackWorkspace || "company-workspace",
channels: [
{
name: slackChannel,
id: slackChannelId || "C0XXXXXXX"
}
]
}
}
} else {
projectConfig.external_pm = {
enabled: false,
type: "linear-only"
}
}
// Code repository
if (repoType === "github") {
const [owner, repo] = repoUrl.split("/")
projectConfig.code_repository = {
type: "github",
github: {
enabled: true,
owner: owner,
repo: repo
}
}
} else if (repoType === "bitbucket") {
const [workspace, repoSlug] = repoUrl.split("/")
projectConfig.code_repository = {
type: "bitbucket",
bitbucket: {
enabled: true,
workspace: workspace,
repo_slug: repoSlug,
base_url: `https://bitbucket.org/${workspace}/${repoSlug}`
}
}
}
// Tech stack (ask for details)
projectConfig.tech_stack = {
languages: techLanguages || ["typescript"],
frameworks: {
frontend: frontendFrameworks || [],
backend: backendFrameworks || []
}
}
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š New Project Configuration Preview
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Project ID: ${projectId}
Name: ${projectName}
Description: ${projectDescription}
Linear:
Team: ${linearTeam}
Project: ${projectName}
Labels: [${projectLabels.join(", ")}]
${jiraEnabled ? `
External PM:
Jira: ${jiraProjectKey}
Confluence: ${confluenceSpaceKey}
Slack: ${slackChannel}
` : `
External PM: Linear-only (no external integration)
`}
Repository:
Type: ${repoType}
${repoType === "github" ? `Owner/Repo: ${owner}/${repo}` : `Workspace/Repo: ${workspace}/${repoSlug}`}
Tech Stack:
Languages: ${languages.join(", ")}
Frontend: ${frontendFrameworks.join(", ") || "N/A"}
Backend: ${backendFrameworks.join(", ") || "N/A"}
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
{
questions: [{
question: "Add this project to CCPM configuration?",
header: "Confirm",
multiSelect: false,
options: [
{
label: "Yes, add it",
description: "Save configuration to ~/.claude/ccpm-config.yaml"
},
{
label: "Edit details",
description: "Go back and modify configuration"
},
{
label: "Cancel",
description: "Don't add project"
}
]
}]
}
If confirmed:
# Add project to configuration using yq
yq eval -i ".projects.$PROJECT_ID = $PROJECT_CONFIG_JSON" "$CONFIG_FILE"
echo ""
echo "ā
Project added successfully!"
echo ""
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
echo "š Next Steps"
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
echo ""
echo "1. View configuration:"
echo " /ccpm:project:list"
echo ""
echo "2. Set as active project (if in project directory):"
echo " /ccpm:project:set $PROJECT_ID"
echo ""
echo "3. Create your first task:"
echo " /ccpm:planning:create \"Task title\" $PROJECT_ID"
echo ""
echo "4. Edit configuration anytime:"
echo " /ccpm:project:update $PROJECT_ID"
echo ""
echo "āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā"
/ccpm:project:add my-app --template simple-linear
# Will prompt for:
# - Project name
# - Linear team
# - Repository details
/ccpm:project:add acme-platform
# Interactive prompts will guide you through:
# - Project type selection (choose "Full-stack with Jira")
# - Jira/Confluence/Slack configuration
# - Repository setup
# - Tech stack details
/ccpm:project:add my-side-project --template open-source
# Quick setup for personal/open-source projects
~/.claude/ccpm-config.yaml/ccpm:project:update to modify existing projects