agileflow 2.61.0 → 2.63.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 (68) 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/commands/uninstall.js +70 -0
  26. package/tools/cli/commands/update.js +21 -4
  27. package/tools/cli/installers/core/installer.js +20 -19
  28. package/tools/cli/installers/ide/_base-ide.js +18 -4
  29. package/tools/cli/installers/ide/claude-code.js +4 -15
  30. package/tools/cli/installers/ide/codex.js +9 -13
  31. package/tools/cli/lib/content-injector.js +162 -31
  32. package/tools/cli/lib/utils.js +87 -0
  33. package/src/core/skills/acceptance-criteria-generator/SKILL.md +0 -46
  34. package/src/core/skills/adr-template/SKILL.md +0 -62
  35. package/src/core/skills/agileflow-acceptance-criteria/SKILL.md +0 -156
  36. package/src/core/skills/agileflow-adr/SKILL.md +0 -147
  37. package/src/core/skills/agileflow-adr/examples/database-choice-example.md +0 -122
  38. package/src/core/skills/agileflow-adr/templates/adr-template.md +0 -69
  39. package/src/core/skills/agileflow-commit-messages/SKILL.md +0 -130
  40. package/src/core/skills/agileflow-commit-messages/reference/bad-examples.md +0 -168
  41. package/src/core/skills/agileflow-commit-messages/reference/good-examples.md +0 -120
  42. package/src/core/skills/agileflow-commit-messages/scripts/check-attribution.sh +0 -15
  43. package/src/core/skills/agileflow-epic-planner/SKILL.md +0 -184
  44. package/src/core/skills/agileflow-retro-facilitator/SKILL.md +0 -119
  45. package/src/core/skills/agileflow-retro-facilitator/cookbook/4ls.md +0 -86
  46. package/src/core/skills/agileflow-retro-facilitator/cookbook/glad-sad-mad.md +0 -79
  47. package/src/core/skills/agileflow-retro-facilitator/cookbook/start-stop-continue.md +0 -142
  48. package/src/core/skills/agileflow-retro-facilitator/prompts/action-items.md +0 -83
  49. package/src/core/skills/agileflow-sprint-planner/SKILL.md +0 -212
  50. package/src/core/skills/agileflow-story-writer/SKILL.md +0 -163
  51. package/src/core/skills/agileflow-story-writer/examples/good-story-example.md +0 -63
  52. package/src/core/skills/agileflow-story-writer/templates/story-template.md +0 -44
  53. package/src/core/skills/agileflow-tech-debt/SKILL.md +0 -215
  54. package/src/core/skills/api-documentation-generator/SKILL.md +0 -65
  55. package/src/core/skills/changelog-entry/SKILL.md +0 -55
  56. package/src/core/skills/commit-message-formatter/SKILL.md +0 -50
  57. package/src/core/skills/deployment-guide-generator/SKILL.md +0 -84
  58. package/src/core/skills/diagram-generator/SKILL.md +0 -65
  59. package/src/core/skills/error-handler-template/SKILL.md +0 -78
  60. package/src/core/skills/migration-checklist/SKILL.md +0 -82
  61. package/src/core/skills/pr-description/SKILL.md +0 -65
  62. package/src/core/skills/sql-schema-generator/SKILL.md +0 -69
  63. package/src/core/skills/story-skeleton/SKILL.md +0 -34
  64. package/src/core/skills/test-case-generator/SKILL.md +0 -63
  65. package/src/core/skills/type-definitions/SKILL.md +0 -65
  66. package/src/core/skills/validation-schema-generator/SKILL.md +0 -64
  67. package/src/core/skills/writing-skills/SKILL.md +0 -352
  68. package/src/core/skills/writing-skills/testing-skills-with-subagents.md +0 -232
@@ -63,22 +63,11 @@ class ClaudeCodeSetup extends BaseIdeSetup {
63
63
  await this.installCommandsRecursive(agentsSource, spawnableAgentsDir, agileflowDir, false);
64
64
  console.log(chalk.dim(` - Spawnable agents: .claude/agents/agileflow/`));
65
65
 
66
- // Install skills (.claude/skills/)
67
- const skillsSource = path.join(agileflowDir, 'skills');
66
+ // Create skills directory for user-generated skills (.claude/skills/)
67
+ // AgileFlow no longer ships static skills - users generate them via /agileflow:skill:create
68
68
  const skillsTargetDir = path.join(claudeDir, 'skills');
69
- let skillCount = 0;
70
- if (await this.exists(skillsSource)) {
71
- const skillResult = await this.installCommandsRecursive(
72
- skillsSource,
73
- skillsTargetDir,
74
- agileflowDir,
75
- false
76
- );
77
- skillCount = skillResult.commands + skillResult.subdirs;
78
- if (skillCount > 0) {
79
- console.log(chalk.dim(` - Skills: .claude/skills/`));
80
- }
81
- }
69
+ await this.ensureDir(skillsTargetDir);
70
+ console.log(chalk.dim(` - Skills directory: .claude/skills/ (for user-generated skills)`));
82
71
 
83
72
  const totalCommands = commandResult.commands + agentResult.commands;
84
73
  const totalSubdirs =
@@ -16,6 +16,7 @@ const fs = require('fs-extra');
16
16
  const chalk = require('chalk');
17
17
  const yaml = require('js-yaml');
18
18
  const { BaseIdeSetup } = require('./_base-ide');
19
+ const { parseFrontmatter } = require('../../../../scripts/lib/frontmatter-parser');
19
20
 
20
21
  /**
21
22
  * OpenAI Codex CLI setup handler
@@ -59,22 +60,17 @@ class CodexSetup extends BaseIdeSetup {
59
60
  * @returns {string} Codex SKILL.md content
60
61
  */
61
62
  convertAgentToSkill(content, agentName) {
62
- // Extract frontmatter if present
63
+ // Extract frontmatter using shared parser
63
64
  let description = `AgileFlow ${agentName} agent`;
64
65
  let model = 'default';
65
66
 
66
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
67
- if (frontmatterMatch) {
68
- try {
69
- const frontmatter = yaml.load(frontmatterMatch[1]);
70
- if (frontmatter.description) {
71
- description = frontmatter.description;
72
- }
73
- if (frontmatter.model) {
74
- model = frontmatter.model;
75
- }
76
- } catch (e) {
77
- // Ignore YAML parse errors
67
+ const frontmatter = parseFrontmatter(content);
68
+ if (frontmatter && Object.keys(frontmatter).length > 0) {
69
+ if (frontmatter.description) {
70
+ description = frontmatter.description;
71
+ }
72
+ if (frontmatter.model) {
73
+ model = frontmatter.model;
78
74
  }
79
75
  }
80
76
 
@@ -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
- ```