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.
- package/CHANGELOG.md +5 -0
- package/README.md +57 -85
- package/lib/dashboard-automations.js +130 -0
- package/lib/dashboard-git.js +254 -0
- package/lib/dashboard-inbox.js +64 -0
- package/lib/dashboard-protocol.js +1 -0
- package/lib/dashboard-server.js +114 -924
- package/lib/dashboard-session.js +136 -0
- package/lib/dashboard-status.js +72 -0
- package/lib/dashboard-terminal.js +354 -0
- package/lib/dashboard-websocket.js +88 -0
- package/lib/drivers/codex-driver.ts +4 -4
- package/lib/logger.js +106 -0
- package/package.json +4 -2
- package/scripts/agileflow-configure.js +2 -2
- package/scripts/agileflow-welcome.js +409 -434
- package/scripts/claude-tmux.sh +80 -2
- package/scripts/context-loader.js +4 -9
- package/scripts/lib/command-prereqs.js +280 -0
- package/scripts/lib/configure-detect.js +92 -2
- package/scripts/lib/configure-features.js +295 -1
- package/scripts/lib/context-formatter.js +468 -233
- package/scripts/lib/context-loader.js +27 -15
- package/scripts/lib/damage-control-utils.js +8 -1
- package/scripts/lib/feature-catalog.js +321 -0
- package/scripts/lib/portable-tasks-cli.js +274 -0
- package/scripts/lib/portable-tasks.js +479 -0
- package/scripts/lib/signal-detectors.js +1 -1
- package/scripts/lib/team-events.js +86 -1
- package/scripts/obtain-context.js +28 -4
- package/scripts/smart-detect.js +17 -0
- package/scripts/strip-ai-attribution.js +63 -0
- package/scripts/team-manager.js +7 -2
- package/scripts/welcome-deferred.js +437 -0
- package/src/core/agents/perf-analyzer-assets.md +174 -0
- package/src/core/agents/perf-analyzer-bundle.md +165 -0
- package/src/core/agents/perf-analyzer-caching.md +160 -0
- package/src/core/agents/perf-analyzer-compute.md +165 -0
- package/src/core/agents/perf-analyzer-memory.md +182 -0
- package/src/core/agents/perf-analyzer-network.md +157 -0
- package/src/core/agents/perf-analyzer-queries.md +155 -0
- package/src/core/agents/perf-analyzer-rendering.md +156 -0
- package/src/core/agents/perf-consensus.md +280 -0
- package/src/core/agents/security-analyzer-api.md +199 -0
- package/src/core/agents/security-analyzer-auth.md +160 -0
- package/src/core/agents/security-analyzer-authz.md +168 -0
- package/src/core/agents/security-analyzer-deps.md +147 -0
- package/src/core/agents/security-analyzer-infra.md +176 -0
- package/src/core/agents/security-analyzer-injection.md +148 -0
- package/src/core/agents/security-analyzer-input.md +191 -0
- package/src/core/agents/security-analyzer-secrets.md +175 -0
- package/src/core/agents/security-consensus.md +276 -0
- package/src/core/agents/test-analyzer-assertions.md +181 -0
- package/src/core/agents/test-analyzer-coverage.md +183 -0
- package/src/core/agents/test-analyzer-fragility.md +185 -0
- package/src/core/agents/test-analyzer-integration.md +155 -0
- package/src/core/agents/test-analyzer-maintenance.md +173 -0
- package/src/core/agents/test-analyzer-mocking.md +178 -0
- package/src/core/agents/test-analyzer-patterns.md +189 -0
- package/src/core/agents/test-analyzer-structure.md +177 -0
- package/src/core/agents/test-consensus.md +294 -0
- package/src/core/commands/{legal/audit.md → audit/legal.md} +13 -13
- package/src/core/commands/{logic/audit.md → audit/logic.md} +12 -12
- package/src/core/commands/audit/performance.md +443 -0
- package/src/core/commands/audit/security.md +443 -0
- package/src/core/commands/audit/test.md +442 -0
- package/src/core/commands/babysit.md +505 -463
- package/src/core/commands/configure.md +8 -8
- package/src/core/commands/research/ask.md +42 -9
- package/src/core/commands/research/import.md +14 -8
- package/src/core/commands/research/list.md +17 -16
- package/src/core/commands/research/synthesize.md +8 -8
- package/src/core/commands/research/view.md +28 -4
- package/src/core/commands/whats-new.md +2 -2
- package/src/core/experts/devops/expertise.yaml +13 -2
- package/src/core/experts/documentation/expertise.yaml +26 -4
- package/src/core/profiles/COMPARISON.md +170 -0
- package/src/core/profiles/README.md +178 -0
- package/src/core/profiles/claude-code.yaml +111 -0
- package/src/core/profiles/codex.yaml +103 -0
- package/src/core/profiles/cursor.yaml +134 -0
- package/src/core/profiles/examples.js +250 -0
- package/src/core/profiles/loader.js +235 -0
- package/src/core/profiles/windsurf.yaml +159 -0
- package/src/core/teams/logic-audit.json +6 -0
- package/src/core/teams/perf-audit.json +71 -0
- package/src/core/teams/security-audit.json +71 -0
- package/src/core/teams/test-audit.json +71 -0
- package/src/core/templates/command-prerequisites.yaml +169 -0
- package/src/core/templates/damage-control-patterns.yaml +9 -0
- package/tools/cli/installers/ide/_base-ide.js +33 -3
- package/tools/cli/installers/ide/claude-code.js +2 -69
- package/tools/cli/installers/ide/codex.js +9 -9
- package/tools/cli/installers/ide/cursor.js +165 -4
- package/tools/cli/installers/ide/windsurf.js +237 -6
- package/tools/cli/lib/content-transformer.js +234 -9
- package/tools/cli/lib/docs-setup.js +1 -1
- package/tools/cli/lib/ide-generator.js +357 -0
- package/tools/cli/lib/ide-registry.js +2 -2
- package/scripts/tmux-task-name.sh +0 -105
- 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
|
|
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
|
|
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(
|
|
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
|
|
182
|
+
* Claude Code to OpenAI Codex conversions
|
|
180
183
|
*/
|
|
181
184
|
codex: {
|
|
182
|
-
'Claude Code': 'Codex
|
|
183
|
-
'claude code': 'Codex
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
107
|
+
description: "OpenAI's Codex",
|
|
108
108
|
handler: 'CodexSetup',
|
|
109
109
|
labels: {
|
|
110
110
|
commands: 'prompts',
|