Initial commit
This commit is contained in:
417
commands/project:update.md
Normal file
417
commands/project:update.md
Normal file
@@ -0,0 +1,417 @@
|
||||
---
|
||||
description: Update an existing project configuration
|
||||
allowed-tools: [Bash, Read, Edit, AskUserQuestion]
|
||||
argument-hint: <project-id> [--field FIELD_PATH]
|
||||
---
|
||||
|
||||
# Update Project Configuration
|
||||
|
||||
Update an existing project in `~/.claude/ccpm-config.yaml`.
|
||||
|
||||
## Arguments
|
||||
|
||||
- **$1** - Project ID (required)
|
||||
- **--field** - Specific field to update (optional, e.g., "linear.team", "external_pm.jira.project_key")
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# Interactive update (all fields)
|
||||
/ccpm:project:update my-app
|
||||
|
||||
# Update specific field
|
||||
/ccpm:project:update my-app --field linear.team
|
||||
/ccpm:project:update my-app --field external_pm.jira.project_key
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Validate and Load Project
|
||||
|
||||
```bash
|
||||
CONFIG_FILE="$HOME/.claude/ccpm-config.yaml"
|
||||
PROJECT_ID=$1
|
||||
|
||||
# Check configuration exists
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
echo "❌ Error: No CCPM configuration found"
|
||||
echo ""
|
||||
echo "Create configuration:"
|
||||
echo " /ccpm:project:add <project-id>"
|
||||
exit(1)
|
||||
fi
|
||||
|
||||
# Check project exists
|
||||
if ! yq eval ".projects.$PROJECT_ID" "$CONFIG_FILE" > /dev/null 2>&1; then
|
||||
echo "❌ Error: Project '$PROJECT_ID' not found"
|
||||
echo ""
|
||||
echo "Available projects:"
|
||||
yq eval '.projects | keys | .[]' "$CONFIG_FILE"
|
||||
exit(1)
|
||||
fi
|
||||
|
||||
# Load current configuration
|
||||
CURRENT_CONFIG=$(yq eval ".projects.$PROJECT_ID" "$CONFIG_FILE" -o=json)
|
||||
```
|
||||
|
||||
### Step 2: Determine Update Mode
|
||||
|
||||
If `--field` is provided → **Targeted field update**
|
||||
Otherwise → **Interactive full update**
|
||||
|
||||
### Mode A: Targeted Field Update
|
||||
|
||||
```javascript
|
||||
const fieldPath = $2 // e.g., "linear.team"
|
||||
const currentValue = await yq(`.projects.${projectId}.${fieldPath}`, CONFIG_FILE)
|
||||
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Update Field: ${fieldPath}
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project: ${projectId}
|
||||
Field: ${fieldPath}
|
||||
Current: ${currentValue}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
|
||||
// Ask for new value
|
||||
{
|
||||
questions: [{
|
||||
question: `What should the new value be for ${fieldPath}?`,
|
||||
header: "New Value",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Enter manually",
|
||||
description: "Type the new value"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
|
||||
// Validate new value (type-specific validation)
|
||||
const newValue = userInput
|
||||
|
||||
// Update configuration
|
||||
await yq(`-i '.projects.${projectId}.${fieldPath} = "${newValue}"'`, CONFIG_FILE)
|
||||
|
||||
console.log(`
|
||||
✅ Updated successfully!
|
||||
|
||||
Field: ${fieldPath}
|
||||
Old: ${currentValue}
|
||||
New: ${newValue}
|
||||
`)
|
||||
```
|
||||
|
||||
### Mode B: Interactive Full Update
|
||||
|
||||
Ask user which category to update:
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "What would you like to update?",
|
||||
header: "Update Category",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Project Info",
|
||||
description: "Name, description, owner"
|
||||
},
|
||||
{
|
||||
label: "Linear Settings",
|
||||
description: "Team, project, labels, workflow states"
|
||||
},
|
||||
{
|
||||
label: "External PM",
|
||||
description: "Jira, Confluence, Slack configuration"
|
||||
},
|
||||
{
|
||||
label: "Code Repository",
|
||||
description: "Repository type and settings"
|
||||
},
|
||||
{
|
||||
label: "Quality Gates",
|
||||
description: "SonarQube, code review settings"
|
||||
},
|
||||
{
|
||||
label: "Tech Stack",
|
||||
description: "Languages, frameworks, databases"
|
||||
},
|
||||
{
|
||||
label: "Custom Commands",
|
||||
description: "Project-specific commands"
|
||||
},
|
||||
{
|
||||
label: "All Settings",
|
||||
description: "Review and update all categories"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
#### Update: Project Info
|
||||
|
||||
```javascript
|
||||
const current = config
|
||||
|
||||
// Show current values
|
||||
console.log(`
|
||||
Current Project Info:
|
||||
Name: ${current.name}
|
||||
Description: ${current.description}
|
||||
Owner: ${current.owner}
|
||||
`)
|
||||
|
||||
// Ask for updates
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "New project name? (or keep current)",
|
||||
header: "Name",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Keep current", description: current.name },
|
||||
{ label: "Change", description: "Enter new name" }
|
||||
]
|
||||
},
|
||||
{
|
||||
question: "New description? (or keep current)",
|
||||
header: "Description",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: "Keep current", description: current.description },
|
||||
{ label: "Change", description: "Enter new description" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
if (nameChanged) {
|
||||
await yq(`-i '.projects.${projectId}.name = "${newName}"'`, CONFIG_FILE)
|
||||
}
|
||||
if (descriptionChanged) {
|
||||
await yq(`-i '.projects.${projectId}.description = "${newDescription}"'`, CONFIG_FILE)
|
||||
}
|
||||
```
|
||||
|
||||
#### Update: Linear Settings
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [
|
||||
{
|
||||
question: "Which Linear setting to update?",
|
||||
header: "Linear",
|
||||
multiSelect: true, // Allow multiple selections
|
||||
options: [
|
||||
{ label: "Team", description: `Current: ${config.linear.team}` },
|
||||
{ label: "Project", description: `Current: ${config.linear.project}` },
|
||||
{ label: "Labels", description: `Current: ${config.linear.default_labels.join(", ")}` },
|
||||
{ label: "Workflow States", description: "Backlog, Planning, etc." }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// For each selected setting, prompt for new value
|
||||
// Then update configuration
|
||||
```
|
||||
|
||||
#### Update: External PM
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Update external PM integration?",
|
||||
header: "External PM",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Enable/Disable",
|
||||
description: `Currently: ${config.external_pm.enabled ? "Enabled" : "Disabled"}`
|
||||
},
|
||||
{
|
||||
label: "Jira Settings",
|
||||
description: "Base URL, project key"
|
||||
},
|
||||
{
|
||||
label: "Confluence Settings",
|
||||
description: "Base URL, space key"
|
||||
},
|
||||
{
|
||||
label: "Slack Settings",
|
||||
description: "Workspace, channels"
|
||||
},
|
||||
{
|
||||
label: "Disable All External PM",
|
||||
description: "Switch to Linear-only mode"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
#### Update: Code Repository
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Update repository settings?",
|
||||
header: "Repository",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Change Type",
|
||||
description: `Currently: ${config.code_repository.type}`
|
||||
},
|
||||
{
|
||||
label: "Update GitHub Settings",
|
||||
description: "Owner, repo name"
|
||||
},
|
||||
{
|
||||
label: "Update BitBucket Settings",
|
||||
description: "Workspace, repo slug"
|
||||
},
|
||||
{
|
||||
label: "Update Repository URL",
|
||||
description: "Change repository URL"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Show Changes Summary
|
||||
|
||||
After collecting all updates:
|
||||
|
||||
```javascript
|
||||
console.log(`
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📝 Changes Summary
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Project: ${projectId}
|
||||
|
||||
${changes.map(change => `
|
||||
${change.field}
|
||||
Old: ${change.oldValue}
|
||||
New: ${change.newValue}
|
||||
`).join("\n")}
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
`)
|
||||
```
|
||||
|
||||
### Step 4: Confirm and Apply
|
||||
|
||||
```javascript
|
||||
{
|
||||
questions: [{
|
||||
question: "Apply these changes?",
|
||||
header: "Confirm",
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{
|
||||
label: "Yes, apply",
|
||||
description: "Save changes to configuration"
|
||||
},
|
||||
{
|
||||
label: "Review again",
|
||||
description: "Go back and modify"
|
||||
},
|
||||
{
|
||||
label: "Cancel",
|
||||
description: "Discard all changes"
|
||||
}
|
||||
]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
If confirmed:
|
||||
|
||||
```bash
|
||||
# Apply all changes using yq
|
||||
for change in "${CHANGES[@]}"; do
|
||||
yq eval -i ".projects.$PROJECT_ID.${change.field} = ${change.value}" "$CONFIG_FILE"
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "✅ Project configuration updated!"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo "📝 Next Steps"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
echo "View updated config: /ccpm:project:show $PROJECT_ID"
|
||||
echo "List all projects: /ccpm:project:list"
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Update Linear team
|
||||
|
||||
```bash
|
||||
/ccpm:project:update my-app --field linear.team
|
||||
|
||||
# Prompts:
|
||||
# Current: Work
|
||||
# New value: Personal
|
||||
|
||||
# Result:
|
||||
# ✅ Updated linear.team from "Work" to "Personal"
|
||||
```
|
||||
|
||||
### Example 2: Interactive update
|
||||
|
||||
```bash
|
||||
/ccpm:project:update my-app
|
||||
|
||||
# Shows menu:
|
||||
# 1. Project Info
|
||||
# 2. Linear Settings
|
||||
# 3. External PM
|
||||
# ...
|
||||
|
||||
# User selects "Linear Settings"
|
||||
# Shows checkboxes for Team, Project, Labels, Workflow States
|
||||
# Updates selected fields
|
||||
```
|
||||
|
||||
### Example 3: Enable Jira integration
|
||||
|
||||
```bash
|
||||
/ccpm:project:update my-side-project
|
||||
|
||||
# Select: External PM
|
||||
# Choose: Enable Jira
|
||||
# Enter: Base URL, project key
|
||||
# Result: external_pm.enabled = true, jira configured
|
||||
```
|
||||
|
||||
## Validation
|
||||
|
||||
The command validates:
|
||||
- Project ID exists
|
||||
- Field paths are valid
|
||||
- Values match expected types
|
||||
- Required fields are not left empty
|
||||
- URLs are properly formatted
|
||||
|
||||
## Notes
|
||||
|
||||
- All changes are applied atomically
|
||||
- Invalid changes are rejected before saving
|
||||
- Can cancel at any point before confirmation
|
||||
- Use `/ccpm:project:show` to verify changes
|
||||
- Configuration file: `~/.claude/ccpm-config.yaml`
|
||||
Reference in New Issue
Block a user