mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-01 12:23:51 +08:00
feat: add Discuss and Explore subagents for dynamic critique and code exploration
- Implement Discuss Subagent for multi-perspective critique with dynamic perspectives. - Create Explore Subagent for shared codebase exploration with centralized caching. - Add tests for CcwToolsMcpCard component to ensure enabled tools are preserved on config save. - Introduce SessionPreviewPanel component for previewing and selecting sessions for Memory V2 extraction. - Develop CommandCreateDialog component for creating/importing commands with import and CLI generate modes.
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
# Phase 1: Parameter Validation
|
||||
|
||||
Validate all required parameters for command generation.
|
||||
|
||||
## Objective
|
||||
|
||||
Ensure all required parameters are provided before proceeding with command generation:
|
||||
- **skillName**: Command identifier (required)
|
||||
- **description**: Command description (required)
|
||||
- **location**: Target scope - "project" or "user" (required)
|
||||
- **group**: Optional grouping subdirectory
|
||||
- **argumentHint**: Optional argument hint string
|
||||
|
||||
## Input
|
||||
|
||||
Parameters received from skill invocation:
|
||||
- `skillName`: string (required)
|
||||
- `description`: string (required)
|
||||
- `location`: "project" | "user" (required)
|
||||
- `group`: string (optional)
|
||||
- `argumentHint`: string (optional)
|
||||
|
||||
## Validation Rules
|
||||
|
||||
### Required Parameters
|
||||
|
||||
```javascript
|
||||
const requiredParams = {
|
||||
skillName: {
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
pattern: /^[a-z][a-z0-9-]*$/, // lowercase, alphanumeric, hyphens
|
||||
error: 'skillName must be lowercase alphanumeric with hyphens, starting with a letter'
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
minLength: 10,
|
||||
error: 'description must be at least 10 characters'
|
||||
},
|
||||
location: {
|
||||
type: 'string',
|
||||
enum: ['project', 'user'],
|
||||
error: 'location must be "project" or "user"'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Optional Parameters
|
||||
|
||||
```javascript
|
||||
const optionalParams = {
|
||||
group: {
|
||||
type: 'string',
|
||||
pattern: /^[a-z][a-z0-9-]*$/,
|
||||
default: null,
|
||||
error: 'group must be lowercase alphanumeric with hyphens'
|
||||
},
|
||||
argumentHint: {
|
||||
type: 'string',
|
||||
default: '',
|
||||
error: 'argumentHint must be a string'
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1: Extract Parameters
|
||||
|
||||
```javascript
|
||||
// Extract from skill args
|
||||
const params = {
|
||||
skillName: args.skillName,
|
||||
description: args.description,
|
||||
location: args.location,
|
||||
group: args.group || null,
|
||||
argumentHint: args.argumentHint || ''
|
||||
};
|
||||
```
|
||||
|
||||
### Step 2: Validate Required Parameters
|
||||
|
||||
```javascript
|
||||
function validateRequired(params, rules) {
|
||||
const errors = [];
|
||||
|
||||
for (const [key, rule] of Object.entries(rules)) {
|
||||
const value = params[key];
|
||||
|
||||
// Check existence
|
||||
if (value === undefined || value === null || value === '') {
|
||||
errors.push(`${key} is required`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check type
|
||||
if (typeof value !== rule.type) {
|
||||
errors.push(`${key} must be a ${rule.type}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check minLength
|
||||
if (rule.minLength && value.length < rule.minLength) {
|
||||
errors.push(`${key} must be at least ${rule.minLength} characters`);
|
||||
}
|
||||
|
||||
// Check pattern
|
||||
if (rule.pattern && !rule.pattern.test(value)) {
|
||||
errors.push(rule.error);
|
||||
}
|
||||
|
||||
// Check enum
|
||||
if (rule.enum && !rule.enum.includes(value)) {
|
||||
errors.push(`${key} must be one of: ${rule.enum.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
const requiredErrors = validateRequired(params, requiredParams);
|
||||
if (requiredErrors.length > 0) {
|
||||
throw new Error(`Validation failed:\n${requiredErrors.join('\n')}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Validate Optional Parameters
|
||||
|
||||
```javascript
|
||||
function validateOptional(params, rules) {
|
||||
const warnings = [];
|
||||
|
||||
for (const [key, rule] of Object.entries(rules)) {
|
||||
const value = params[key];
|
||||
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
if (rule.pattern && !rule.pattern.test(value)) {
|
||||
warnings.push(`${key}: ${rule.error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return warnings;
|
||||
}
|
||||
|
||||
const optionalWarnings = validateOptional(params, optionalParams);
|
||||
// Log warnings but continue
|
||||
```
|
||||
|
||||
### Step 4: Normalize Parameters
|
||||
|
||||
```javascript
|
||||
const validatedParams = {
|
||||
skillName: params.skillName.trim().toLowerCase(),
|
||||
description: params.description.trim(),
|
||||
location: params.location.trim().toLowerCase(),
|
||||
group: params.group ? params.group.trim().toLowerCase() : null,
|
||||
argumentHint: params.argumentHint ? params.argumentHint.trim() : ''
|
||||
};
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
```javascript
|
||||
{
|
||||
status: 'validated',
|
||||
params: validatedParams,
|
||||
warnings: optionalWarnings
|
||||
}
|
||||
```
|
||||
|
||||
## Next Phase
|
||||
|
||||
Proceed to [Phase 2: Target Path Resolution](02-target-path-resolution.md) with `validatedParams`.
|
||||
@@ -0,0 +1,171 @@
|
||||
# Phase 2: Target Path Resolution
|
||||
|
||||
Resolve the target commands directory based on location parameter.
|
||||
|
||||
## Objective
|
||||
|
||||
Determine the correct target path for the command file based on:
|
||||
- **location**: "project" or "user" scope
|
||||
- **group**: Optional subdirectory for command organization
|
||||
- **skillName**: Command filename (with .md extension)
|
||||
|
||||
## Input
|
||||
|
||||
From Phase 1 validation:
|
||||
```javascript
|
||||
{
|
||||
skillName: string, // e.g., "create"
|
||||
description: string,
|
||||
location: "project" | "user",
|
||||
group: string | null, // e.g., "issue"
|
||||
argumentHint: string
|
||||
}
|
||||
```
|
||||
|
||||
## Path Resolution Rules
|
||||
|
||||
### Location Mapping
|
||||
|
||||
```javascript
|
||||
const locationMap = {
|
||||
project: '.claude/commands',
|
||||
user: '~/.claude/commands' // Expands to user home directory
|
||||
};
|
||||
```
|
||||
|
||||
### Path Construction
|
||||
|
||||
```javascript
|
||||
function resolveTargetPath(params) {
|
||||
const baseDir = locationMap[params.location];
|
||||
|
||||
if (!baseDir) {
|
||||
throw new Error(`Invalid location: ${params.location}. Must be "project" or "user".`);
|
||||
}
|
||||
|
||||
// Expand ~ to user home if present
|
||||
const expandedBase = baseDir.startsWith('~')
|
||||
? path.join(os.homedir(), baseDir.slice(1))
|
||||
: baseDir;
|
||||
|
||||
// Build full path
|
||||
let targetPath;
|
||||
if (params.group) {
|
||||
// Grouped command: .claude/commands/{group}/{skillName}.md
|
||||
targetPath = path.join(expandedBase, params.group, `${params.skillName}.md`);
|
||||
} else {
|
||||
// Top-level command: .claude/commands/{skillName}.md
|
||||
targetPath = path.join(expandedBase, `${params.skillName}.md`);
|
||||
}
|
||||
|
||||
return targetPath;
|
||||
}
|
||||
```
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1: Get Base Directory
|
||||
|
||||
```javascript
|
||||
const location = validatedParams.location;
|
||||
const baseDir = locationMap[location];
|
||||
|
||||
if (!baseDir) {
|
||||
throw new Error(`Invalid location: ${location}. Must be "project" or "user".`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Expand User Path (if applicable)
|
||||
|
||||
```javascript
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
|
||||
let expandedBase = baseDir;
|
||||
if (baseDir.startsWith('~')) {
|
||||
expandedBase = path.join(os.homedir(), baseDir.slice(1));
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Construct Full Path
|
||||
|
||||
```javascript
|
||||
let targetPath;
|
||||
let targetDir;
|
||||
|
||||
if (validatedParams.group) {
|
||||
// Command with group subdirectory
|
||||
targetDir = path.join(expandedBase, validatedParams.group);
|
||||
targetPath = path.join(targetDir, `${validatedParams.skillName}.md`);
|
||||
} else {
|
||||
// Top-level command
|
||||
targetDir = expandedBase;
|
||||
targetPath = path.join(targetDir, `${validatedParams.skillName}.md`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Ensure Target Directory Exists
|
||||
|
||||
```javascript
|
||||
// Check and create directory if needed
|
||||
Bash(`mkdir -p "${targetDir}"`);
|
||||
```
|
||||
|
||||
### Step 5: Check File Existence
|
||||
|
||||
```javascript
|
||||
const fileExists = Bash(`test -f "${targetPath}" && echo "EXISTS" || echo "NOT_FOUND"`);
|
||||
|
||||
if (fileExists.includes('EXISTS')) {
|
||||
console.warn(`Warning: Command file already exists at ${targetPath}. Will overwrite.`);
|
||||
}
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
```javascript
|
||||
{
|
||||
status: 'resolved',
|
||||
targetPath: targetPath, // Full path to command file
|
||||
targetDir: targetDir, // Directory containing command
|
||||
fileName: `${skillName}.md`,
|
||||
fileExists: fileExists.includes('EXISTS'),
|
||||
params: validatedParams // Pass through to next phase
|
||||
}
|
||||
```
|
||||
|
||||
## Path Examples
|
||||
|
||||
### Project Scope (No Group)
|
||||
```
|
||||
location: "project"
|
||||
skillName: "deploy"
|
||||
-> .claude/commands/deploy.md
|
||||
```
|
||||
|
||||
### Project Scope (With Group)
|
||||
```
|
||||
location: "project"
|
||||
skillName: "create"
|
||||
group: "issue"
|
||||
-> .claude/commands/issue/create.md
|
||||
```
|
||||
|
||||
### User Scope (No Group)
|
||||
```
|
||||
location: "user"
|
||||
skillName: "global-status"
|
||||
-> ~/.claude/commands/global-status.md
|
||||
```
|
||||
|
||||
### User Scope (With Group)
|
||||
```
|
||||
location: "user"
|
||||
skillName: "sync"
|
||||
group: "session"
|
||||
-> ~/.claude/commands/session/sync.md
|
||||
```
|
||||
|
||||
## Next Phase
|
||||
|
||||
Proceed to [Phase 3: Template Loading](03-template-loading.md) with `targetPath` and `params`.
|
||||
123
.claude/skills/command-generator/phases/03-template-loading.md
Normal file
123
.claude/skills/command-generator/phases/03-template-loading.md
Normal file
@@ -0,0 +1,123 @@
|
||||
# Phase 3: Template Loading
|
||||
|
||||
Load the command template file for content generation.
|
||||
|
||||
## Objective
|
||||
|
||||
Load the command template from the skill's templates directory. The template provides:
|
||||
- YAML frontmatter structure
|
||||
- Placeholder variables for substitution
|
||||
- Standard command file sections
|
||||
|
||||
## Input
|
||||
|
||||
From Phase 2:
|
||||
```javascript
|
||||
{
|
||||
targetPath: string,
|
||||
targetDir: string,
|
||||
fileName: string,
|
||||
fileExists: boolean,
|
||||
params: {
|
||||
skillName: string,
|
||||
description: string,
|
||||
location: string,
|
||||
group: string | null,
|
||||
argumentHint: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Template Location
|
||||
|
||||
```
|
||||
.claude/skills/command-generator/templates/command-md.md
|
||||
```
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1: Locate Template File
|
||||
|
||||
```javascript
|
||||
// Template is located in the skill's templates directory
|
||||
const skillDir = '.claude/skills/command-generator';
|
||||
const templatePath = `${skillDir}/templates/command-md.md`;
|
||||
```
|
||||
|
||||
### Step 2: Read Template Content
|
||||
|
||||
```javascript
|
||||
const templateContent = Read(templatePath);
|
||||
|
||||
if (!templateContent) {
|
||||
throw new Error(`Command template not found at ${templatePath}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Validate Template Structure
|
||||
|
||||
```javascript
|
||||
// Verify template contains expected placeholders
|
||||
const requiredPlaceholders = ['{{name}}', '{{description}}'];
|
||||
const optionalPlaceholders = ['{{group}}', '{{argumentHint}}'];
|
||||
|
||||
for (const placeholder of requiredPlaceholders) {
|
||||
if (!templateContent.includes(placeholder)) {
|
||||
throw new Error(`Template missing required placeholder: ${placeholder}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Store Template for Next Phase
|
||||
|
||||
```javascript
|
||||
const template = {
|
||||
content: templateContent,
|
||||
requiredPlaceholders: requiredPlaceholders,
|
||||
optionalPlaceholders: optionalPlaceholders
|
||||
};
|
||||
```
|
||||
|
||||
## Template Format Reference
|
||||
|
||||
The template should follow this structure:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: {{name}}
|
||||
description: {{description}}
|
||||
{{#if group}}group: {{group}}{{/if}}
|
||||
{{#if argumentHint}}argument-hint: {{argumentHint}}{{/if}}
|
||||
---
|
||||
|
||||
# {{name}} Command
|
||||
|
||||
[Template content with placeholders]
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
```javascript
|
||||
{
|
||||
status: 'loaded',
|
||||
template: {
|
||||
content: templateContent,
|
||||
requiredPlaceholders: requiredPlaceholders,
|
||||
optionalPlaceholders: optionalPlaceholders
|
||||
},
|
||||
targetPath: targetPath,
|
||||
params: params
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Action |
|
||||
|-------|--------|
|
||||
| Template file not found | Throw error with path |
|
||||
| Missing required placeholder | Throw error with missing placeholder name |
|
||||
| Empty template | Throw error |
|
||||
|
||||
## Next Phase
|
||||
|
||||
Proceed to [Phase 4: Content Formatting](04-content-formatting.md) with `template`, `targetPath`, and `params`.
|
||||
184
.claude/skills/command-generator/phases/04-content-formatting.md
Normal file
184
.claude/skills/command-generator/phases/04-content-formatting.md
Normal file
@@ -0,0 +1,184 @@
|
||||
# Phase 4: Content Formatting
|
||||
|
||||
Format template content by substituting placeholders with parameter values.
|
||||
|
||||
## Objective
|
||||
|
||||
Replace all placeholder variables in the template with validated parameter values:
|
||||
- `{{name}}` -> skillName
|
||||
- `{{description}}` -> description
|
||||
- `{{group}}` -> group (if provided)
|
||||
- `{{argumentHint}}` -> argumentHint (if provided)
|
||||
|
||||
## Input
|
||||
|
||||
From Phase 3:
|
||||
```javascript
|
||||
{
|
||||
template: {
|
||||
content: string,
|
||||
requiredPlaceholders: string[],
|
||||
optionalPlaceholders: string[]
|
||||
},
|
||||
targetPath: string,
|
||||
params: {
|
||||
skillName: string,
|
||||
description: string,
|
||||
location: string,
|
||||
group: string | null,
|
||||
argumentHint: string
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Placeholder Mapping
|
||||
|
||||
```javascript
|
||||
const placeholderMap = {
|
||||
'{{name}}': params.skillName,
|
||||
'{{description}}': params.description,
|
||||
'{{group}}': params.group || '',
|
||||
'{{argumentHint}}': params.argumentHint || ''
|
||||
};
|
||||
```
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1: Initialize Content
|
||||
|
||||
```javascript
|
||||
let formattedContent = template.content;
|
||||
```
|
||||
|
||||
### Step 2: Substitute Required Placeholders
|
||||
|
||||
```javascript
|
||||
// These must always be replaced
|
||||
formattedContent = formattedContent.replace(/\{\{name\}\}/g, params.skillName);
|
||||
formattedContent = formattedContent.replace(/\{\{description\}\}/g, params.description);
|
||||
```
|
||||
|
||||
### Step 3: Handle Optional Placeholders
|
||||
|
||||
```javascript
|
||||
// Group placeholder
|
||||
if (params.group) {
|
||||
formattedContent = formattedContent.replace(/\{\{group\}\}/g, params.group);
|
||||
} else {
|
||||
// Remove group line if not provided
|
||||
formattedContent = formattedContent.replace(/^group: \{\{group\}\}\n?/gm, '');
|
||||
formattedContent = formattedContent.replace(/\{\{group\}\}/g, '');
|
||||
}
|
||||
|
||||
// Argument hint placeholder
|
||||
if (params.argumentHint) {
|
||||
formattedContent = formattedContent.replace(/\{\{argumentHint\}\}/g, params.argumentHint);
|
||||
} else {
|
||||
// Remove argument-hint line if not provided
|
||||
formattedContent = formattedContent.replace(/^argument-hint: \{\{argumentHint\}\}\n?/gm, '');
|
||||
formattedContent = formattedContent.replace(/\{\{argumentHint\}\}/g, '');
|
||||
}
|
||||
```
|
||||
|
||||
### Step 4: Handle Conditional Sections
|
||||
|
||||
```javascript
|
||||
// Remove empty frontmatter lines (caused by missing optional fields)
|
||||
formattedContent = formattedContent.replace(/\n{3,}/g, '\n\n');
|
||||
|
||||
// Handle {{#if group}} style conditionals
|
||||
if (formattedContent.includes('{{#if')) {
|
||||
// Process group conditional
|
||||
if (params.group) {
|
||||
formattedContent = formattedContent.replace(/\{\{#if group\}\}([\s\S]*?)\{\{\/if\}\}/g, '$1');
|
||||
} else {
|
||||
formattedContent = formattedContent.replace(/\{\{#if group\}\}[\s\S]*?\{\{\/if\}\}/g, '');
|
||||
}
|
||||
|
||||
// Process argumentHint conditional
|
||||
if (params.argumentHint) {
|
||||
formattedContent = formattedContent.replace(/\{\{#if argumentHint\}\}([\s\S]*?)\{\{\/if\}\}/g, '$1');
|
||||
} else {
|
||||
formattedContent = formattedContent.replace(/\{\{#if argumentHint\}\}[\s\S]*?\{\{\/if\}\}/g, '');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Validate Final Content
|
||||
|
||||
```javascript
|
||||
// Ensure no unresolved placeholders remain
|
||||
const unresolvedPlaceholders = formattedContent.match(/\{\{[^}]+\}\}/g);
|
||||
if (unresolvedPlaceholders) {
|
||||
console.warn(`Warning: Unresolved placeholders found: ${unresolvedPlaceholders.join(', ')}`);
|
||||
}
|
||||
|
||||
// Ensure frontmatter is valid
|
||||
const frontmatterMatch = formattedContent.match(/^---\n([\s\S]*?)\n---/);
|
||||
if (!frontmatterMatch) {
|
||||
throw new Error('Generated content has invalid frontmatter structure');
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Generate Summary
|
||||
|
||||
```javascript
|
||||
const summary = {
|
||||
name: params.skillName,
|
||||
description: params.description.substring(0, 50) + (params.description.length > 50 ? '...' : ''),
|
||||
location: params.location,
|
||||
group: params.group,
|
||||
hasArgumentHint: !!params.argumentHint
|
||||
};
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
```javascript
|
||||
{
|
||||
status: 'formatted',
|
||||
content: formattedContent,
|
||||
targetPath: targetPath,
|
||||
summary: summary
|
||||
}
|
||||
```
|
||||
|
||||
## Content Example
|
||||
|
||||
### Input Template
|
||||
```markdown
|
||||
---
|
||||
name: {{name}}
|
||||
description: {{description}}
|
||||
{{#if group}}group: {{group}}{{/if}}
|
||||
{{#if argumentHint}}argument-hint: {{argumentHint}}{{/if}}
|
||||
---
|
||||
|
||||
# {{name}} Command
|
||||
```
|
||||
|
||||
### Output (with all fields)
|
||||
```markdown
|
||||
---
|
||||
name: create
|
||||
description: Create structured issue from GitHub URL or text description
|
||||
group: issue
|
||||
argument-hint: [-y|--yes] <github-url | text-description> [--priority 1-5]
|
||||
---
|
||||
|
||||
# create Command
|
||||
```
|
||||
|
||||
### Output (minimal fields)
|
||||
```markdown
|
||||
---
|
||||
name: deploy
|
||||
description: Deploy application to production environment
|
||||
---
|
||||
|
||||
# deploy Command
|
||||
```
|
||||
|
||||
## Next Phase
|
||||
|
||||
Proceed to [Phase 5: File Generation](05-file-generation.md) with `content` and `targetPath`.
|
||||
185
.claude/skills/command-generator/phases/05-file-generation.md
Normal file
185
.claude/skills/command-generator/phases/05-file-generation.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# Phase 5: File Generation
|
||||
|
||||
Write the formatted content to the target command file.
|
||||
|
||||
## Objective
|
||||
|
||||
Generate the final command file by:
|
||||
1. Checking for existing file (warn if present)
|
||||
2. Writing formatted content to target path
|
||||
3. Confirming successful generation
|
||||
|
||||
## Input
|
||||
|
||||
From Phase 4:
|
||||
```javascript
|
||||
{
|
||||
status: 'formatted',
|
||||
content: string,
|
||||
targetPath: string,
|
||||
summary: {
|
||||
name: string,
|
||||
description: string,
|
||||
location: string,
|
||||
group: string | null,
|
||||
hasArgumentHint: boolean
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1: Pre-Write Check
|
||||
|
||||
```javascript
|
||||
// Check if file already exists
|
||||
const fileExists = Bash(`test -f "${targetPath}" && echo "EXISTS" || echo "NOT_FOUND"`);
|
||||
|
||||
if (fileExists.includes('EXISTS')) {
|
||||
console.warn(`
|
||||
WARNING: Command file already exists at: ${targetPath}
|
||||
The file will be overwritten with new content.
|
||||
`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Ensure Directory Exists
|
||||
|
||||
```javascript
|
||||
// Get directory from target path
|
||||
const targetDir = path.dirname(targetPath);
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
Bash(`mkdir -p "${targetDir}"`);
|
||||
```
|
||||
|
||||
### Step 3: Write File
|
||||
|
||||
```javascript
|
||||
// Write the formatted content
|
||||
Write(targetPath, content);
|
||||
```
|
||||
|
||||
### Step 4: Verify Write
|
||||
|
||||
```javascript
|
||||
// Confirm file was created
|
||||
const verifyExists = Bash(`test -f "${targetPath}" && echo "SUCCESS" || echo "FAILED"`);
|
||||
|
||||
if (!verifyExists.includes('SUCCESS')) {
|
||||
throw new Error(`Failed to create command file at ${targetPath}`);
|
||||
}
|
||||
|
||||
// Verify content was written
|
||||
const writtenContent = Read(targetPath);
|
||||
if (!writtenContent || writtenContent.length === 0) {
|
||||
throw new Error(`Command file created but appears to be empty`);
|
||||
}
|
||||
```
|
||||
|
||||
### Step 5: Generate Success Report
|
||||
|
||||
```javascript
|
||||
const report = {
|
||||
status: 'completed',
|
||||
file: {
|
||||
path: targetPath,
|
||||
name: summary.name,
|
||||
location: summary.location,
|
||||
group: summary.group,
|
||||
size: writtenContent.length,
|
||||
created: new Date().toISOString()
|
||||
},
|
||||
command: {
|
||||
name: summary.name,
|
||||
description: summary.description,
|
||||
hasArgumentHint: summary.hasArgumentHint
|
||||
},
|
||||
nextSteps: [
|
||||
`Edit ${targetPath} to add implementation details`,
|
||||
'Add usage examples and execution flow',
|
||||
'Test the command with Claude Code'
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
### Success Output
|
||||
|
||||
```javascript
|
||||
{
|
||||
status: 'completed',
|
||||
file: {
|
||||
path: '.claude/commands/issue/create.md',
|
||||
name: 'create',
|
||||
location: 'project',
|
||||
group: 'issue',
|
||||
size: 1234,
|
||||
created: '2026-02-27T12:00:00.000Z'
|
||||
},
|
||||
command: {
|
||||
name: 'create',
|
||||
description: 'Create structured issue from GitHub URL...',
|
||||
hasArgumentHint: true
|
||||
},
|
||||
nextSteps: [
|
||||
'Edit .claude/commands/issue/create.md to add implementation details',
|
||||
'Add usage examples and execution flow',
|
||||
'Test the command with Claude Code'
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Console Output
|
||||
|
||||
```
|
||||
Command generated successfully!
|
||||
|
||||
File: .claude/commands/issue/create.md
|
||||
Name: create
|
||||
Description: Create structured issue from GitHub URL...
|
||||
Location: project
|
||||
Group: issue
|
||||
|
||||
Next Steps:
|
||||
1. Edit .claude/commands/issue/create.md to add implementation details
|
||||
2. Add usage examples and execution flow
|
||||
3. Test the command with Claude Code
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Error | Action |
|
||||
|-------|--------|
|
||||
| Directory creation failed | Throw error with directory path |
|
||||
| File write failed | Throw error with target path |
|
||||
| Empty file detected | Throw error and attempt cleanup |
|
||||
| Permission denied | Throw error with permission hint |
|
||||
|
||||
## Cleanup on Failure
|
||||
|
||||
```javascript
|
||||
// If any step fails, attempt to clean up partial artifacts
|
||||
function cleanup(targetPath) {
|
||||
try {
|
||||
Bash(`rm -f "${targetPath}"`);
|
||||
} catch (e) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Completion
|
||||
|
||||
The command file has been successfully generated. The skill execution is complete.
|
||||
|
||||
### Usage Example
|
||||
|
||||
```bash
|
||||
# Use the generated command
|
||||
/issue:create https://github.com/owner/repo/issues/123
|
||||
|
||||
# Or with the group prefix
|
||||
/issue:create "Login fails with special chars"
|
||||
```
|
||||
Reference in New Issue
Block a user