agileflow 2.30.1 → 2.31.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agileflow",
3
- "version": "2.30.1",
3
+ "version": "2.31.0",
4
4
  "description": "AI-driven agile development system for Claude Code, Cursor, Windsurf, and more",
5
5
  "keywords": [
6
6
  "agile",
@@ -9,6 +9,7 @@ const path = require('node:path');
9
9
  const { Installer } = require('../installers/core/installer');
10
10
  const { IdeManager } = require('../installers/ide/manager');
11
11
  const { promptInstall, success, error, info, displaySection } = require('../lib/ui');
12
+ const { createDocsStructure } = require('../lib/docs-setup');
12
13
 
13
14
  const installer = new Installer();
14
15
  const ideManager = new IdeManager();
@@ -31,6 +32,7 @@ module.exports = {
31
32
  ides: ['claude-code'],
32
33
  userName: 'Developer',
33
34
  agileflowFolder: '.agileflow',
35
+ docsFolder: 'docs',
34
36
  };
35
37
  } else {
36
38
  // Interactive prompts
@@ -55,11 +57,23 @@ module.exports = {
55
57
  displaySection('Configuring IDEs');
56
58
 
57
59
  ideManager.setAgileflowFolder(config.agileflowFolder);
60
+ ideManager.setDocsFolder(config.docsFolder);
58
61
 
59
62
  for (const ide of config.ides) {
60
63
  await ideManager.setup(ide, config.directory, coreResult.path);
61
64
  }
62
65
 
66
+ // Create docs structure
67
+ displaySection('Creating Documentation Structure', `Folder: ${config.docsFolder}/`);
68
+ const docsResult = await createDocsStructure(config.directory, config.docsFolder);
69
+
70
+ if (!docsResult.success) {
71
+ error('Failed to create docs structure');
72
+ if (docsResult.errors.length > 0) {
73
+ docsResult.errors.forEach((err) => error(` ${err}`));
74
+ }
75
+ }
76
+
63
77
  // Final summary
64
78
  console.log(chalk.green('\n✨ Installation complete!\n'));
65
79
 
@@ -9,6 +9,7 @@ const path = require('node:path');
9
9
  const { Installer } = require('../installers/core/installer');
10
10
  const { IdeManager } = require('../installers/ide/manager');
11
11
  const { displayLogo, displaySection, success, warning, error, info, confirm } = require('../lib/ui');
12
+ const { createDocsStructure, getDocsFolderName } = require('../lib/docs-setup');
12
13
 
13
14
  const installer = new Installer();
14
15
  const ideManager = new IdeManager();
@@ -60,12 +61,16 @@ module.exports = {
60
61
 
61
62
  console.log();
62
63
 
64
+ // Get docs folder name from metadata (or default to 'docs')
65
+ const docsFolder = await getDocsFolderName(directory);
66
+
63
67
  // Re-run installation with existing config
64
68
  const config = {
65
69
  directory,
66
70
  ides: status.ides || ['claude-code'],
67
71
  userName: 'Developer', // Could read from existing config
68
72
  agileflowFolder: path.basename(status.path),
73
+ docsFolder,
69
74
  };
70
75
 
71
76
  // Run core installation
@@ -80,11 +85,23 @@ module.exports = {
80
85
 
81
86
  // Re-setup IDEs
82
87
  ideManager.setAgileflowFolder(config.agileflowFolder);
88
+ ideManager.setDocsFolder(config.docsFolder);
83
89
 
84
90
  for (const ide of config.ides) {
85
91
  await ideManager.setup(ide, directory, status.path);
86
92
  }
87
93
 
94
+ // Create/update docs structure (idempotent - only creates missing files)
95
+ displaySection('Updating Documentation Structure', `Folder: ${docsFolder}/`);
96
+ const docsResult = await createDocsStructure(directory, docsFolder);
97
+
98
+ if (!docsResult.success) {
99
+ warning('Failed to update docs structure');
100
+ if (docsResult.errors.length > 0) {
101
+ docsResult.errors.forEach((err) => error(` ${err}`));
102
+ }
103
+ }
104
+
88
105
  console.log(chalk.green(`\n✨ Update complete! (${status.version} → ${newVersion})\n`));
89
106
 
90
107
  process.exit(0);
@@ -19,6 +19,7 @@ class BaseIdeSetup {
19
19
  this.preferred = preferred;
20
20
  this.configDir = null; // Override in subclasses
21
21
  this.agileflowFolder = '.agileflow';
22
+ this.docsFolder = 'docs';
22
23
  }
23
24
 
24
25
  /**
@@ -29,6 +30,35 @@ class BaseIdeSetup {
29
30
  this.agileflowFolder = folderName;
30
31
  }
31
32
 
33
+ /**
34
+ * Set the docs folder name
35
+ * @param {string} folderName - The docs folder name
36
+ */
37
+ setDocsFolder(folderName) {
38
+ this.docsFolder = folderName;
39
+ }
40
+
41
+ /**
42
+ * Replace docs/ references in content with custom folder name
43
+ * @param {string} content - File content
44
+ * @returns {string} Updated content
45
+ */
46
+ replaceDocsReferences(content) {
47
+ if (this.docsFolder === 'docs') {
48
+ return content; // No replacement needed
49
+ }
50
+
51
+ // Replace all variations of docs/ references
52
+ return content
53
+ .replace(/docs\//g, `${this.docsFolder}/`)
54
+ .replace(/`docs\//g, `\`${this.docsFolder}/`)
55
+ .replace(/"docs\//g, `"${this.docsFolder}/`)
56
+ .replace(/'docs\//g, `'${this.docsFolder}/`)
57
+ .replace(/\(docs\//g, `(${this.docsFolder}/`)
58
+ .replace(/docs\/\)/g, `${this.docsFolder}/)`)
59
+ .replace(/\bdocs\b(?!\.)/g, this.docsFolder); // Replace standalone "docs" word
60
+ }
61
+
32
62
  /**
33
63
  * Main setup method - must be implemented by subclasses
34
64
  * @param {string} projectDir - Project directory
@@ -46,11 +46,14 @@ class ClaudeCodeSetup extends BaseIdeSetup {
46
46
  const commands = await this.scanDirectory(commandsSource, '.md');
47
47
 
48
48
  for (const command of commands) {
49
- // Create launcher file that references the command
50
- const launcherContent = await this.createCommandLauncher(command, agileflowDir, projectDir);
51
- const targetPath = path.join(agileflowCommandsDir, `${command.name}.md`);
49
+ // Read the original command content
50
+ let content = await this.readFile(command.path);
51
+
52
+ // Replace docs/ references with custom folder name
53
+ content = this.replaceDocsReferences(content);
52
54
 
53
- await this.writeFile(targetPath, launcherContent);
55
+ const targetPath = path.join(agileflowCommandsDir, `${command.name}.md`);
56
+ await this.writeFile(targetPath, content);
54
57
  commandCount++;
55
58
  }
56
59
  }
@@ -67,11 +70,14 @@ class ClaudeCodeSetup extends BaseIdeSetup {
67
70
  const agents = await this.scanDirectory(agentsSource, '.md');
68
71
 
69
72
  for (const agent of agents) {
70
- // Create launcher file that references the agent
71
- const launcherContent = await this.createAgentLauncher(agent, agileflowDir, projectDir);
72
- const targetPath = path.join(agileflowAgentsDir, `${agent.name}.md`);
73
+ // Read the original agent content
74
+ let content = await this.readFile(agent.path);
75
+
76
+ // Replace docs/ references with custom folder name
77
+ content = this.replaceDocsReferences(content);
73
78
 
74
- await this.writeFile(targetPath, launcherContent);
79
+ const targetPath = path.join(agileflowAgentsDir, `${agent.name}.md`);
80
+ await this.writeFile(targetPath, content);
75
81
  agentCount++;
76
82
  }
77
83
  }
@@ -87,88 +93,6 @@ class ClaudeCodeSetup extends BaseIdeSetup {
87
93
  agents: agentCount,
88
94
  };
89
95
  }
90
-
91
- /**
92
- * Create a command launcher file
93
- * @param {Object} command - Command info
94
- * @param {string} agileflowDir - AgileFlow directory
95
- * @param {string} projectDir - Project directory
96
- * @returns {Promise<string>} Launcher content
97
- */
98
- async createCommandLauncher(command, agileflowDir, projectDir) {
99
- // Read the original command file
100
- const content = await this.readFile(command.path);
101
-
102
- // Extract description from frontmatter if present
103
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
104
- let description = command.name;
105
-
106
- if (frontmatterMatch) {
107
- const descMatch = frontmatterMatch[1].match(/description:\s*["']?([^"'\n]+)["']?/);
108
- if (descMatch) {
109
- description = descMatch[1];
110
- }
111
- }
112
-
113
- // Create launcher that loads the full command
114
- const relativePath = path.relative(projectDir, command.path);
115
-
116
- return `---
117
- description: "${description}"
118
- ---
119
-
120
- # ${command.name}
121
-
122
- Load and execute the AgileFlow command.
123
-
124
- \`\`\`
125
- Read and follow the instructions in: ${relativePath}
126
- \`\`\`
127
- `;
128
- }
129
-
130
- /**
131
- * Create an agent launcher file
132
- * @param {Object} agent - Agent info
133
- * @param {string} agileflowDir - AgileFlow directory
134
- * @param {string} projectDir - Project directory
135
- * @returns {Promise<string>} Launcher content
136
- */
137
- async createAgentLauncher(agent, agileflowDir, projectDir) {
138
- // Read the original agent file
139
- const content = await this.readFile(agent.path);
140
-
141
- // Extract metadata from frontmatter
142
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
143
- let description = agent.name;
144
- let name = agent.name;
145
-
146
- if (frontmatterMatch) {
147
- const descMatch = frontmatterMatch[1].match(/description:\s*["']?([^"'\n]+)["']?/);
148
- const nameMatch = frontmatterMatch[1].match(/name:\s*["']?([^"'\n]+)["']?/);
149
-
150
- if (descMatch) description = descMatch[1];
151
- if (nameMatch) name = nameMatch[1];
152
- }
153
-
154
- // Create launcher that loads the full agent
155
- const relativePath = path.relative(projectDir, agent.path);
156
-
157
- return `---
158
- description: "${description}"
159
- ---
160
-
161
- # ${name}
162
-
163
- Activate the AgileFlow agent.
164
-
165
- \`\`\`
166
- Read and fully embody the agent defined in: ${relativePath}
167
-
168
- Follow all instructions, adopt the persona, and use the specified tools.
169
- \`\`\`
170
- `;
171
- }
172
96
  }
173
97
 
174
98
  module.exports = { ClaudeCodeSetup };
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * AgileFlow CLI - Cursor IDE Installer
3
3
  *
4
- * Installs AgileFlow rules for Cursor IDE.
5
- * Cursor uses .mdc (Markdown with Context) files in .cursor/rules/
4
+ * Installs AgileFlow commands for Cursor IDE.
5
+ * Cursor uses plain Markdown files in .cursor/commands/
6
6
  */
7
7
 
8
8
  const path = require('node:path');
@@ -15,9 +15,9 @@ const { BaseIdeSetup } = require('./_base-ide');
15
15
  */
16
16
  class CursorSetup extends BaseIdeSetup {
17
17
  constructor() {
18
- super('cursor', 'Cursor', true);
18
+ super('cursor', 'Cursor', false);
19
19
  this.configDir = '.cursor';
20
- this.rulesDir = 'rules';
20
+ this.commandsDir = 'commands';
21
21
  }
22
22
 
23
23
  /**
@@ -32,12 +32,12 @@ class CursorSetup extends BaseIdeSetup {
32
32
  // Clean up old installation first
33
33
  await this.cleanup(projectDir);
34
34
 
35
- // Create .cursor/rules/agileflow directory
35
+ // Create .cursor/commands/AgileFlow directory
36
36
  const cursorDir = path.join(projectDir, this.configDir);
37
- const rulesDir = path.join(cursorDir, this.rulesDir);
38
- const agileflowRulesDir = path.join(rulesDir, 'agileflow');
37
+ const commandsDir = path.join(cursorDir, this.commandsDir);
38
+ const agileflowCommandsDir = path.join(commandsDir, 'AgileFlow');
39
39
 
40
- await this.ensureDir(agileflowRulesDir);
40
+ await this.ensureDir(agileflowCommandsDir);
41
41
 
42
42
  // Get commands from AgileFlow installation
43
43
  const commandsSource = path.join(agileflowDir, 'commands');
@@ -47,17 +47,20 @@ class CursorSetup extends BaseIdeSetup {
47
47
  const commands = await this.scanDirectory(commandsSource, '.md');
48
48
 
49
49
  for (const command of commands) {
50
- // Create .mdc file with MDC format
51
- const mdcContent = await this.createCommandMdc(command, agileflowDir, projectDir);
52
- const targetPath = path.join(agileflowRulesDir, `${command.name}.mdc`);
50
+ // Read the original command content
51
+ let content = await this.readFile(command.path);
53
52
 
54
- await this.writeFile(targetPath, mdcContent);
53
+ // Replace docs/ references with custom folder name
54
+ content = this.replaceDocsReferences(content);
55
+
56
+ const targetPath = path.join(agileflowCommandsDir, `${command.name}.md`);
57
+ await this.writeFile(targetPath, content);
55
58
  commandCount++;
56
59
  }
57
60
  }
58
61
 
59
62
  // Create agents subdirectory
60
- const agileflowAgentsDir = path.join(agileflowRulesDir, 'agents');
63
+ const agileflowAgentsDir = path.join(agileflowCommandsDir, 'agents');
61
64
  await this.ensureDir(agileflowAgentsDir);
62
65
 
63
66
  // Get agents from AgileFlow installation
@@ -68,19 +71,22 @@ class CursorSetup extends BaseIdeSetup {
68
71
  const agents = await this.scanDirectory(agentsSource, '.md');
69
72
 
70
73
  for (const agent of agents) {
71
- // Create .mdc file with MDC format
72
- const mdcContent = await this.createAgentMdc(agent, agileflowDir, projectDir);
73
- const targetPath = path.join(agileflowAgentsDir, `${agent.name}.mdc`);
74
+ // Read the original agent content
75
+ let content = await this.readFile(agent.path);
76
+
77
+ // Replace docs/ references with custom folder name
78
+ content = this.replaceDocsReferences(content);
74
79
 
75
- await this.writeFile(targetPath, mdcContent);
80
+ const targetPath = path.join(agileflowAgentsDir, `${agent.name}.md`);
81
+ await this.writeFile(targetPath, content);
76
82
  agentCount++;
77
83
  }
78
84
  }
79
85
 
80
86
  console.log(chalk.green(` ✓ ${this.displayName} configured:`));
81
- console.log(chalk.dim(` - ${commandCount} rules installed`));
82
- console.log(chalk.dim(` - ${agentCount} agent rules installed`));
83
- console.log(chalk.dim(` - Path: ${path.relative(projectDir, agileflowRulesDir)}`));
87
+ console.log(chalk.dim(` - ${commandCount} commands installed`));
88
+ console.log(chalk.dim(` - ${agentCount} agents installed`));
89
+ console.log(chalk.dim(` - Path: ${path.relative(projectDir, agileflowCommandsDir)}`));
84
90
 
85
91
  return {
86
92
  success: true,
@@ -94,95 +100,18 @@ class CursorSetup extends BaseIdeSetup {
94
100
  * @param {string} projectDir - Project directory
95
101
  */
96
102
  async cleanup(projectDir) {
97
- const agileflowPath = path.join(projectDir, this.configDir, this.rulesDir, 'agileflow');
98
- if (await this.exists(agileflowPath)) {
99
- await fs.remove(agileflowPath);
103
+ // Remove old .cursor/rules/agileflow (deprecated)
104
+ const oldRulesPath = path.join(projectDir, this.configDir, 'rules', 'agileflow');
105
+ if (await this.exists(oldRulesPath)) {
106
+ await fs.remove(oldRulesPath);
100
107
  console.log(chalk.dim(` Removed old AgileFlow rules from ${this.displayName}`));
101
108
  }
102
- }
103
-
104
- /**
105
- * Create an MDC file for a command
106
- * @param {Object} command - Command info
107
- * @param {string} agileflowDir - AgileFlow directory
108
- * @param {string} projectDir - Project directory
109
- * @returns {Promise<string>} MDC content
110
- */
111
- async createCommandMdc(command, agileflowDir, projectDir) {
112
- // Read the original command file
113
- const content = await this.readFile(command.path);
114
-
115
- // Extract description from frontmatter if present
116
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
117
- let description = command.name;
118
-
119
- if (frontmatterMatch) {
120
- const descMatch = frontmatterMatch[1].match(/description:\s*["']?([^"'\n]+)["']?/);
121
- if (descMatch) {
122
- description = descMatch[1];
123
- }
124
- }
125
-
126
- // Create MDC format with metadata
127
- const relativePath = path.relative(projectDir, command.path);
128
-
129
- return `---
130
- description: "${description}"
131
- globs: []
132
- alwaysApply: false
133
- ---
134
-
135
- # AgileFlow: ${command.name}
136
-
137
- Load and execute the AgileFlow command from: ${relativePath}
138
109
 
139
- When this rule is activated, read the full command file and follow its instructions.
140
- `;
141
- }
142
-
143
- /**
144
- * Create an MDC file for an agent
145
- * @param {Object} agent - Agent info
146
- * @param {string} agileflowDir - AgileFlow directory
147
- * @param {string} projectDir - Project directory
148
- * @returns {Promise<string>} MDC content
149
- */
150
- async createAgentMdc(agent, agileflowDir, projectDir) {
151
- // Read the original agent file
152
- const content = await this.readFile(agent.path);
153
-
154
- // Extract metadata from frontmatter
155
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
156
- let description = agent.name;
157
- let name = agent.name;
158
-
159
- if (frontmatterMatch) {
160
- const descMatch = frontmatterMatch[1].match(/description:\s*["']?([^"'\n]+)["']?/);
161
- const nameMatch = frontmatterMatch[1].match(/name:\s*["']?([^"'\n]+)["']?/);
162
-
163
- if (descMatch) description = descMatch[1];
164
- if (nameMatch) name = nameMatch[1];
110
+ // Remove .cursor/commands/AgileFlow (for re-installation)
111
+ const commandsPath = path.join(projectDir, this.configDir, this.commandsDir, 'AgileFlow');
112
+ if (await this.exists(commandsPath)) {
113
+ await fs.remove(commandsPath);
165
114
  }
166
-
167
- // Create MDC format
168
- const relativePath = path.relative(projectDir, agent.path);
169
-
170
- return `---
171
- description: "${description}"
172
- globs: []
173
- alwaysApply: false
174
- ---
175
-
176
- # AgileFlow Agent: ${name}
177
-
178
- Activate the AgileFlow agent from: ${relativePath}
179
-
180
- When this rule is activated:
181
- 1. Read the full agent file
182
- 2. Adopt the agent's persona and communication style
183
- 3. Follow all instructions and use specified tools
184
- 4. Stay in character until given an exit command
185
- `;
186
115
  }
187
116
  }
188
117
 
@@ -15,6 +15,7 @@ class IdeManager {
15
15
  constructor() {
16
16
  this.handlers = new Map();
17
17
  this.agileflowFolder = '.agileflow';
18
+ this.docsFolder = 'docs';
18
19
  this.loadHandlers();
19
20
  }
20
21
 
@@ -31,6 +32,19 @@ class IdeManager {
31
32
  }
32
33
  }
33
34
 
35
+ /**
36
+ * Set the docs folder name for all IDE handlers
37
+ * @param {string} folderName - The docs folder name
38
+ */
39
+ setDocsFolder(folderName) {
40
+ this.docsFolder = folderName;
41
+ for (const handler of this.handlers.values()) {
42
+ if (typeof handler.setDocsFolder === 'function') {
43
+ handler.setDocsFolder(folderName);
44
+ }
45
+ }
46
+ }
47
+
34
48
  /**
35
49
  * Dynamically load all IDE handlers from directory
36
50
  */
@@ -47,11 +47,14 @@ class WindsurfSetup extends BaseIdeSetup {
47
47
  const commands = await this.scanDirectory(commandsSource, '.md');
48
48
 
49
49
  for (const command of commands) {
50
- // Create workflow file
51
- const workflowContent = await this.createCommandWorkflow(command, agileflowDir, projectDir);
52
- const targetPath = path.join(agileflowWorkflowsDir, `${command.name}.md`);
50
+ // Read the original command content
51
+ let content = await this.readFile(command.path);
52
+
53
+ // Replace docs/ references with custom folder name
54
+ content = this.replaceDocsReferences(content);
53
55
 
54
- await this.writeFile(targetPath, workflowContent);
56
+ const targetPath = path.join(agileflowWorkflowsDir, `${command.name}.md`);
57
+ await this.writeFile(targetPath, content);
55
58
  commandCount++;
56
59
  }
57
60
  }
@@ -68,11 +71,14 @@ class WindsurfSetup extends BaseIdeSetup {
68
71
  const agents = await this.scanDirectory(agentsSource, '.md');
69
72
 
70
73
  for (const agent of agents) {
71
- // Create workflow file
72
- const workflowContent = await this.createAgentWorkflow(agent, agileflowDir, projectDir);
73
- const targetPath = path.join(agileflowAgentsDir, `${agent.name}.md`);
74
+ // Read the original agent content
75
+ let content = await this.readFile(agent.path);
76
+
77
+ // Replace docs/ references with custom folder name
78
+ content = this.replaceDocsReferences(content);
74
79
 
75
- await this.writeFile(targetPath, workflowContent);
80
+ const targetPath = path.join(agileflowAgentsDir, `${agent.name}.md`);
81
+ await this.writeFile(targetPath, content);
76
82
  agentCount++;
77
83
  }
78
84
  }
@@ -100,93 +106,6 @@ class WindsurfSetup extends BaseIdeSetup {
100
106
  console.log(chalk.dim(` Removed old AgileFlow workflows from ${this.displayName}`));
101
107
  }
102
108
  }
103
-
104
- /**
105
- * Create a workflow file for a command
106
- * @param {Object} command - Command info
107
- * @param {string} agileflowDir - AgileFlow directory
108
- * @param {string} projectDir - Project directory
109
- * @returns {Promise<string>} Workflow content
110
- */
111
- async createCommandWorkflow(command, agileflowDir, projectDir) {
112
- // Read the original command file
113
- const content = await this.readFile(command.path);
114
-
115
- // Extract description from frontmatter if present
116
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
117
- let description = command.name;
118
-
119
- if (frontmatterMatch) {
120
- const descMatch = frontmatterMatch[1].match(/description:\s*["']?([^"'\n]+)["']?/);
121
- if (descMatch) {
122
- description = descMatch[1];
123
- }
124
- }
125
-
126
- // Create Windsurf workflow format
127
- const relativePath = path.relative(projectDir, command.path);
128
-
129
- return `---
130
- description: ${description}
131
- auto_execution_mode: 2
132
- ---
133
-
134
- # AgileFlow: ${command.name}
135
-
136
- Load and execute the AgileFlow command.
137
-
138
- ## Instructions
139
-
140
- Read and follow the full command from: \`${relativePath}\`
141
-
142
- Execute the command according to its specifications.
143
- `;
144
- }
145
-
146
- /**
147
- * Create a workflow file for an agent
148
- * @param {Object} agent - Agent info
149
- * @param {string} agileflowDir - AgileFlow directory
150
- * @param {string} projectDir - Project directory
151
- * @returns {Promise<string>} Workflow content
152
- */
153
- async createAgentWorkflow(agent, agileflowDir, projectDir) {
154
- // Read the original agent file
155
- const content = await this.readFile(agent.path);
156
-
157
- // Extract metadata from frontmatter
158
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
159
- let description = agent.name;
160
- let name = agent.name;
161
-
162
- if (frontmatterMatch) {
163
- const descMatch = frontmatterMatch[1].match(/description:\s*["']?([^"'\n]+)["']?/);
164
- const nameMatch = frontmatterMatch[1].match(/name:\s*["']?([^"'\n]+)["']?/);
165
-
166
- if (descMatch) description = descMatch[1];
167
- if (nameMatch) name = nameMatch[1];
168
- }
169
-
170
- // Create Windsurf workflow format
171
- const relativePath = path.relative(projectDir, agent.path);
172
-
173
- return `---
174
- description: ${description}
175
- auto_execution_mode: 3
176
- ---
177
-
178
- # AgileFlow Agent: ${name}
179
-
180
- Activate the AgileFlow agent.
181
-
182
- ## Instructions
183
-
184
- 1. Read the full agent definition from: \`${relativePath}\`
185
- 2. Adopt the agent's persona and communication style
186
- 3. Follow all instructions and use the specified tools
187
- 4. Maintain the agent's character throughout the session
188
- `;
189
- }
190
109
  }
191
110
 
192
111
  module.exports = { WindsurfSetup };
@@ -0,0 +1,425 @@
1
+ /**
2
+ * AgileFlow CLI - Docs Structure Setup
3
+ *
4
+ * Creates the complete AgileFlow docs/ directory structure with README files.
5
+ * Idempotent - checks for existing files and only creates missing ones.
6
+ */
7
+
8
+ const fs = require('fs-extra');
9
+ const path = require('node:path');
10
+ const chalk = require('chalk');
11
+
12
+ /**
13
+ * Directory structure to create
14
+ * @param {string} docsFolder - Name of the docs folder (default: "docs")
15
+ */
16
+ function getDirectoryStructure(docsFolder = 'docs') {
17
+ return [
18
+ `${docsFolder}/00-meta/templates`,
19
+ `${docsFolder}/00-meta/guides`,
20
+ `${docsFolder}/00-meta/scripts`,
21
+ `${docsFolder}/01-brainstorming/ideas`,
22
+ `${docsFolder}/01-brainstorming/sketches`,
23
+ `${docsFolder}/02-practices/prompts/agents`,
24
+ `${docsFolder}/03-decisions`,
25
+ `${docsFolder}/04-architecture`,
26
+ `${docsFolder}/05-epics`,
27
+ `${docsFolder}/06-stories`,
28
+ `${docsFolder}/07-testing/acceptance`,
29
+ `${docsFolder}/07-testing/test-cases`,
30
+ `${docsFolder}/08-project`,
31
+ `${docsFolder}/09-agents/bus`,
32
+ `${docsFolder}/10-research`,
33
+ ];
34
+ }
35
+
36
+ /**
37
+ * README content templates
38
+ * @param {string} docsFolder - Name of the docs folder
39
+ */
40
+ function getReadmeTemplates(docsFolder = 'docs') {
41
+ return {
42
+ [`${docsFolder}/README.md`]: `# AgileFlow Documentation
43
+
44
+ This directory contains all AgileFlow documentation and project management files.
45
+
46
+ ## Directory Structure
47
+
48
+ - **00-meta/**: System metadata, templates, and guides
49
+ - **01-brainstorming/**: Ideas and sketches for features
50
+ - **02-practices/**: Project practices, conventions, and standards
51
+ - **03-decisions/**: Architecture Decision Records (ADRs)
52
+ - **04-architecture/**: System architecture documentation
53
+ - **05-epics/**: Epic definitions and planning
54
+ - **06-stories/**: User stories and implementation details
55
+ - **07-testing/**: Test cases and acceptance criteria
56
+ - **08-project/**: Project management (roadmap, backlog, milestones)
57
+ - **09-agents/**: Agent status tracking and communication
58
+ - **10-research/**: Research notes and findings
59
+
60
+ ## Getting Started
61
+
62
+ Use AgileFlow slash commands to work with these directories:
63
+ - \`/AgileFlow:epic\` - Create a new epic
64
+ - \`/AgileFlow:story\` - Create a user story
65
+ - \`/AgileFlow:status\` - Update story status
66
+ - \`/AgileFlow:help\` - See all available commands
67
+ `,
68
+
69
+ [`${docsFolder}/00-meta/README.md`]: `# Meta Documentation
70
+
71
+ System metadata, templates, and guides for AgileFlow.
72
+
73
+ ## Contents
74
+
75
+ - **templates/**: Document templates (story, epic, ADR, etc.)
76
+ - **guides/**: How-to guides and best practices
77
+ - **scripts/**: Utility scripts for automation
78
+ - **agileflow-metadata.json**: System configuration and settings
79
+ - **glossary.md**: Project terminology and definitions
80
+ - **conventions.md**: Coding and documentation conventions
81
+ `,
82
+
83
+ [`${docsFolder}/01-brainstorming/README.md`]: `# Brainstorming
84
+
85
+ Ideas and sketches for future features and improvements.
86
+
87
+ ## Contents
88
+
89
+ - **ideas/**: Feature ideas and proposals
90
+ - **sketches/**: UI/UX sketches and mockups
91
+
92
+ Use this space to capture thoughts before they become formal stories.
93
+ `,
94
+
95
+ [`${docsFolder}/02-practices/README.md`]: `# Project Practices
96
+
97
+ Project-specific practices, conventions, and standards for YOUR codebase.
98
+
99
+ ## Contents
100
+
101
+ - **testing.md**: Testing strategies and patterns
102
+ - **git-branching.md**: Git workflow and branching strategy
103
+ - **releasing.md**: Release process and versioning
104
+ - **security.md**: Security practices and guidelines
105
+ - **ci.md**: CI/CD configuration and workflows
106
+ - **prompts/agents/**: Custom agent prompts for this project
107
+
108
+ **Note**: This is for YOUR project's practices (styling, typography, component patterns, API conventions), NOT AgileFlow system practices (those go in 00-meta/).
109
+ `,
110
+
111
+ [`${docsFolder}/03-decisions/README.md`]: `# Architecture Decision Records (ADRs)
112
+
113
+ Technical decisions, trade-offs, and alternatives considered.
114
+
115
+ ## Format
116
+
117
+ Use \`/AgileFlow:adr\` to create new decision records.
118
+
119
+ Each ADR should include:
120
+ - **Context**: What problem are we solving?
121
+ - **Decision**: What did we decide?
122
+ - **Consequences**: What are the impacts?
123
+ `,
124
+
125
+ [`${docsFolder}/04-architecture/README.md`]: `# Architecture Documentation
126
+
127
+ System architecture, data models, API specifications, and technical designs.
128
+
129
+ ## Contents
130
+
131
+ - Data models and schemas
132
+ - API specifications
133
+ - Component architecture
134
+ - File/directory structure
135
+ - Testing architecture
136
+ - Technical constraints
137
+
138
+ Use this documentation to maintain architectural context for development.
139
+ `,
140
+
141
+ [`${docsFolder}/05-epics/README.md`]: `# Epics
142
+
143
+ Large features broken down into user stories.
144
+
145
+ ## Format
146
+
147
+ Use \`/AgileFlow:epic\` to create new epics.
148
+
149
+ Each epic includes:
150
+ - Epic ID (EP-XXXX)
151
+ - Description and goals
152
+ - Related stories
153
+ - Milestones and timeline
154
+ `,
155
+
156
+ [`${docsFolder}/06-stories/README.md`]: `# User Stories
157
+
158
+ Implementation tasks with acceptance criteria and technical details.
159
+
160
+ ## Format
161
+
162
+ Use \`/AgileFlow:story\` to create new stories.
163
+
164
+ Each story includes:
165
+ - Story ID (US-XXXX)
166
+ - Description
167
+ - Acceptance criteria (Given/When/Then)
168
+ - Architecture context
169
+ - Testing strategy
170
+ - Implementation notes
171
+ `,
172
+
173
+ [`${docsFolder}/07-testing/README.md`]: `# Testing Documentation
174
+
175
+ Test cases, acceptance criteria, and testing strategies.
176
+
177
+ ## Contents
178
+
179
+ - **acceptance/**: Acceptance test definitions
180
+ - **test-cases/**: Detailed test case documentation
181
+
182
+ Use \`/AgileFlow:tests\` to set up testing infrastructure.
183
+ `,
184
+
185
+ [`${docsFolder}/08-project/README.md`]: `# Project Management
186
+
187
+ Project-level planning and tracking.
188
+
189
+ ## Contents
190
+
191
+ - **roadmap.md**: Product roadmap and vision
192
+ - **backlog.md**: Prioritized backlog
193
+ - **milestones.md**: Release milestones
194
+ - **risks.md**: Project risks and mitigation strategies
195
+ `,
196
+
197
+ [`${docsFolder}/09-agents/README.md`]: `# Agent Status Tracking
198
+
199
+ Real-time status of stories being worked on by agents.
200
+
201
+ ## Files
202
+
203
+ - **status.json**: Active stories and recent completions
204
+ - **status-archive.json**: Archived completed stories
205
+ - **bus/log.jsonl**: Agent communication log
206
+
207
+ Use \`/AgileFlow:status\` to update story status.
208
+ `,
209
+
210
+ [`${docsFolder}/10-research/README.md`]: `# Research Notes
211
+
212
+ Research findings, investigations, and technical explorations.
213
+
214
+ ## Format
215
+
216
+ Use \`/AgileFlow:research\` to create new research notes.
217
+
218
+ | Date | Topic | Path | Summary |
219
+ |------|-------|------|---------|
220
+ | | | | |
221
+ `,
222
+ };
223
+ }
224
+
225
+ /**
226
+ * Create docs structure with README files
227
+ * @param {string} targetDir - Target directory for installation
228
+ * @param {string} docsFolder - Name of the docs folder (default: "docs")
229
+ * @returns {Promise<Object>} Result object with counts
230
+ */
231
+ async function createDocsStructure(targetDir, docsFolder = 'docs') {
232
+ const result = {
233
+ success: false,
234
+ counts: {
235
+ directoriesCreated: 0,
236
+ filesCreated: 0,
237
+ filesSkipped: 0,
238
+ },
239
+ errors: [],
240
+ };
241
+
242
+ try {
243
+ console.log(chalk.cyan(`\nCreating ${docsFolder}/ structure...`));
244
+
245
+ // Create directories
246
+ const directories = getDirectoryStructure(docsFolder);
247
+ for (const dir of directories) {
248
+ const fullPath = path.join(targetDir, dir);
249
+ if (!fs.existsSync(fullPath)) {
250
+ await fs.ensureDir(fullPath);
251
+ result.counts.directoriesCreated++;
252
+ }
253
+ }
254
+
255
+ // Create README files
256
+ const readmes = getReadmeTemplates(docsFolder);
257
+ for (const [filePath, content] of Object.entries(readmes)) {
258
+ const fullPath = path.join(targetDir, filePath);
259
+ if (!fs.existsSync(fullPath)) {
260
+ await fs.writeFile(fullPath, content, 'utf8');
261
+ result.counts.filesCreated++;
262
+ console.log(chalk.green(` ✓ Created ${filePath}`));
263
+ } else {
264
+ result.counts.filesSkipped++;
265
+ console.log(chalk.dim(` ⊘ Skipped ${filePath} (already exists)`));
266
+ }
267
+ }
268
+
269
+ // Create agileflow-metadata.json
270
+ const metadataPath = path.join(targetDir, docsFolder, '00-meta', 'agileflow-metadata.json');
271
+ if (!fs.existsSync(metadataPath)) {
272
+ const metadata = {
273
+ version: '2.30.1',
274
+ created: new Date().toISOString(),
275
+ updated: new Date().toISOString(),
276
+ docsFolder: docsFolder,
277
+ archival: {
278
+ threshold_days: 30,
279
+ enabled: true,
280
+ },
281
+ };
282
+ await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2), 'utf8');
283
+ result.counts.filesCreated++;
284
+ console.log(chalk.green(` ✓ Created ${docsFolder}/00-meta/agileflow-metadata.json`));
285
+ } else {
286
+ // Update existing metadata with docsFolder if missing
287
+ try {
288
+ const existing = JSON.parse(await fs.readFile(metadataPath, 'utf8'));
289
+ if (!existing.docsFolder) {
290
+ existing.docsFolder = docsFolder;
291
+ existing.updated = new Date().toISOString();
292
+ await fs.writeFile(metadataPath, JSON.stringify(existing, null, 2), 'utf8');
293
+ console.log(chalk.yellow(` ↻ Updated ${docsFolder}/00-meta/agileflow-metadata.json (added docsFolder)`));
294
+ }
295
+ } catch (err) {
296
+ console.log(chalk.yellow(` ⚠ Could not update metadata: ${err.message}`));
297
+ }
298
+ result.counts.filesSkipped++;
299
+ }
300
+
301
+ // Create status.json
302
+ const statusPath = path.join(targetDir, docsFolder, '09-agents', 'status.json');
303
+ if (!fs.existsSync(statusPath)) {
304
+ const status = {
305
+ updated: new Date().toISOString(),
306
+ stories: {},
307
+ };
308
+ await fs.writeFile(statusPath, JSON.stringify(status, null, 2), 'utf8');
309
+ result.counts.filesCreated++;
310
+ console.log(chalk.green(` ✓ Created ${docsFolder}/09-agents/status.json`));
311
+ } else {
312
+ result.counts.filesSkipped++;
313
+ console.log(chalk.dim(` ⊘ Skipped ${docsFolder}/09-agents/status.json (already exists)`));
314
+ }
315
+
316
+ // Create empty bus log
317
+ const busLogPath = path.join(targetDir, docsFolder, '09-agents', 'bus', 'log.jsonl');
318
+ if (!fs.existsSync(busLogPath)) {
319
+ await fs.writeFile(busLogPath, '', 'utf8');
320
+ result.counts.filesCreated++;
321
+ console.log(chalk.green(` ✓ Created ${docsFolder}/09-agents/bus/log.jsonl`));
322
+ } else {
323
+ result.counts.filesSkipped++;
324
+ }
325
+
326
+ // Create basic practice files
327
+ const practiceFiles = {
328
+ [`${docsFolder}/02-practices/testing.md`]: `# Testing Practices
329
+
330
+ Document your testing strategies and patterns here.
331
+ `,
332
+ [`${docsFolder}/02-practices/git-branching.md`]: `# Git Branching Strategy
333
+
334
+ Document your git workflow and branching strategy here.
335
+ `,
336
+ [`${docsFolder}/02-practices/releasing.md`]: `# Release Process
337
+
338
+ Document your release process and versioning strategy here.
339
+ `,
340
+ [`${docsFolder}/02-practices/security.md`]: `# Security Practices
341
+
342
+ Document your security guidelines and practices here.
343
+ `,
344
+ [`${docsFolder}/02-practices/ci.md`]: `# CI/CD Configuration
345
+
346
+ Document your CI/CD workflows and configuration here.
347
+ `,
348
+ };
349
+
350
+ for (const [filePath, content] of Object.entries(practiceFiles)) {
351
+ const fullPath = path.join(targetDir, filePath);
352
+ if (!fs.existsSync(fullPath)) {
353
+ await fs.writeFile(fullPath, content, 'utf8');
354
+ result.counts.filesCreated++;
355
+ console.log(chalk.green(` ✓ Created ${filePath}`));
356
+ } else {
357
+ result.counts.filesSkipped++;
358
+ }
359
+ }
360
+
361
+ // Create .gitignore additions for docs folder
362
+ const gitignorePath = path.join(targetDir, '.gitignore');
363
+ const gitignoreEntries = [
364
+ '.env',
365
+ '.env.*',
366
+ '!.env.example',
367
+ '.DS_Store',
368
+ 'node_modules/',
369
+ 'dist/',
370
+ 'build/',
371
+ 'coverage/',
372
+ ];
373
+
374
+ if (fs.existsSync(gitignorePath)) {
375
+ const existingGitignore = await fs.readFile(gitignorePath, 'utf8');
376
+ const newEntries = gitignoreEntries.filter(entry => !existingGitignore.includes(entry));
377
+ if (newEntries.length > 0) {
378
+ await fs.appendFile(gitignorePath, '\n' + newEntries.join('\n') + '\n', 'utf8');
379
+ console.log(chalk.yellow(` ↻ Updated .gitignore with ${newEntries.length} entries`));
380
+ }
381
+ } else {
382
+ await fs.writeFile(gitignorePath, gitignoreEntries.join('\n') + '\n', 'utf8');
383
+ result.counts.filesCreated++;
384
+ console.log(chalk.green(` ✓ Created .gitignore`));
385
+ }
386
+
387
+ result.success = true;
388
+ console.log(
389
+ chalk.green(
390
+ `\n✨ Docs structure created: ${result.counts.directoriesCreated} directories, ${result.counts.filesCreated} files`
391
+ )
392
+ );
393
+ if (result.counts.filesSkipped > 0) {
394
+ console.log(chalk.dim(` ${result.counts.filesSkipped} files skipped (already exist)`));
395
+ }
396
+ } catch (err) {
397
+ result.errors.push(err.message);
398
+ console.error(chalk.red(`\n✗ Failed to create docs structure: ${err.message}`));
399
+ }
400
+
401
+ return result;
402
+ }
403
+
404
+ /**
405
+ * Get the docs folder name from metadata or default
406
+ * @param {string} targetDir - Target directory
407
+ * @returns {Promise<string>} Docs folder name
408
+ */
409
+ async function getDocsFolderName(targetDir) {
410
+ try {
411
+ const metadataPath = path.join(targetDir, 'docs', '00-meta', 'agileflow-metadata.json');
412
+ if (fs.existsSync(metadataPath)) {
413
+ const metadata = JSON.parse(await fs.readFile(metadataPath, 'utf8'));
414
+ return metadata.docsFolder || 'docs';
415
+ }
416
+ } catch (err) {
417
+ // Ignore errors, return default
418
+ }
419
+ return 'docs';
420
+ }
421
+
422
+ module.exports = {
423
+ createDocsStructure,
424
+ getDocsFolderName,
425
+ };
@@ -18,12 +18,12 @@ const packageJson = require(packageJsonPath);
18
18
  */
19
19
  function displayLogo() {
20
20
  const logo = `
21
- _____ _ __ ________
22
- / _ \\ ____/ | \\_/ ____/
23
- / /_\\ \\ / ___\\ | |\\____ \\ ____ _ __
24
- / | \\ \\___|__|__/ \\/ __ \\/ \\/ /
25
- \\____|____/\\____/____ /_______/\\____/ \\__/
26
- `;
21
+ _ _ _ _____ _
22
+ / \\ __ _(_) | ___| ___| | _____ __
23
+ / _ \\ / _\` | | |/ _ \\ |_ | |/ _ \\ \\ /\\ / /
24
+ / ___ \\ (_| | | | __/ _| | | (_) \\ V V /
25
+ /_/ \\_\\__, |_|_|\\___|_| |_|\\___/ \\_/\\_/
26
+ |___/ `;
27
27
  console.log(chalk.cyan(logo));
28
28
  console.log(chalk.dim(` AgileFlow v${packageJson.version} - AI-Driven Agile Development\n`));
29
29
  }
@@ -152,6 +152,18 @@ async function promptInstall() {
152
152
  return true;
153
153
  },
154
154
  },
155
+ {
156
+ type: 'input',
157
+ name: 'docsFolder',
158
+ message: 'Documentation folder name:',
159
+ default: 'docs',
160
+ validate: (input) => {
161
+ if (!/^[a-zA-Z0-9._-]+$/.test(input)) {
162
+ return 'Folder name can only contain letters, numbers, dots, underscores, and hyphens';
163
+ }
164
+ return true;
165
+ },
166
+ },
155
167
  ]);
156
168
 
157
169
  return {
@@ -159,6 +171,7 @@ async function promptInstall() {
159
171
  ides: answers.ides,
160
172
  userName: answers.userName,
161
173
  agileflowFolder: answers.agileflowFolder,
174
+ docsFolder: answers.docsFolder,
162
175
  };
163
176
  }
164
177