11 KiB
11 KiB
description, allowed-tools, argument-hint
| description | allowed-tools | argument-hint | |||||
|---|---|---|---|---|---|---|---|
| Add a new project to CCPM configuration |
|
<project-id> [--template TEMPLATE] |
Add New Project to CCPM
Add a new project configuration to ~/.claude/ccpm-config.yaml.
Arguments
- $1 - Project ID (required, e.g., "my-app", "acme-platform")
- --template - Use a template (optional: "fullstack-with-jira", "simple-linear", "open-source")
Workflow
Step 1: Validate Project ID
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)
}
Step 2: Check if Configuration Exists
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
Step 3: Check if Project Already Exists
# 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
Step 4: Gather Project Information
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
Step 5: Gather Additional Details Based on Type
If "Full-stack with Jira" selected:
{
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"
}
]
}
]
}
If GitHub/BitBucket/GitLab selected:
{
questions: [
{
question: `What's your ${repoType} repository? (format: owner/repo)`,
header: "Repository",
multiSelect: false,
options: [
{
label: "Enter manually",
description: "e.g., company/project-name"
}
]
}
]
}
Step 6: Build Project Configuration
// 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 || []
}
}
Step 7: Show Configuration Preview
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 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"}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 8: Confirm and Save
{
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 "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
Examples
Example 1: Add with template
/ccpm:project:add my-app --template simple-linear
# Will prompt for:
# - Project name
# - Linear team
# - Repository details
Example 2: Add full-stack project
/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
Example 3: Add personal project
/ccpm:project:add my-side-project --template open-source
# Quick setup for personal/open-source projects
Notes
- Configuration is stored in
~/.claude/ccpm-config.yaml - You can manually edit this file later
- Templates provide quick starting points
- All fields can be customized after creation
- Use
/ccpm:project:updateto modify existing projects