@cloudkinetix/bmad-enhanced
Version:
Cloud-Kinetix enhanced fork of BMAD-METHOD - Breakthrough Method of Agile AI-driven Development with robust versioning and unified validation.
785 lines (649 loc) ⢠19.2 kB
Markdown
name: JIRA Interaction Flows
version: 1.0.0
role: Reusable interaction patterns for common JIRA workflows
description: Provides tested conversation flows that guide users through complex operations
capabilities:
- Pre-built conversation templates
- Adaptive flow management
- Context-aware responses
- Error recovery flows
- Learning from interactions
# JIRA Interaction Flows
You provide reusable interaction patterns that make complex JIRA operations feel like natural conversations.
## Flow Architecture
### 1. Flow Definition Structure
```typescript
interface InteractionFlow {
id: string;
name: string;
description: string;
trigger_patterns: string[]; // What activates this flow
// Flow configuration
config: {
max_turns: number;
timeout_minutes: number;
allow_interruption: boolean;
save_checkpoints: boolean;
personality: "professional" | "friendly" | "concise";
};
// Flow stages
stages: FlowStage[];
// Adaptive elements
adaptations: {
user_expertise: AdaptationRule[];
context_richness: AdaptationRule[];
time_pressure: AdaptationRule[];
};
// Success metrics
metrics: {
avg_completion_time: number;
success_rate: number;
user_satisfaction: number;
abandonment_rate: number;
};
}
interface FlowStage {
id: string;
type: "greeting" | "gather" | "confirm" | "execute" | "summary";
// Turn templates
turns: TurnTemplate[];
// Stage logic
entry_conditions: Condition[];
exit_conditions: Condition[];
skip_conditions: Condition[];
// Error handling
error_recovery: ErrorRecovery;
}
```
### 2. Turn Templates
```javascript
const turnTemplates = {
// Greeting variations
greeting: {
first_time: {
template: `Hello! I'll help you ${action}. ${enthusiasm}
${context_summary}
${next_step_preview}`,
variables: {
action: (context) => describeAction(context.intent),
enthusiasm: (context) => getEnthusiasm(context.user_mood),
context_summary: (context) => summarizeContext(context),
next_step_preview: () => "Let me start by understanding what you need.",
},
},
returning: {
template: `Welcome back! ${previous_context}
${continuation_option}`,
variables: {
previous_context: (context) => describePreviousWork(context),
continuation_option: (context) => getContinuationOptions(context),
},
},
},
// Information gathering
gather: {
missing_required: {
template: `I need a bit more information to ${goal}:
${questions}
${help_text}`,
variables: {
goal: (context) => context.current_goal,
questions: (context) => formatQuestions(context.missing_info),
help_text: (context) => (context.show_help ? getHelpText(context) : ""),
},
},
clarification: {
template: `Just to clarify - when you said "${user_input}", did you mean:
${options}
Or something else?`,
variables: {
user_input: (context) => context.ambiguous_input,
options: (context) => formatClarificationOptions(context),
},
},
},
// Confirmation patterns
confirm: {
before_execution: {
template: `${summary_icon} Here's what I'm about to do:
${action_list}
${impact_statement}
${confirmation_prompt}`,
variables: {
summary_icon: () => "š",
action_list: (context) => formatActionList(context.planned_actions),
impact_statement: (context) => describeImpact(context),
confirmation_prompt: (context) =>
context.high_risk
? "Please type 'confirm' to proceed:"
: "Ready to proceed?",
},
},
},
};
```
## Common Interaction Flows
### 1. Sprint Planning Flow
```javascript
const sprintPlanningFlow = {
id: "sprint_planning_flow",
name: "Sprint Planning Assistant",
trigger_patterns: ["plan sprint", "sprint planning", "fill sprint"],
config: {
max_turns: 15,
timeout_minutes: 30,
allow_interruption: true,
save_checkpoints: true,
personality: "friendly",
},
stages: [
{
id: "greeting",
type: "greeting",
turns: [
{
id: "welcome",
template: turnTemplates.greeting.first_time,
conditions: { is_first_interaction: true },
},
],
},
{
id: "capacity_analysis",
type: "gather",
turns: [
{
id: "check_capacity",
content: async (context) => {
const capacity = await calculateCapacity(context);
const velocity = await getVelocity(context);
return `Let's check your team's capacity for ${context.sprint_name || "the upcoming sprint"}:
š **Capacity Analysis**
⢠Team size: ${capacity.team_size} members
⢠Available hours: ${capacity.hours} (${capacity.story_points} points)
⢠Historical velocity: ${velocity.average} ± ${velocity.std_dev} points
⢠Recommended load: ${velocity.recommended} points
How would you like to fill the sprint?`;
},
options: [
{
label: "Use recommended load",
value: "recommended",
description: `Target ${velocity.recommended} points`,
},
{
label: "Fill to capacity",
value: "full_capacity",
description: `Target ${capacity.story_points} points`,
warning:
capacity.story_points > velocity.average * 1.2
? "ā ļø This is above your usual velocity"
: null,
},
{
label: "Custom target",
value: "custom",
description: "Set your own target",
},
{
label: "Focus on specific epic",
value: "epic_focus",
description: "Prioritize one epic's stories",
},
],
handlers: {
recommended: (context) => {
context.target_points = context.velocity.recommended;
return "backlog_review";
},
custom: () => "custom_target_input",
epic_focus: () => "epic_selection",
},
},
],
},
{
id: "story_selection",
type: "gather",
turns: [
{
id: "review_candidates",
content: async (context) => {
const stories = await getCandidateStories(context);
const groups = groupStoriesByEpic(stories);
return `I found ${stories.length} ready stories totaling ${getTotalPoints(stories)} points:
${formatStoryGroups(groups)}
How would you like to proceed?`;
},
options: [
{
label: "Auto-select best mix",
value: "auto_select",
description: "I'll optimize for value and dependencies",
},
{
label: "Review each story",
value: "manual_review",
description: "Go through stories one by one",
},
{
label: "Filter stories",
value: "filter",
description: "Narrow down the list",
},
],
},
],
},
{
id: "confirmation",
type: "confirm",
turns: [
{
id: "final_confirmation",
content: (context) => {
const selected = context.selected_stories;
return `## Sprint ${context.sprint_name} Plan
**Selected Stories:** ${selected.length}
**Total Points:** ${getTotalPoints(selected)}
**Target:** ${context.target_points} points
### By Epic:
${formatSelectedByEpic(selected)}
### Risks & Dependencies:
${identifyRisks(selected)}
Ready to create the sprint with these stories?`;
},
actions: [
{
label: "ā
Create sprint",
value: "create",
primary: true,
},
{
label: "š Adjust selection",
value: "adjust",
},
{
label: "ā Cancel",
value: "cancel",
},
],
},
],
},
],
};
```
### 2. Issue Investigation Flow
```javascript
const issueInvestigationFlow = {
id: "issue_investigation_flow",
name: "Issue Investigation Assistant",
trigger_patterns: ["investigate", "debug", "why is", "check issue"],
stages: [
{
id: "identify_issue",
type: "gather",
turns: [
{
id: "what_issue",
content: `What issue would you like me to investigate?`,
input_type: "smart_search",
handlers: {
found_single: (context, issue) => {
context.issue = issue;
return "investigation_type";
},
found_multiple: (context, issues) => {
context.candidates = issues;
return "disambiguate";
},
not_found: () => "manual_input",
},
},
],
},
{
id: "investigation",
type: "execute",
turns: [
{
id: "deep_analysis",
content: async (context) => {
const analysis = await performDeepAnalysis(context.issue);
return `## Investigation Results for ${context.issue.key}
### Issue Timeline
${formatTimeline(analysis.timeline)}
### State Changes
${formatStateChanges(analysis.state_changes)}
### Related Issues
${formatRelatedIssues(analysis.related)}
### Potential Causes
${formatPotentialCauses(analysis.causes)}
What would you like to explore further?`;
},
dynamic_options: (analysis) => {
const options = [];
if (analysis.has_blockers) {
options.push({
label: "Analyze blockers",
value: "analyze_blockers",
});
}
if (analysis.has_automation_errors) {
options.push({
label: "Check automation logs",
value: "check_automation",
});
}
return options;
},
},
],
},
],
};
```
### 3. Bulk Update Flow
```javascript
const bulkUpdateFlow = {
id: "bulk_update_flow",
name: "Bulk Update Assistant",
trigger_patterns: ["bulk update", "update multiple", "mass update"],
stages: [
{
id: "safety_check",
type: "confirm",
turns: [
{
id: "understand_scope",
content: `I'll help you update multiple issues. First, let's understand the scope.
What issues do you want to update?`,
options: [
{
label: "Issues from a search/filter",
value: "jql_search",
},
{
label: "Specific issue list",
value: "issue_list",
},
{
label: "All issues in sprint",
value: "sprint_issues",
},
{
label: "All issues in epic",
value: "epic_issues",
},
],
},
],
},
{
id: "preview_changes",
type: "confirm",
turns: [
{
id: "show_preview",
content: async (context) => {
const preview = await generatePreview(context);
return `## Bulk Update Preview
**Affected Issues:** ${preview.count}
**Update Type:** ${preview.update_type}
### Changes to Apply:
${formatChanges(preview.changes)}
### Sample Before/After:
${formatBeforeAfter(preview.samples)}
### ā ļø Warnings:
${formatWarnings(preview.warnings)}
This operation ${preview.reversible ? "CAN" : "CANNOT"} be easily reversed.
Proceed with the update?`;
},
require_confirmation: (preview) => preview.count > 50,
confirmation_prompt: (preview) =>
`This will update ${preview.count} issues. Type 'UPDATE ${preview.count}' to confirm:`,
},
],
},
],
error_recovery: {
partial_failure: {
content: (context, error) => `The bulk update partially failed:
ā
Successfully updated: ${error.success_count}
ā Failed: ${error.failure_count}
### Failed Issues:
${formatFailures(error.failures)}
What would you like to do?`,
options: [
{
label: "Retry failed issues",
value: "retry_failed",
},
{
label: "Skip and continue",
value: "skip",
},
{
label: "Rollback all changes",
value: "rollback",
available: (context) => context.rollback_available,
},
],
},
},
};
```
## Adaptive Behaviors
### 1. User Expertise Adaptation
```javascript
class ExpertiseAdapter {
adaptFlowForUser(flow, userProfile) {
const expertise = userProfile.jira_expertise || "intermediate";
const adaptedFlow = deepClone(flow);
switch (expertise) {
case "beginner":
// Add more explanations
adaptedFlow.stages.forEach((stage) => {
stage.turns.forEach((turn) => {
turn.include_help = true;
turn.show_examples = true;
turn.confirmation_required = true;
});
});
break;
case "expert":
// Streamline for efficiency
adaptedFlow.stages.forEach((stage) => {
stage.turns.forEach((turn) => {
turn.compact_mode = true;
turn.skip_confirmations = true;
turn.batch_operations = true;
});
});
break;
}
return adaptedFlow;
}
}
```
### 2. Context-Aware Responses
```javascript
class ContextAwareResponder {
enhanceResponse(baseResponse, context) {
let enhanced = baseResponse;
// Add relevant context
if (context.previous_issues) {
enhanced += `\n\nš” Based on your recent work with ${context.previous_issues.join(
", ",
)}, you might also want to update those.`;
}
// Add warnings based on context
if (context.near_sprint_end) {
enhanced += `\n\nā° Note: The current sprint ends in ${context.days_until_sprint_end} days.`;
}
// Add suggestions based on patterns
if (context.user_patterns.common_next_action) {
enhanced += `\n\nš® You usually ${
context.user_patterns.common_next_action
} after this. Would you like to do that next?`;
}
return enhanced;
}
}
```
### 3. Progressive Complexity
```javascript
class ProgressiveComplexity {
getComplexityLevel(interaction_count, success_rate) {
if (interaction_count < 5) return "simple";
if (success_rate < 0.8) return "simple";
if (interaction_count < 20) return "moderate";
return "advanced";
}
adjustFlowComplexity(flow, level) {
switch (level) {
case "simple":
return {
...flow,
max_options: 3,
hide_advanced: true,
verbose_explanations: true,
};
case "moderate":
return {
...flow,
max_options: 5,
show_shortcuts: true,
moderate_explanations: true,
};
case "advanced":
return {
...flow,
max_options: 10,
show_all_features: true,
minimal_explanations: true,
enable_power_features: true,
};
}
}
}
```
## Flow Analytics
### 1. Flow Performance Tracking
```javascript
class FlowAnalytics {
trackFlowExecution(flowId, execution) {
const metrics = {
flow_id: flowId,
started_at: execution.started_at,
completed_at: execution.completed_at,
duration_ms: execution.duration,
turns: {
total: execution.turn_count,
user_initiated: execution.user_turn_count,
system_initiated: execution.system_turn_count,
},
outcome: {
completed: execution.completed,
abandoned: execution.abandoned,
error: execution.error,
satisfaction: execution.user_satisfaction,
},
efficiency: {
backtracking_count: execution.backtrack_count,
clarification_count: execution.clarification_count,
error_recovery_count: execution.error_recovery_count,
},
user_behavior: {
options_explored: execution.options_explored,
help_requested: execution.help_request_count,
shortcuts_used: execution.shortcut_count,
},
};
this.saveMetrics(metrics);
this.updateFlowStatistics(flowId, metrics);
}
}
```
### 2. Flow Optimization
```javascript
class FlowOptimizer {
optimizeFlow(flowId, analytics) {
const optimizations = [];
// Identify bottlenecks
const bottlenecks = analytics.turns
.filter((t) => t.avg_duration > 30000)
.map((t) => ({
turn_id: t.id,
avg_duration: t.avg_duration,
abandonment_rate: t.abandonment_rate,
}));
bottlenecks.forEach((bottleneck) => {
optimizations.push({
type: "simplify_turn",
turn_id: bottleneck.turn_id,
reason: "High duration and abandonment",
});
});
// Find unused paths
const unused_paths = analytics.paths
.filter((p) => p.usage_count === 0)
.map((p) => p.path_id);
unused_paths.forEach((path) => {
optimizations.push({
type: "remove_path",
path_id: path,
reason: "Never used",
});
});
return optimizations;
}
}
```
## Error Recovery Flows
### 1. Generic Error Recovery
```javascript
const errorRecoveryFlow = {
connection_error: {
content: `I'm having trouble connecting to JIRA. This might be temporary.`,
options: [
{
label: "Retry now",
value: "retry",
action: () => retryLastOperation(),
},
{
label: "Save progress and continue later",
value: "save",
action: () => createCheckpoint(),
},
],
},
permission_error: {
content: (error) => `You don't have permission to ${error.operation}.
This requires: ${error.required_permission}`,
options: [
{
label: "Request permission",
value: "request",
action: () => createPermissionRequest(),
},
{
label: "Try alternative approach",
value: "alternative",
action: () => suggestAlternative(),
},
],
},
};
```
## Best Practices
1. **Start Simple**: Begin with basic flows, add complexity based on usage
2. **Learn Continuously**: Track what works and what doesn't
3. **Respect User Time**: Make interactions efficient
4. **Provide Escape Routes**: Always allow users to change course
5. **Test with Real Users**: Flows should match natural conversation patterns
Remember: The best interaction flow is one that feels so natural, users don't realize they're being guided.