agileflow 2.60.0 → 2.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +9 -9
  2. package/package.json +1 -1
  3. package/scripts/lib/counter.js +103 -0
  4. package/src/core/commands/auto.md +1 -0
  5. package/src/core/commands/babysit.md +170 -29
  6. package/src/core/commands/board.md +1 -0
  7. package/src/core/commands/ci.md +1 -0
  8. package/src/core/commands/compress.md +1 -0
  9. package/src/core/commands/deploy.md +1 -0
  10. package/src/core/commands/help.md +1 -0
  11. package/src/core/commands/research.md +1 -0
  12. package/src/core/commands/skill/create.md +566 -0
  13. package/src/core/commands/skill/delete.md +189 -0
  14. package/src/core/commands/skill/edit.md +245 -0
  15. package/src/core/commands/skill/list.md +155 -0
  16. package/src/core/commands/skill/test.md +249 -0
  17. package/src/core/commands/template.md +1 -0
  18. package/src/core/commands/tests.md +1 -0
  19. package/src/core/commands/update.md +1 -0
  20. package/src/core/commands/velocity.md +1 -0
  21. package/src/core/experts/refactor/expertise.yaml +17 -12
  22. package/src/core/templates/claude-settings.advanced.example.json +1 -1
  23. package/src/core/templates/claude-settings.example.json +1 -1
  24. package/tools/cli/commands/list.js +8 -13
  25. package/tools/cli/installers/core/installer.js +20 -19
  26. package/tools/cli/installers/ide/_base-ide.js +18 -4
  27. package/tools/cli/installers/ide/claude-code.js +4 -15
  28. package/tools/cli/installers/ide/codex.js +9 -13
  29. package/tools/cli/lib/content-injector.js +162 -31
  30. package/tools/cli/lib/utils.js +87 -0
  31. package/src/core/skills/acceptance-criteria-generator/SKILL.md +0 -46
  32. package/src/core/skills/adr-template/SKILL.md +0 -62
  33. package/src/core/skills/agileflow-acceptance-criteria/SKILL.md +0 -156
  34. package/src/core/skills/agileflow-adr/SKILL.md +0 -147
  35. package/src/core/skills/agileflow-adr/examples/database-choice-example.md +0 -122
  36. package/src/core/skills/agileflow-adr/templates/adr-template.md +0 -69
  37. package/src/core/skills/agileflow-commit-messages/SKILL.md +0 -130
  38. package/src/core/skills/agileflow-commit-messages/reference/bad-examples.md +0 -168
  39. package/src/core/skills/agileflow-commit-messages/reference/good-examples.md +0 -120
  40. package/src/core/skills/agileflow-commit-messages/scripts/check-attribution.sh +0 -15
  41. package/src/core/skills/agileflow-epic-planner/SKILL.md +0 -184
  42. package/src/core/skills/agileflow-retro-facilitator/SKILL.md +0 -119
  43. package/src/core/skills/agileflow-retro-facilitator/cookbook/4ls.md +0 -86
  44. package/src/core/skills/agileflow-retro-facilitator/cookbook/glad-sad-mad.md +0 -79
  45. package/src/core/skills/agileflow-retro-facilitator/cookbook/start-stop-continue.md +0 -142
  46. package/src/core/skills/agileflow-retro-facilitator/prompts/action-items.md +0 -83
  47. package/src/core/skills/agileflow-sprint-planner/SKILL.md +0 -212
  48. package/src/core/skills/agileflow-story-writer/SKILL.md +0 -163
  49. package/src/core/skills/agileflow-story-writer/examples/good-story-example.md +0 -63
  50. package/src/core/skills/agileflow-story-writer/templates/story-template.md +0 -44
  51. package/src/core/skills/agileflow-tech-debt/SKILL.md +0 -215
  52. package/src/core/skills/api-documentation-generator/SKILL.md +0 -65
  53. package/src/core/skills/changelog-entry/SKILL.md +0 -55
  54. package/src/core/skills/commit-message-formatter/SKILL.md +0 -50
  55. package/src/core/skills/deployment-guide-generator/SKILL.md +0 -84
  56. package/src/core/skills/diagram-generator/SKILL.md +0 -65
  57. package/src/core/skills/error-handler-template/SKILL.md +0 -78
  58. package/src/core/skills/migration-checklist/SKILL.md +0 -82
  59. package/src/core/skills/pr-description/SKILL.md +0 -65
  60. package/src/core/skills/sql-schema-generator/SKILL.md +0 -69
  61. package/src/core/skills/story-skeleton/SKILL.md +0 -34
  62. package/src/core/skills/test-case-generator/SKILL.md +0 -63
  63. package/src/core/skills/type-definitions/SKILL.md +0 -65
  64. package/src/core/skills/validation-schema-generator/SKILL.md +0 -64
  65. package/src/core/skills/writing-skills/SKILL.md +0 -352
  66. package/src/core/skills/writing-skills/testing-skills-with-subagents.md +0 -232
@@ -1,13 +1,36 @@
1
1
  /**
2
- * Content Injector - Dynamic content injection for command files
2
+ * Content Injector - Dynamic content injection for AgileFlow files
3
3
  *
4
- * Generates agent lists and command lists from source directories
5
- * and injects them into command files during installation.
4
+ * Supports template variables that get replaced at install time:
5
+ *
6
+ * COUNTS:
7
+ * {{COMMAND_COUNT}} - Total number of commands
8
+ * {{AGENT_COUNT}} - Total number of agents
9
+ * {{SKILL_COUNT}} - Total number of skills
10
+ *
11
+ * LISTS:
12
+ * <!-- {{AGENT_LIST}} --> - Full formatted agent list
13
+ * <!-- {{COMMAND_LIST}} --> - Full formatted command list
14
+ *
15
+ * METADATA:
16
+ * {{VERSION}} - AgileFlow version from package.json
17
+ * {{INSTALL_DATE}} - Date of installation (YYYY-MM-DD)
18
+ *
19
+ * FOLDER REFERENCES:
20
+ * {agileflow_folder} - Name of the agileflow folder (e.g., .agileflow)
21
+ * {project-root} - Project root reference
6
22
  */
7
23
 
8
24
  const fs = require('fs');
9
25
  const path = require('path');
26
+
27
+ // Use shared modules
10
28
  const { parseFrontmatter, normalizeTools } = require('../../../scripts/lib/frontmatter-parser');
29
+ const { countCommands, countAgents, countSkills, getCounts } = require('../../../scripts/lib/counter');
30
+
31
+ // =============================================================================
32
+ // List Generation Functions
33
+ // =============================================================================
11
34
 
12
35
  /**
13
36
  * Scan agents directory and generate formatted agent list
@@ -15,17 +38,16 @@ const { parseFrontmatter, normalizeTools } = require('../../../scripts/lib/front
15
38
  * @returns {string} Formatted agent list
16
39
  */
17
40
  function generateAgentList(agentsDir) {
41
+ if (!fs.existsSync(agentsDir)) return '';
42
+
18
43
  const files = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md'));
19
44
  const agents = [];
20
45
 
21
46
  for (const file of files) {
22
47
  const filePath = path.join(agentsDir, file);
23
48
  const content = fs.readFileSync(filePath, 'utf8');
24
-
25
- // Parse frontmatter using shared parser
26
49
  const frontmatter = parseFrontmatter(content);
27
50
 
28
- // Skip if no frontmatter found
29
51
  if (!frontmatter || Object.keys(frontmatter).length === 0) {
30
52
  continue;
31
53
  }
@@ -38,17 +60,15 @@ function generateAgentList(agentsDir) {
38
60
  });
39
61
  }
40
62
 
41
- // Sort alphabetically by name
42
63
  agents.sort((a, b) => a.name.localeCompare(b.name));
43
64
 
44
- // Generate formatted output
45
65
  let output = `**AVAILABLE AGENTS (${agents.length} total)**:\n\n`;
46
66
 
47
67
  agents.forEach((agent, index) => {
48
68
  output += `${index + 1}. **${agent.name}** (model: ${agent.model})\n`;
49
69
  output += ` - **Purpose**: ${agent.description}\n`;
50
70
  output += ` - **Tools**: ${agent.tools.join(', ')}\n`;
51
- output += ` - **Usage**: \`subagent_type: "AgileFlow:${agent.name}"\`\n`;
71
+ output += ` - **Usage**: \`subagent_type: "agileflow-${agent.name}"\`\n`;
52
72
  output += `\n`;
53
73
  });
54
74
 
@@ -61,18 +81,18 @@ function generateAgentList(agentsDir) {
61
81
  * @returns {string} Formatted command list
62
82
  */
63
83
  function generateCommandList(commandsDir) {
64
- const files = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md'));
84
+ if (!fs.existsSync(commandsDir)) return '';
85
+
65
86
  const commands = [];
66
87
 
67
- for (const file of files) {
88
+ // Scan main commands
89
+ const mainFiles = fs.readdirSync(commandsDir).filter(f => f.endsWith('.md'));
90
+ for (const file of mainFiles) {
68
91
  const filePath = path.join(commandsDir, file);
69
92
  const content = fs.readFileSync(filePath, 'utf8');
70
-
71
- // Parse frontmatter using shared parser
72
93
  const frontmatter = parseFrontmatter(content);
73
94
  const cmdName = path.basename(file, '.md');
74
95
 
75
- // Skip if no frontmatter found
76
96
  if (!frontmatter || Object.keys(frontmatter).length === 0) {
77
97
  continue;
78
98
  }
@@ -84,10 +104,34 @@ function generateCommandList(commandsDir) {
84
104
  });
85
105
  }
86
106
 
87
- // Sort alphabetically by name
107
+ // Scan subdirectories (e.g., session/)
108
+ const entries = fs.readdirSync(commandsDir, { withFileTypes: true });
109
+ for (const entry of entries) {
110
+ if (entry.isDirectory()) {
111
+ const subDir = path.join(commandsDir, entry.name);
112
+ const subFiles = fs.readdirSync(subDir).filter(f => f.endsWith('.md'));
113
+
114
+ for (const file of subFiles) {
115
+ const filePath = path.join(subDir, file);
116
+ const content = fs.readFileSync(filePath, 'utf8');
117
+ const frontmatter = parseFrontmatter(content);
118
+ const cmdName = `${entry.name}:${path.basename(file, '.md')}`;
119
+
120
+ if (!frontmatter || Object.keys(frontmatter).length === 0) {
121
+ continue;
122
+ }
123
+
124
+ commands.push({
125
+ name: cmdName,
126
+ description: frontmatter.description || '',
127
+ argumentHint: frontmatter['argument-hint'] || '',
128
+ });
129
+ }
130
+ }
131
+ }
132
+
88
133
  commands.sort((a, b) => a.name.localeCompare(b.name));
89
134
 
90
- // Generate formatted output
91
135
  let output = `Available commands (${commands.length} total):\n`;
92
136
 
93
137
  commands.forEach(cmd => {
@@ -98,33 +142,120 @@ function generateCommandList(commandsDir) {
98
142
  return output;
99
143
  }
100
144
 
145
+ // =============================================================================
146
+ // Main Injection Function
147
+ // =============================================================================
148
+
101
149
  /**
102
- * Inject dynamic content into a template file
103
- * @param {string} templateContent - Template file content with placeholders
104
- * @param {string} agentsDir - Path to agents directory
105
- * @param {string} commandsDir - Path to commands directory
106
- * @returns {string} Content with placeholders replaced
150
+ * Inject all template variables into content
151
+ * @param {string} content - Template content with placeholders
152
+ * @param {Object} context - Context for replacements
153
+ * @param {string} context.coreDir - Path to core directory (commands/, agents/, skills/)
154
+ * @param {string} context.agileflowFolder - AgileFlow folder name
155
+ * @param {string} context.version - AgileFlow version
156
+ * @returns {string} Content with all placeholders replaced
107
157
  */
108
- function injectContent(templateContent, agentsDir, commandsDir) {
109
- let result = templateContent;
158
+ function injectContent(content, context = {}) {
159
+ const { coreDir, agileflowFolder = '.agileflow', version = 'unknown' } = context;
160
+
161
+ let result = content;
110
162
 
111
- // Replace {{AGENT_LIST}} placeholder
112
- if (result.includes('{{AGENT_LIST}}')) {
113
- const agentList = generateAgentList(agentsDir);
114
- result = result.replace(/<!-- {{AGENT_LIST}} -->/g, agentList);
163
+ // Get counts if core directory is available
164
+ let counts = { commands: 0, agents: 0, skills: 0 };
165
+ if (coreDir && fs.existsSync(coreDir)) {
166
+ counts = getCounts(coreDir);
115
167
  }
116
168
 
117
- // Replace {{COMMAND_LIST}} placeholder
118
- if (result.includes('{{COMMAND_LIST}}')) {
119
- const commandList = generateCommandList(commandsDir);
120
- result = result.replace(/<!-- {{COMMAND_LIST}} -->/g, commandList);
169
+ // Replace count placeholders (both formats: {{X}} and <!-- {{X}} -->)
170
+ result = result.replace(/\{\{COMMAND_COUNT\}\}/g, String(counts.commands));
171
+ result = result.replace(/\{\{AGENT_COUNT\}\}/g, String(counts.agents));
172
+ result = result.replace(/\{\{SKILL_COUNT\}\}/g, String(counts.skills));
173
+
174
+ // Replace metadata placeholders
175
+ result = result.replace(/\{\{VERSION\}\}/g, version);
176
+ result = result.replace(/\{\{INSTALL_DATE\}\}/g, new Date().toISOString().split('T')[0]);
177
+
178
+ // Replace list placeholders (only if core directory available)
179
+ if (coreDir && fs.existsSync(coreDir)) {
180
+ if (result.includes('{{AGENT_LIST}}')) {
181
+ const agentList = generateAgentList(path.join(coreDir, 'agents'));
182
+ result = result.replace(/<!-- \{\{AGENT_LIST\}\} -->/g, agentList);
183
+ result = result.replace(/\{\{AGENT_LIST\}\}/g, agentList);
184
+ }
185
+
186
+ if (result.includes('{{COMMAND_LIST}}')) {
187
+ const commandList = generateCommandList(path.join(coreDir, 'commands'));
188
+ result = result.replace(/<!-- \{\{COMMAND_LIST\}\} -->/g, commandList);
189
+ result = result.replace(/\{\{COMMAND_LIST\}\}/g, commandList);
190
+ }
121
191
  }
122
192
 
193
+ // Replace folder placeholders
194
+ result = result.replace(/\{agileflow_folder\}/g, agileflowFolder);
195
+ result = result.replace(/\{project-root\}/g, '{project-root}'); // Keep as-is for runtime
196
+
123
197
  return result;
124
198
  }
125
199
 
200
+ /**
201
+ * Check if content has any template variables
202
+ * @param {string} content - Content to check
203
+ * @returns {boolean} True if content has placeholders
204
+ */
205
+ function hasPlaceholders(content) {
206
+ const patterns = [
207
+ /\{\{COMMAND_COUNT\}\}/,
208
+ /\{\{AGENT_COUNT\}\}/,
209
+ /\{\{SKILL_COUNT\}\}/,
210
+ /\{\{VERSION\}\}/,
211
+ /\{\{INSTALL_DATE\}\}/,
212
+ /\{\{AGENT_LIST\}\}/,
213
+ /\{\{COMMAND_LIST\}\}/,
214
+ /\{agileflow_folder\}/,
215
+ ];
216
+
217
+ return patterns.some(pattern => pattern.test(content));
218
+ }
219
+
220
+ /**
221
+ * List all supported placeholders
222
+ * @returns {Object} Placeholder documentation
223
+ */
224
+ function getPlaceholderDocs() {
225
+ return {
226
+ counts: {
227
+ '{{COMMAND_COUNT}}': 'Total number of slash commands',
228
+ '{{AGENT_COUNT}}': 'Total number of specialized agents',
229
+ '{{SKILL_COUNT}}': 'Total number of skills',
230
+ },
231
+ lists: {
232
+ '<!-- {{AGENT_LIST}} -->': 'Full formatted agent list with details',
233
+ '<!-- {{COMMAND_LIST}} -->': 'Full formatted command list',
234
+ },
235
+ metadata: {
236
+ '{{VERSION}}': 'AgileFlow version from package.json',
237
+ '{{INSTALL_DATE}}': 'Installation date (YYYY-MM-DD)',
238
+ },
239
+ folders: {
240
+ '{agileflow_folder}': 'Name of the AgileFlow folder',
241
+ '{project-root}': 'Project root reference (kept as-is)',
242
+ },
243
+ };
244
+ }
245
+
126
246
  module.exports = {
247
+ // Count functions
248
+ countCommands,
249
+ countAgents,
250
+ countSkills,
251
+ getCounts,
252
+
253
+ // List generation
127
254
  generateAgentList,
128
255
  generateCommandList,
256
+
257
+ // Main injection
129
258
  injectContent,
259
+ hasPlaceholders,
260
+ getPlaceholderDocs,
130
261
  };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Shared Utilities Module
3
+ *
4
+ * Common utility functions used across the CLI.
5
+ * Consolidates duplicated code from installer.js, doctor.js, etc.
6
+ */
7
+
8
+ const crypto = require('crypto');
9
+ const path = require('path');
10
+
11
+ /**
12
+ * Calculate SHA256 hash of data and return as hex string
13
+ * @param {string|Buffer} data - Data to hash
14
+ * @returns {string} Hex-encoded SHA256 hash
15
+ */
16
+ function sha256Hex(data) {
17
+ return crypto.createHash('sha256').update(data).digest('hex');
18
+ }
19
+
20
+ /**
21
+ * Convert a file path to POSIX format (forward slashes)
22
+ * @param {string} filePath - Path to convert
23
+ * @returns {string} POSIX-style path
24
+ */
25
+ function toPosixPath(filePath) {
26
+ return filePath.split(path.sep).join('/');
27
+ }
28
+
29
+ /**
30
+ * Generate a safe timestamp string for use in file/folder names
31
+ * @param {Date} date - Date to format (defaults to now)
32
+ * @returns {string} Timestamp like "2025-12-27T10-30-45-123Z"
33
+ */
34
+ function safeTimestampForPath(date = new Date()) {
35
+ return date.toISOString().replace(/[:.]/g, '-');
36
+ }
37
+
38
+ /**
39
+ * Compare two semantic version strings
40
+ * @param {string} v1 - First version (e.g., "2.61.0")
41
+ * @param {string} v2 - Second version (e.g., "2.60.0")
42
+ * @returns {number} -1 if v1 < v2, 0 if equal, 1 if v1 > v2
43
+ */
44
+ function compareVersions(v1, v2) {
45
+ const parts1 = v1.replace(/^v/, '').split('.').map(Number);
46
+ const parts2 = v2.replace(/^v/, '').split('.').map(Number);
47
+
48
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
49
+ const p1 = parts1[i] || 0;
50
+ const p2 = parts2[i] || 0;
51
+ if (p1 < p2) return -1;
52
+ if (p1 > p2) return 1;
53
+ }
54
+ return 0;
55
+ }
56
+
57
+ /**
58
+ * Create a debug logger function
59
+ * @param {boolean} enabled - Whether debug logging is enabled
60
+ * @param {string} prefix - Optional prefix for log messages
61
+ * @returns {Function} Debug log function
62
+ */
63
+ function createDebugLogger(enabled, prefix = '') {
64
+ return (...args) => {
65
+ if (enabled) {
66
+ if (prefix) {
67
+ console.log(`[${prefix}]`, ...args);
68
+ } else {
69
+ console.log(...args);
70
+ }
71
+ }
72
+ };
73
+ }
74
+
75
+ /**
76
+ * Brand color for AgileFlow (burnt orange/terracotta)
77
+ */
78
+ const BRAND_COLOR = '#e8683a';
79
+
80
+ module.exports = {
81
+ sha256Hex,
82
+ toPosixPath,
83
+ safeTimestampForPath,
84
+ compareVersions,
85
+ createDebugLogger,
86
+ BRAND_COLOR,
87
+ };
@@ -1,46 +0,0 @@
1
- ---
2
- name: acceptance-criteria-generator
3
- description: Generate properly-formatted Given/When/Then acceptance criteria for user stories
4
- ---
5
-
6
- # acceptance-criteria-generator
7
-
8
- Generate properly-formatted Given/When/Then acceptance criteria.
9
-
10
- ## Activation Keywords
11
- - "AC", "acceptance criteria", "Given When Then", "given when then", "acceptance", "criteria"
12
-
13
- ## When to Use
14
- - User is writing acceptance criteria
15
- - Need to format AC in proper Given/When/Then structure
16
- - Ensuring clarity and testability of requirements
17
-
18
- ## What This Does
19
- Takes user's plain English requirements and converts to structured Given/When/Then format:
20
- - **Given**: Initial state/preconditions
21
- - **When**: User action or trigger
22
- - **Then**: Expected outcome/result
23
-
24
- Generates multiple AC items if needed (typically 2-5 per story).
25
-
26
- Ensures each criterion is:
27
- - Testable (not vague)
28
- - Independent (doesn't depend on other AC)
29
- - Clear (unambiguous language)
30
- - Measurable (has a clear success/failure)
31
-
32
- ## Output
33
- Well-formatted acceptance criteria ready to add to story.
34
-
35
- ## Example Activation
36
- User: "User should be able to log in with email and password, and receive a JWT token"
37
- Skill: Generates:
38
- ```
39
- - **Given** a registered user with valid email and password
40
- **When** user POSTs to /api/auth/login with credentials
41
- **Then** they receive a 200 response with JWT token (24h expiration)
42
-
43
- - **Given** a user enters wrong password
44
- **When** they attempt login
45
- **Then** they receive 401 Unauthorized and rate limit applied
46
- ```
@@ -1,62 +0,0 @@
1
- ---
2
- name: adr-template
3
- description: Generate Architecture Decision Record structure with context/decision/consequences
4
- ---
5
-
6
- # adr-template
7
-
8
- Generate Architecture Decision Record structure with context/decision/consequences.
9
-
10
- ## Activation Keywords
11
- - "ADR", "architecture decision", "decision record", "adr template"
12
-
13
- ## When to Use
14
- - Major architectural decisions made during implementation
15
- - Documenting why a technology/pattern was chosen
16
- - Recording decision trade-offs and alternatives considered
17
-
18
- ## What This Does
19
- Generates complete ADR structure with:
20
- - **Title**: Clear, concise decision title
21
- - **Status**: Proposed, Accepted, Deprecated, Superseded
22
- - **Context**: Why this decision was needed, what problem does it solve
23
- - **Decision**: What was decided and why
24
- - **Consequences**: Positive outcomes and risks introduced
25
- - **Alternatives**: Other options considered and why rejected
26
- - **Related Stories**: Links to affected user stories
27
-
28
- Includes proper YAML frontmatter for integration with status.json.
29
-
30
- ## Output
31
- Ready-to-use ADR file in docs/03-decisions/adr-XXXX.md format
32
-
33
- ## Example Activation
34
- User: "We decided to use JWT for authentication instead of sessions"
35
- Skill: Generates:
36
- ```
37
- ---
38
- adr_id: ADR-0042
39
- title: Use JWT for stateless authentication
40
- status: accepted
41
- date: 2025-10-28
42
- ---
43
-
44
- ## Context
45
- Application needs scalable authentication across microservices.
46
- Session-based auth requires shared state/cache.
47
-
48
- ## Decision
49
- Adopt JWT (JSON Web Tokens) for stateless authentication.
50
-
51
- ## Consequences
52
- ✅ Benefits: Scalable, distributed, microservice-friendly
53
- ❌ Risks: Token revocation requires blacklist, larger payload
54
-
55
- ## Alternatives Considered
56
- 1. Session + Redis: Requires shared state
57
- 2. OAuth2: Overkill for internal auth
58
-
59
- ## Related Stories
60
- - US-0001: User Login API
61
- - US-0002: Password Reset
62
- ```
@@ -1,156 +0,0 @@
1
- ---
2
- name: agileflow-acceptance-criteria
3
- description: Generates detailed Given/When/Then acceptance criteria when user describes feature behavior or requirements. Enhances user stories with testable scenarios.
4
- ---
5
-
6
- # AgileFlow Acceptance Criteria
7
-
8
- Automatically generates detailed, testable acceptance criteria in Given/When/Then (Gherkin) format from feature discussions and requirements.
9
-
10
- ## When to Use
11
-
12
- This skill activates when:
13
- - User describes how a feature should behave
14
- - Discussing user workflows or interactions
15
- - Keywords: "acceptance criteria", "test scenarios", "should work like"
16
- - Creating or reviewing user stories
17
- - User describes "when user does X, then Y should happen"
18
-
19
- ## What This Does
20
-
21
- 1. Extracts feature behavior from user discussions
22
- 2. Identifies different scenarios (happy path, errors, edge cases)
23
- 3. Generates criteria in Given/When/Then format
24
- 4. Ensures criteria are specific, testable, and independent
25
- 5. Enhances story's AC section
26
-
27
- ## Instructions
28
-
29
- 1. **Listen for behavior descriptions**: User explains "when X happens, Y should occur"
30
-
31
- 2. **Extract scenarios**:
32
- - Happy path (normal use)
33
- - Error cases (what goes wrong)
34
- - Edge cases (boundary conditions)
35
- - Permission-based scenarios
36
-
37
- 3. **Generate criteria**:
38
- - One AC per distinct scenario
39
- - Clear, unambiguous language
40
- - Make it testable (observable outcome)
41
- - Include edge cases
42
-
43
- 4. **Add to story**: If working on a story, enhance its AC section
44
-
45
- ## Given/When/Then Format
46
-
47
- ```markdown
48
- ### AC#: [Criterion Name]
49
- **Given** [initial context or precondition]
50
- **When** [user action or trigger event]
51
- **Then** [expected outcome or system behavior]
52
- ```
53
-
54
- **Given** (Precondition):
55
- - User's current state, system state, data that exists, permissions/roles
56
- - Example: "Given I am logged in as an admin"
57
-
58
- **When** (Action):
59
- - What the user does, event that occurs, API call made
60
- - Example: "When I click the 'Add to Cart' button"
61
-
62
- **Then** (Outcome):
63
- - What the user sees, system state changes, data modifications, error messages
64
- - Example: "Then the item is added to my cart"
65
-
66
- ## Common Patterns
67
-
68
- **Happy Path**:
69
- ```markdown
70
- ### AC1: Successful Login
71
- **Given** I am on the login page
72
- **When** I enter valid credentials and click "Login"
73
- **Then** I am redirected to my dashboard
74
- **And** I see a welcome message with my name
75
- ```
76
-
77
- **Error Handling**:
78
- ```markdown
79
- ### AC2: Invalid Password
80
- **Given** I am on the login page
81
- **When** I enter a valid email but incorrect password
82
- **Then** I see an error message "Invalid credentials"
83
- **And** I remain on the login page
84
- ```
85
-
86
- **Edge Cases**:
87
- ```markdown
88
- ### AC3: Account Locked After Failed Attempts
89
- **Given** I have failed to login 4 times
90
- **When** I attempt to login with incorrect password again
91
- **Then** my account is locked for 30 minutes
92
- **And** an email is sent to my registered address
93
- ```
94
-
95
- **Permission-Based**:
96
- ```markdown
97
- ### AC4: Admin-Only Feature Access
98
- **Given** I am logged in as a regular user
99
- **When** I try to access the admin dashboard
100
- **Then** I see a 403 Forbidden error
101
- ```
102
-
103
- **Form Validation**:
104
- ```markdown
105
- ### AC1: Email Format Validation
106
- **Given** I am filling out the registration form
107
- **When** I enter an invalid email format (missing @)
108
- **Then** I see an error "Please enter a valid email address"
109
- **And** the submit button remains disabled
110
- ```
111
-
112
- ## Multiple Conditions
113
-
114
- Use **And** for additional conditions:
115
- ```markdown
116
- **Given** I am logged in
117
- **And** I have items in my cart
118
- **When** I click "Checkout"
119
- **Then** I am redirected to payment page
120
- **And** I see my cart summary
121
- ```
122
-
123
- Use **But** for contrasting outcomes:
124
- ```markdown
125
- **Given** I am on the search page
126
- **When** I search for "nonexistent product"
127
- **Then** I see "No results found" message
128
- **But** I see suggested categories
129
- ```
130
-
131
- ## Quality Checklist
132
-
133
- Good acceptance criteria are:
134
- - [ ] **Specific**: Clearly describes one scenario
135
- - [ ] **Testable**: Can be verified with a test
136
- - [ ] **Independent**: Doesn't depend on order of execution
137
- - [ ] **Unambiguous**: Only one interpretation possible
138
- - [ ] **Complete**: Covers happy path, errors, and edge cases
139
- - [ ] **User-focused**: Written from user's perspective
140
- - [ ] **3-7 criteria per story**: Enough coverage, not excessive
141
-
142
- ## Integration
143
-
144
- - **agileflow-story-writer**: Automatically enhances story AC sections
145
- - **agileflow-sprint-planner**: Helps estimate story complexity from AC count
146
- - **agileflow-tech-debt**: Identifies missing test coverage from AC
147
-
148
- ## Notes
149
-
150
- - Aim for 3-7 acceptance criteria per story
151
- - Too few = incomplete requirements
152
- - Too many = story should be split
153
- - Cover at least one error case per story
154
- - Include accessibility criteria when relevant
155
- - Consider mobile vs desktop differences
156
- - Think about internationalization if applicable