agileflow 2.43.0 → 2.45.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.
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Command Registry Scanner
5
+ *
6
+ * Scans commands/ directory and extracts metadata from frontmatter.
7
+ * Returns structured command registry for use in generators.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ /**
14
+ * Extract frontmatter from markdown file
15
+ * @param {string} filePath - Path to markdown file
16
+ * @returns {object} Frontmatter object
17
+ */
18
+ function extractFrontmatter(filePath) {
19
+ const content = fs.readFileSync(filePath, 'utf-8');
20
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
21
+
22
+ if (!frontmatterMatch) {
23
+ return {};
24
+ }
25
+
26
+ const frontmatter = {};
27
+ const lines = frontmatterMatch[1].split('\n');
28
+
29
+ for (const line of lines) {
30
+ const match = line.match(/^(\w+):\s*(.+)$/);
31
+ if (match) {
32
+ const [, key, value] = match;
33
+ // Remove quotes if present
34
+ frontmatter[key] = value.replace(/^["']|["']$/g, '');
35
+ }
36
+ }
37
+
38
+ return frontmatter;
39
+ }
40
+
41
+ /**
42
+ * Categorize command based on its name/description
43
+ * @param {string} name - Command name
44
+ * @param {string} description - Command description
45
+ * @returns {string} Category name
46
+ */
47
+ function categorizeCommand(name, description) {
48
+ const categories = {
49
+ 'Story Management': ['story', 'epic', 'assign', 'status'],
50
+ 'Development': ['verify', 'baseline', 'resume', 'session-init', 'babysit'],
51
+ 'Quality & Testing': ['tests', 'review', 'ci'],
52
+ 'Documentation': ['docs', 'adr', 'readme-sync'],
53
+ 'Planning & Metrics': ['sprint', 'velocity', 'metrics', 'board', 'deps'],
54
+ 'Research & Strategy': ['research', 'product'],
55
+ 'Deployment & Operations': ['deploy', 'packages'],
56
+ 'Collaboration': ['update', 'handoff', 'feedback', 'retro'],
57
+ 'Maintenance': ['debt', 'compress', 'template'],
58
+ 'System': ['setup', 'help', 'diagnose', 'auto', 'agent']
59
+ };
60
+
61
+ for (const [category, keywords] of Object.entries(categories)) {
62
+ if (keywords.some(kw => name.includes(kw))) {
63
+ return category;
64
+ }
65
+ }
66
+
67
+ return 'Other';
68
+ }
69
+
70
+ /**
71
+ * Scan commands directory and build registry
72
+ * @param {string} commandsDir - Path to commands directory
73
+ * @returns {Array} Array of command metadata objects
74
+ */
75
+ function scanCommands(commandsDir) {
76
+ const commands = [];
77
+ const files = fs.readdirSync(commandsDir);
78
+
79
+ for (const file of files) {
80
+ if (!file.endsWith('.md')) continue;
81
+
82
+ const filePath = path.join(commandsDir, file);
83
+ const frontmatter = extractFrontmatter(filePath);
84
+ const name = file.replace('.md', '');
85
+
86
+ commands.push({
87
+ name,
88
+ file,
89
+ path: filePath,
90
+ description: frontmatter.description || '',
91
+ argumentHint: frontmatter['argument-hint'] || '',
92
+ category: categorizeCommand(name, frontmatter.description || '')
93
+ });
94
+ }
95
+
96
+ // Sort by category, then by name
97
+ commands.sort((a, b) => {
98
+ if (a.category !== b.category) {
99
+ return a.category.localeCompare(b.category);
100
+ }
101
+ return a.name.localeCompare(b.name);
102
+ });
103
+
104
+ return commands;
105
+ }
106
+
107
+ /**
108
+ * Main function
109
+ */
110
+ function main() {
111
+ const rootDir = path.resolve(__dirname, '../..');
112
+ const commandsDir = path.join(rootDir, 'src/core/commands');
113
+
114
+ if (!fs.existsSync(commandsDir)) {
115
+ console.error(`Commands directory not found: ${commandsDir}`);
116
+ process.exit(1);
117
+ }
118
+
119
+ const commands = scanCommands(commandsDir);
120
+
121
+ // If called directly, output JSON
122
+ if (require.main === module) {
123
+ console.log(JSON.stringify(commands, null, 2));
124
+ }
125
+
126
+ return commands;
127
+ }
128
+
129
+ // Export for use in other scripts
130
+ module.exports = { scanCommands, extractFrontmatter, categorizeCommand };
131
+
132
+ // Run if called directly
133
+ if (require.main === module) {
134
+ main();
135
+ }
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Content Generation Orchestrator
5
+ *
6
+ * Runs all content generators to update AgileFlow plugin files.
7
+ * Single source of truth: frontmatter and directory structure.
8
+ */
9
+
10
+ const path = require('path');
11
+ const { execSync } = require('child_process');
12
+
13
+ /**
14
+ * Run a generator script
15
+ * @param {string} scriptName - Name of the generator script
16
+ * @returns {boolean} Success status
17
+ */
18
+ function runGenerator(scriptName) {
19
+ const scriptPath = path.join(__dirname, scriptName);
20
+
21
+ console.log(`\n${'='.repeat(60)}`);
22
+ console.log(`Running: ${scriptName}`);
23
+ console.log('='.repeat(60));
24
+
25
+ try {
26
+ execSync(`node "${scriptPath}"`, {
27
+ cwd: __dirname,
28
+ stdio: 'inherit'
29
+ });
30
+ console.log(`✅ ${scriptName} completed successfully`);
31
+ return true;
32
+ } catch (error) {
33
+ console.error(`❌ ${scriptName} failed:`, error.message);
34
+ return false;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Main orchestrator
40
+ */
41
+ function main() {
42
+ console.log('🚀 AgileFlow Content Generation System');
43
+ console.log('Generating content from metadata...\n');
44
+
45
+ const generators = [
46
+ 'inject-help.js',
47
+ 'inject-babysit.js',
48
+ 'inject-readme.js'
49
+ ];
50
+
51
+ const results = [];
52
+
53
+ for (const generator of generators) {
54
+ const success = runGenerator(generator);
55
+ results.push({ generator, success });
56
+ }
57
+
58
+ // Summary
59
+ console.log(`\n${'='.repeat(60)}`);
60
+ console.log('GENERATION SUMMARY');
61
+ console.log('='.repeat(60));
62
+
63
+ let allSuccess = true;
64
+ for (const { generator, success } of results) {
65
+ const status = success ? '✅' : '❌';
66
+ console.log(`${status} ${generator}`);
67
+ if (!success) allSuccess = false;
68
+ }
69
+
70
+ console.log('');
71
+
72
+ if (allSuccess) {
73
+ console.log('🎉 All generators completed successfully!');
74
+ console.log('📝 Generated content is ready for commit.');
75
+ process.exit(0);
76
+ } else {
77
+ console.log('⚠️ Some generators failed. Please check errors above.');
78
+ process.exit(1);
79
+ }
80
+ }
81
+
82
+ // Run if called directly
83
+ if (require.main === module) {
84
+ main();
85
+ }
86
+
87
+ module.exports = { runGenerator };
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Babysit Command Content Injector
5
+ *
6
+ * Injects agent list and command references into /agileflow:babysit command file.
7
+ * Handles multiple AUTOGEN sections.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const { scanAgents } = require('./agent-registry');
13
+ const { scanCommands } = require('./command-registry');
14
+
15
+ /**
16
+ * Generate agent list content with details
17
+ * @param {Array} agents - Array of agent metadata
18
+ * @returns {string} Formatted agent list
19
+ */
20
+ function generateAgentList(agents) {
21
+ const lines = [];
22
+
23
+ lines.push(`**AVAILABLE AGENTS** (${agents.length} total):`);
24
+ lines.push('');
25
+
26
+ let count = 1;
27
+ for (const agent of agents) {
28
+ lines.push(`${count}. **${agent.name}** (model: ${agent.model})`);
29
+ lines.push(` - **Purpose**: ${agent.description}`);
30
+ lines.push(` - **Tools**: ${agent.tools.join(', ')}`);
31
+ lines.push(` - **Category**: ${agent.category}`);
32
+ lines.push('');
33
+ count++;
34
+ }
35
+
36
+ return lines.join('\n');
37
+ }
38
+
39
+ /**
40
+ * Generate command reference list (compact format for babysit)
41
+ * @param {Array} commands - Array of command metadata
42
+ * @returns {string} Formatted command list
43
+ */
44
+ function generateCommandReference(commands) {
45
+ const lines = [];
46
+
47
+ // Group by category
48
+ const categories = {};
49
+ for (const cmd of commands) {
50
+ if (!categories[cmd.category]) {
51
+ categories[cmd.category] = [];
52
+ }
53
+ categories[cmd.category].push(cmd);
54
+ }
55
+
56
+ for (const [category, cmds] of Object.entries(categories)) {
57
+ const cmdNames = cmds.map(c => c.name).join(', ');
58
+ lines.push(`- **${category}**: ${cmdNames}`);
59
+ }
60
+
61
+ return lines.join('\n');
62
+ }
63
+
64
+ /**
65
+ * Inject content between specified AUTOGEN markers
66
+ * @param {string} content - Original file content
67
+ * @param {string} markerName - Name of the marker (e.g., AGENT_LIST, COMMAND_REF)
68
+ * @param {string} generated - Generated content to inject
69
+ * @returns {string} Updated file content
70
+ */
71
+ function injectContentByMarker(content, markerName, generated) {
72
+ const startMarker = `<!-- AUTOGEN:${markerName}:START -->`;
73
+ const endMarker = `<!-- AUTOGEN:${markerName}:END -->`;
74
+
75
+ const startIdx = content.indexOf(startMarker);
76
+ const endIdx = content.indexOf(endMarker);
77
+
78
+ if (startIdx === -1 || endIdx === -1) {
79
+ console.warn(`AUTOGEN:${markerName} markers not found - skipping`);
80
+ return content;
81
+ }
82
+
83
+ const timestamp = new Date().toISOString().split('T')[0];
84
+ const injectedContent = `${startMarker}\n<!-- Auto-generated on ${timestamp}. Do not edit manually. -->\n\n${generated}\n${endMarker}`;
85
+
86
+ return content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length);
87
+ }
88
+
89
+ /**
90
+ * Add AUTOGEN markers to babysit file if they don't exist
91
+ * @param {string} content - Original file content
92
+ * @returns {string} Updated content with markers
93
+ */
94
+ function addMarkersIfMissing(content) {
95
+ let updated = content;
96
+
97
+ // Add AGENT_LIST markers around the agent list section
98
+ if (!content.includes('<!-- AUTOGEN:AGENT_LIST:START -->')) {
99
+ // Find "**AVAILABLE AGENTS**" and wrap it
100
+ const agentSectionStart = content.indexOf('**AVAILABLE AGENTS**');
101
+ if (agentSectionStart !== -1) {
102
+ // Find the end of the agent list (before "**WHEN TO SPAWN AGENTS**")
103
+ const agentSectionEnd = content.indexOf('**WHEN TO SPAWN AGENTS**', agentSectionStart);
104
+ if (agentSectionEnd !== -1) {
105
+ const before = content.substring(0, agentSectionStart);
106
+ const agentSection = content.substring(agentSectionStart, agentSectionEnd);
107
+ const after = content.substring(agentSectionEnd);
108
+
109
+ updated = `${before}<!-- AUTOGEN:AGENT_LIST:START -->\n${agentSection}<!-- AUTOGEN:AGENT_LIST:END -->\n\n${after}`;
110
+ console.log('✅ Added AGENT_LIST markers to babysit.md');
111
+ }
112
+ }
113
+ }
114
+
115
+ return updated;
116
+ }
117
+
118
+ /**
119
+ * Main function
120
+ */
121
+ function main() {
122
+ const rootDir = path.resolve(__dirname, '../..');
123
+ const babysitFile = path.join(rootDir, 'src/core/commands/babysit.md');
124
+ const agentsDir = path.join(rootDir, 'src/core/agents');
125
+ const commandsDir = path.join(rootDir, 'src/core/commands');
126
+
127
+ // Check if babysit file exists
128
+ if (!fs.existsSync(babysitFile)) {
129
+ console.error(`Babysit file not found: ${babysitFile}`);
130
+ process.exit(1);
131
+ }
132
+
133
+ // Scan agents and commands
134
+ console.log('Scanning agents...');
135
+ const agents = scanAgents(agentsDir);
136
+ console.log(`Found ${agents.length} agents`);
137
+
138
+ console.log('Scanning commands...');
139
+ const commands = scanCommands(commandsDir);
140
+ console.log(`Found ${commands.length} commands`);
141
+
142
+ // Read babysit file
143
+ let babysitContent = fs.readFileSync(babysitFile, 'utf-8');
144
+
145
+ // Add markers if missing
146
+ babysitContent = addMarkersIfMissing(babysitContent);
147
+
148
+ // Generate content
149
+ console.log('Generating agent list...');
150
+ const agentList = generateAgentList(agents);
151
+
152
+ // Inject content
153
+ console.log('Injecting content into babysit.md...');
154
+ babysitContent = injectContentByMarker(babysitContent, 'AGENT_LIST', agentList);
155
+
156
+ // Write back
157
+ fs.writeFileSync(babysitFile, babysitContent, 'utf-8');
158
+ console.log('✅ Successfully updated babysit.md');
159
+ }
160
+
161
+ // Export for use in orchestrator
162
+ module.exports = { generateAgentList, generateCommandReference, injectContentByMarker, addMarkersIfMissing };
163
+
164
+ // Run if called directly
165
+ if (require.main === module) {
166
+ main();
167
+ }
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Help Command Content Injector
5
+ *
6
+ * Injects command list into /agileflow:help command file.
7
+ * Finds AUTOGEN markers and replaces content with generated command directory.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const { scanCommands } = require('./command-registry');
13
+
14
+ /**
15
+ * Generate command list content grouped by category
16
+ * @param {Array} commands - Array of command metadata
17
+ * @returns {string} Formatted command list
18
+ */
19
+ function generateCommandList(commands) {
20
+ const lines = [];
21
+
22
+ // Group commands by category
23
+ const categories = {};
24
+ for (const cmd of commands) {
25
+ if (!categories[cmd.category]) {
26
+ categories[cmd.category] = [];
27
+ }
28
+ categories[cmd.category].push(cmd);
29
+ }
30
+
31
+ // Generate markdown for each category
32
+ for (const [category, cmds] of Object.entries(categories)) {
33
+ lines.push(`**${category}:**`);
34
+ for (const cmd of cmds) {
35
+ const hint = cmd.argumentHint ? ` ${cmd.argumentHint}` : '';
36
+ lines.push(`- \`/agileflow:${cmd.name}${hint}\` - ${cmd.description}`);
37
+ }
38
+ lines.push(''); // Blank line between categories
39
+ }
40
+
41
+ return lines.join('\n');
42
+ }
43
+
44
+ /**
45
+ * Inject content between AUTOGEN markers
46
+ * @param {string} content - Original file content
47
+ * @param {string} generated - Generated content to inject
48
+ * @returns {string} Updated file content
49
+ */
50
+ function injectContent(content, generated) {
51
+ const startMarker = '<!-- AUTOGEN:COMMAND_LIST:START -->';
52
+ const endMarker = '<!-- AUTOGEN:COMMAND_LIST:END -->';
53
+
54
+ const startIdx = content.indexOf(startMarker);
55
+ const endIdx = content.indexOf(endMarker);
56
+
57
+ if (startIdx === -1 || endIdx === -1) {
58
+ console.error('AUTOGEN markers not found in file');
59
+ return content;
60
+ }
61
+
62
+ const timestamp = new Date().toISOString().split('T')[0];
63
+ const injectedContent = `${startMarker}\n<!-- Auto-generated on ${timestamp}. Do not edit manually. -->\n\n${generated}\n${endMarker}`;
64
+
65
+ return content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length);
66
+ }
67
+
68
+ /**
69
+ * Main function
70
+ */
71
+ function main() {
72
+ const rootDir = path.resolve(__dirname, '../..');
73
+ const helpFile = path.join(rootDir, 'src/core/commands/help.md');
74
+ const commandsDir = path.join(rootDir, 'src/core/commands');
75
+
76
+ // Check if help file exists
77
+ if (!fs.existsSync(helpFile)) {
78
+ console.error(`Help file not found: ${helpFile}`);
79
+ process.exit(1);
80
+ }
81
+
82
+ // Scan commands
83
+ console.log('Scanning commands...');
84
+ const commands = scanCommands(commandsDir);
85
+ console.log(`Found ${commands.length} commands`);
86
+
87
+ // Generate command list
88
+ console.log('Generating command list...');
89
+ const commandList = generateCommandList(commands);
90
+
91
+ // Read help file
92
+ const helpContent = fs.readFileSync(helpFile, 'utf-8');
93
+
94
+ // Inject content
95
+ console.log('Injecting content into help.md...');
96
+ const updatedContent = injectContent(helpContent, commandList);
97
+
98
+ // Write back
99
+ fs.writeFileSync(helpFile, updatedContent, 'utf-8');
100
+ console.log('✅ Successfully updated help.md');
101
+ }
102
+
103
+ // Export for use in orchestrator
104
+ module.exports = { generateCommandList, injectContent };
105
+
106
+ // Run if called directly
107
+ if (require.main === module) {
108
+ main();
109
+ }
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * README Content Injector
5
+ *
6
+ * Injects stats, agent tables, and skill lists into README.md.
7
+ * Handles multiple AUTOGEN sections for different content types.
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+ const { scanAgents } = require('./agent-registry');
13
+ const { scanCommands } = require('./command-registry');
14
+ const { scanSkills } = require('./skill-registry');
15
+
16
+ /**
17
+ * Generate stats content (command/agent/skill counts)
18
+ * @param {Object} counts - {commands, agents, skills}
19
+ * @returns {string} Formatted stats
20
+ */
21
+ function generateStats(counts) {
22
+ return `- **${counts.commands}** slash commands\n- **${counts.agents}** specialized agents\n- **${counts.skills}** code generation skills`;
23
+ }
24
+
25
+ /**
26
+ * Generate agent table (markdown table format)
27
+ * @param {Array} agents - Array of agent metadata
28
+ * @returns {string} Formatted table
29
+ */
30
+ function generateAgentTable(agents) {
31
+ const lines = [];
32
+
33
+ lines.push('| Agent | Description | Model | Category |');
34
+ lines.push('|-------|-------------|-------|----------|');
35
+
36
+ for (const agent of agents) {
37
+ const tools = agent.tools.slice(0, 3).join(', ') + (agent.tools.length > 3 ? '...' : '');
38
+ lines.push(`| ${agent.name} | ${agent.description} | ${agent.model} | ${agent.category} |`);
39
+ }
40
+
41
+ return lines.join('\n');
42
+ }
43
+
44
+ /**
45
+ * Generate skill list (bulleted list grouped by category)
46
+ * @param {Array} skills - Array of skill metadata
47
+ * @returns {string} Formatted list
48
+ */
49
+ function generateSkillList(skills) {
50
+ const lines = [];
51
+
52
+ // Group by category
53
+ const categories = {};
54
+ for (const skill of skills) {
55
+ if (!categories[skill.category]) {
56
+ categories[skill.category] = [];
57
+ }
58
+ categories[skill.category].push(skill);
59
+ }
60
+
61
+ for (const [category, categorySkills] of Object.entries(categories)) {
62
+ lines.push(`**${category}:**`);
63
+ for (const skill of categorySkills) {
64
+ lines.push(`- **${skill.name}**: ${skill.description}`);
65
+ }
66
+ lines.push('');
67
+ }
68
+
69
+ return lines.join('\n');
70
+ }
71
+
72
+ /**
73
+ * Inject content between specified AUTOGEN markers
74
+ * @param {string} content - Original file content
75
+ * @param {string} markerName - Name of the marker (e.g., STATS, AGENT_TABLE)
76
+ * @param {string} generated - Generated content to inject
77
+ * @returns {string} Updated file content
78
+ */
79
+ function injectContentByMarker(content, markerName, generated) {
80
+ const startMarker = `<!-- AUTOGEN:${markerName}:START -->`;
81
+ const endMarker = `<!-- AUTOGEN:${markerName}:END -->`;
82
+
83
+ const startIdx = content.indexOf(startMarker);
84
+ const endIdx = content.indexOf(endMarker);
85
+
86
+ if (startIdx === -1 || endIdx === -1) {
87
+ console.warn(`AUTOGEN:${markerName} markers not found - skipping`);
88
+ return content;
89
+ }
90
+
91
+ const timestamp = new Date().toISOString().split('T')[0];
92
+ const injectedContent = `${startMarker}\n<!-- Auto-generated on ${timestamp}. Do not edit manually. -->\n\n${generated}\n${endMarker}`;
93
+
94
+ return content.substring(0, startIdx) + injectedContent + content.substring(endIdx + endMarker.length);
95
+ }
96
+
97
+ /**
98
+ * Main function
99
+ */
100
+ function main() {
101
+ const cliDir = path.resolve(__dirname, '../..');
102
+ const rootDir = path.resolve(cliDir, '../..');
103
+ const readmeFile = path.join(rootDir, 'README.md');
104
+ const agentsDir = path.join(cliDir, 'src/core/agents');
105
+ const commandsDir = path.join(cliDir, 'src/core/commands');
106
+ const skillsDir = path.join(cliDir, 'src/core/skills');
107
+
108
+ // Check if README exists
109
+ if (!fs.existsSync(readmeFile)) {
110
+ console.error(`README not found: ${readmeFile}`);
111
+ process.exit(1);
112
+ }
113
+
114
+ // Scan all registries
115
+ console.log('Scanning commands, agents, and skills...');
116
+ const commands = scanCommands(commandsDir);
117
+ const agents = scanAgents(agentsDir);
118
+ const skills = scanSkills(skillsDir);
119
+
120
+ console.log(`Found: ${commands.length} commands, ${agents.length} agents, ${skills.length} skills`);
121
+
122
+ // Read README
123
+ let readmeContent = fs.readFileSync(readmeFile, 'utf-8');
124
+
125
+ // Generate content
126
+ console.log('Generating stats...');
127
+ const stats = generateStats({
128
+ commands: commands.length,
129
+ agents: agents.length,
130
+ skills: skills.length
131
+ });
132
+
133
+ console.log('Generating agent table...');
134
+ const agentTable = generateAgentTable(agents);
135
+
136
+ console.log('Generating skill list...');
137
+ const skillList = generateSkillList(skills);
138
+
139
+ // Inject content
140
+ console.log('Injecting content into README.md...');
141
+ readmeContent = injectContentByMarker(readmeContent, 'STATS', stats);
142
+ readmeContent = injectContentByMarker(readmeContent, 'AGENT_TABLE', agentTable);
143
+ readmeContent = injectContentByMarker(readmeContent, 'SKILL_LIST', skillList);
144
+
145
+ // Write back
146
+ fs.writeFileSync(readmeFile, readmeContent, 'utf-8');
147
+ console.log('✅ Successfully updated README.md');
148
+ }
149
+
150
+ // Export for use in orchestrator
151
+ module.exports = { generateStats, generateAgentTable, generateSkillList, injectContentByMarker };
152
+
153
+ // Run if called directly
154
+ if (require.main === module) {
155
+ main();
156
+ }