agileflow 2.55.0 → 2.57.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.
@@ -32,69 +32,39 @@ class CursorSetup extends BaseIdeSetup {
32
32
  // Clean up old installation first
33
33
  await this.cleanup(projectDir);
34
34
 
35
- // Create .cursor/commands/agileflow directory
35
+ // Create .cursor/commands/AgileFlow directory
36
36
  const cursorDir = path.join(projectDir, this.configDir);
37
37
  const commandsDir = path.join(cursorDir, this.commandsDir);
38
38
  const agileflowCommandsDir = path.join(commandsDir, 'AgileFlow');
39
39
 
40
- await this.ensureDir(agileflowCommandsDir);
41
-
42
- // Get commands from AgileFlow installation
40
+ // Install commands using shared recursive method
43
41
  const commandsSource = path.join(agileflowDir, 'commands');
44
- let commandCount = 0;
45
-
46
- if (await this.exists(commandsSource)) {
47
- const commands = await this.scanDirectory(commandsSource, '.md');
48
-
49
- for (const command of commands) {
50
- // Read the original command content
51
- let content = await this.readFile(command.path);
52
-
53
- // Inject dynamic content (agent lists, command lists)
54
- content = this.injectDynamicContent(content, agileflowDir);
55
-
56
- // Replace docs/ references with custom folder name
57
- content = this.replaceDocsReferences(content);
58
-
59
- const targetPath = path.join(agileflowCommandsDir, `${command.name}.md`);
60
- await this.writeFile(targetPath, content);
61
- commandCount++;
62
- }
63
- }
64
-
65
- // Create agents subdirectory
66
- const agileflowAgentsDir = path.join(agileflowCommandsDir, 'agents');
67
- await this.ensureDir(agileflowAgentsDir);
68
-
69
- // Get agents from AgileFlow installation
42
+ const commandResult = await this.installCommandsRecursive(
43
+ commandsSource,
44
+ agileflowCommandsDir,
45
+ agileflowDir,
46
+ true // Inject dynamic content
47
+ );
48
+
49
+ // Install agents as subdirectory
70
50
  const agentsSource = path.join(agileflowDir, 'agents');
71
- let agentCount = 0;
72
-
73
- if (await this.exists(agentsSource)) {
74
- const agents = await this.scanDirectory(agentsSource, '.md');
75
-
76
- for (const agent of agents) {
77
- // Read the original agent content
78
- let content = await this.readFile(agent.path);
79
-
80
- // Replace docs/ references with custom folder name
81
- content = this.replaceDocsReferences(content);
82
-
83
- const targetPath = path.join(agileflowAgentsDir, `${agent.name}.md`);
84
- await this.writeFile(targetPath, content);
85
- agentCount++;
86
- }
87
- }
51
+ const agentsTargetDir = path.join(agileflowCommandsDir, 'agents');
52
+ const agentResult = await this.installCommandsRecursive(
53
+ agentsSource,
54
+ agentsTargetDir,
55
+ agileflowDir,
56
+ false // No dynamic content for agents
57
+ );
88
58
 
89
59
  console.log(chalk.green(` ✓ ${this.displayName} configured:`));
90
- console.log(chalk.dim(` - ${commandCount} commands installed`));
91
- console.log(chalk.dim(` - ${agentCount} agents installed`));
60
+ console.log(chalk.dim(` - ${commandResult.commands} commands installed`));
61
+ console.log(chalk.dim(` - ${agentResult.commands} agents installed`));
92
62
  console.log(chalk.dim(` - Path: ${path.relative(projectDir, agileflowCommandsDir)}`));
93
63
 
94
64
  return {
95
65
  success: true,
96
- commands: commandCount,
97
- agents: agentCount,
66
+ commands: commandResult.commands,
67
+ agents: agentResult.commands,
98
68
  };
99
69
  }
100
70
 
@@ -37,64 +37,34 @@ class WindsurfSetup extends BaseIdeSetup {
37
37
  const workflowsDir = path.join(windsurfDir, this.workflowsDir);
38
38
  const agileflowWorkflowsDir = path.join(workflowsDir, 'agileflow');
39
39
 
40
- await this.ensureDir(agileflowWorkflowsDir);
41
-
42
- // Get commands from AgileFlow installation
40
+ // Install commands using shared recursive method
43
41
  const commandsSource = path.join(agileflowDir, 'commands');
44
- let commandCount = 0;
45
-
46
- if (await this.exists(commandsSource)) {
47
- const commands = await this.scanDirectory(commandsSource, '.md');
48
-
49
- for (const command of commands) {
50
- // Read the original command content
51
- let content = await this.readFile(command.path);
52
-
53
- // Inject dynamic content (agent lists, command lists)
54
- content = this.injectDynamicContent(content, agileflowDir);
55
-
56
- // Replace docs/ references with custom folder name
57
- content = this.replaceDocsReferences(content);
58
-
59
- const targetPath = path.join(agileflowWorkflowsDir, `${command.name}.md`);
60
- await this.writeFile(targetPath, content);
61
- commandCount++;
62
- }
63
- }
64
-
65
- // Create agents subdirectory
66
- const agileflowAgentsDir = path.join(agileflowWorkflowsDir, 'agents');
67
- await this.ensureDir(agileflowAgentsDir);
68
-
69
- // Get agents from AgileFlow installation
42
+ const commandResult = await this.installCommandsRecursive(
43
+ commandsSource,
44
+ agileflowWorkflowsDir,
45
+ agileflowDir,
46
+ true // Inject dynamic content
47
+ );
48
+
49
+ // Install agents as subdirectory
70
50
  const agentsSource = path.join(agileflowDir, 'agents');
71
- let agentCount = 0;
72
-
73
- if (await this.exists(agentsSource)) {
74
- const agents = await this.scanDirectory(agentsSource, '.md');
75
-
76
- for (const agent of agents) {
77
- // Read the original agent content
78
- let content = await this.readFile(agent.path);
79
-
80
- // Replace docs/ references with custom folder name
81
- content = this.replaceDocsReferences(content);
82
-
83
- const targetPath = path.join(agileflowAgentsDir, `${agent.name}.md`);
84
- await this.writeFile(targetPath, content);
85
- agentCount++;
86
- }
87
- }
51
+ const agentsTargetDir = path.join(agileflowWorkflowsDir, 'agents');
52
+ const agentResult = await this.installCommandsRecursive(
53
+ agentsSource,
54
+ agentsTargetDir,
55
+ agileflowDir,
56
+ false // No dynamic content for agents
57
+ );
88
58
 
89
59
  console.log(chalk.green(` ✓ ${this.displayName} configured:`));
90
- console.log(chalk.dim(` - ${commandCount} workflows installed`));
91
- console.log(chalk.dim(` - ${agentCount} agent workflows installed`));
60
+ console.log(chalk.dim(` - ${commandResult.commands} workflows installed`));
61
+ console.log(chalk.dim(` - ${agentResult.commands} agent workflows installed`));
92
62
  console.log(chalk.dim(` - Path: ${path.relative(projectDir, agileflowWorkflowsDir)}`));
93
63
 
94
64
  return {
95
65
  success: true,
96
- commands: commandCount,
97
- agents: agentCount,
66
+ commands: commandResult.commands,
67
+ agents: agentResult.commands,
98
68
  };
99
69
  }
100
70
 
@@ -7,7 +7,7 @@
7
7
 
8
8
  const fs = require('fs');
9
9
  const path = require('path');
10
- const yaml = require('js-yaml');
10
+ const { parseFrontmatter, normalizeTools } = require('../../../scripts/lib/frontmatter-parser');
11
11
 
12
12
  /**
13
13
  * Scan agents directory and generate formatted agent list
@@ -22,34 +22,20 @@ function generateAgentList(agentsDir) {
22
22
  const filePath = path.join(agentsDir, file);
23
23
  const content = fs.readFileSync(filePath, 'utf8');
24
24
 
25
- // Extract YAML frontmatter
26
- const match = content.match(/^---\n([\s\S]*?)\n---/);
27
- if (!match) continue;
28
-
29
- try {
30
- const frontmatter = yaml.load(match[1]);
31
-
32
- // Skip if frontmatter is null or not an object
33
- if (!frontmatter || typeof frontmatter !== 'object') {
34
- continue;
35
- }
36
-
37
- // Handle tools field - can be array or string
38
- let tools = frontmatter.tools || [];
39
- if (typeof tools === 'string') {
40
- tools = tools.split(',').map(t => t.trim());
41
- }
42
-
43
- agents.push({
44
- name: frontmatter.name || path.basename(file, '.md'),
45
- description: frontmatter.description || '',
46
- tools: tools,
47
- model: frontmatter.model || 'haiku',
48
- });
49
- } catch (err) {
50
- // Silently skip files with parsing errors
25
+ // Parse frontmatter using shared parser
26
+ const frontmatter = parseFrontmatter(content);
27
+
28
+ // Skip if no frontmatter found
29
+ if (!frontmatter || Object.keys(frontmatter).length === 0) {
51
30
  continue;
52
31
  }
32
+
33
+ agents.push({
34
+ name: frontmatter.name || path.basename(file, '.md'),
35
+ description: frontmatter.description || '',
36
+ tools: normalizeTools(frontmatter.tools),
37
+ model: frontmatter.model || 'haiku',
38
+ });
53
39
  }
54
40
 
55
41
  // Sort alphabetically by name
@@ -82,29 +68,20 @@ function generateCommandList(commandsDir) {
82
68
  const filePath = path.join(commandsDir, file);
83
69
  const content = fs.readFileSync(filePath, 'utf8');
84
70
 
85
- // Extract YAML frontmatter
86
- const match = content.match(/^---\n([\s\S]*?)\n---/);
87
- if (!match) continue;
88
-
89
- try {
90
- const frontmatter = yaml.load(match[1]);
91
- const cmdName = path.basename(file, '.md');
92
-
93
- // Skip if frontmatter is null or not an object
94
- if (!frontmatter || typeof frontmatter !== 'object') {
95
- continue;
96
- }
97
-
98
- commands.push({
99
- name: cmdName,
100
- description: frontmatter.description || '',
101
- argumentHint: frontmatter['argument-hint'] || '',
102
- });
103
- } catch (err) {
104
- // Silently skip files with parsing errors - they might be generated files
105
- // with content that looks like frontmatter but isn't
71
+ // Parse frontmatter using shared parser
72
+ const frontmatter = parseFrontmatter(content);
73
+ const cmdName = path.basename(file, '.md');
74
+
75
+ // Skip if no frontmatter found
76
+ if (!frontmatter || Object.keys(frontmatter).length === 0) {
106
77
  continue;
107
78
  }
79
+
80
+ commands.push({
81
+ name: cmdName,
82
+ description: frontmatter.description || '',
83
+ argumentHint: frontmatter['argument-hint'] || '',
84
+ });
108
85
  }
109
86
 
110
87
  // Sort alphabetically by name
@@ -2,14 +2,33 @@
2
2
  * AgileFlow CLI - npm Registry Utilities
3
3
  *
4
4
  * Utilities for interacting with the npm registry.
5
+ * Set DEBUG_NPM=1 environment variable for verbose error logging.
5
6
  */
6
7
 
7
8
  const https = require('https');
8
9
 
10
+ // Debug mode: set DEBUG_NPM=1 to see error details
11
+ const DEBUG = process.env.DEBUG_NPM === '1';
12
+
13
+ /**
14
+ * Log debug messages when DEBUG_NPM=1
15
+ * @param {string} message - Message to log
16
+ * @param {*} data - Optional data to include
17
+ */
18
+ function debugLog(message, data = null) {
19
+ if (DEBUG) {
20
+ console.error(`[npm-utils] ${message}`, data ? JSON.stringify(data) : '');
21
+ }
22
+ }
23
+
9
24
  /**
10
25
  * Get the latest version of a package from npm registry
11
- * @param {string} packageName - Name of the package
12
- * @returns {Promise<string|null>} Latest version or null if error
26
+ *
27
+ * Returns null on any error (network, timeout, invalid response) since
28
+ * version checking should not block the CLI. Set DEBUG_NPM=1 to see errors.
29
+ *
30
+ * @param {string} packageName - Name of the package (e.g., 'agileflow' or '@scope/pkg')
31
+ * @returns {Promise<string|null>} Latest version string or null if unavailable
13
32
  */
14
33
  async function getLatestVersion(packageName) {
15
34
  return new Promise(resolve => {
@@ -23,6 +42,8 @@ async function getLatestVersion(packageName) {
23
42
  },
24
43
  };
25
44
 
45
+ debugLog('Fetching version', { package: packageName, path: options.path });
46
+
26
47
  const req = https.request(options, res => {
27
48
  let data = '';
28
49
 
@@ -31,24 +52,30 @@ async function getLatestVersion(packageName) {
31
52
  });
32
53
 
33
54
  res.on('end', () => {
55
+ if (res.statusCode !== 200) {
56
+ debugLog('Non-200 status', { statusCode: res.statusCode });
57
+ return resolve(null);
58
+ }
59
+
34
60
  try {
35
- if (res.statusCode === 200) {
36
- const json = JSON.parse(data);
37
- resolve(json.version || null);
38
- } else {
39
- resolve(null);
40
- }
61
+ const json = JSON.parse(data);
62
+ const version = json.version || null;
63
+ debugLog('Version found', { version });
64
+ resolve(version);
41
65
  } catch (err) {
66
+ debugLog('JSON parse error', { error: err.message });
42
67
  resolve(null);
43
68
  }
44
69
  });
45
70
  });
46
71
 
47
- req.on('error', () => {
72
+ req.on('error', err => {
73
+ debugLog('Network error', { error: err.message });
48
74
  resolve(null);
49
75
  });
50
76
 
51
77
  req.setTimeout(5000, () => {
78
+ debugLog('Request timeout');
52
79
  req.destroy();
53
80
  resolve(null);
54
81
  });
@@ -8,6 +8,7 @@ const chalk = require('chalk');
8
8
  const inquirer = require('inquirer');
9
9
  const path = require('node:path');
10
10
  const fs = require('node:fs');
11
+ const { IdeManager } = require('../installers/ide/manager');
11
12
 
12
13
  // Load package.json for version
13
14
  const packageJsonPath = path.join(__dirname, '..', '..', '..', 'package.json');
@@ -73,8 +74,27 @@ function info(message) {
73
74
  console.log(chalk.dim(` ${message}`));
74
75
  }
75
76
 
77
+ // IDE Manager instance for dynamic IDE discovery
78
+ const ideManager = new IdeManager();
79
+
80
+ /**
81
+ * Get available IDE choices dynamically from installed handlers
82
+ * @returns {Array} IDE choices formatted for inquirer
83
+ */
84
+ function getIdeChoices() {
85
+ const ides = ideManager.getAvailableIdes();
86
+
87
+ return ides.map((ide, index) => ({
88
+ name: ide.name,
89
+ value: ide.value,
90
+ // First IDE (preferred) is checked by default
91
+ checked: ide.preferred || index === 0,
92
+ }));
93
+ }
94
+
76
95
  /**
77
- * Available IDE configurations
96
+ * @deprecated Use getIdeChoices() instead - dynamically loaded from IDE handlers
97
+ * Legacy hardcoded IDE choices kept for backward compatibility
78
98
  */
79
99
  const IDE_CHOICES = [
80
100
  {
@@ -126,7 +146,7 @@ async function promptInstall() {
126
146
  type: 'checkbox',
127
147
  name: 'ides',
128
148
  message: 'Select your IDE(s):',
129
- choices: IDE_CHOICES,
149
+ choices: getIdeChoices(),
130
150
  validate: input => {
131
151
  if (input.length === 0) {
132
152
  return 'Please select at least one IDE';
@@ -219,5 +239,6 @@ module.exports = {
219
239
  promptInstall,
220
240
  confirm,
221
241
  getIdeConfig,
222
- IDE_CHOICES,
242
+ getIdeChoices,
243
+ IDE_CHOICES, // @deprecated - kept for backward compatibility
223
244
  };