agileflow 3.1.0 → 3.2.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 (101) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +57 -85
  3. package/lib/dashboard-automations.js +130 -0
  4. package/lib/dashboard-git.js +254 -0
  5. package/lib/dashboard-inbox.js +64 -0
  6. package/lib/dashboard-protocol.js +1 -0
  7. package/lib/dashboard-server.js +114 -924
  8. package/lib/dashboard-session.js +136 -0
  9. package/lib/dashboard-status.js +72 -0
  10. package/lib/dashboard-terminal.js +354 -0
  11. package/lib/dashboard-websocket.js +88 -0
  12. package/lib/drivers/codex-driver.ts +4 -4
  13. package/lib/logger.js +106 -0
  14. package/package.json +4 -2
  15. package/scripts/agileflow-configure.js +2 -2
  16. package/scripts/agileflow-welcome.js +409 -434
  17. package/scripts/claude-tmux.sh +80 -2
  18. package/scripts/context-loader.js +4 -9
  19. package/scripts/lib/command-prereqs.js +280 -0
  20. package/scripts/lib/configure-detect.js +92 -2
  21. package/scripts/lib/configure-features.js +295 -1
  22. package/scripts/lib/context-formatter.js +468 -233
  23. package/scripts/lib/context-loader.js +27 -15
  24. package/scripts/lib/damage-control-utils.js +8 -1
  25. package/scripts/lib/feature-catalog.js +321 -0
  26. package/scripts/lib/portable-tasks-cli.js +274 -0
  27. package/scripts/lib/portable-tasks.js +479 -0
  28. package/scripts/lib/signal-detectors.js +1 -1
  29. package/scripts/lib/team-events.js +86 -1
  30. package/scripts/obtain-context.js +28 -4
  31. package/scripts/smart-detect.js +17 -0
  32. package/scripts/strip-ai-attribution.js +63 -0
  33. package/scripts/team-manager.js +7 -2
  34. package/scripts/welcome-deferred.js +437 -0
  35. package/src/core/agents/perf-analyzer-assets.md +174 -0
  36. package/src/core/agents/perf-analyzer-bundle.md +165 -0
  37. package/src/core/agents/perf-analyzer-caching.md +160 -0
  38. package/src/core/agents/perf-analyzer-compute.md +165 -0
  39. package/src/core/agents/perf-analyzer-memory.md +182 -0
  40. package/src/core/agents/perf-analyzer-network.md +157 -0
  41. package/src/core/agents/perf-analyzer-queries.md +155 -0
  42. package/src/core/agents/perf-analyzer-rendering.md +156 -0
  43. package/src/core/agents/perf-consensus.md +280 -0
  44. package/src/core/agents/security-analyzer-api.md +199 -0
  45. package/src/core/agents/security-analyzer-auth.md +160 -0
  46. package/src/core/agents/security-analyzer-authz.md +168 -0
  47. package/src/core/agents/security-analyzer-deps.md +147 -0
  48. package/src/core/agents/security-analyzer-infra.md +176 -0
  49. package/src/core/agents/security-analyzer-injection.md +148 -0
  50. package/src/core/agents/security-analyzer-input.md +191 -0
  51. package/src/core/agents/security-analyzer-secrets.md +175 -0
  52. package/src/core/agents/security-consensus.md +276 -0
  53. package/src/core/agents/test-analyzer-assertions.md +181 -0
  54. package/src/core/agents/test-analyzer-coverage.md +183 -0
  55. package/src/core/agents/test-analyzer-fragility.md +185 -0
  56. package/src/core/agents/test-analyzer-integration.md +155 -0
  57. package/src/core/agents/test-analyzer-maintenance.md +173 -0
  58. package/src/core/agents/test-analyzer-mocking.md +178 -0
  59. package/src/core/agents/test-analyzer-patterns.md +189 -0
  60. package/src/core/agents/test-analyzer-structure.md +177 -0
  61. package/src/core/agents/test-consensus.md +294 -0
  62. package/src/core/commands/{legal/audit.md → audit/legal.md} +13 -13
  63. package/src/core/commands/{logic/audit.md → audit/logic.md} +12 -12
  64. package/src/core/commands/audit/performance.md +443 -0
  65. package/src/core/commands/audit/security.md +443 -0
  66. package/src/core/commands/audit/test.md +442 -0
  67. package/src/core/commands/babysit.md +505 -463
  68. package/src/core/commands/configure.md +8 -8
  69. package/src/core/commands/research/ask.md +42 -9
  70. package/src/core/commands/research/import.md +14 -8
  71. package/src/core/commands/research/list.md +17 -16
  72. package/src/core/commands/research/synthesize.md +8 -8
  73. package/src/core/commands/research/view.md +28 -4
  74. package/src/core/commands/whats-new.md +2 -2
  75. package/src/core/experts/devops/expertise.yaml +13 -2
  76. package/src/core/experts/documentation/expertise.yaml +26 -4
  77. package/src/core/profiles/COMPARISON.md +170 -0
  78. package/src/core/profiles/README.md +178 -0
  79. package/src/core/profiles/claude-code.yaml +111 -0
  80. package/src/core/profiles/codex.yaml +103 -0
  81. package/src/core/profiles/cursor.yaml +134 -0
  82. package/src/core/profiles/examples.js +250 -0
  83. package/src/core/profiles/loader.js +235 -0
  84. package/src/core/profiles/windsurf.yaml +159 -0
  85. package/src/core/teams/logic-audit.json +6 -0
  86. package/src/core/teams/perf-audit.json +71 -0
  87. package/src/core/teams/security-audit.json +71 -0
  88. package/src/core/teams/test-audit.json +71 -0
  89. package/src/core/templates/command-prerequisites.yaml +169 -0
  90. package/src/core/templates/damage-control-patterns.yaml +9 -0
  91. package/tools/cli/installers/ide/_base-ide.js +33 -3
  92. package/tools/cli/installers/ide/claude-code.js +2 -69
  93. package/tools/cli/installers/ide/codex.js +9 -9
  94. package/tools/cli/installers/ide/cursor.js +165 -4
  95. package/tools/cli/installers/ide/windsurf.js +237 -6
  96. package/tools/cli/lib/content-transformer.js +234 -9
  97. package/tools/cli/lib/docs-setup.js +1 -1
  98. package/tools/cli/lib/ide-generator.js +357 -0
  99. package/tools/cli/lib/ide-registry.js +2 -2
  100. package/scripts/tmux-task-name.sh +0 -105
  101. package/scripts/tmux-task-watcher.sh +0 -344
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * AgileFlow CLI - Cursor IDE Installer
3
3
  *
4
- * Installs AgileFlow commands for Cursor IDE.
5
- * Cursor uses plain Markdown files in .cursor/commands/
4
+ * Installs AgileFlow commands, agents, and hooks for Cursor IDE.
5
+ * Cursor uses:
6
+ * - Plain Markdown files in .cursor/commands/ for slash commands
7
+ * - Markdown files in .cursor/agents/ for subagents (with YAML frontmatter)
8
+ * - .cursor/hooks.json for lifecycle hooks
6
9
  */
7
10
 
8
11
  const path = require('node:path');
@@ -27,10 +30,162 @@ class CursorSetup extends BaseIdeSetup {
27
30
  * @param {Object} options - Setup options
28
31
  */
29
32
  async setup(projectDir, agileflowDir, options = {}) {
30
- return this.setupStandard(projectDir, agileflowDir, {
33
+ // Use standard setup for commands and agents
34
+ const result = await this.setupStandard(projectDir, agileflowDir, {
31
35
  targetSubdir: this.commandsDir,
32
36
  agileflowFolder: 'AgileFlow',
33
37
  });
38
+
39
+ const { ideDir, agileflowTargetDir } = result;
40
+ const agentsSource = path.join(agileflowDir, 'agents');
41
+
42
+ // Cursor specific: Install agents as spawnable subagents (.cursor/agents/AgileFlow/)
43
+ // This allows Cursor's async subagent spawning feature
44
+ const spawnableAgentsDir = path.join(ideDir, 'agents', 'AgileFlow');
45
+
46
+ // Clean existing spawnable agents directory to prevent duplicates during update
47
+ if (await fs.pathExists(spawnableAgentsDir)) {
48
+ await fs.remove(spawnableAgentsDir);
49
+ }
50
+
51
+ const agentInstallResult = await this.installCommandsRecursive(
52
+ agentsSource,
53
+ spawnableAgentsDir,
54
+ agileflowDir,
55
+ false
56
+ );
57
+ console.log(chalk.dim(` - Spawnable agents: .cursor/agents/AgileFlow/`));
58
+
59
+ // Cursor specific: Setup damage control hooks
60
+ await this.setupDamageControlHooks(projectDir, agileflowDir, ideDir, options);
61
+
62
+ return {
63
+ ...result,
64
+ agents: agentInstallResult.commands,
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Setup damage control hooks for Cursor
70
+ * Maps Claude Code's PreToolUse hooks to Cursor's lifecycle events:
71
+ * - beforeShellExecution (for bash commands)
72
+ * - afterFileEdit (for file edits)
73
+ *
74
+ * @param {string} projectDir - Project directory
75
+ * @param {string} agileflowDir - AgileFlow installation directory
76
+ * @param {string} cursorDir - .cursor directory path
77
+ * @param {Object} options - Setup options
78
+ */
79
+ async setupDamageControlHooks(projectDir, agileflowDir, cursorDir, options = {}) {
80
+ if (options.skipDamageControl) {
81
+ return;
82
+ }
83
+
84
+ const hooksPath = path.join(cursorDir, 'hooks.json');
85
+ let hooks = [];
86
+
87
+ // Load existing hooks if they exist
88
+ if (fs.existsSync(hooksPath)) {
89
+ try {
90
+ const content = await fs.readFile(hooksPath, 'utf8');
91
+ hooks = JSON.parse(content);
92
+ if (!Array.isArray(hooks)) {
93
+ hooks = [];
94
+ }
95
+ } catch (e) {
96
+ hooks = [];
97
+ }
98
+ }
99
+
100
+ // Define damage control hooks for Cursor
101
+ // Cursor's hooks.json format: Array of {event, command, args, order}
102
+ const damageControlHooks = [
103
+ {
104
+ event: 'beforeShellExecution',
105
+ command: 'node',
106
+ args: ['$CURSOR_PROJECT_DIR/.cursor/hooks/damage-control/bash-tool-damage-control.js'],
107
+ order: 1,
108
+ },
109
+ {
110
+ event: 'afterFileEdit',
111
+ command: 'node',
112
+ args: ['$CURSOR_PROJECT_DIR/.cursor/hooks/damage-control/edit-tool-damage-control.js'],
113
+ order: 1,
114
+ },
115
+ ];
116
+
117
+ // Check if damage control hooks already exist and merge
118
+ let hasUpdates = false;
119
+ for (const newHook of damageControlHooks) {
120
+ const existingIdx = hooks.findIndex(
121
+ h =>
122
+ h.event === newHook.event &&
123
+ h.command === newHook.command &&
124
+ (h.args?.join(' ') === newHook.args?.join(' ') ||
125
+ (h.args && h.args.some(arg => arg.includes('damage-control'))))
126
+ );
127
+
128
+ if (existingIdx === -1) {
129
+ hooks.push(newHook);
130
+ hasUpdates = true;
131
+ }
132
+ }
133
+
134
+ // Only write if we have updates or if file didn't exist
135
+ if (hasUpdates || !fs.existsSync(hooksPath)) {
136
+ await fs.ensureDir(path.dirname(hooksPath));
137
+ await fs.writeFile(hooksPath, JSON.stringify(hooks, null, 2));
138
+ console.log(chalk.dim(` - Damage control: hooks enabled`));
139
+ }
140
+
141
+ // Copy damage control scripts
142
+ await this.setupDamageControlScripts(agileflowDir, cursorDir);
143
+ }
144
+
145
+ /**
146
+ * Copy damage control scripts to .cursor/hooks/damage-control/
147
+ * @param {string} agileflowDir - AgileFlow installation directory
148
+ * @param {string} cursorDir - .cursor directory path
149
+ */
150
+ async setupDamageControlScripts(agileflowDir, cursorDir) {
151
+ const damageControlSource = path.join(agileflowDir, 'scripts', 'damage-control');
152
+ const damageControlTarget = path.join(cursorDir, 'hooks', 'damage-control');
153
+
154
+ if (!fs.existsSync(damageControlSource)) {
155
+ return;
156
+ }
157
+
158
+ await this.ensureDir(damageControlTarget);
159
+
160
+ // Copy hook scripts
161
+ const scripts = [
162
+ 'bash-tool-damage-control.js',
163
+ 'edit-tool-damage-control.js',
164
+ 'write-tool-damage-control.js',
165
+ ];
166
+
167
+ for (const script of scripts) {
168
+ const src = path.join(damageControlSource, script);
169
+ const dest = path.join(damageControlTarget, script);
170
+ if (fs.existsSync(src)) {
171
+ await fs.copy(src, dest);
172
+ }
173
+ }
174
+
175
+ // Copy lib/damage-control-utils.js (required by hook scripts)
176
+ const libSource = path.join(agileflowDir, 'scripts', 'lib', 'damage-control-utils.js');
177
+ const libTarget = path.join(cursorDir, 'hooks', 'lib', 'damage-control-utils.js');
178
+ if (fs.existsSync(libSource)) {
179
+ await this.ensureDir(path.dirname(libTarget));
180
+ await fs.copy(libSource, libTarget);
181
+ }
182
+
183
+ // Copy patterns.yaml (preserve existing)
184
+ const patternsSource = path.join(damageControlSource, 'patterns.yaml');
185
+ const patternsTarget = path.join(damageControlTarget, 'patterns.yaml');
186
+ if (fs.existsSync(patternsSource) && !fs.existsSync(patternsTarget)) {
187
+ await fs.copy(patternsSource, patternsTarget);
188
+ }
34
189
  }
35
190
 
36
191
  /**
@@ -45,11 +200,17 @@ class CursorSetup extends BaseIdeSetup {
45
200
  console.log(chalk.dim(` Removed old AgileFlow rules from ${this.displayName}`));
46
201
  }
47
202
 
48
- // Remove .cursor/commands/agileflow (for re-installation)
203
+ // Remove .cursor/commands/AgileFlow (for re-installation)
49
204
  const commandsPath = path.join(projectDir, this.configDir, this.commandsDir, 'AgileFlow');
50
205
  if (await this.exists(commandsPath)) {
51
206
  await fs.remove(commandsPath);
52
207
  }
208
+
209
+ // Remove .cursor/agents/AgileFlow (for re-installation)
210
+ const agentsPath = path.join(projectDir, this.configDir, 'agents', 'AgileFlow');
211
+ if (await this.exists(agentsPath)) {
212
+ await fs.remove(agentsPath);
213
+ }
53
214
  }
54
215
  }
55
216
 
@@ -1,14 +1,26 @@
1
1
  /**
2
2
  * AgileFlow CLI - Windsurf IDE Installer
3
3
  *
4
- * Installs AgileFlow workflows for Windsurf IDE.
5
- * Windsurf uses markdown files in .windsurf/workflows/
4
+ * Installs AgileFlow for Windsurf IDE:
5
+ * - Commands as workflows to .windsurf/workflows/agileflow/
6
+ * - Agents as skills to .windsurf/skills/agileflow-<NAME>/SKILL.md
7
+ * - Damage control hooks to .windsurf/hooks.json
8
+ *
9
+ * Windsurf supports 11 lifecycle events and 12,000 character limit per workflow.
10
+ * Skills follow agentskills.io specification with YAML frontmatter.
6
11
  */
7
12
 
8
13
  const path = require('node:path');
9
14
  const fs = require('fs-extra');
10
15
  const chalk = require('chalk');
16
+ const { yaml } = require('../../../../lib/yaml-utils');
11
17
  const { BaseIdeSetup } = require('./_base-ide');
18
+ const {
19
+ getFrontmatter,
20
+ stripFrontmatter,
21
+ replaceReferences,
22
+ IDE_REPLACEMENTS,
23
+ } = require('../../lib/content-transformer');
12
24
 
13
25
  /**
14
26
  * Windsurf IDE setup handler
@@ -20,6 +32,187 @@ class WindsurfSetup extends BaseIdeSetup {
20
32
  this.workflowsDir = 'workflows';
21
33
  }
22
34
 
35
+ /**
36
+ * Convert an AgileFlow agent markdown file to Windsurf SKILL.md format
37
+ * Windsurf skills follow the agentskills.io specification with YAML frontmatter
38
+ *
39
+ * @param {string} content - Original agent markdown content
40
+ * @param {string} agentName - Agent name (e.g., 'database')
41
+ * @returns {string} Windsurf SKILL.md content
42
+ */
43
+ convertAgentToSkill(content, agentName) {
44
+ // Extract frontmatter using content-transformer
45
+ const frontmatter = getFrontmatter(content);
46
+ const description = frontmatter.description || `AgileFlow ${agentName} agent`;
47
+
48
+ // Create SKILL.md with YAML frontmatter (agentskills.io spec)
49
+ const skillFrontmatter = yaml
50
+ .dump({
51
+ name: `agileflow-${agentName}`,
52
+ description: description,
53
+ })
54
+ .trim();
55
+
56
+ // Remove original frontmatter from content using content-transformer
57
+ let bodyContent = stripFrontmatter(content);
58
+
59
+ // Add Windsurf-specific header
60
+ const windsurfHeader = `# AgileFlow: ${agentName.charAt(0).toUpperCase() + agentName.slice(1)} Skill
61
+
62
+ > Use this skill via \`@agileflow-${agentName}\` or /cascade
63
+
64
+ `;
65
+
66
+ // Replace Claude-specific references using content-transformer
67
+ bodyContent = replaceReferences(bodyContent, IDE_REPLACEMENTS.windsurf);
68
+
69
+ // Add Windsurf-specific replacements
70
+ bodyContent = replaceReferences(bodyContent, {
71
+ 'Task tool': 'workflow chaining',
72
+ AskUserQuestion: 'numbered list prompt',
73
+ '.claude/agents/agileflow': '.windsurf/skills/agileflow',
74
+ });
75
+
76
+ // Replace /agileflow: prefix for Windsurf workflow chaining
77
+ // e.g., /agileflow:story:list → /agileflow-story-list
78
+ bodyContent = bodyContent.replace(/\/agileflow:([a-zA-Z0-9:_-]+)/g, (_match, rest) => {
79
+ return '/agileflow-' + rest.replace(/:/g, '-');
80
+ });
81
+
82
+ // Warn if content exceeds Windsurf's 12,000 character limit
83
+ const totalLength = skillFrontmatter.length + windsurfHeader.length + bodyContent.length;
84
+ if (totalLength > 12000) {
85
+ console.warn(
86
+ chalk.yellow(
87
+ ` ⚠ Skill '${agentName}' exceeds 12,000 character limit (${totalLength} chars). Consider splitting.`
88
+ )
89
+ );
90
+ }
91
+
92
+ return `---
93
+ ${skillFrontmatter}
94
+ ---
95
+
96
+ ${windsurfHeader}${bodyContent}`;
97
+ }
98
+
99
+ /**
100
+ * Install AgileFlow agents as Windsurf skills
101
+ * Skills are installed to .windsurf/skills/agileflow-{name}/SKILL.md
102
+ *
103
+ * @param {string} projectDir - Project directory
104
+ * @param {string} agileflowDir - AgileFlow installation directory
105
+ * @returns {Promise<number>} Number of skills installed
106
+ */
107
+ async installSkills(projectDir, agileflowDir) {
108
+ const agentsSource = path.join(agileflowDir, 'agents');
109
+ const skillsTarget = path.join(projectDir, this.configDir, 'skills');
110
+
111
+ if (!(await this.exists(agentsSource))) {
112
+ return 0;
113
+ }
114
+
115
+ let skillCount = 0;
116
+ const agents = await this.scanDirectory(agentsSource, '.md');
117
+
118
+ for (const agent of agents) {
119
+ const content = await this.readFile(agent.path);
120
+ const skillContent = this.convertAgentToSkill(content, agent.name);
121
+
122
+ // Create skill directory: .windsurf/skills/agileflow-{name}/
123
+ const skillDir = path.join(skillsTarget, `agileflow-${agent.name}`);
124
+ await this.ensureDir(skillDir);
125
+
126
+ // Write SKILL.md
127
+ await this.writeFile(path.join(skillDir, 'SKILL.md'), skillContent);
128
+ skillCount++;
129
+ }
130
+
131
+ return skillCount;
132
+ }
133
+
134
+ /**
135
+ * Setup damage control hooks for Windsurf
136
+ * Maps Claude Code's PreToolUse hooks to Windsurf's lifecycle events:
137
+ * - pre_run_command (for bash commands)
138
+ * - post_write_code (for file edits)
139
+ * - pre_mcp_tool_use (for MCP tools)
140
+ *
141
+ * @param {string} projectDir - Project directory
142
+ * @param {string} agileflowDir - AgileFlow installation directory
143
+ * @param {string} windsurfDir - .windsurf directory path
144
+ * @param {Object} options - Setup options
145
+ */
146
+ async setupDamageControlHooks(projectDir, agileflowDir, windsurfDir, options = {}) {
147
+ if (options.skipDamageControl) {
148
+ return;
149
+ }
150
+
151
+ const hooksPath = path.join(windsurfDir, 'hooks.json');
152
+ let hooks = [];
153
+
154
+ // Load existing hooks if they exist
155
+ if (fs.existsSync(hooksPath)) {
156
+ try {
157
+ const content = await fs.readFile(hooksPath, 'utf8');
158
+ const parsed = JSON.parse(content);
159
+ if (!Array.isArray(parsed)) {
160
+ console.warn(
161
+ '[AgileFlow] hooks.json exists but is not an array, preserving existing file'
162
+ );
163
+ return;
164
+ }
165
+ hooks = parsed;
166
+ } catch (e) {
167
+ console.warn(
168
+ `[AgileFlow] hooks.json exists but is malformed (${e.message}), preserving existing file`
169
+ );
170
+ return;
171
+ }
172
+ }
173
+
174
+ // Define damage control hooks for Windsurf
175
+ // Windsurf's hooks.json format differs from Cursor - it uses event, command, args
176
+ const damageControlHooks = [
177
+ {
178
+ event: 'pre_run_command',
179
+ command: 'node',
180
+ args: ['.agileflow/scripts/damage-control/damage-control-bash.js'],
181
+ description: 'AgileFlow damage control for shell commands',
182
+ },
183
+ {
184
+ event: 'post_write_code',
185
+ command: 'node',
186
+ args: ['.agileflow/scripts/damage-control/damage-control-edit.js'],
187
+ description: 'AgileFlow damage control for file edits',
188
+ },
189
+ ];
190
+
191
+ // Check if damage control hooks already exist and merge
192
+ let hasUpdates = false;
193
+ for (const newHook of damageControlHooks) {
194
+ const existingIdx = hooks.findIndex(
195
+ h =>
196
+ h.event === newHook.event &&
197
+ h.command === newHook.command &&
198
+ (h.args?.join(' ') === newHook.args?.join(' ') ||
199
+ (h.args && h.args.some(arg => arg.includes('damage-control'))))
200
+ );
201
+
202
+ if (existingIdx === -1) {
203
+ hooks.push(newHook);
204
+ hasUpdates = true;
205
+ }
206
+ }
207
+
208
+ // Only write if we have updates or if file didn't exist
209
+ if (hasUpdates || !fs.existsSync(hooksPath)) {
210
+ await fs.ensureDir(path.dirname(hooksPath));
211
+ await fs.writeFile(hooksPath, JSON.stringify(hooks, null, 2));
212
+ console.log(chalk.dim(` - Damage control: hooks enabled`));
213
+ }
214
+ }
215
+
23
216
  /**
24
217
  * Setup Windsurf IDE configuration
25
218
  * @param {string} projectDir - Project directory
@@ -27,12 +220,37 @@ class WindsurfSetup extends BaseIdeSetup {
27
220
  * @param {Object} options - Setup options
28
221
  */
29
222
  async setup(projectDir, agileflowDir, options = {}) {
30
- return this.setupStandard(projectDir, agileflowDir, {
223
+ console.log(chalk.hex('#e8683a')(` Setting up ${this.displayName}...`));
224
+
225
+ // Note: cleanup is handled inside setupStandard(), no need to call explicitly
226
+
227
+ // 1. Install workflows using standard setup
228
+ const workflowsResult = await this.setupStandard(projectDir, agileflowDir, {
31
229
  targetSubdir: this.workflowsDir,
32
230
  agileflowFolder: 'agileflow',
33
231
  commandLabel: 'workflows',
34
232
  agentLabel: 'agent workflows',
35
233
  });
234
+
235
+ // 2. Install agents as skills
236
+ const skillCount = await this.installSkills(projectDir, agileflowDir);
237
+ if (skillCount > 0) {
238
+ console.log(chalk.dim(` - ${skillCount} skills installed to .windsurf/skills/`));
239
+ }
240
+
241
+ // 3. Setup damage control hooks
242
+ const windsurfDir = path.join(projectDir, this.configDir);
243
+ await this.setupDamageControlHooks(projectDir, agileflowDir, windsurfDir, options);
244
+
245
+ console.log(chalk.green(` ✓ ${this.displayName} configured:`));
246
+ console.log(chalk.dim(` - Workflows: .windsurf/workflows/agileflow/`));
247
+ console.log(chalk.dim(` - Skills: .windsurf/skills/agileflow-*/`));
248
+ console.log(chalk.dim(` - Hooks: .windsurf/hooks.json`));
249
+
250
+ return {
251
+ ...workflowsResult,
252
+ skills: skillCount,
253
+ };
36
254
  }
37
255
 
38
256
  /**
@@ -40,11 +258,24 @@ class WindsurfSetup extends BaseIdeSetup {
40
258
  * @param {string} projectDir - Project directory
41
259
  */
42
260
  async cleanup(projectDir) {
43
- const agileflowPath = path.join(projectDir, this.configDir, this.workflowsDir, 'agileflow');
44
- if (await this.exists(agileflowPath)) {
45
- await fs.remove(agileflowPath);
261
+ // Remove old workflows directory
262
+ const workflowsPath = path.join(projectDir, this.configDir, this.workflowsDir, 'agileflow');
263
+ if (await this.exists(workflowsPath)) {
264
+ await fs.remove(workflowsPath);
46
265
  console.log(chalk.dim(` Removed old AgileFlow workflows from ${this.displayName}`));
47
266
  }
267
+
268
+ // Remove old skills directories
269
+ const skillsDir = path.join(projectDir, this.configDir, 'skills');
270
+ if (await this.exists(skillsDir)) {
271
+ const entries = await fs.readdir(skillsDir);
272
+ for (const entry of entries) {
273
+ if (entry.startsWith('agileflow-')) {
274
+ await fs.remove(path.join(skillsDir, entry));
275
+ }
276
+ }
277
+ console.log(chalk.dim(` Removed old AgileFlow skills from ${this.displayName}`));
278
+ }
48
279
  }
49
280
  }
50
281