Initial commit
This commit is contained in:
455
commands/project:add.md
Normal file
455
commands/project:add.md
Normal file
@@ -0,0 +1,455 @@
|
||||
---
|
||||
description: Add a new project to CCPM configuration
|
||||
allowed-tools: [Bash, Read, Write, Edit, AskUserQuestion]
|
||||
argument-hint: <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
|
||||
|
||||
```javascript
|
||||
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
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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:
|
||||
|
||||
```javascript
|
||||
{
|
||||
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 use
|
||||
- `projectName` → human-readable name
|
||||
- `linearTeam` → Linear team
|
||||
- `repoType` → repository type
|
||||
|
||||
### Step 5: Gather Additional Details Based on Type
|
||||
|
||||
#### If "Full-stack with Jira" selected:
|
||||
|
||||
```javascript
|
||||
{
|
||||
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:
|
||||
|
||||
```javascript
|
||||
{
|
||||
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
|
||||
|
||||
```javascript
|
||||
// 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
|
||||
|
||||
```yaml
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📋 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
|
||||
|
||||
```javascript
|
||||
{
|
||||
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:
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
/ccpm:project:add my-app --template simple-linear
|
||||
|
||||
# Will prompt for:
|
||||
# - Project name
|
||||
# - Linear team
|
||||
# - Repository details
|
||||
```
|
||||
|
||||
### Example 2: Add full-stack project
|
||||
|
||||
```bash
|
||||
/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
|
||||
|
||||
```bash
|
||||
/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:update` to modify existing projects
|
||||
Reference in New Issue
Block a user