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
@@ -23,7 +23,7 @@ const { parseFrontmatter, extractBody } = require('../../../scripts/lib/frontmat
23
23
  * @example
24
24
  * // Object form (simple string replacement)
25
25
  * replaceReferences(content, {
26
- * 'Claude Code': 'Codex CLI',
26
+ * 'Claude Code': 'OpenAI Codex',
27
27
  * '.claude/': '.codex/',
28
28
  * 'CLAUDE.md': 'AGENTS.md'
29
29
  * });
@@ -31,7 +31,7 @@ const { parseFrontmatter, extractBody } = require('../../../scripts/lib/frontmat
31
31
  * @example
32
32
  * // Array form (with regex flags)
33
33
  * replaceReferences(content, [
34
- * { pattern: 'Claude Code', replacement: 'Codex CLI', flags: 'gi' },
34
+ * { pattern: 'Claude Code', replacement: 'OpenAI Codex', flags: 'gi' },
35
35
  * { pattern: /\.claude\//g, replacement: '.codex/' }
36
36
  * ]);
37
37
  */
@@ -54,7 +54,10 @@ function replaceReferences(content, replacements) {
54
54
  const flags = item.flags || 'g';
55
55
  regex = new RegExp(escapeRegex(item.pattern), flags);
56
56
  }
57
- result = result.replace(regex, item.replacement || '');
57
+ result = result.replace(
58
+ regex,
59
+ item.replacement !== undefined && item.replacement !== null ? item.replacement : ''
60
+ );
58
61
  }
59
62
  } else if (typeof replacements === 'object' && replacements !== null) {
60
63
  // Object form: {pattern: replacement}
@@ -176,11 +179,11 @@ function getFrontmatter(content) {
176
179
  */
177
180
  const IDE_REPLACEMENTS = {
178
181
  /**
179
- * Claude Code to Codex CLI conversions
182
+ * Claude Code to OpenAI Codex conversions
180
183
  */
181
184
  codex: {
182
- 'Claude Code': 'Codex CLI',
183
- 'claude code': 'Codex CLI',
185
+ 'Claude Code': 'OpenAI Codex',
186
+ 'claude code': 'OpenAI Codex',
184
187
  CLAUDE_CODE: 'CODEX_CLI',
185
188
  'CLAUDE.md': 'AGENTS.md',
186
189
  '.claude/': '.codex/',
@@ -197,6 +200,10 @@ const IDE_REPLACEMENTS = {
197
200
  'claude code': 'Cursor',
198
201
  '.claude/': '.cursor/',
199
202
  '.claude\\': '.cursor\\',
203
+ '.claude/agents/agileflow': '.cursor/agents/AgileFlow',
204
+ 'Task tool': 'subagent spawning',
205
+ 'Task agent': 'subagent',
206
+ AskUserQuestion: 'numbered list prompt',
200
207
  },
201
208
 
202
209
  /**
@@ -207,9 +214,173 @@ const IDE_REPLACEMENTS = {
207
214
  'claude code': 'Windsurf',
208
215
  '.claude/': '.windsurf/',
209
216
  '.claude\\': '.windsurf\\',
217
+ 'Task tool': 'workflow chaining',
218
+ 'Task agent': 'workflow',
219
+ AskUserQuestion: 'numbered list prompt',
220
+ '.claude/agents/agileflow': '.windsurf/skills/agileflow',
210
221
  },
211
222
  };
212
223
 
224
+ /**
225
+ * Tool reference replacements for IDE capability-aware transformation
226
+ * Maps Claude Code tool/pattern names to IDE-appropriate alternatives
227
+ *
228
+ * @private
229
+ */
230
+ const TOOL_REFERENCE_REPLACEMENTS = {
231
+ /**
232
+ * Cursor: Has subagents but no structured AskUserQuestion
233
+ * Replaces abstract tool patterns with concrete instructions
234
+ */
235
+ cursor: [
236
+ // AskUserQuestion references
237
+ {
238
+ pattern: /call the AskUserQuestion tool/gi,
239
+ replacement: 'ask the user to reply with their choice (as a numbered list)',
240
+ },
241
+ {
242
+ pattern: /\bAskUserQuestion\b/g,
243
+ replacement: 'numbered list prompt',
244
+ },
245
+ // Task-related references (keep Task, but clarify it's for subagents)
246
+ {
247
+ pattern: /Task\(\s*$/gm,
248
+ replacement: '/* Use Cursor subagents to spawn async work */',
249
+ },
250
+ // Task tracking references (not available in Cursor)
251
+ {
252
+ pattern: /TaskCreate\b/g,
253
+ replacement: '(not available - track progress in conversation)',
254
+ },
255
+ {
256
+ pattern: /TaskUpdate\b/g,
257
+ replacement: '(not available in Cursor)',
258
+ },
259
+ {
260
+ pattern: /TaskList\b/g,
261
+ replacement: '(not available in Cursor)',
262
+ },
263
+ ],
264
+
265
+ /**
266
+ * Windsurf: No subagents, suggests workflow chaining instead
267
+ * Uses "megaplan" for plan mode
268
+ */
269
+ windsurf: [
270
+ // AskUserQuestion references
271
+ {
272
+ pattern: /call the AskUserQuestion tool/gi,
273
+ replacement: 'ask the user to reply with their choice (as a numbered list)',
274
+ },
275
+ {
276
+ pattern: /\bAskUserQuestion\b/g,
277
+ replacement: 'numbered list prompt',
278
+ },
279
+ // Task references - suggest workflows instead
280
+ {
281
+ pattern: /\bTask\s*tool\b/gi,
282
+ replacement: 'workflow chaining',
283
+ },
284
+ {
285
+ pattern: /call the Task tool/gi,
286
+ replacement: 'suggest running the relevant /workflow',
287
+ },
288
+ {
289
+ pattern: /Task\(\s*$/gm,
290
+ replacement: '/* Suggest running /agileflow workflow via cascade */',
291
+ },
292
+ // Subagent type references - convert to workflow
293
+ {
294
+ pattern: /subagent_type:\s*"agileflow-([^"]+)"/g,
295
+ replacement: 'workflow: "/agileflow-$1"',
296
+ },
297
+ // Task tracking (not available)
298
+ {
299
+ pattern: /TaskCreate\b/g,
300
+ replacement: '(not available - use conversation comments)',
301
+ },
302
+ {
303
+ pattern: /TaskUpdate\b/g,
304
+ replacement: '(not available)',
305
+ },
306
+ {
307
+ pattern: /TaskList\b/g,
308
+ replacement: '(not available)',
309
+ },
310
+ // Plan mode - use megaplan keyword
311
+ {
312
+ pattern: /EnterPlanMode/g,
313
+ replacement: 'megaplan',
314
+ },
315
+ {
316
+ pattern: /ExitPlanMode/g,
317
+ replacement: '(end megaplan)',
318
+ },
319
+ ],
320
+
321
+ /**
322
+ * Codex: Most limited - no plan mode, hooks, or subagents
323
+ * Suggests skills and text-only interaction instead
324
+ */
325
+ codex: [
326
+ // AskUserQuestion references - use text-only function
327
+ {
328
+ pattern: /call the AskUserQuestion tool/gi,
329
+ replacement: 'call ask_user_question (text-only, no menus)',
330
+ },
331
+ {
332
+ pattern: /\bAskUserQuestion\b/g,
333
+ replacement: 'ask_user_question',
334
+ },
335
+ // Task/delegation references - suggest skills instead
336
+ {
337
+ pattern: /\bTask\s*tool\b/gi,
338
+ replacement: 'skill invocation',
339
+ },
340
+ {
341
+ pattern: /call the Task tool/gi,
342
+ replacement: 'invoke the relevant $agileflow skill',
343
+ },
344
+ {
345
+ pattern: /Task\(\s*$/gm,
346
+ replacement: '/* Invoke relevant skill via $agileflow-name */',
347
+ },
348
+ // Subagent type references - convert to skill syntax
349
+ {
350
+ pattern: /subagent_type:\s*"agileflow-([^"]+)"/g,
351
+ replacement: 'skill: "$agileflow-$1"',
352
+ },
353
+ // Task tracking (not available)
354
+ {
355
+ pattern: /TaskCreate\b/g,
356
+ replacement: '(not available in Codex)',
357
+ },
358
+ {
359
+ pattern: /TaskUpdate\b/g,
360
+ replacement: '(not available)',
361
+ },
362
+ {
363
+ pattern: /TaskList\b/g,
364
+ replacement: '(not available)',
365
+ },
366
+ // Plan mode (available in Codex since v0.96 - keep references)
367
+ // EnterPlanMode and ExitPlanMode are native in Codex, no transformation needed
368
+ // Hooks (not available)
369
+ {
370
+ pattern: /PreToolUse\b/g,
371
+ replacement: '(not available - no hooks)',
372
+ },
373
+ {
374
+ pattern: /PostToolUse\b/g,
375
+ replacement: '(not available - no hooks)',
376
+ },
377
+ {
378
+ pattern: /SessionStart\b/g,
379
+ replacement: '(not available - no hooks)',
380
+ },
381
+ ],
382
+ };
383
+
213
384
  /**
214
385
  * Create docs folder reference replacements
215
386
  *
@@ -231,18 +402,63 @@ function createDocsReplacements(targetFolder) {
231
402
  };
232
403
  }
233
404
 
405
+ /**
406
+ * Transform tool references in content based on IDE capabilities.
407
+ * Replaces Claude Code tool names and patterns with IDE-appropriate alternatives.
408
+ *
409
+ * For Claude Code (canonical format), returns content unchanged.
410
+ * For other IDEs, replaces tool references with alternatives based on capabilities:
411
+ * - Cursor: Converts AskUserQuestion to "numbered list prompt", Task stays as subagents
412
+ * - Windsurf: Converts to "megaplan" and workflow suggestions, removes Task references
413
+ * - Codex: Converts to skill invocations, text-only prompts, removes Plan mode
414
+ *
415
+ * @param {string} content - Content with Claude Code tool references
416
+ * @param {string} targetIde - Target IDE name: 'claude-code', 'cursor', 'windsurf', 'codex'
417
+ * @returns {string} Content with IDE-appropriate tool references
418
+ *
419
+ * @example
420
+ * const content = 'Use the AskUserQuestion tool to get user input';
421
+ * const cursor = transformToolReferences(content, 'cursor');
422
+ * // Returns: 'Use numbered list prompt to get user input'
423
+ *
424
+ * @example
425
+ * const content = 'Call the Task tool with subagent_type: "agileflow-test"';
426
+ * const windsurf = transformToolReferences(content, 'windsurf');
427
+ * // Returns: 'Suggest running the relevant /workflow with workflow: "/agileflow-test"'
428
+ */
429
+ function transformToolReferences(content, targetIde) {
430
+ // Claude Code is the canonical format - return unchanged
431
+ if (targetIde === 'claude-code') {
432
+ return content;
433
+ }
434
+
435
+ if (!content || typeof content !== 'string') {
436
+ return content || '';
437
+ }
438
+
439
+ const replacements = TOOL_REFERENCE_REPLACEMENTS[targetIde];
440
+ if (!replacements) {
441
+ // Unknown IDE - return unchanged
442
+ return content;
443
+ }
444
+
445
+ // Apply all replacements in order
446
+ return replaceReferences(content, replacements);
447
+ }
448
+
234
449
  /**
235
450
  * Transform content for a specific IDE target
236
451
  *
237
452
  * @param {string} content - Source content
238
- * @param {string} targetIde - Target IDE: 'codex', 'cursor', 'windsurf'
453
+ * @param {string} targetIde - Target IDE: 'codex', 'cursor', 'windsurf', 'claude-code'
239
454
  * @param {Object} [options] - Additional options
240
455
  * @param {string} [options.docsFolder] - Custom docs folder name
241
456
  * @param {Object} [options.additionalReplacements] - Extra replacements to apply
457
+ * @param {boolean} [options.transformTools] - Apply tool reference transformations (default: false for backward compatibility)
242
458
  * @returns {string} Transformed content
243
459
  */
244
460
  function transformForIde(content, targetIde, options = {}) {
245
- const { docsFolder, additionalReplacements = {} } = options;
461
+ const { docsFolder, additionalReplacements = {}, transformTools = false } = options;
246
462
 
247
463
  // Start with IDE-specific replacements
248
464
  const replacements = { ...(IDE_REPLACEMENTS[targetIde] || {}) };
@@ -255,7 +471,14 @@ function transformForIde(content, targetIde, options = {}) {
255
471
  // Add any additional custom replacements
256
472
  Object.assign(replacements, additionalReplacements);
257
473
 
258
- return replaceReferences(content, replacements);
474
+ let result = replaceReferences(content, replacements);
475
+
476
+ // Apply tool reference transformations if requested
477
+ if (transformTools) {
478
+ result = transformToolReferences(result, targetIde);
479
+ }
480
+
481
+ return result;
259
482
  }
260
483
 
261
484
  module.exports = {
@@ -266,6 +489,8 @@ module.exports = {
266
489
  getFrontmatter,
267
490
  escapeRegex,
268
491
  IDE_REPLACEMENTS,
492
+ TOOL_REFERENCE_REPLACEMENTS,
269
493
  createDocsReplacements,
494
+ transformToolReferences,
270
495
  transformForIde,
271
496
  };
@@ -190,7 +190,7 @@ Project-level planning and tracking.
190
190
  - **milestones.md**: Release milestones
191
191
  - **risks.md**: Project risks and mitigation strategies
192
192
  - **ideation/**: Ideation reports from \`/agileflow:ideate:new\`
193
- - **logic-audits/**: Logic audit reports from \`/agileflow:logic:audit\`
193
+ - **logic-audits/**: Logic audit reports from \`/agileflow:audit:logic\`
194
194
  `,
195
195
 
196
196
  [`${docsFolder}/09-agents/README.md`]: `# Agent Status Tracking
@@ -0,0 +1,357 @@
1
+ /**
2
+ * ide-generator.js - Build-time IDE-specific prompt/skill/agent generator
3
+ *
4
+ * Integrates IDE capability profiles with content transformer to produce
5
+ * IDE-native commands and agents during the install process.
6
+ *
7
+ * Main entry points:
8
+ * - generateForIde(content, targetIde, options) - Base transformation
9
+ * - generateCommandForIde(content, commandName, targetIde, options) - Command-specific
10
+ * - generateAgentForIde(content, agentName, targetIde, options) - Agent-specific
11
+ */
12
+
13
+ const {
14
+ transformForIde,
15
+ transformToolReferences,
16
+ replaceReferences,
17
+ stripFrontmatter,
18
+ getFrontmatter,
19
+ convertFrontmatter,
20
+ } = require('./content-transformer');
21
+
22
+ // Lazy-load profile loader - only if profiles are needed
23
+ let profileLoader = null;
24
+
25
+ function getProfileLoader() {
26
+ if (!profileLoader) {
27
+ try {
28
+ profileLoader = require('../../../src/core/profiles/loader');
29
+ } catch (e) {
30
+ // Profiles not available - return null to indicate fallback mode
31
+ return null;
32
+ }
33
+ }
34
+ return profileLoader;
35
+ }
36
+
37
+ /**
38
+ * Get the IDE prefix style for commands
39
+ * Claude Code uses "/agileflow:", others use "/" or "$" as prefix
40
+ * @param {string} targetIde - Target IDE: 'claude-code', 'cursor', 'windsurf', 'codex'
41
+ * @returns {string} Command prefix pattern
42
+ * @private
43
+ */
44
+ function getCommandPrefix(targetIde) {
45
+ const prefixes = {
46
+ 'claude-code': '/agileflow:',
47
+ cursor: '/',
48
+ windsurf: '/',
49
+ codex: '$agileflow-',
50
+ };
51
+ return prefixes[targetIde] || '/agileflow:';
52
+ }
53
+
54
+ /**
55
+ * Convert /agileflow:style:commands to IDE-specific format
56
+ * - Claude Code: /agileflow:foo:bar → /agileflow:foo:bar (unchanged)
57
+ * - Cursor: /agileflow:foo:bar → /foo-bar
58
+ * - Windsurf: /agileflow:foo:bar → /agileflow-foo-bar
59
+ * - Codex: /agileflow:foo:bar → $agileflow-foo-bar
60
+ *
61
+ * @param {string} content - Content with command references
62
+ * @param {string} targetIde - Target IDE
63
+ * @returns {string} Content with converted command prefixes
64
+ * @private
65
+ */
66
+ function convertCommandPrefixes(content, targetIde) {
67
+ if (targetIde === 'claude-code') {
68
+ return content; // No conversion needed
69
+ }
70
+
71
+ let result = content;
72
+
73
+ if (targetIde === 'cursor') {
74
+ // /agileflow:foo:bar → /foo-bar
75
+ result = result.replace(/\/agileflow:([a-zA-Z0-9:_-]+)/g, (_match, rest) => {
76
+ return '/' + rest.replace(/:/g, '-');
77
+ });
78
+ } else if (targetIde === 'windsurf') {
79
+ // /agileflow:foo:bar → /agileflow-foo-bar
80
+ result = result.replace(/\/agileflow:([a-zA-Z0-9:_-]+)/g, (_match, rest) => {
81
+ return '/agileflow-' + rest.replace(/:/g, '-');
82
+ });
83
+ } else if (targetIde === 'codex') {
84
+ // /agileflow:foo:bar → $agileflow-foo-bar
85
+ result = result.replace(/\/agileflow:([a-zA-Z0-9:_-]+)/g, (_match, rest) => {
86
+ return '$agileflow-' + rest.replace(/:/g, '-');
87
+ });
88
+ }
89
+
90
+ return result;
91
+ }
92
+
93
+ /**
94
+ * Main entry point for IDE-specific content generation
95
+ * Takes canonical Claude Code markdown content and transforms it for a target IDE
96
+ *
97
+ * @param {string} content - Source content (Claude Code format)
98
+ * @param {string} targetIde - Target IDE: 'claude-code', 'cursor', 'windsurf', 'codex'
99
+ * @param {Object} [options] - Generation options
100
+ * @param {string} [options.docsFolder] - Custom docs folder name
101
+ * @param {boolean} [options.transformTools] - Apply tool reference transformations (default: true)
102
+ * @param {boolean} [options.transformPrefixes] - Convert command prefixes (default: true)
103
+ * @param {Object} [options.additionalReplacements] - Extra IDE-specific replacements
104
+ * @returns {string} Transformed content for target IDE
105
+ *
106
+ * @example
107
+ * const content = await fs.readFile('command.md', 'utf8');
108
+ * const cursorVersion = generateForIde(content, 'cursor', { transformTools: true });
109
+ */
110
+ function generateForIde(content, targetIde, options = {}) {
111
+ if (!content || typeof content !== 'string') {
112
+ return content || '';
113
+ }
114
+
115
+ // Claude Code is canonical format - return as-is
116
+ if (targetIde === 'claude-code') {
117
+ return content;
118
+ }
119
+
120
+ const {
121
+ docsFolder,
122
+ transformTools = true,
123
+ transformPrefixes = true,
124
+ additionalReplacements = {},
125
+ } = options;
126
+
127
+ // Step 1: Apply IDE-specific replacements and docs folder updates
128
+ let result = transformForIde(content, targetIde, {
129
+ docsFolder,
130
+ additionalReplacements,
131
+ transformTools: false, // Will apply separately
132
+ });
133
+
134
+ // Step 2: Apply tool reference transformations if requested
135
+ if (transformTools) {
136
+ result = transformToolReferences(result, targetIde);
137
+ }
138
+
139
+ // Step 3: Convert command prefixes if requested
140
+ if (transformPrefixes) {
141
+ result = convertCommandPrefixes(result, targetIde);
142
+ }
143
+
144
+ return result;
145
+ }
146
+
147
+ /**
148
+ * Generate IDE-specific command content with IDE-native wrapping
149
+ *
150
+ * For Codex, adds {{input}} placeholder at end for context injection.
151
+ * For other IDEs, uses base generateForIde() transformation.
152
+ *
153
+ * @param {string} content - Source command content (Claude Code format)
154
+ * @param {string} commandName - Command name for display
155
+ * @param {string} targetIde - Target IDE
156
+ * @param {Object} [options] - Generation options (same as generateForIde)
157
+ * @returns {string} IDE-native command content
158
+ *
159
+ * @example
160
+ * const cmd = await fs.readFile('commands/deploy.md', 'utf8');
161
+ * const codexPrompt = generateCommandForIde(cmd, 'deploy', 'codex');
162
+ */
163
+ function generateCommandForIde(content, commandName, targetIde, options = {}) {
164
+ // Handle null/undefined content
165
+ if (content === null || content === undefined || typeof content !== 'string') {
166
+ return '';
167
+ }
168
+
169
+ // Start with base transformation
170
+ let result = generateForIde(content, targetIde, options);
171
+
172
+ // Codex-specific wrapping: Add {{input}} placeholder
173
+ if (targetIde === 'codex' && result !== '') {
174
+ // Strip frontmatter for Codex format
175
+ const bodyContent = stripFrontmatter(result);
176
+ const frontmatter = getFrontmatter(content);
177
+ const description = frontmatter.description || `AgileFlow ${commandName} command`;
178
+
179
+ const header = `# AgileFlow: ${commandName}
180
+
181
+ > ${description}
182
+
183
+ ## Instructions
184
+
185
+ `;
186
+
187
+ const footer = `
188
+
189
+ ## Context
190
+
191
+ {{input}}
192
+ `;
193
+
194
+ result = header + bodyContent + footer;
195
+ }
196
+
197
+ return result;
198
+ }
199
+
200
+ /**
201
+ * Generate IDE-specific agent/skill content
202
+ *
203
+ * Performs IDE-specific transformations including:
204
+ * - For Codex: Converts to SKILL.md format with skill-specific frontmatter
205
+ * - For Windsurf: Converts to agentskills.io format with skill-specific frontmatter
206
+ * - For Cursor: Adds agent-specific frontmatter for spawnable agents
207
+ * - For Claude Code: Returns unchanged (canonical format)
208
+ *
209
+ * @param {string} content - Source agent content (Claude Code format)
210
+ * @param {string} agentName - Agent name (e.g., 'database', 'security')
211
+ * @param {string} targetIde - Target IDE: 'claude-code', 'cursor', 'windsurf', 'codex'
212
+ * @param {Object} [options] - Generation options
213
+ * @param {string} [options.docsFolder] - Custom docs folder name
214
+ * @param {boolean} [options.transformTools] - Apply tool reference transformations (default: true)
215
+ * @returns {string} IDE-native agent/skill content
216
+ *
217
+ * @example
218
+ * const agent = await fs.readFile('agents/security.md', 'utf8');
219
+ * const windsurfSkill = generateAgentForIde(agent, 'security', 'windsurf');
220
+ */
221
+ function generateAgentForIde(content, agentName, targetIde, options = {}) {
222
+ if (!content || typeof content !== 'string') {
223
+ return content || '';
224
+ }
225
+
226
+ // Claude Code is canonical format
227
+ if (targetIde === 'claude-code') {
228
+ return content;
229
+ }
230
+
231
+ const { docsFolder, transformTools = true } = options;
232
+
233
+ // Get base transformations
234
+ const baseContent = generateForIde(content, targetIde, {
235
+ docsFolder,
236
+ transformTools,
237
+ transformPrefixes: false, // Don't convert prefixes in agent bodies
238
+ });
239
+
240
+ // Extract frontmatter and body
241
+ const frontmatter = getFrontmatter(content);
242
+ const description = frontmatter.description || `AgileFlow ${agentName} agent`;
243
+
244
+ // Apply IDE-specific formatting
245
+ if (targetIde === 'codex') {
246
+ return formatAgentForCodex(baseContent, agentName, description);
247
+ } else if (targetIde === 'windsurf') {
248
+ return formatAgentForWindsurf(baseContent, agentName, description);
249
+ } else if (targetIde === 'cursor') {
250
+ return formatAgentForCursor(baseContent, agentName, description);
251
+ }
252
+
253
+ return baseContent;
254
+ }
255
+
256
+ /**
257
+ * Format agent content for Codex SKILL.md format
258
+ * @private
259
+ */
260
+ function formatAgentForCodex(content, agentName, description) {
261
+ const yaml = require('../../../lib/yaml-utils').yaml;
262
+
263
+ const bodyContent = stripFrontmatter(content);
264
+
265
+ // Create SKILL.md with YAML frontmatter
266
+ const skillFrontmatter = yaml
267
+ .dump({
268
+ name: `agileflow-${agentName}`,
269
+ description: description,
270
+ version: '1.0.0',
271
+ })
272
+ .trim();
273
+
274
+ const codexHeader = `# AgileFlow: ${agentName.charAt(0).toUpperCase() + agentName.slice(1)} Agent
275
+
276
+ > Invoke with \`$agileflow-${agentName}\` or via \`/skills\`
277
+
278
+ `;
279
+
280
+ return `---
281
+ ${skillFrontmatter}
282
+ ---
283
+
284
+ ${codexHeader}${bodyContent}`;
285
+ }
286
+
287
+ /**
288
+ * Format agent content for Windsurf SKILL.md (agentskills.io spec)
289
+ * @private
290
+ */
291
+ function formatAgentForWindsurf(content, agentName, description) {
292
+ const yaml = require('../../../lib/yaml-utils').yaml;
293
+
294
+ const bodyContent = stripFrontmatter(content);
295
+
296
+ // Create SKILL.md with YAML frontmatter (agentskills.io spec)
297
+ const skillFrontmatter = yaml
298
+ .dump({
299
+ name: `agileflow-${agentName}`,
300
+ description: description,
301
+ })
302
+ .trim();
303
+
304
+ const windsurfHeader = `# AgileFlow: ${agentName.charAt(0).toUpperCase() + agentName.slice(1)} Skill
305
+
306
+ > Use this skill via \`@agileflow-${agentName}\` or /cascade
307
+
308
+ `;
309
+
310
+ return `---
311
+ ${skillFrontmatter}
312
+ ---
313
+
314
+ ${windsurfHeader}${bodyContent}`;
315
+ }
316
+
317
+ /**
318
+ * Format agent content for Cursor spawnable agent
319
+ * Cursor agents use YAML frontmatter with name, description, and model fields
320
+ * @private
321
+ */
322
+ function formatAgentForCursor(content, agentName, description) {
323
+ const yaml = require('../../../lib/yaml-utils').yaml;
324
+
325
+ // Extract frontmatter from source
326
+ const sourceFrontmatter = getFrontmatter(content);
327
+ const bodyContent = stripFrontmatter(content);
328
+
329
+ // Create agent frontmatter for Cursor
330
+ const agentFrontmatter = yaml
331
+ .dump({
332
+ name: `agileflow-${agentName}`,
333
+ description: description,
334
+ model: sourceFrontmatter.model || 'claude-3-5-sonnet',
335
+ readonly: false,
336
+ })
337
+ .trim();
338
+
339
+ return `---
340
+ ${agentFrontmatter}
341
+ ---
342
+
343
+ ${bodyContent}`;
344
+ }
345
+
346
+ module.exports = {
347
+ generateForIde,
348
+ generateCommandForIde,
349
+ generateAgentForIde,
350
+ getCommandPrefix,
351
+ // Export private functions for testing
352
+ _convertCommandPrefixes: convertCommandPrefixes,
353
+ _getProfileLoader: getProfileLoader,
354
+ _formatAgentForCodex: formatAgentForCodex,
355
+ _formatAgentForWindsurf: formatAgentForWindsurf,
356
+ _formatAgentForCursor: formatAgentForCursor,
357
+ };
@@ -98,13 +98,13 @@ const IDE_REGISTRY = {
98
98
  },
99
99
  codex: {
100
100
  name: 'codex',
101
- displayName: 'OpenAI Codex CLI',
101
+ displayName: 'OpenAI Codex',
102
102
  configDir: '.codex',
103
103
  commandsSubdir: 'skills',
104
104
  agileflowFolder: 'agileflow',
105
105
  targetSubdir: 'skills', // Codex uses skills directory
106
106
  preferred: false,
107
- description: "OpenAI's Codex CLI",
107
+ description: "OpenAI's Codex",
108
108
  handler: 'CodexSetup',
109
109
  labels: {
110
110
  commands: 'prompts',