mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-28 09:23:08 +08:00
feat: Add roles for issue resolution pipeline including planner, reviewer, integrator, and implementer
- Implemented `planner` role for solution design and task decomposition using issue-plan-agent. - Introduced `reviewer` role for solution review, technical feasibility validation, and risk assessment. - Created `integrator` role for queue formation and conflict detection using issue-queue-agent. - Added `implementer` role for code implementation and test verification via code-developer. - Defined message types and role boundaries for each role to ensure clear responsibilities. - Established a team configuration file to manage roles, pipelines, and collaboration patterns for the issue processing pipeline.
This commit is contained in:
@@ -304,7 +304,7 @@ const workflowConfig = {
|
||||
|
||||
// Features
|
||||
features: {
|
||||
hasAutoMode: true, // --yes flag support
|
||||
hasAutoMode: true, // Interactive preference collection (AskUserQuestion)
|
||||
hasConditionalPhases: true, // some phases may be skipped
|
||||
hasTodoWriteSubTasks: true, // phases expand into sub-tasks
|
||||
hasPlanningNotes: true, // accumulated state document
|
||||
|
||||
@@ -267,10 +267,11 @@ Extract from source orchestrator or generate from config:
|
||||
function generateOrchestratorSections(config, sourceContent) {
|
||||
const sections = [];
|
||||
|
||||
// Auto Mode (if feature enabled)
|
||||
// Interactive Preference Collection + Auto Mode (if feature enabled)
|
||||
if (config.features.hasAutoMode) {
|
||||
sections.push(extractOrGenerate(sourceContent, 'Auto Mode',
|
||||
'## Auto Mode\n\nWhen `--yes` or `-y`: Auto-continue all phases, use recommended defaults.\n'));
|
||||
sections.push(generateInteractivePreferenceCollection(config));
|
||||
sections.push(extractOrGenerate(sourceContent, 'Auto Mode Defaults',
|
||||
'## Auto Mode Defaults\n\nWhen `workflowPreferences.autoYes === true`: Auto-continue all phases, use recommended defaults.\n'));
|
||||
}
|
||||
|
||||
// Core Rules
|
||||
@@ -334,7 +335,69 @@ function generateDefaultErrorHandling() {
|
||||
}
|
||||
```
|
||||
|
||||
## Step 2.8: Assemble SKILL.md
|
||||
## Step 2.8: Generate Interactive Preference Collection
|
||||
|
||||
When the skill has configurable behaviors (auto mode, force options, etc.), generate the AskUserQuestion-based preference collection section for SKILL.md:
|
||||
|
||||
```javascript
|
||||
function generateInteractivePreferenceCollection(config) {
|
||||
if (!config.features.hasAutoMode && !config.preferenceQuestions?.length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let section = '## Interactive Preference Collection\n\n';
|
||||
section += 'Collect workflow preferences via AskUserQuestion before dispatching to phases:\n\n';
|
||||
section += '```javascript\n';
|
||||
section += 'const prefResponse = AskUserQuestion({\n';
|
||||
section += ' questions: [\n';
|
||||
|
||||
// Always include auto mode question if feature enabled
|
||||
if (config.features.hasAutoMode) {
|
||||
section += ' {\n';
|
||||
section += ' question: "是否跳过所有确认步骤(自动模式)?",\n';
|
||||
section += ' header: "Auto Mode",\n';
|
||||
section += ' multiSelect: false,\n';
|
||||
section += ' options: [\n';
|
||||
section += ' { label: "Interactive (Recommended)", description: "交互模式,包含确认步骤" },\n';
|
||||
section += ' { label: "Auto", description: "跳过所有确认,自动执行" }\n';
|
||||
section += ' ]\n';
|
||||
section += ' },\n';
|
||||
}
|
||||
|
||||
// Add custom preference questions
|
||||
for (const pq of (config.preferenceQuestions || [])) {
|
||||
section += ` {\n`;
|
||||
section += ` question: "${pq.question}",\n`;
|
||||
section += ` header: "${pq.header}",\n`;
|
||||
section += ` multiSelect: false,\n`;
|
||||
section += ` options: [\n`;
|
||||
for (const opt of pq.options) {
|
||||
section += ` { label: "${opt.label}", description: "${opt.description}" },\n`;
|
||||
}
|
||||
section += ` ]\n`;
|
||||
section += ` },\n`;
|
||||
}
|
||||
|
||||
section += ' ]\n';
|
||||
section += '})\n\n';
|
||||
section += '// Derive workflowPreferences from user selection\n';
|
||||
section += 'workflowPreferences = {\n';
|
||||
if (config.features.hasAutoMode) {
|
||||
section += ' autoYes: prefResponse.autoMode === "Auto",\n';
|
||||
}
|
||||
for (const pq of (config.preferenceQuestions || [])) {
|
||||
section += ` ${pq.key}: prefResponse.${pq.header.toLowerCase().replace(/\\s+/g, '')} === "${pq.activeValue}",\n`;
|
||||
}
|
||||
section += '}\n';
|
||||
section += '```\n\n';
|
||||
section += '**workflowPreferences** is passed to phase execution as context variable.\n';
|
||||
section += 'Phases reference as `workflowPreferences.autoYes`, `workflowPreferences.{key}`, etc.\n';
|
||||
|
||||
return section;
|
||||
}
|
||||
```
|
||||
|
||||
## Step 2.9: Assemble SKILL.md
|
||||
|
||||
```javascript
|
||||
function assembleSkillMd(config, sourceContent) {
|
||||
|
||||
@@ -25,6 +25,98 @@ Generate phase files in `phases/` directory, preserving full execution detail fr
|
||||
|
||||
**Anti-Pattern**: Creating a phase file that says "See original command for details" or "Execute the agent with appropriate parameters" - this defeats the purpose of the skill structure. The phase file must be self-contained.
|
||||
|
||||
## Phase File Content Restrictions
|
||||
|
||||
Phase files are internal execution documents. They MUST NOT contain the following prohibited content:
|
||||
|
||||
| Prohibited Pattern | Detection | Correct Location |
|
||||
|-------------------|-----------|-----------------|
|
||||
| Flag parsing (`$ARGUMENTS.includes(...)`) | Grep: `\$ARGUMENTS\.includes` | SKILL.md via AskUserQuestion → `workflowPreferences` |
|
||||
| Invocation syntax (`/skill-name "..."`) | Grep: `\/\w+[\-:]\w+\s+"` | Removed entirely (phase files are not user-facing) |
|
||||
| Conversion provenance (`Source: Converted from...`) | Grep: `Source:.*Converted from` | Removed entirely (implementation detail) |
|
||||
| Skill routing for inter-phase (`Skill(skill="...")`) | Grep: `Skill\(skill=` | Direct `Read("phases/0N-xxx.md")` |
|
||||
|
||||
### Preference Reference Pattern
|
||||
|
||||
Phase files may **reference** workflow preferences but must NOT **parse** them from arguments:
|
||||
|
||||
```javascript
|
||||
// CORRECT: Reference workflowPreferences (set by SKILL.md)
|
||||
const autoYes = workflowPreferences.autoYes
|
||||
const forceExplore = workflowPreferences.forceExplore
|
||||
|
||||
// WRONG: Parse from $ARGUMENTS
|
||||
const autoYes = $ARGUMENTS.includes('--yes') || $ARGUMENTS.includes('-y')
|
||||
const forceExplore = $ARGUMENTS.includes('--explore') || $ARGUMENTS.includes('-e')
|
||||
```
|
||||
|
||||
### Inter-Phase Handoff Pattern
|
||||
|
||||
When phase N needs to invoke phase M, use direct phase reading:
|
||||
|
||||
```javascript
|
||||
// CORRECT: Direct handoff (executionContext already set)
|
||||
Read("phases/02-lite-execute.md")
|
||||
// Execute with executionContext (Mode 1)
|
||||
|
||||
// WRONG: Skill routing (unnecessary round-trip)
|
||||
Skill(skill="workflow:lite-execute", args="--in-memory")
|
||||
```
|
||||
|
||||
### Content Restriction Enforcement
|
||||
|
||||
When extracting from commands (Step 3.2), apply content sanitization after verbatim extraction:
|
||||
|
||||
```javascript
|
||||
function sanitizePhaseContent(content) {
|
||||
let sanitized = content;
|
||||
|
||||
// Remove flag parsing blocks
|
||||
sanitized = sanitized.replace(
|
||||
/\/\/.*flag.*parsing[\s\S]*?\n(?=\n)/gi, ''
|
||||
);
|
||||
|
||||
// Remove invocation syntax examples
|
||||
sanitized = sanitized.replace(
|
||||
/^.*\/\w+[\-:]\w+\s+"[^"]*".*$/gm, ''
|
||||
);
|
||||
|
||||
// Remove conversion provenance notes
|
||||
sanitized = sanitized.replace(
|
||||
/^\*\*Source\*\*:.*Converted from.*$/gm, ''
|
||||
);
|
||||
|
||||
// Replace all $ARGUMENTS.includes patterns with workflowPreferences reference
|
||||
// Handles any flag name, not just --yes/-y
|
||||
sanitized = sanitized.replace(
|
||||
/\$ARGUMENTS\.includes\(['"]--?([^"']+)['"]\)/g,
|
||||
(match, flagName) => {
|
||||
// Map common flag names to workflowPreferences keys
|
||||
const flagMap = {
|
||||
'yes': 'workflowPreferences.autoYes',
|
||||
'y': 'workflowPreferences.autoYes',
|
||||
'explore': 'workflowPreferences.forceExplore',
|
||||
'e': 'workflowPreferences.forceExplore'
|
||||
};
|
||||
return flagMap[flagName] || `workflowPreferences.${flagName}`;
|
||||
}
|
||||
);
|
||||
// Also clean up residual || chains from multi-flag expressions
|
||||
sanitized = sanitized.replace(
|
||||
/workflowPreferences\.(\w+)\s*\|\|\s*workflowPreferences\.\1/g,
|
||||
'workflowPreferences.$1'
|
||||
);
|
||||
|
||||
// Replace Skill() inter-phase routing with direct Read (with or without args)
|
||||
sanitized = sanitized.replace(
|
||||
/Skill\(skill=["']([^"']+)["'](?:,\s*args=["']([^"']+)["'])?\)/g,
|
||||
(match, skillName) => `Read("phases/0N-xxx.md")\n// Execute with context`
|
||||
);
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
```
|
||||
|
||||
## Step 3.1: Phase File Generation Strategy
|
||||
|
||||
```javascript
|
||||
@@ -77,6 +169,16 @@ function extractPhaseFromCommand(phase, config) {
|
||||
|
||||
phaseContent += bodyContent;
|
||||
|
||||
// 2.5. Ensure Objective section exists
|
||||
if (!bodyContent.includes('## Objective')) {
|
||||
// Insert Objective after phase header, before main content
|
||||
const objectiveSection = `## Objective\n\n- ${phase.description}\n`;
|
||||
phaseContent = phaseContent.replace(
|
||||
`${phase.description}.\n\n${bodyContent}`,
|
||||
`${phase.description}.\n\n${objectiveSection}\n${bodyContent}`
|
||||
);
|
||||
}
|
||||
|
||||
// 3. Ensure Output section exists
|
||||
if (!bodyContent.includes('## Output')) {
|
||||
phaseContent += '\n## Output\n\n';
|
||||
|
||||
@@ -231,7 +231,8 @@ function validateSkillMdSections(config) {
|
||||
|
||||
// Conditional sections
|
||||
const conditionalSections = [
|
||||
{ name: 'Auto Mode', pattern: /## Auto Mode/, condition: config.features.hasAutoMode },
|
||||
{ name: 'Interactive Preference Collection', pattern: /## Interactive Preference Collection/, condition: config.features.hasAutoMode },
|
||||
{ name: 'Auto Mode Defaults', pattern: /## Auto Mode Defaults/, condition: config.features.hasAutoMode },
|
||||
{ name: 'Post-Phase Updates', pattern: /## Post-Phase Updates/, condition: config.features.hasPostPhaseUpdates }
|
||||
];
|
||||
|
||||
@@ -257,7 +258,77 @@ function validateSkillMdSections(config) {
|
||||
}
|
||||
```
|
||||
|
||||
## Step 4.6: Aggregate Results and Report
|
||||
## Step 4.6: Phase File Hygiene
|
||||
|
||||
Scan generated phase files for prohibited content patterns. Phase files are internal execution documents and must not contain user-facing syntax, flag parsing, or inter-phase routing.
|
||||
|
||||
```javascript
|
||||
function validatePhaseFileHygiene(config) {
|
||||
const skillDir = `.claude/skills/${config.skillName}`;
|
||||
const results = { errors: [], warnings: [], info: [] };
|
||||
|
||||
const prohibitedPatterns = [
|
||||
{
|
||||
name: 'Flag parsing ($ARGUMENTS)',
|
||||
regex: /\$ARGUMENTS\.includes/g,
|
||||
severity: 'error',
|
||||
fix: 'Replace with workflowPreferences.{key} reference'
|
||||
},
|
||||
{
|
||||
name: 'Invocation syntax (/skill-name)',
|
||||
regex: /\/\w+[\-:]\w+\s+["']/g,
|
||||
severity: 'warning',
|
||||
fix: 'Remove (phase files are not user-facing docs)'
|
||||
},
|
||||
{
|
||||
name: 'Conversion provenance (Source: Converted from)',
|
||||
regex: /Source:.*Converted from/g,
|
||||
severity: 'warning',
|
||||
fix: 'Remove (implementation detail)'
|
||||
},
|
||||
{
|
||||
name: 'Skill routing for inter-phase (Skill(skill=...)',
|
||||
regex: /Skill\(skill=/g,
|
||||
severity: 'error',
|
||||
fix: 'Replace with direct Read("phases/0N-xxx.md")'
|
||||
},
|
||||
{
|
||||
name: 'CLI flag definitions (--flag)',
|
||||
regex: /^\s*-\w,\s+--\w+\s+/gm,
|
||||
severity: 'warning',
|
||||
fix: 'Move flag definitions to SKILL.md Interactive Preference Collection'
|
||||
}
|
||||
];
|
||||
|
||||
for (const phase of config.phases) {
|
||||
const filename = `${String(phase.number).padStart(2, '0')}-${phase.slug}.md`;
|
||||
const filepath = `${skillDir}/phases/${filename}`;
|
||||
if (!fileExists(filepath)) continue;
|
||||
|
||||
const content = Read(filepath);
|
||||
|
||||
for (const pattern of prohibitedPatterns) {
|
||||
const matches = content.match(pattern.regex);
|
||||
if (matches && matches.length > 0) {
|
||||
const msg = `Phase ${phase.number} (${filename}): ${pattern.name} found (${matches.length} occurrence(s)). Fix: ${pattern.fix}`;
|
||||
if (pattern.severity === 'error') {
|
||||
results.errors.push(msg);
|
||||
} else {
|
||||
results.warnings.push(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (results.errors.length === 0 && results.warnings.length === 0) {
|
||||
results.info.push('Phase file hygiene: All phase files clean ✓');
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
```
|
||||
|
||||
## Step 4.7: Aggregate Results and Report
|
||||
|
||||
```javascript
|
||||
function generateValidationReport(config) {
|
||||
@@ -266,6 +337,7 @@ function generateValidationReport(config) {
|
||||
const content = validateContentQuality(config);
|
||||
const dataFlow = validateDataFlow(config);
|
||||
const sections = validateSkillMdSections(config);
|
||||
const hygiene = validatePhaseFileHygiene(config);
|
||||
|
||||
// Aggregate
|
||||
const allErrors = [
|
||||
@@ -273,21 +345,24 @@ function generateValidationReport(config) {
|
||||
...references.errors,
|
||||
...content.errors,
|
||||
...dataFlow.errors,
|
||||
...sections.errors
|
||||
...sections.errors,
|
||||
...hygiene.errors
|
||||
];
|
||||
const allWarnings = [
|
||||
...structural.warnings,
|
||||
...references.warnings,
|
||||
...content.warnings,
|
||||
...dataFlow.warnings,
|
||||
...sections.warnings
|
||||
...sections.warnings,
|
||||
...hygiene.warnings
|
||||
];
|
||||
const allInfo = [
|
||||
...structural.info,
|
||||
...references.info,
|
||||
...content.info,
|
||||
...dataFlow.info,
|
||||
...sections.info
|
||||
...sections.info,
|
||||
...hygiene.info
|
||||
];
|
||||
|
||||
// Quality gate
|
||||
@@ -325,7 +400,7 @@ ${allInfo.length > 0 ? `Info:\n${allInfo.map(i => ` ℹ ${i}`).join('\n')}` : '
|
||||
}
|
||||
```
|
||||
|
||||
## Step 4.7: Error Recovery
|
||||
## Step 4.8: Error Recovery
|
||||
|
||||
If validation fails, offer recovery options:
|
||||
|
||||
@@ -356,7 +431,7 @@ if (report.gate === 'FAIL') {
|
||||
}
|
||||
```
|
||||
|
||||
## Step 4.8: Integration Summary
|
||||
## Step 4.9: Integration Summary
|
||||
|
||||
```javascript
|
||||
function displayIntegrationSummary(config) {
|
||||
@@ -367,21 +442,20 @@ Integration Complete:
|
||||
|
||||
Usage:
|
||||
Trigger: ${config.triggers.map(t => `"${t}"`).join(', ')}
|
||||
Auto: /${config.triggers[0]} --yes "task description"
|
||||
|
||||
Design Patterns Applied:
|
||||
✓ Progressive phase loading (Ref: markers)
|
||||
✓ Phase Reference Documents table
|
||||
✓ Phase file hygiene (no flag parsing, no invocation syntax)
|
||||
${config.features.hasTodoWriteSubTasks ? '✓' : '○'} TodoWrite attachment/collapse
|
||||
${config.features.hasConditionalPhases ? '✓' : '○'} Conditional phase execution
|
||||
${config.features.hasAutoMode ? '✓' : '○'} Auto mode (--yes flag)
|
||||
${config.features.hasAutoMode ? '✓' : '○'} Interactive preference collection (AskUserQuestion)
|
||||
${config.features.hasPostPhaseUpdates ? '✓' : '○'} Post-phase state updates
|
||||
${config.features.hasPlanningNotes ? '✓' : '○'} Accumulated planning notes
|
||||
|
||||
Next Steps:
|
||||
1. Review SKILL.md orchestrator logic
|
||||
2. Review each phase file for completeness
|
||||
3. Test skill invocation: /${config.triggers[0]} "test task"
|
||||
3. Test skill invocation with trigger phrase
|
||||
4. Iterate based on execution results
|
||||
`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user